diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/InterestPeriod.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/InterestPeriod.java index 62c79443d3b..1b7c10f3072 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/InterestPeriod.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/InterestPeriod.java @@ -27,6 +27,7 @@ import lombok.Getter; import lombok.Setter; import lombok.ToString; +import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.organisation.monetary.domain.Money; @@ -42,27 +43,31 @@ public class InterestPeriod implements Comparable { private LocalDate dueDate; @Setter private BigDecimal rateFactor; + @Setter + private BigDecimal rateFactorTillPeriodDueDate; private Money disbursementAmount; private Money balanceCorrectionAmount; private Money outstandingLoanBalance; private final MathContext mc; public InterestPeriod(RepaymentPeriod repaymentPeriod, LocalDate fromDate, LocalDate dueDate, BigDecimal rateFactor, - Money disbursementAmount, Money balanceCorrectionAmount, Money outstandingLoanBalance, MathContext mc) { + BigDecimal rateFactorTillPeriodDueDate, Money disbursementAmount, Money balanceCorrectionAmount, Money outstandingLoanBalance, + MathContext mc) { this.repaymentPeriod = repaymentPeriod; this.fromDate = fromDate; this.dueDate = dueDate; this.rateFactor = rateFactor; + this.rateFactorTillPeriodDueDate = rateFactorTillPeriodDueDate; this.disbursementAmount = disbursementAmount; this.balanceCorrectionAmount = balanceCorrectionAmount; this.outstandingLoanBalance = outstandingLoanBalance; this.mc = mc; } - public InterestPeriod(RepaymentPeriod repaymentPeriod, InterestPeriod interestPeriod, MathContext mc) { + public InterestPeriod(RepaymentPeriod repaymentPeriod, InterestPeriod interestPeriod) { this(repaymentPeriod, interestPeriod.getFromDate(), interestPeriod.getDueDate(), interestPeriod.getRateFactor(), - interestPeriod.getDisbursementAmount(), interestPeriod.getBalanceCorrectionAmount(), - interestPeriod.getOutstandingLoanBalance(), mc); + interestPeriod.getRateFactorTillPeriodDueDate(), interestPeriod.getDisbursementAmount(), + interestPeriod.getBalanceCorrectionAmount(), interestPeriod.getOutstandingLoanBalance(), interestPeriod.getMc()); } @Override @@ -79,7 +84,19 @@ public void addDisbursementAmount(final Money disbursementAmount) { } public Money getCalculatedDueInterest() { - return getOutstandingLoanBalance().multipliedBy(getRateFactor(), mc); + final BigDecimal interestDueTillRepaymentDueDate = getOutstandingLoanBalance()// + .multipliedBy(getRateFactorTillPeriodDueDate(), mc).getAmount() // + .divide(BigDecimal.valueOf(getLengthTillPeriodDueDate()), mc) // + .multiply(BigDecimal.valueOf(getLength()), mc); // + return Money.of(outstandingLoanBalance.getCurrency(), interestDueTillRepaymentDueDate, mc); + } + + public long getLength() { + return DateUtils.getDifferenceInDays(fromDate, dueDate); + } + + public long getLengthTillPeriodDueDate() { + return DateUtils.getDifferenceInDays(fromDate, repaymentPeriod.getDueDate()); } public void updateOutstandingLoanBalance() { diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/ProgressiveLoanInterestScheduleModel.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/ProgressiveLoanInterestScheduleModel.java index c687e137f8c..5451886da2b 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/ProgressiveLoanInterestScheduleModel.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/ProgressiveLoanInterestScheduleModel.java @@ -189,7 +189,7 @@ void insertInterestPeriod(final RepaymentPeriod repaymentPeriod, final LocalDate previousInterestPeriod.addDisbursementAmount(disbursedAmount); previousInterestPeriod.addBalanceCorrectionAmount(correctionAmount); final InterestPeriod interestPeriod = new InterestPeriod(repaymentPeriod, previousInterestPeriod.getDueDate(), originalDueDate, - BigDecimal.ZERO, getZero(), getZero(), getZero(), mc); + BigDecimal.ZERO, BigDecimal.ZERO, getZero(), getZero(), getZero(), mc); repaymentPeriod.getInterestPeriods().add(interestPeriod); } diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/RepaymentPeriod.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/RepaymentPeriod.java index 94d36a5f5ee..6a531a90230 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/RepaymentPeriod.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/data/RepaymentPeriod.java @@ -69,8 +69,8 @@ public RepaymentPeriod(RepaymentPeriod previous, LocalDate fromDate, LocalDate d this.mc = mc; this.interestPeriods = new ArrayList<>(); // There is always at least 1 interest period, by default with same from-due date as repayment period - getInterestPeriods() - .add(new InterestPeriod(this, getFromDate(), getDueDate(), BigDecimal.ZERO, getZero(mc), getZero(mc), getZero(mc), mc)); + getInterestPeriods().add(new InterestPeriod(this, getFromDate(), getDueDate(), BigDecimal.ZERO, BigDecimal.ZERO, getZero(mc), + getZero(mc), getZero(mc), mc)); this.paidInterest = getZero(mc); this.paidPrincipal = getZero(mc); } @@ -87,7 +87,7 @@ public RepaymentPeriod(RepaymentPeriod previous, RepaymentPeriod repaymentPeriod this.mc = mc; // There is always at least 1 interest period, by default with same from-due date as repayment period for (InterestPeriod interestPeriod : repaymentPeriod.interestPeriods) { - interestPeriods.add(new InterestPeriod(this, interestPeriod, mc)); + interestPeriods.add(new InterestPeriod(this, interestPeriod)); } } 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 fa1036e4448..8ccda6535e7 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 @@ -314,38 +314,42 @@ void calculateRateFactorForPeriods(final List repaymentPeriods, void calculateRateFactorForRepaymentPeriod(final RepaymentPeriod repaymentPeriod, final ProgressiveLoanInterestScheduleModel scheduleModel) { - repaymentPeriod.getInterestPeriods().forEach(interestPeriod -> interestPeriod - .setRateFactor(calculateRateFactorPerPeriod(repaymentPeriod, interestPeriod, scheduleModel))); + repaymentPeriod.getInterestPeriods().forEach(interestPeriod -> { + interestPeriod.setRateFactor(calculateRateFactorPerPeriod(scheduleModel, repaymentPeriod, interestPeriod.getFromDate(), + interestPeriod.getDueDate())); + interestPeriod.setRateFactorTillPeriodDueDate(calculateRateFactorPerPeriod(scheduleModel, repaymentPeriod, + interestPeriod.getFromDate(), repaymentPeriod.getDueDate())); + }); } /** * Calculate Rate Factor for an exact Period */ - BigDecimal calculateRateFactorPerPeriod(final RepaymentPeriod repaymentPeriod, final InterestPeriod interestPeriod, - final ProgressiveLoanInterestScheduleModel scheduleModel) { + BigDecimal calculateRateFactorPerPeriod(final ProgressiveLoanInterestScheduleModel scheduleModel, final RepaymentPeriod repaymentPeriod, + final LocalDate interestPeriodFromDate, final LocalDate interestPeriodDueDate) { final MathContext mc = scheduleModel.mc(); final LoanProductRelatedDetail loanProductRelatedDetail = scheduleModel.loanProductRelatedDetail(); - final BigDecimal interestRate = calcNominalInterestRatePercentage(scheduleModel.getInterestRate(interestPeriod.getFromDate()), + final BigDecimal interestRate = calcNominalInterestRatePercentage(scheduleModel.getInterestRate(interestPeriodFromDate), scheduleModel.mc()); final DaysInYearType daysInYearType = DaysInYearType.fromInt(loanProductRelatedDetail.getDaysInYearType()); final DaysInMonthType daysInMonthType = DaysInMonthType.fromInt(loanProductRelatedDetail.getDaysInMonthType()); final PeriodFrequencyType repaymentFrequency = loanProductRelatedDetail.getRepaymentPeriodFrequencyType(); final BigDecimal repaymentEvery = BigDecimal.valueOf(loanProductRelatedDetail.getRepayEvery()); - final BigDecimal daysInMonth = BigDecimal.valueOf(daysInMonthType.getNumberOfDays(interestPeriod.getFromDate())); - final BigDecimal daysInYear = BigDecimal.valueOf(daysInYearType.getNumberOfDays(interestPeriod.getFromDate())); + final BigDecimal daysInMonth = BigDecimal.valueOf(daysInMonthType.getNumberOfDays(interestPeriodFromDate)); + final BigDecimal daysInYear = BigDecimal.valueOf(daysInYearType.getNumberOfDays(interestPeriodFromDate)); final BigDecimal actualDaysInPeriod = BigDecimal - .valueOf(DateUtils.getDifferenceInDays(interestPeriod.getFromDate(), interestPeriod.getDueDate())); + .valueOf(DateUtils.getDifferenceInDays(interestPeriodFromDate, interestPeriodDueDate)); final BigDecimal calculatedDaysInPeriod = BigDecimal .valueOf(DateUtils.getDifferenceInDays(repaymentPeriod.getFromDate(), repaymentPeriod.getDueDate())); - final int numberOfYearsDifferenceInPeriod = interestPeriod.getDueDate().getYear() - interestPeriod.getFromDate().getYear(); + final int numberOfYearsDifferenceInPeriod = interestPeriodDueDate.getYear() - interestPeriodFromDate.getYear(); final boolean partialPeriodCalculationNeeded = daysInYearType == DaysInYearType.ACTUAL && numberOfYearsDifferenceInPeriod > 0; // TODO check: loanApplicationTerms.calculatePeriodsBetweenDates(startDate, endDate); // calculate period data // TODO review: (repayment frequency: days, weeks, years; validation day is month fix 30) // TODO refactor this logic to represent in interest period if (partialPeriodCalculationNeeded) { - final BigDecimal cumulatedPeriodFractions = calculatePeriodFractions(interestPeriod, mc); + final BigDecimal cumulatedPeriodFractions = calculatePeriodFractions(interestPeriodFromDate, interestPeriodDueDate, mc); return rateFactorByRepaymentPartialPeriod(interestRate, repaymentEvery, cumulatedPeriodFractions, BigDecimal.ONE, BigDecimal.ONE, mc); } @@ -357,18 +361,19 @@ BigDecimal calculateRateFactorPerPeriod(final RepaymentPeriod repaymentPeriod, f /** * Calculate Period fractions part based on how much year has in the period * - * @param interestPeriod + * @param interestPeriodFromDate + * @param interestPeriodDueDate * @return */ - BigDecimal calculatePeriodFractions(InterestPeriod interestPeriod, MathContext mc) { + BigDecimal calculatePeriodFractions(final LocalDate interestPeriodFromDate, final LocalDate interestPeriodDueDate, MathContext mc) { BigDecimal cumulatedRateFactor = BigDecimal.ZERO; - int actualYear = interestPeriod.getFromDate().getYear(); - int endYear = interestPeriod.getDueDate().getYear(); - LocalDate actualDate = interestPeriod.getFromDate(); + int actualYear = interestPeriodFromDate.getYear(); + int endYear = interestPeriodDueDate.getYear(); + LocalDate actualDate = interestPeriodFromDate; LocalDate endOfActualYear; while (actualYear <= endYear) { - endOfActualYear = actualYear == endYear ? interestPeriod.getDueDate() : LocalDate.of(actualYear, 12, 31); + endOfActualYear = actualYear == endYear ? interestPeriodDueDate : LocalDate.of(actualYear, 12, 31); BigDecimal numberOfDaysInYear = BigDecimal.valueOf(Year.of(actualYear).length()); BigDecimal calculatedDaysInActualYear = BigDecimal.valueOf(DateUtils.getDifferenceInDays(actualDate, endOfActualYear)); cumulatedRateFactor = cumulatedRateFactor.add(calculatedDaysInActualYear.divide(numberOfDaysInYear, mc), mc); 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 aca3641bc6d..a2bd699ba3f 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 @@ -1110,6 +1110,138 @@ public void test_disbursedAmt100_dayInYears360_daysInMonthDoesntMatter_repayEver checkPeriod(interestSchedule, 5, 0, 16.89, 0.003950916667, 0.07, 16.82, 0.0); } + @Test + public void test_dailyInterest_disbursedAmt1000_dayInYears360_daysInMonth30_repayIn1Month() { + + final List expectedRepaymentPeriods = new ArrayList<>(); + + expectedRepaymentPeriods.add(repayment(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 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.getCurrency()).thenReturn(monetaryCurrency); + + final ProgressiveLoanInterestScheduleModel interestModel = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods, + loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); + + final Money disbursedAmount = toMoney(1000.0); + emiCalculator.addDisbursement(interestModel, LocalDate.of(2024, 1, 1), disbursedAmount); + + checkPeriod(interestModel, 0, 0, 1005.83, 0.0, 0.0, 5.83, 1000.0, 0.0); + checkPeriod(interestModel, 0, 1, 1005.83, 0.005833333333, 5.83, 1000.0, 0.0); + + final LocalDate dueDate = LocalDate.of(2024, 2, 1); + final LocalDate startDay = LocalDate.of(2024, 1, 1); + + checkDailyInterest(interestModel, dueDate, startDay, 1, 0.19, 0.19); + checkDailyInterest(interestModel, dueDate, startDay, 2, 0.19, 0.38); + checkDailyInterest(interestModel, dueDate, startDay, 3, 0.18, 0.56); + checkDailyInterest(interestModel, dueDate, startDay, 4, 0.19, 0.75); + checkDailyInterest(interestModel, dueDate, startDay, 5, 0.19, 0.94); + checkDailyInterest(interestModel, dueDate, startDay, 6, 0.19, 1.13); + checkDailyInterest(interestModel, dueDate, startDay, 7, 0.19, 1.32); + checkDailyInterest(interestModel, dueDate, startDay, 8, 0.18, 1.50); + checkDailyInterest(interestModel, dueDate, startDay, 9, 0.19, 1.69); + checkDailyInterest(interestModel, dueDate, startDay, 10, 0.19, 1.88); + checkDailyInterest(interestModel, dueDate, startDay, 11, 0.19, 2.07); + checkDailyInterest(interestModel, dueDate, startDay, 12, 0.19, 2.26); + checkDailyInterest(interestModel, dueDate, startDay, 13, 0.18, 2.44); + checkDailyInterest(interestModel, dueDate, startDay, 14, 0.19, 2.63); + checkDailyInterest(interestModel, dueDate, startDay, 15, 0.19, 2.82); + checkDailyInterest(interestModel, dueDate, startDay, 16, 0.19, 3.01); + checkDailyInterest(interestModel, dueDate, startDay, 17, 0.19, 3.20); + checkDailyInterest(interestModel, dueDate, startDay, 18, 0.19, 3.39); + checkDailyInterest(interestModel, dueDate, startDay, 19, 0.18, 3.57); + checkDailyInterest(interestModel, dueDate, startDay, 20, 0.19, 3.76); + checkDailyInterest(interestModel, dueDate, startDay, 21, 0.19, 3.95); + checkDailyInterest(interestModel, dueDate, startDay, 22, 0.19, 4.14); + checkDailyInterest(interestModel, dueDate, startDay, 23, 0.19, 4.33); + checkDailyInterest(interestModel, dueDate, startDay, 24, 0.18, 4.51); + checkDailyInterest(interestModel, dueDate, startDay, 25, 0.19, 4.70); + checkDailyInterest(interestModel, dueDate, startDay, 26, 0.19, 4.89); + checkDailyInterest(interestModel, dueDate, startDay, 27, 0.19, 5.08); + checkDailyInterest(interestModel, dueDate, startDay, 28, 0.19, 5.27); + checkDailyInterest(interestModel, dueDate, startDay, 29, 0.18, 5.45); + checkDailyInterest(interestModel, dueDate, startDay, 30, 0.19, 5.64); + checkDailyInterest(interestModel, dueDate, startDay, 31, 0.19, 5.83); + } + + @Test + public void test_dailyInterest_disbursedAmt2000_dayInYears360_daysInMonth30_repayIn2Month() { + + final List expectedRepaymentPeriods = new ArrayList<>(); + + expectedRepaymentPeriods.add(repayment(1, LocalDate.of(2024, 1, 1), LocalDate.of(2024, 2, 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.getCurrency()).thenReturn(monetaryCurrency); + + final ProgressiveLoanInterestScheduleModel interestModel = emiCalculator.generateInterestScheduleModel(expectedRepaymentPeriods, + loanProductRelatedDetail, installmentAmountInMultiplesOf, mc); + + final Money disbursedAmount1st = toMoney(1000.0); + final Money disbursedAmount2nd = toMoney(1000.0); + emiCalculator.addDisbursement(interestModel, LocalDate.of(2024, 1, 1), disbursedAmount1st); + emiCalculator.addDisbursement(interestModel, LocalDate.of(2024, 1, 15), disbursedAmount2nd); + + checkPeriod(interestModel, 0, 0, 2009.03, 0.0, 0.0, 9.03, 2000.0, 0.0); + checkPeriod(interestModel, 0, 1, 2009.03, 0.002634408602, 2.63, 9.03, 2000.0, 0.0); + checkPeriod(interestModel, 0, 2, 2009.03, 0.003198924731, 6.40, 9.03, 2000.0, 0.0); + + final LocalDate dueDate = LocalDate.of(2024, 2, 1); + final LocalDate startDay = LocalDate.of(2024, 1, 1); + + // 1st 1000 disbursement accruals + // Total Interest: 5.83 (31 days), 2.63 (14 days) + checkDailyInterest(interestModel, dueDate, startDay, 1, 0.19, 0.19); + checkDailyInterest(interestModel, dueDate, startDay, 2, 0.19, 0.38); + checkDailyInterest(interestModel, dueDate, startDay, 3, 0.18, 0.56); + checkDailyInterest(interestModel, dueDate, startDay, 4, 0.19, 0.75); + checkDailyInterest(interestModel, dueDate, startDay, 5, 0.19, 0.94); + checkDailyInterest(interestModel, dueDate, startDay, 6, 0.19, 1.13); + checkDailyInterest(interestModel, dueDate, startDay, 7, 0.19, 1.32); + checkDailyInterest(interestModel, dueDate, startDay, 8, 0.18, 1.50); + checkDailyInterest(interestModel, dueDate, startDay, 9, 0.19, 1.69); + checkDailyInterest(interestModel, dueDate, startDay, 10, 0.19, 1.88); + checkDailyInterest(interestModel, dueDate, startDay, 11, 0.19, 2.07); + checkDailyInterest(interestModel, dueDate, startDay, 12, 0.19, 2.26); + checkDailyInterest(interestModel, dueDate, startDay, 13, 0.18, 2.44); + checkDailyInterest(interestModel, dueDate, startDay, 14, 0.19, 2.63); + + // 2nd 1000 disbursement accruals + // Total Interest: 6.40 (17 days) + checkDailyInterest(interestModel, dueDate, startDay, 15, 0.38, 3.01); + checkDailyInterest(interestModel, dueDate, startDay, 16, 0.37, 3.38); + checkDailyInterest(interestModel, dueDate, startDay, 17, 0.38, 3.76); + checkDailyInterest(interestModel, dueDate, startDay, 18, 0.38, 4.14); + checkDailyInterest(interestModel, dueDate, startDay, 19, 0.37, 4.51); + checkDailyInterest(interestModel, dueDate, startDay, 20, 0.38, 4.89); + checkDailyInterest(interestModel, dueDate, startDay, 21, 0.38, 5.27); + checkDailyInterest(interestModel, dueDate, startDay, 22, 0.37, 5.64); + checkDailyInterest(interestModel, dueDate, startDay, 23, 0.38, 6.02); + checkDailyInterest(interestModel, dueDate, startDay, 24, 0.37, 6.39); + checkDailyInterest(interestModel, dueDate, startDay, 25, 0.38, 6.77); + checkDailyInterest(interestModel, dueDate, startDay, 26, 0.38, 7.15); + checkDailyInterest(interestModel, dueDate, startDay, 27, 0.37, 7.52); + checkDailyInterest(interestModel, dueDate, startDay, 28, 0.38, 7.90); + checkDailyInterest(interestModel, dueDate, startDay, 29, 0.38, 8.28); + checkDailyInterest(interestModel, dueDate, startDay, 30, 0.37, 8.65); + checkDailyInterest(interestModel, dueDate, startDay, 31, 0.38, 9.03); + } + private static LoanScheduleModelRepaymentPeriod repayment(int periodNumber, LocalDate fromDate, LocalDate dueDate) { final Money zeroAmount = Money.zero(monetaryCurrency); return LoanScheduleModelRepaymentPeriod.repayment(periodNumber, fromDate, dueDate, zeroAmount, zeroAmount, zeroAmount, zeroAmount, @@ -1126,6 +1258,17 @@ private static LoanRepaymentScheduleInstallment createPeriod(int periodId, Local return period; } + private static void checkDailyInterest(final ProgressiveLoanInterestScheduleModel interestModel, final LocalDate repaymentPeriodDueDate, + final LocalDate interestStartDay, final int dayOffset, final double dailyInterest, final double interest) { + Money previousInterest = emiCalculator + .getPayableDetails(interestModel, repaymentPeriodDueDate, interestStartDay.plusDays(dayOffset - 1)).getPayableInterest(); + Money currentInterest = emiCalculator.getPayableDetails(interestModel, repaymentPeriodDueDate, interestStartDay.plusDays(dayOffset)) + .getPayableInterest(); + + Assertions.assertEquals(dailyInterest, toDouble(currentInterest.minus(previousInterest))); + Assertions.assertEquals(interest, toDouble(currentInterest)); + } + 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) { diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalBusinessEventTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalBusinessEventTest.java index 7d8b137e44a..06d3b9d8fd7 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalBusinessEventTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ExternalBusinessEventTest.java @@ -182,8 +182,8 @@ public void verifyInterestRefundPostBusinessEventCreatedForMerchantIssuedRefundW Assertions.assertNotNull(postLoansLoanIdTransactionsResponse); Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId()); - verifyBusinessEvents(new LoanTransactionBusinessEvent("LoanTransactionInterestRefundPostBusinessEvent", "22 January 2021", 5.75, - 0.0, 5.75, 0.0, 0.0, 0.0)); + verifyBusinessEvents(new LoanTransactionBusinessEvent("LoanTransactionInterestRefundPostBusinessEvent", "22 January 2021", 5.74, + 0.0, 5.74, 0.0, 0.0, 0.0)); }); enableLoanInterestRefundPstBusinessEvent(false); } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java index c720099a81a..5631fc7baea 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanInterestRefundTest.java @@ -148,8 +148,8 @@ public void verifyInterestRefundCreatedForPayoutRefund() { verifyTransactions(loanId, // transaction(1000.0, "Disbursement", "01 January 2021"), // transaction(1000.0, "Payout Refund", "22 January 2021"), // - transaction(5.75, "Interest Refund", "22 January 2021"), // - transaction(5.75, "Accrual", "22 January 2021")); // + transaction(5.74, "Interest Refund", "22 January 2021"), // + transaction(5.74, "Accrual", "22 January 2021")); // checkTransactionWasNotReverseReplayed(postLoansLoanIdTransactionsResponse.getLoanId(), postLoansLoanIdTransactionsResponse.getResourceId()); @@ -158,12 +158,12 @@ public void verifyInterestRefundCreatedForPayoutRefund() { verifyTRJournalEntries(postLoansLoanIdTransactionsResponse.getResourceId(), // journalEntry(1000, fundSource, "DEBIT"), // - journalEntry(5.75, interestReceivableAccount, "CREDIT"), // - journalEntry(994.25, loansReceivableAccount, "CREDIT")); + journalEntry(5.74, interestReceivableAccount, "CREDIT"), // + journalEntry(994.26, loansReceivableAccount, "CREDIT")); verifyTRJournalEntries(postLoansLoanIdTransactionsResponse.getSubResourceId(), - journalEntry(5.75, interestIncomeAccount, "DEBIT"), // - journalEntry(5.75, loansReceivableAccount, "CREDIT")); // + journalEntry(5.74, interestIncomeAccount, "DEBIT"), // + journalEntry(5.74, loansReceivableAccount, "CREDIT")); // }); } @@ -204,8 +204,8 @@ public void verifyInterestRefundCreatedForMerchantIssuedRefund() { logLoanTransactions(loanId); verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), - transaction(1000.0, "Merchant Issued Refund", "22 January 2021"), transaction(5.75, "Accrual", "22 January 2021"), - transaction(5.75, "Interest Refund", "22 January 2021")); + transaction(1000.0, "Merchant Issued Refund", "22 January 2021"), transaction(5.74, "Accrual", "22 January 2021"), + transaction(5.74, "Interest Refund", "22 January 2021")); }); } @@ -235,8 +235,8 @@ public void verifyUC01() { logLoanTransactions(loanId); verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), - transaction(1000.0, "Payout Refund", "22 January 2021"), transaction(5.75, "Accrual", "22 January 2021"), - transaction(5.75, "Interest Refund", "22 January 2021")); + transaction(1000.0, "Payout Refund", "22 January 2021"), transaction(5.74, "Accrual", "22 January 2021"), + transaction(5.74, "Interest Refund", "22 January 2021")); }); } @@ -310,7 +310,7 @@ public void verifyUC02b() { logLoanTransactions(loanId); verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), transaction(1000.0, "Payout Refund", "09 February 2021"), transaction(87.89, "Repayment", "01 February 2021"), - transaction(10.50, "Interest Refund", "09 February 2021")); + transaction(10.49, "Interest Refund", "09 February 2021")); }); } @@ -345,7 +345,7 @@ public void verifyUC03() { logLoanTransactions(loanId); verifyTransactions(loanId, transaction(750.0, "Disbursement", "01 January 2021"), transaction(250.0, "Disbursement", "01 January 2021"), transaction(1000.0, "Payout Refund", "22 January 2021"), - transaction(5.75, "Accrual", "22 January 2021"), transaction(5.75, "Interest Refund", "22 January 2021")); + transaction(5.74, "Accrual", "22 January 2021"), transaction(5.74, "Interest Refund", "22 January 2021")); }); } @@ -381,7 +381,7 @@ public void verifyUC04() { logLoanTransactions(loanId); verifyTransactions(loanId, transaction(750.0, "Disbursement", "04 January 2021"), transaction(250.0, "Disbursement", "01 January 2021"), transaction(1000.0, "Payout Refund", "22 January 2021"), - transaction(5.14, "Accrual", "22 January 2021"), transaction(5.14, "Interest Refund", "22 January 2021")); + transaction(5.13, "Accrual", "22 January 2021"), transaction(5.13, "Interest Refund", "22 January 2021")); }); } @@ -508,7 +508,7 @@ public void verifyUC07() { verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), // transaction(87.89, "Repayment", "01 February 2021"), // transaction(500.0, "Payout Refund", "09 February 2021"), // - transaction(5.35, "Interest Refund", "09 February 2021")); + transaction(5.34, "Interest Refund", "09 February 2021")); }); } @@ -543,7 +543,7 @@ public void verifyUC08() { verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 January 2021"), // transaction(750.0, "Disbursement", "01 January 2021"), // transaction(500.0, "Payout Refund", "22 January 2021"), // - transaction(2.88, "Interest Refund", "22 January 2021")); + transaction(2.87, "Interest Refund", "22 January 2021")); }); } @@ -580,7 +580,7 @@ public void verifyUC09() { verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 January 2021"), // transaction(750.0, "Disbursement", "07 January 2021"), // transaction(500.0, "Payout Refund", "22 January 2021"), // - transaction(2.47, "Interest Refund", "22 January 2021")); + transaction(2.46, "Interest Refund", "22 January 2021")); }); } @@ -766,7 +766,7 @@ public void verifyUC13() { verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 January 2021"), // transaction(750.0, "Disbursement", "01 January 2021"), // transaction(500.0, "Merchant Issued Refund", "22 January 2021"), // - transaction(2.88, "Interest Refund", "22 January 2021") // + transaction(2.87, "Interest Refund", "22 January 2021") // ); }); runAt("26 January 2021", () -> { @@ -776,14 +776,14 @@ public void verifyUC13() { verifyTransactions(loanId, transaction(250.0, "Disbursement", "01 January 2021"), // transaction(750.0, "Disbursement", "01 January 2021"), // transaction(500.0, "Merchant Issued Refund", "22 January 2021"), // - transaction(2.88, "Interest Refund", "22 January 2021"), // + transaction(2.87, "Interest Refund", "22 January 2021"), // transaction(400.0, "Payout Refund", "26 January 2021"), // - transaction(2.74, "Interest Refund", "26 January 2021") // + transaction(2.73, "Interest Refund", "26 January 2021") // ); }); runAt("1 February 2021", () -> { Long loanId = loanIdRef.get(); - loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 2021", 100.85F, loanId.intValue()); + loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 2021", 100.86F, loanId.intValue()); GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId); Assertions.assertNotNull(loanDetails); Assertions.assertNotNull(loanDetails.getStatus()); @@ -844,7 +844,7 @@ public void verifyUC14() { Long loanId = loanIdRef.get(); loanTransactionHelper.makeLoanRepayment("Repayment", "1 February 2021", 171.41F, loanId.intValue()); loanTransactionHelper.makeLoanRepayment("Repayment", "1 March 2021", 171.41F, loanId.intValue()); - loanTransactionHelper.makeLoanRepayment("Repayment", "1 April 2021", 11.26F, loanId.intValue()); + loanTransactionHelper.makeLoanRepayment("Repayment", "1 April 2021", 11.25F, loanId.intValue()); GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId); Assertions.assertNotNull(loanDetails); @@ -912,7 +912,7 @@ public void verifyUC15() { runAt("1 April 2021", () -> { Long loanId = loanIdRef.get(); loanTransactionHelper.makeLoanRepayment("Repayment", "1 March 2021", 171.41F, loanId.intValue()); - loanTransactionHelper.makeLoanRepayment("Repayment", "1 April 2021", 11.16F, loanId.intValue()); + loanTransactionHelper.makeLoanRepayment("Repayment", "1 April 2021", 11.17F, loanId.intValue()); GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId); Assertions.assertNotNull(loanDetails);