FINERACT-1968: Progressive Loan schedule handling - interest handling (DECLINING BALANCE interest, Same as repayment period, Equal installments)

This commit is contained in:
Soma Sörös
2025-08-05 13:49:41 +02:00
committed by Adam Saghy
parent 8d829b1063
commit dfab66132b
@@ -104,6 +104,7 @@ class ProgressiveEMICalculatorTest {
public void setupTestDefaults() {
Mockito.when(loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate()).thenReturn(false);
Mockito.when(loanProductRelatedDetail.getDaysInYearCustomStrategy()).thenReturn(null);
Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency);
Mockito.when(loanProductRelatedDetail.getInterestMethod()).thenReturn(InterestMethod.DECLINING_BALANCE);
Mockito.when(loanProductRelatedDetail.getInterestCalculationPeriodMethod()).thenReturn(InterestCalculationPeriodMethod.DAILY);
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(true);
@@ -3767,6 +3768,191 @@ class ProgressiveEMICalculatorTest {
}
}
@Nested
public class DecliningBalanceSameAsRepaymentPeriod {
@BeforeEach
void beforeEach() {
Mockito.when(loanProductRelatedDetail.getInterestMethod()).thenReturn(InterestMethod.DECLINING_BALANCE);
Mockito.when(loanProductRelatedDetail.getInterestCalculationPeriodMethod())
.thenReturn(InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD);
Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.ACTUAL.getValue());
Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue());
}
@Test
void test_3_of_1_month_period_multi_disbursement__exact_days() {
final BigDecimal interestRate = BigDecimal.valueOf(12.0);
final LocalDate disbursementDate = LocalDate.of(2024, 6, 1);
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(true);
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
final Integer installmentAmountInMultiplesOf = null;
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(500.0));
checkPeriod(interestSchedule, 0, 170.01, 5.00, 165.01, 334.99, false); //
checkPeriod(interestSchedule, 1, 170.01, 3.35, 166.66, 168.33, false); //
checkPeriod(interestSchedule, 2, 170.01, 1.68, 168.33, 0.00, false); //
emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(500.0));
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 669.98, false); //
checkPeriod(interestSchedule, 1, 340.02, 6.70, 333.32, 336.66, false); //
checkPeriod(interestSchedule, 2, 340.03, 3.37, 336.66, 0.00, false); //
final LocalDate disbursementDate2 = LocalDate.of(2024, 7, 15);
emiCalculator.addDisbursement(interestSchedule, disbursementDate2, toMoney(250.0));
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 669.98, false); //
checkPeriod(interestSchedule, 1, 466.33, 8.07, 458.26, 461.72, false); //
checkPeriod(interestSchedule, 2, 466.34, 4.62, 461.72, 0.00, false); //
}
@Test
void test_3_of_1_month_period_multi_disbursement__exact_days__on_period_due_date() {
final BigDecimal interestRate = BigDecimal.valueOf(12.0);
final LocalDate disbursementDate = LocalDate.of(2024, 6, 1);
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(true);
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
final Integer installmentAmountInMultiplesOf = null;
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(500.0));
checkPeriod(interestSchedule, 0, 170.01, 5.00, 165.01, 334.99, false); //
checkPeriod(interestSchedule, 1, 170.01, 3.35, 166.66, 168.33, false); //
checkPeriod(interestSchedule, 2, 170.01, 1.68, 168.33, 0.00, false); //
emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(500.0));
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 669.98, false); //
checkPeriod(interestSchedule, 1, 340.02, 6.70, 333.32, 336.66, false); //
checkPeriod(interestSchedule, 2, 340.03, 3.37, 336.66, 0.00, false); //
final LocalDate disbursementDate2 = LocalDate.of(2024, 7, 1);
emiCalculator.addDisbursement(interestSchedule, disbursementDate2, toMoney(250.0));
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 919.98, false); //
checkPeriod(interestSchedule, 1, 466.90, 9.20, 457.70, 462.28, false); //
checkPeriod(interestSchedule, 2, 466.90, 4.62, 462.28, 0.00, false); //
}
@Test
void test_3_of_1_month_period_multi_disbursement__NOT_exact_days__different_days() {
final BigDecimal interestRate = BigDecimal.valueOf(12.0);
final LocalDate disbursementDate = LocalDate.of(2024, 6, 1);
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(false);
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
final Integer installmentAmountInMultiplesOf = null;
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(500.0));
checkPeriod(interestSchedule, 0, 170.01, 5.00, 165.01, 334.99, false); //
checkPeriod(interestSchedule, 1, 170.01, 3.35, 166.66, 168.33, false); //
checkPeriod(interestSchedule, 2, 170.01, 1.68, 168.33, 0.00, false); //
emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(500.0));
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 669.98, false); //
checkPeriod(interestSchedule, 1, 340.02, 6.70, 333.32, 336.66, false); //
checkPeriod(interestSchedule, 2, 340.03, 3.37, 336.66, 0.00, false); //
final LocalDate disbursementDate2 = LocalDate.of(2024, 7, 1);
final LocalDate disbursementDate3 = LocalDate.of(2024, 7, 15);
final LocalDate disbursementDate4 = LocalDate.of(2024, 7, 23);
emiCalculator.addDisbursement(interestSchedule, disbursementDate2, toMoney(50.0));
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 719.98, false); //
checkPeriod(interestSchedule, 1, 365.40, 7.20, 358.20, 361.78, false); //
checkPeriod(interestSchedule, 2, 365.40, 3.62, 361.78, 0.00, false); //
emiCalculator.addDisbursement(interestSchedule, disbursementDate3, toMoney(50.0));
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 769.98, false); //
checkPeriod(interestSchedule, 1, 390.77, 7.70, 383.07, 386.91, false); //
checkPeriod(interestSchedule, 2, 390.78, 3.87, 386.91, 0.00, false); //
emiCalculator.addDisbursement(interestSchedule, disbursementDate3, toMoney(50.0));
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 819.98, false); //
checkPeriod(interestSchedule, 1, 416.15, 8.20, 407.95, 412.03, false); //
checkPeriod(interestSchedule, 2, 416.15, 4.12, 412.03, 0.00, false); //
emiCalculator.addDisbursement(interestSchedule, disbursementDate4, toMoney(50.0));
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 869.98, false); //
checkPeriod(interestSchedule, 1, 441.53, 8.70, 432.83, 437.15, false); //
checkPeriod(interestSchedule, 2, 441.52, 4.37, 437.15, 0.00, false); //
emiCalculator.addDisbursement(interestSchedule, disbursementDate4, toMoney(50.0));
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 919.98, false); //
checkPeriod(interestSchedule, 1, 466.90, 9.20, 457.70, 462.28, false); //
checkPeriod(interestSchedule, 2, 466.90, 4.62, 462.28, 0.00, false); //
}
@Test
void test_3_of_1_month_period_multi_disbursement__NOT_exact_days() {
final BigDecimal interestRate = BigDecimal.valueOf(12.0);
final LocalDate disbursementDate = LocalDate.of(2024, 6, 1);
Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate);
Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS);
Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(3);
Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1);
Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(false);
final List<LoanScheduleModelRepaymentPeriod> expectedRepaymentPeriods = generateExpectedRepaymentPeriods(disbursementDate);
final Integer installmentAmountInMultiplesOf = null;
final ProgressiveLoanInterestScheduleModel interestSchedule = emiCalculator.generatePeriodInterestScheduleModel(
expectedRepaymentPeriods, loanProductRelatedDetail, List.of(), installmentAmountInMultiplesOf, mc);
emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(500.0));
checkPeriod(interestSchedule, 0, 170.01, 5.00, 165.01, 334.99, false); //
checkPeriod(interestSchedule, 1, 170.01, 3.35, 166.66, 168.33, false); //
checkPeriod(interestSchedule, 2, 170.01, 1.68, 168.33, 0.00, false); //
emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(500.0));
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 669.98, false); //
checkPeriod(interestSchedule, 1, 340.02, 6.70, 333.32, 336.66, false); //
checkPeriod(interestSchedule, 2, 340.03, 3.37, 336.66, 0.00, false); //
final LocalDate disbursementDate2 = LocalDate.of(2024, 7, 15);
emiCalculator.addDisbursement(interestSchedule, disbursementDate2, toMoney(130.0));
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 799.98, false); //
checkPeriod(interestSchedule, 1, 406.00, 8.00, 398.00, 401.98, false); //
checkPeriod(interestSchedule, 2, 406.00, 4.02, 401.98, 0.00, false); //
emiCalculator.addDisbursement(interestSchedule, disbursementDate2, toMoney(120.0));
checkPeriod(interestSchedule, 0, 340.02, 10.00, 330.02, 919.98, false); //
checkPeriod(interestSchedule, 1, 466.90, 9.20, 457.70, 462.28, false); //
checkPeriod(interestSchedule, 2, 466.90, 4.62, 462.28, 0.00, false); //
}
}
// utilities
private List<LoanScheduleModelRepaymentPeriod> generateExpectedRepaymentPeriods(LocalDate disbursementDate) {
return switch (loanProductRelatedDetail.getRepaymentPeriodFrequencyType()) {