From a919254cde4b7f5e1d3c9f6d7014b0a9b6d03cce Mon Sep 17 00:00:00 2001 From: Janos Meszaros Date: Thu, 9 Jan 2025 16:06:21 +0100 Subject: [PATCH] FINERACT-1981: Enhance EMI Adjustment --- .../calc/ProgressiveEMICalculator.java | 4 +- .../calc/ProgressiveEMICalculatorTest.java | 49 +++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java index 4e0383c31af..5e270f56aec 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java @@ -318,7 +318,7 @@ private void checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(final Progressiv final List relatedRepaymentPeriods) { MathContext mc = scheduleModel.mc(); ProgressiveLoanInterestScheduleModel newScheduleModel = null; - int adjustCounter = 0; + int adjustCounter = 1; EmiAdjustment emiAdjustment; do { @@ -361,7 +361,7 @@ private void checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(final Progressiv }); calculateOutstandingBalance(scheduleModel); adjustCounter++; - } while (emiAdjustment.hasUncountablePeriods() && adjustCounter < 3); + } while (adjustCounter <= 3); } /** diff --git a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java index c6c5589be55..c952e455b06 100644 --- a/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java +++ b/fineract-progressive-loan/src/test/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculatorTest.java @@ -999,6 +999,50 @@ public void test_disbursedAmt100_dayInYearsActual_daysInMonthActual_repayEvery1M checkPeriod(interestSchedule, 5, 0, 17.13, 0.008031371585, 0.14, 16.99, 0.0); } + @Test + public void test_multidisbursement_total_repay1st_dayInYears360_daysInMonth30_repayEvery1Month() { + + final List expectedRepaymentPeriods = List.of( + repayment(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 1)), + repayment(2, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 3, 1)), + repayment(3, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 4, 1)), + repayment(4, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 5, 1)), + repayment(5, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 6, 1)), + repayment(6, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 7, 1))); + + final BigDecimal interestRate = BigDecimal.valueOf(7.0); + final Integer installmentAmountInMultiplesOf = null; + + Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate); + Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue()); + Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); + Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); + Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); + + final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( + expectedRepaymentPeriods, loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); + + emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), toMoney(300.0)); + Assertions.assertEquals(6.15, toDouble(interestSchedule.getTotalDueInterest())); + + // pay back the whole loan on first day + emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 2, 1), LocalDate.of(2024, 1, 1), toMoney(51.03)); + emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 3, 1), LocalDate.of(2024, 1, 1), toMoney(51.03)); + emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 4, 1), LocalDate.of(2024, 1, 1), toMoney(51.03)); + emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 5, 1), LocalDate.of(2024, 1, 1), toMoney(51.03)); + emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 6, 1), LocalDate.of(2024, 1, 1), toMoney(51.03)); + emiCalculator.payPrincipal(interestSchedule, LocalDate.of(2024, 7, 1), LocalDate.of(2024, 1, 1), toMoney(44.85)); + + Assertions.assertEquals(0.0, toDouble(interestSchedule.getTotalDueInterest())); + + emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), toMoney(200.0)); + Assertions.assertEquals(4.20, toDouble(interestSchedule.getTotalDueInterest())); + + checkEmi(interestSchedule, 4, 84.03); + checkEmi(interestSchedule, 5, 84.05); + } + @Test public void test_disbursedAmt1000_NoInterest_repayEvery1Month() { @@ -1291,6 +1335,11 @@ private static void checkDailyInterest(final ProgressiveLoanInterestScheduleMode Assertions.assertEquals(interest, toDouble(currentInterest)); } + private static void checkEmi(final ProgressiveLoanInterestScheduleModel interestScheduleModel, final int repaymentIdx, + final double emiValue) { + Assertions.assertEquals(emiValue, toDouble(interestScheduleModel.repaymentPeriods().get(repaymentIdx).getEmi())); + } + private static void checkPeriod(final ProgressiveLoanInterestScheduleModel interestScheduleModel, final int repaymentIdx, final int interestIdx, final double emiValue, final double rateFactor, final double interestDue, final double principalDue, final double remaingBalance) {