FINERACT-1745: Loan Schedule Final Installment calculation issue (#2690)
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
index e7c3899..d8d2e54 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java
@@ -2313,6 +2313,7 @@
int period = periodNumber;
if (!lastInstallmentDate.isEqual(installment.getDueDate())) {
period--;
+ periodNumber = period;
}
reducePrincipal = fetchEarlyPaidAmount(installment.getPrincipal(currency), principalPortionCalculated, reducePrincipal,
loanApplicationTerms, totalCumulativePrincipal, period, mc);
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DecliningBalanceInterestLoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DecliningBalanceInterestLoanScheduleGenerator.java
index c391125..5a8af04 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DecliningBalanceInterestLoanScheduleGenerator.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DecliningBalanceInterestLoanScheduleGenerator.java
@@ -60,7 +60,7 @@
final double interestCalculationGraceOnRepaymentPeriodFraction, final Money totalCumulativePrincipal,
@SuppressWarnings("unused") final Money totalCumulativeInterest,
@SuppressWarnings("unused") final Money totalInterestDueForLoan, final Money cumulatingInterestPaymentDueToGrace,
- final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms, final int periodNumber, final MathContext mc,
+ final Money outstandingBalance, final LoanApplicationTerms loanApplicationTerms, int periodNumber, final MathContext mc,
final TreeMap<LocalDate, Money> principalVariation, final Map<LocalDate, Money> compoundingMap, final LocalDate periodStartDate,
final LocalDate periodEndDate, final Collection<LoanTermVariationsData> termVariations) {
@@ -157,7 +157,6 @@
// update cumulative fields for principal & interest
final Money interestBroughtFowardDueToGrace = cumulatingInterestDueToGrace;
final Money totalCumulativePrincipalToDate = totalCumulativePrincipal.plus(principalForThisInstallment);
-
// adjust if needed
principalForThisInstallment = loanApplicationTerms.adjustPrincipalIfLastRepaymentPeriod(principalForThisInstallment,
totalCumulativePrincipalToDate, periodNumber);
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java
index e9cd83a..2e39e41 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java
@@ -6091,6 +6091,86 @@
WorkingDaysHelper.updateWorkingDays(this.requestSpec, this.responseSpec);
}
+ @Test
+ public void testLoanScheduleWithInterestRecalculationForLastInstallmentAmountNotMoreThanEMI() {
+ this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
+ WorkingDaysHelper.updateWorkingDaysWeekDays(this.requestSpec, this.responseSpec);
+ DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.US);
+ dateFormat.setTimeZone(Utils.getTimeZoneOfTenant());
+ GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "42", true);
+ GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "43", true);
+ final String loanDisbursementDate = "06 May 2022";
+ String firstRepayment = "27 May 2022";
+ final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
+ ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
+ String principalAmount = "7800.00";
+ String rateOfInterest = "8.9";
+ String numberOfRepayments = "12";
+ final Integer loanProductID = createLoanProductWithInterestRecalculationAndCompoundingDetails(
+ LoanProductTestBuilder.INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY,
+ LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE,
+ LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS,
+ LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_SAME_AS_REPAYMENT_PERIOD,
+ LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, null, "12", numberOfRepayments, principalAmount,
+ rateOfInterest);
+
+ final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, loanDisbursementDate,
+ LoanApplicationTestBuilder.INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY, firstRepayment, numberOfRepayments,
+ principalAmount, rateOfInterest);
+
+ Assertions.assertNotNull(loanID);
+ HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
+ LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
+
+ LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------");
+ loanStatusHashMap = this.loanTransactionHelper.approveLoan(loanDisbursementDate, loanID);
+ LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
+ LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
+
+ LOG.info("-------------------------------DISBURSE LOAN-------------------------------------------");
+ String loanDetails = this.loanTransactionHelper.getLoanDetails(this.requestSpec, this.responseSpec, loanID);
+ loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(loanDisbursementDate, loanID,
+ JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
+ LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
+
+ ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID);
+ Assertions.assertNotNull(loanSchedule);
+
+ this.loanTransactionHelper.makeRepayment("27 May 2022", 1080.0F, loanID);
+ Assertions.assertTrue(
+ this.loanTransactionHelper.checkForLastInstallmentLessThanEMI(this.requestSpec, this.responseSpec, loanID, 1080.0F));
+
+ this.loanTransactionHelper.makeRepayment("27 June 2022", 1080.0F, loanID);
+ Assertions.assertTrue(
+ this.loanTransactionHelper.checkForLastInstallmentLessThanEMI(this.requestSpec, this.responseSpec, loanID, 1080.0F));
+
+ this.loanTransactionHelper.makeRepayment("27 July 2022", 1080.0F, loanID);
+ Assertions.assertTrue(
+ this.loanTransactionHelper.checkForLastInstallmentLessThanEMI(this.requestSpec, this.responseSpec, loanID, 1080.0F));
+
+ this.loanTransactionHelper.makeRepayment("30 August 2022", 1080.0F, loanID);
+ Assertions.assertTrue(
+ this.loanTransactionHelper.checkForLastInstallmentLessThanEMI(this.requestSpec, this.responseSpec, loanID, 1080.0F));
+
+ this.loanTransactionHelper.makeRepayment("01 September 2022", 17.01F, loanID);
+ Assertions.assertTrue(
+ this.loanTransactionHelper.checkForLastInstallmentLessThanEMI(this.requestSpec, this.responseSpec, loanID, 1080.0F));
+
+ this.loanTransactionHelper.makeRepayment("01 September 2022", 34.02F, loanID);
+ Assertions.assertTrue(
+ this.loanTransactionHelper.checkForLastInstallmentLessThanEMI(this.requestSpec, this.responseSpec, loanID, 1080.0F));
+
+ this.loanTransactionHelper.makeRepayment("27 September 2022", 1080.0F, loanID);
+ Assertions.assertTrue(
+ this.loanTransactionHelper.checkForLastInstallmentLessThanEMI(this.requestSpec, this.responseSpec, loanID, 1080.0F));
+
+ this.loanTransactionHelper.makeRepayment("27 October 2022", 1080.0F, loanID);
+ Assertions.assertTrue(
+ this.loanTransactionHelper.checkForLastInstallmentLessThanEMI(this.requestSpec, this.responseSpec, loanID, 1080.0F));
+
+ WorkingDaysHelper.updateWorkingDays(this.requestSpec, this.responseSpec);
+ }
+
private Calendar convertStringDateToCalender(final String stringDate) {
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.US);
Calendar date = Calendar.getInstance();
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index 36fbfdc..fdc8af8 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -943,4 +943,13 @@
return Utils.performServerOutputTemplateLocationGet(requestSpec, responseSpec,
"/fineract-provider/api/v1/imports/getOutputTemplateLocation" + "?" + Utils.TENANT_IDENTIFIER, importDocumentId);
}
+
+ public boolean checkForLastInstallmentLessThanEMI(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
+ final Integer loanID, final Float emiAmount) {
+ ArrayList<HashMap> loanSchedule = this.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID);
+ String principal = String.valueOf(loanSchedule.get(loanSchedule.size() - 1).get("principalOriginalDue"));
+ String interest = String.valueOf(loanSchedule.get(loanSchedule.size() - 1).get("interestOriginalDue"));
+ Float amount = Float.parseFloat(principal) + Float.parseFloat(interest);
+ return amount.compareTo(emiAmount) <= 0;
+ }
}