This is an automated email from the ASF dual-hosted git repository. arnold pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/fineract.git
commit 42f5de937d68deeebefc4557098eedbe6d769b61 Author: Oleksii Novikov <[email protected]> AuthorDate: Sat May 31 01:50:12 2025 +0300 FINERACT-2152: Fix interest pause on the 1st day of the 1st period --- .../data/ProgressiveLoanInterestScheduleModel.java | 16 ++++------ .../calc/ProgressiveEMICalculatorTest.java | 37 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java index 38cde0ba2b..c7c4410adc 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/ProgressiveLoanInterestScheduleModel.java @@ -161,13 +161,13 @@ public class ProgressiveLoanInterestScheduleModel { if (repaymentPeriods.isEmpty()) { return 0; } - final RepaymentPeriod firstPeriod = repaymentPeriods.get(0); + final RepaymentPeriod firstPeriod = repaymentPeriods.getFirst(); final RepaymentPeriod lastPeriod = repaymentPeriods.size() > 1 ? getLastRepaymentPeriod() : firstPeriod; return DateUtils.getExactDifferenceInDays(firstPeriod.getFromDate(), lastPeriod.getDueDate()); } public LocalDate getStartDate() { - return !repaymentPeriods.isEmpty() ? repaymentPeriods.get(0).getFromDate() : null; + return !repaymentPeriods.isEmpty() ? repaymentPeriods.getFirst().getFromDate() : null; } public LocalDate getMaturityDate() { @@ -264,7 +264,9 @@ public class ProgressiveLoanInterestScheduleModel { } private void insertInterestPausePeriods(final RepaymentPeriod repaymentPeriod, final LocalDate pauseStart, final LocalDate pauseEnd) { - final LocalDate effectivePauseStart = pauseStart.minusDays(1); + final boolean isPauseStartOnFirstDayOfPeriod = pauseStart.isEqual(repaymentPeriod.getFromDate().plusDays(1)); + final LocalDate effectivePauseStart = isPauseStartOnFirstDayOfPeriod ? pauseStart : pauseStart.minusDays(1); + final LocalDate finalPauseStart = effectivePauseStart.isBefore(repaymentPeriod.getFromDate()) ? repaymentPeriod.getFromDate() : effectivePauseStart; final LocalDate finalPauseEnd = pauseEnd.isAfter(repaymentPeriod.getDueDate()) ? repaymentPeriod.getDueDate() : pauseEnd; @@ -302,7 +304,7 @@ public class ProgressiveLoanInterestScheduleModel { } else { return repaymentPeriod.getInterestPeriods().stream() .filter(ip -> date.isAfter(ip.getFromDate()) && !date.isAfter(ip.getDueDate())).reduce((first, second) -> second) - .orElse(repaymentPeriod.getInterestPeriods().get(0)); + .orElse(repaymentPeriod.getInterestPeriods().getFirst()); } } @@ -357,10 +359,6 @@ public class ProgressiveLoanInterestScheduleModel { return MathUtil.negativeToZero(getTotalDuePrincipal().minus(getTotalPaidPrincipal())); } - public Money getTotalOutstandingInterest() { - return MathUtil.negativeToZero(getTotalDueInterest().minus(getTotalPaidInterest())); - } - public Optional<RepaymentPeriod> findRepaymentPeriod(@NotNull LocalDate transactionDate) { return repaymentPeriods.stream() // .filter(period -> isInPeriod(transactionDate, period.getFromDate(), period.getDueDate(), period.isFirstRepaymentPeriod()))// @@ -381,7 +379,7 @@ public class ProgressiveLoanInterestScheduleModel { @NotNull public RepaymentPeriod getLastRepaymentPeriod() { - return repaymentPeriods.get(repaymentPeriods.size() - 1); + return repaymentPeriods.getLast(); } public boolean isLastRepaymentPeriod(@NotNull RepaymentPeriod repaymentPeriod) { 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 298c57c6b4..207da71ab7 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 @@ -1809,6 +1809,43 @@ class ProgressiveEMICalculatorTest { checkPeriod(interestSchedule, 5, 0, 16.48, 0.005833333333, 0.0955499999946, 0.1, 16.38, 0.0); } + @Test + public void test_interestPauseOnTheFirstDayOfFirstPeriodAmt100_dayInYears360_daysInMonth30_repayEvery1Month() { + final List<LoanScheduleModelRepaymentPeriod> 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); + List<LoanTermVariationsData> loanTermVariations = new ArrayList<>(); + + final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel( + expectedRepaymentPeriods, loanProductRelatedDetail, loanTermVariations, installmentAmountInMultiplesOf, mc); + + final Money disbursedAmount = toMoney(100.0); + emiCalculator.addDisbursement(interestSchedule, LocalDate.of(2024, 1, 1), disbursedAmount); + emiCalculator.applyInterestPause(interestSchedule, LocalDate.of(2024, 1, 2), LocalDate.of(2024, 1, 10)); + checkPeriod(interestSchedule, 0, 0, 17.01, 0.0, 0.0, 0.43, 16.58, 83.42); + checkPeriod(interestSchedule, 0, 1, 17.01, 0.000188172043, 0.0188172043, 0.43, 16.58, 83.42); + checkPeriod(interestSchedule, 0, 2, 17.01, 0.001505376344, 0.0, 0.43, 16.58, 83.42); + checkPeriod(interestSchedule, 1, 0, 17.01, 0.005833333333, 0.486616666638, 0.49, 16.52, 66.90); + checkPeriod(interestSchedule, 2, 0, 17.01, 0.005833333333, 0.390249999978, 0.39, 16.62, 50.28); + checkPeriod(interestSchedule, 3, 0, 17.01, 0.005833333333, 0.293299999983, 0.29, 16.72, 33.56); + checkPeriod(interestSchedule, 4, 0, 17.01, 0.005833333333, 0.195766666655, 0.20, 16.81, 16.75); + checkPeriod(interestSchedule, 5, 0, 16.85, 0.005833333333, 0.0977083333278, 0.10, 16.75, 0.0); + } + @Test public void test_reschedule_disbursedAmt100_dayInYears360_daysInMonth30_repayEvery1Month() { final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = List.of(
