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;
+    }
 }