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 e7c389985f2..d8d2e54d20e 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 @@ private LoanScheduleDTO rescheduleNextInstallments(final MathContext mc, final L 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 c39112577ed..5a8af048349 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 @@ public PrincipalInterest calculatePrincipalInterestComponentsForPeriod(final Pay 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 principalVariation, final Map compoundingMap, final LocalDate periodStartDate, final LocalDate periodEndDate, final Collection termVariations) { @@ -157,7 +157,6 @@ public PrincipalInterest calculatePrincipalInterestComponentsForPeriod(final Pay // 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 e9cd83abd61..2e39e417218 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 @@ public void testLoanScheduleWithInterestRecalculationForLateRepaymentOfLateRepay 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 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 36fbfdca2f0..fdc8af86620 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 @@ public String getOutputTemplateLocation(final String importDocumentId) { 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 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; + } }