FINERACT-1981: Enhance EMI Adjustment

This commit is contained in:
Janos Meszaros
2025-01-15 13:30:39 +01:00
committed by Arnold Gálovics
parent 6994bc687b
commit d704852228
3 changed files with 55 additions and 6 deletions
@@ -4115,10 +4115,10 @@ Feature: EMI calculation and repayment schedule checks for interest bearing loan
| Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| | | 01 January 2024 | | 250.0 | | | 0.0 | | 0.0 | 0.0 | | | | | | | 01 January 2024 | | 250.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| | | 04 January 2024 | | 750.0 | | | 0.0 | | 0.0 | 0.0 | | | | | | | 04 January 2024 | | 750.0 | | | 0.0 | | 0.0 | 0.0 | | | |
| 1 | 31 | 01 February 2024 | 22 January 2024 | 750.09 | 249.91 | 5.07 | 0.0 | 0.0 | 254.98 | 254.98 | 254.98 | 0.0 | 0.0 | | 1 | 31 | 01 February 2024 | 22 January 2024 | 750.08 | 249.92 | 5.07 | 0.0 | 0.0 | 254.99 | 254.99 | 254.99 | 0.0 | 0.0 |
| 2 | 29 | 01 March 2024 | 22 January 2024 | 495.11 | 254.98 | 0.0 | 0.0 | 0.0 | 254.98 | 254.98 | 254.98 | 0.0 | 0.0 | | 2 | 29 | 01 March 2024 | 22 January 2024 | 495.09 | 254.99 | 0.0 | 0.0 | 0.0 | 254.99 | 254.99 | 254.99 | 0.0 | 0.0 |
| 3 | 31 | 01 April 2024 | 22 January 2024 | 240.13 | 254.98 | 0.0 | 0.0 | 0.0 | 254.98 | 254.98 | 254.98 | 0.0 | 0.0 | | 3 | 31 | 01 April 2024 | 22 January 2024 | 240.1 | 254.99 | 0.0 | 0.0 | 0.0 | 254.99 | 254.99 | 254.99 | 0.0 | 0.0 |
| 4 | 30 | 01 May 2024 | 22 January 2024 | 0.0 | 240.13 | 0.0 | 0.0 | 0.0 | 240.13 | 240.13 | 240.13 | 0.0 | 0.0 | | 4 | 30 | 01 May 2024 | 22 January 2024 | 0.0 | 240.1 | 0.0 | 0.0 | 0.0 | 240.1 | 240.1 | 240.1 | 0.0 | 0.0 |
Then Loan Repayment schedule has the following data in Total row: Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 1000.0 | 5.07 | 0.0 | 0.0 | 1005.07 | 1005.07 | 1005.07 | 0.0 | 0.0 | | 1000.0 | 5.07 | 0.0 | 0.0 | 1005.07 | 1005.07 | 1005.07 | 0.0 | 0.0 |
@@ -324,7 +324,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
final List<RepaymentPeriod> relatedRepaymentPeriods) { final List<RepaymentPeriod> relatedRepaymentPeriods) {
MathContext mc = scheduleModel.mc(); MathContext mc = scheduleModel.mc();
ProgressiveLoanInterestScheduleModel newScheduleModel = null; ProgressiveLoanInterestScheduleModel newScheduleModel = null;
int adjustCounter = 0; int adjustCounter = 1;
EmiAdjustment emiAdjustment; EmiAdjustment emiAdjustment;
do { do {
@@ -367,7 +367,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
}); });
calculateOutstandingBalance(scheduleModel); calculateOutstandingBalance(scheduleModel);
adjustCounter++; adjustCounter++;
} while (emiAdjustment.hasUncountablePeriods() && adjustCounter < 3); } while (adjustCounter <= 3);
} }
/** /**
@@ -999,6 +999,50 @@ class ProgressiveEMICalculatorTest {
checkPeriod(interestSchedule, 5, 0, 17.13, 0.008031371585, 0.14, 16.99, 0.0); 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<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);
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 @Test
public void test_disbursedAmt1000_NoInterest_repayEvery1Month() { public void test_disbursedAmt1000_NoInterest_repayEvery1Month() {
@@ -1349,6 +1393,11 @@ class ProgressiveEMICalculatorTest {
Assertions.assertEquals(interest, toDouble(currentInterest)); 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, 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 int interestIdx, final double emiValue, final double rateFactor, final double interestDue, final double principalDue,
final double remaingBalance) { final double remaingBalance) {