diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index bf7e2d77c..f57235390 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -3765,6 +3765,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { this.loanRepaymentScheduleDetail.setInterestPeriodFrequencyType(this.loanProduct.getInterestPeriodFrequencyType()); } + public void updateInterestRateFrequencyType(PeriodFrequencyType periodFrequencyType) { + this.loanRepaymentScheduleDetail.setInterestPeriodFrequencyType(periodFrequencyType); + } + public void addLoanTransaction(final LoanTransaction loanTransaction) { this.loanTransactions.add(loanTransaction); } diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java index 5b1b8e9bb..3837c3fc7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java @@ -1253,6 +1253,8 @@ final class LoansApiResourceSwagger { public Integer fixedLength; @Schema(example = "2") public BigDecimal interestRatePerPeriod; + @Schema(example = "3") + public Integer interestRateFrequencyType; @Schema(example = "1") public Integer amortizationType; @Schema(example = "5.5") @@ -1414,6 +1416,8 @@ final class LoansApiResourceSwagger { public Integer fixedLength; @Schema(example = "2") public BigDecimal interestRatePerPeriod; + @Schema(example = "3") + public Integer interestRateFrequencyType; @Schema(example = "0") public Integer interestType; @Schema(example = "0") diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java index cafeb9f61..bf6a554f0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java @@ -225,7 +225,12 @@ public class LoanScheduleAssembler { } final BigDecimal interestRatePerPeriod = this.fromApiJsonHelper.extractBigDecimalWithLocaleNamed("interestRatePerPeriod", element); - final PeriodFrequencyType interestRatePeriodFrequencyType = loanProduct.getInterestPeriodFrequencyType(); + PeriodFrequencyType interestRatePeriodFrequencyType = loanProduct.getInterestPeriodFrequencyType(); + if (this.fromApiJsonHelper.parameterExists(LoanApiConstants.interestRateFrequencyTypeParameterName, element)) { + final Integer interestRateFrequencyType = this.fromApiJsonHelper + .extractIntegerWithLocaleNamed(LoanApiConstants.interestRateFrequencyTypeParameterName, element); + interestRatePeriodFrequencyType = PeriodFrequencyType.fromInt(interestRateFrequencyType); + } BigDecimal annualNominalInterestRate = BigDecimal.ZERO; if (interestRatePerPeriod != null) { @@ -647,7 +652,13 @@ public class LoanScheduleAssembler { public LoanProductRelatedDetail assembleLoanProductRelatedDetail(final JsonElement element, final LoanProduct loanProduct) { final LoanApplicationTerms loanApplicationTerms = assembleLoanApplicationTermsFrom(element, loanProduct); - return loanApplicationTerms.toLoanProductRelatedDetail(); + LoanProductRelatedDetail loanProductRelatedDetail = loanApplicationTerms.toLoanProductRelatedDetail(); + final String interestRateFrequencyTypeParamName = "interestRateFrequencyType"; + if (this.fromApiJsonHelper.parameterExists(interestRateFrequencyTypeParamName, element)) { + final Integer newValue = this.fromApiJsonHelper.extractIntegerSansLocaleNamed(interestRateFrequencyTypeParamName, element); + loanProductRelatedDetail.setInterestPeriodFrequencyType(PeriodFrequencyType.fromInt(newValue)); + } + return loanProductRelatedDetail; } public LoanScheduleModel assembleLoanScheduleFrom(final JsonElement element) { diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java index c01171f47..286b69680 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java @@ -18,6 +18,7 @@ */ package org.apache.fineract.integrationtests; +import static org.apache.fineract.integrationtests.BaseLoanIntegrationTest.InterestRateFrequencyType.WHOLE_TERM; import static org.apache.fineract.integrationtests.BaseLoanIntegrationTest.InterestRateFrequencyType.YEARS; import static org.apache.fineract.integrationtests.BaseLoanIntegrationTest.RepaymentFrequencyType.DAYS; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -45,6 +46,7 @@ import org.apache.fineract.client.models.BusinessDateRequest; import org.apache.fineract.client.models.CreditAllocationData; import org.apache.fineract.client.models.CreditAllocationOrder; import org.apache.fineract.client.models.GetLoanProductsProductIdResponse; +import org.apache.fineract.client.models.GetLoansLoanIdInterestRateFrequencyType; import org.apache.fineract.client.models.GetLoansLoanIdLoanChargeData; import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod; import org.apache.fineract.client.models.GetLoansLoanIdResponse; @@ -5473,6 +5475,49 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan }); } + // UC150: Validate interestRateFrequencyType to set as Whole Term at the Loan account level + // 1. Create a Loan product with Adv. Pment. Alloc. and with Declining Balance and Interest rate frquency type in + // Years + // 2. Submit a Loan account to valedate interestRateFrequencyType attribute + @Test + public void uc150() { + String operationDate = "22 April 2024"; + runAt(operationDate, () -> { + Long clientId = client.getClientId(); + PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation() + .interestRateFrequencyType(YEARS).numberOfRepayments(4)// + .maxInterestRatePerPeriod((double) 12)// + .repaymentEvery(1)// + .repaymentFrequencyType(RepaymentFrequencyType.MONTHS.longValue())// + .allowPartialPeriodInterestCalcualtion(false)// + .multiDisburseLoan(false)// + .disallowExpectedDisbursements(null)// + .allowApprovedDisbursedAmountsOverApplied(null)// + .overAppliedCalculationType(null)// + .overAppliedNumber(null)// + .installmentAmountInMultiplesOf(null)// + .loanScheduleType(LoanScheduleType.PROGRESSIVE.toString()) // + ;// + PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product); + PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), operationDate, 400.0, 4); + final BigDecimal interestRatePerPeriod = BigDecimal.valueOf(4.0); + applicationRequest = applicationRequest.interestCalculationPeriodType(DAYS).interestRatePerPeriod(interestRatePerPeriod) + .interestRateFrequencyType(WHOLE_TERM).loanTermFrequencyType(RepaymentFrequencyType.MONTHS).repaymentEvery(1) + .repaymentFrequencyType(RepaymentFrequencyType.MONTHS).loanTermFrequency(4) + .transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY); + + PostLoansResponse loanResponse = loanTransactionHelper.applyLoan(applicationRequest); + + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId()); + GetLoansLoanIdInterestRateFrequencyType interestRateFrequencyType = loanDetails.getInterestRateFrequencyType(); + assertNotNull(interestRateFrequencyType); + assertEquals(interestRateFrequencyType.getId(), WHOLE_TERM); + + // Annual Interest rate validation, in this case It must be 12 % + assertEquals(BigDecimal.valueOf(12), loanDetails.getAnnualInterestRate().stripTrailingZeros()); + }); + } + private Long applyAndApproveLoanProgressiveAdvancedPaymentAllocationStrategyMonthlyRepayments(Long clientId, Long loanProductId, Integer numberOfRepayments, String loanDisbursementDate, double amount) { LOG.info("------------------------------APPLY AND APPROVE LOAN ---------------------------------------");