Skip to content

Commit

Permalink
FINERACT-1981: Enhance EMI Calculator Daily Interest calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
janez89 authored and adamsaghy committed Nov 6, 2024
1 parent 0bc14ab commit 51b6dfa
Show file tree
Hide file tree
Showing 7 changed files with 214 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -42,27 +43,31 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
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
Expand All @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -314,38 +314,42 @@ void calculateRateFactorForPeriods(final List<RepaymentPeriod> 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);
}
Expand All @@ -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);
Expand Down
Loading

0 comments on commit 51b6dfa

Please sign in to comment.