This is an automated email from the ASF dual-hosted git repository. adamsaghy pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/fineract.git
commit bbe3ead849a16e0bda5c2e3f57e74ea2140fc9dc Author: Soma Sörös <[email protected]> AuthorDate: Mon Jul 28 14:12:54 2025 +0200 FINERACT-1968: Progressive Loan schedule handling - interest handling (FLAT interest, Daily, Equal installments) --- .../calc/ProgressiveEMICalculatorTest.java | 174 ++++++++++++++++++++- 1 file changed, 172 insertions(+), 2 deletions(-) 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 8ee3018bb8..ae11225e3e 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 @@ -3235,7 +3235,178 @@ class ProgressiveEMICalculatorTest { } @Nested - public class InterestTypeFlat { + public class InterestTypeFlatAndCalculationPeriodDaily { + + @BeforeEach + public void setupTestDefaults() { + Mockito.when(loanProductRelatedDetail.isInterestRecognitionOnDisbursementDate()).thenReturn(false); + Mockito.when(loanProductRelatedDetail.getDaysInYearCustomStrategy()).thenReturn(null); + Mockito.when(loanProductRelatedDetail.getInterestMethod()).thenReturn(InterestMethod.FLAT); + Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); + Mockito.when(loanProductRelatedDetail.getInterestCalculationPeriodMethod()).thenReturn(InterestCalculationPeriodMethod.DAILY); + Mockito.when(loanProductRelatedDetail.isAllowPartialPeriodInterestCalculation()).thenReturn(true); + + } + + @Test + public void testFlatDaily_1_Month_Actual_Actual() { + + final BigDecimal interestRate = BigDecimal.valueOf(12.0); + final LocalDate disbursementDate = LocalDate.of(2024, 1, 1); + + Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate); + Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); + Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(4); + Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); + + Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.ACTUAL.getValue()); + Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue()); + + 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(700.0)); + checkPeriod(interestSchedule, 0, 181.94, 7.11, 174.83, 525.17, false); // + checkPeriod(interestSchedule, 1, 181.94, 6.66, 175.28, 349.89, false); // + checkPeriod(interestSchedule, 2, 181.94, 7.11, 174.83, 175.06, false); // + checkPeriod(interestSchedule, 3, 181.95, 6.89, 175.06, 0.00, false); // + + emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(300.0)); + checkPeriod(interestSchedule, 0, 259.92, 10.16, 249.76, 750.24, false); // + checkPeriod(interestSchedule, 1, 259.92, 9.51, 250.41, 499.83, false); // + checkPeriod(interestSchedule, 2, 259.92, 10.16, 249.76, 250.07, false); // + checkPeriod(interestSchedule, 3, 259.91, 9.84, 250.07, 0.00, false); // + + emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(1000.0)); + checkPeriod(interestSchedule, 0, 519.84, 20.33, 499.51, 1500.49, false); // + checkPeriod(interestSchedule, 1, 519.84, 19.02, 500.82, 999.67, false); // + checkPeriod(interestSchedule, 2, 519.84, 20.33, 499.51, 500.16, false); // + checkPeriod(interestSchedule, 3, 519.83, 19.67, 500.16, 0.00, false); // + } + + @Test + public void testFlatDaily_1_Month_360_30() { + + final BigDecimal interestRate = BigDecimal.valueOf(12.0); + final LocalDate disbursementDate = LocalDate.of(2024, 1, 1); + + Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate); + Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.MONTHS); + Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(4); + Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); + + Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.DAYS_360.getValue()); + Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.DAYS_30.getValue()); + + 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(750.0)); + checkPeriod(interestSchedule, 0, 195.00, 7.50, 187.50, 562.50, false); // + checkPeriod(interestSchedule, 1, 195.00, 7.50, 187.50, 375.00, false); // + checkPeriod(interestSchedule, 2, 195.00, 7.50, 187.50, 187.50, false); // + checkPeriod(interestSchedule, 3, 195.00, 7.50, 187.50, 0.00, false); // + + emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(250.0)); + checkPeriod(interestSchedule, 0, 260.00, 10.00, 250.00, 750.00, false); // + checkPeriod(interestSchedule, 1, 260.00, 10.00, 250.00, 500.00, false); // + checkPeriod(interestSchedule, 2, 260.00, 10.00, 250.00, 250.00, false); // + checkPeriod(interestSchedule, 3, 260.00, 10.00, 250.00, 0.00, false); // + + emiCalculator.addDisbursement(interestSchedule, disbursementDate.plusMonths(1).plusDays(5), toMoney(250.0)); + checkPeriod(interestSchedule, 0, 260.00, 10.00, 250.00, 750.00, false); // + checkPeriod(interestSchedule, 1, 345.69, 12.07, 333.62, 666.38, false); // + checkPeriod(interestSchedule, 2, 345.69, 12.50, 333.19, 333.19, false); // + checkPeriod(interestSchedule, 3, 345.69, 12.50, 333.19, 0.00, false); // + } + + @Test + public void testFlatDaily_1_week_Actual_Actual() { + + final BigDecimal interestRate = BigDecimal.valueOf(12.0); + final LocalDate disbursementDate = LocalDate.of(2024, 1, 1); + + Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate); + Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.WEEKS); + Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(4); + Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(1); + + Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.ACTUAL.getValue()); + Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue()); + + 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(600.0)); + checkPeriod(interestSchedule, 0, 151.38, 1.38, 150.00, 450.00, false); // + checkPeriod(interestSchedule, 1, 151.38, 1.38, 150.00, 300.00, false); // + checkPeriod(interestSchedule, 2, 151.38, 1.38, 150.00, 150.00, false); // + checkPeriod(interestSchedule, 3, 151.38, 1.38, 150.00, 0.00, false); // + + emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(400.0)); + checkPeriod(interestSchedule, 0, 252.30, 2.30, 250.00, 750.00, false); // + checkPeriod(interestSchedule, 1, 252.30, 2.30, 250.00, 500.00, false); // + checkPeriod(interestSchedule, 2, 252.30, 2.30, 250.00, 250.00, false); // + checkPeriod(interestSchedule, 3, 252.30, 2.30, 250.00, 0.00, false); // + + emiCalculator.addDisbursement(interestSchedule, disbursementDate.plusDays(3), toMoney(1000.0)); + checkPeriod(interestSchedule, 0, 504.34, 3.61, 500.73, 1499.27, false); // + checkPeriod(interestSchedule, 1, 504.34, 4.59, 499.75, 999.52, false); // + checkPeriod(interestSchedule, 2, 504.34, 4.59, 499.75, 499.77, false); // + checkPeriod(interestSchedule, 3, 504.36, 4.59, 499.77, 0.00, false); // + } + + @Test + public void testFlatDaily_30_Days_Actual_Actual() { + + final BigDecimal interestRate = BigDecimal.valueOf(12.0); + final LocalDate disbursementDate = LocalDate.of(2024, 1, 1); + + Mockito.when(loanProductRelatedDetail.getAnnualNominalInterestRate()).thenReturn(interestRate); + Mockito.when(loanProductRelatedDetail.getRepaymentPeriodFrequencyType()).thenReturn(PeriodFrequencyType.DAYS); + Mockito.when(loanProductRelatedDetail.getNumberOfRepayments()).thenReturn(4); + Mockito.when(loanProductRelatedDetail.getRepayEvery()).thenReturn(30); + + Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.ACTUAL.getValue()); + Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue()); + + 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(400.0)); + checkPeriod(interestSchedule, 0, 103.93, 3.93, 100.00, 300.00, false); // + checkPeriod(interestSchedule, 1, 103.93, 3.93, 100.00, 200.00, false); // + checkPeriod(interestSchedule, 2, 103.93, 3.93, 100.00, 100.00, false); // + checkPeriod(interestSchedule, 3, 103.93, 3.93, 100.00, 0.00, false); // + + emiCalculator.addDisbursement(interestSchedule, disbursementDate, toMoney(600.0)); + checkPeriod(interestSchedule, 0, 259.84, 9.84, 250.00, 750.00, false); // + checkPeriod(interestSchedule, 1, 259.84, 9.84, 250.00, 500.00, false); // + checkPeriod(interestSchedule, 2, 259.84, 9.84, 250.00, 250.00, false); // + checkPeriod(interestSchedule, 3, 259.84, 9.84, 250.00, 0.00, false); // + + emiCalculator.addDisbursement(interestSchedule, disbursementDate.plusDays(15), toMoney(500.0)); + checkPeriod(interestSchedule, 0, 389.14, 12.30, 376.84, 1123.16, false); // + checkPeriod(interestSchedule, 1, 389.14, 14.75, 374.39, 748.77, false); // + checkPeriod(interestSchedule, 2, 389.14, 14.75, 374.39, 374.38, false); // + checkPeriod(interestSchedule, 3, 389.13, 14.75, 374.38, 0.00, false); // + } + } + + @Nested + public class InterestTypeFlatAndCalculationPeriodSameAsRepaymentPeriod { @BeforeEach public void setupTestDefaults() { @@ -3243,7 +3414,6 @@ class ProgressiveEMICalculatorTest { Mockito.when(loanProductRelatedDetail.getDaysInYearCustomStrategy()).thenReturn(null); Mockito.when(loanProductRelatedDetail.getInterestMethod()).thenReturn(InterestMethod.FLAT); Mockito.when(loanProductRelatedDetail.getCurrencyData()).thenReturn(currency); - // InterestCalculationPeriodMethod.SAME_AS_REPAYMENT_PERIOD Mockito.when(loanProductRelatedDetail.getDaysInYearType()).thenReturn(DaysInYearType.ACTUAL.getValue()); Mockito.when(loanProductRelatedDetail.getDaysInMonthType()).thenReturn(DaysInMonthType.ACTUAL.getValue()); Mockito.when(loanProductRelatedDetail.getInterestCalculationPeriodMethod())
