FINERACT-1981: revise last installment payment strategy

This commit is contained in:
Adam Saghy
2024-10-30 16:26:23 +01:00
committed by Kristof Jozsa
parent 94360c9f8a
commit 3b2ba174e1
7 changed files with 135 additions and 1 deletions
@@ -196,4 +196,5 @@ public class ProgressiveLoanInterestScheduleModel {
public Money getZero() {
return Money.zero(loanProductRelatedDetail.getCurrency(), mc);
}
}
@@ -46,6 +46,9 @@ public class RepaymentPeriod {
@Setter
@Getter
private Money emi;
@Setter
@Getter
private Money originalEmi;
@Getter
private Money paidPrincipal;
@Getter
@@ -62,6 +65,7 @@ public class RepaymentPeriod {
this.fromDate = fromDate;
this.dueDate = dueDate;
this.emi = emi;
this.originalEmi = emi;
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
@@ -76,6 +80,7 @@ public class RepaymentPeriod {
this.fromDate = repaymentPeriod.fromDate;
this.dueDate = repaymentPeriod.dueDate;
this.emi = repaymentPeriod.emi;
this.originalEmi = repaymentPeriod.originalEmi;
this.interestPeriods = new ArrayList<>();
this.paidPrincipal = repaymentPeriod.paidPrincipal;
this.paidInterest = repaymentPeriod.paidInterest;
@@ -136,13 +136,23 @@ public final class ProgressiveEMICalculator implements EMICalculator {
@Override
public void payPrincipal(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate repaymentPeriodDueDate,
LocalDate transactionDate, Money principalAmount) {
findRepaymentPeriod(scheduleModel, repaymentPeriodDueDate).ifPresent(rp -> rp.addPaidPrincipalAmount(principalAmount));
Optional<RepaymentPeriod> repaymentPeriod = findRepaymentPeriod(scheduleModel, repaymentPeriodDueDate);
repaymentPeriod.ifPresent(rp -> rp.addPaidPrincipalAmount(principalAmount));
LocalDate balanceCorrectionDate = transactionDate;
if (repaymentPeriodDueDate.isBefore(transactionDate)) {
// If it is paid late, we need to calculate with the period due date
balanceCorrectionDate = repaymentPeriodDueDate;
}
addBalanceCorrection(scheduleModel, balanceCorrectionDate, principalAmount.negated());
repaymentPeriod.ifPresent(rp -> {
// If any period total paid > calculated EMI, then set EMI to total paid -> effectively it is marked as
// fully paid
if (rp.getTotalPaidAmount().compareTo(rp.getEmi()) > 0) {
rp.setEmi(rp.getTotalPaidAmount());
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
}
});
}
@Override
@@ -173,6 +183,11 @@ public final class ProgressiveEMICalculator implements EMICalculator {
calculateOutstandingBalance(scheduleModelCopy);
calculateLastUnpaidRepaymentPeriodEMI(scheduleModelCopy);
boolean multiplePeriodIsUnpaid = scheduleModelCopy.repaymentPeriods().stream().filter(rp -> !rp.isFullyPaid()).count() > 1L;
if (multiplePeriodIsUnpaid && !targetDate.isAfter(repaymentPeriod.getFromDate())) {
repaymentPeriod.setEmi(repaymentPeriod.getOriginalEmi());
}
return new PayableDetails(repaymentPeriod.getEmi(), repaymentPeriod.getDuePrincipal(), repaymentPeriod.getDueInterest(),
interestPeriod.getOutstandingLoanBalance().add(interestPeriod.getDisbursementAmount(), mc));
}
@@ -245,6 +260,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
if (!period.getDueDate().isBefore(relatedPeriodsFirstDueDate)
&& !adjustedEqualMonthlyInstallmentValue.isLessThan(period.getTotalPaidAmount())) {
period.setEmi(adjustedEqualMonthlyInstallmentValue);
period.setOriginalEmi(adjustedEqualMonthlyInstallmentValue);
}
});
calculateOutstandingBalance(newScheduleModel);
@@ -265,6 +281,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
}
final RepaymentPeriod newRepaymentPeriod = relatedPeriodFromNewModelIterator.next();
relatedRepaymentPeriod.setEmi(newRepaymentPeriod.getEmi());
relatedRepaymentPeriod.setOriginalEmi(newRepaymentPeriod.getEmi());
});
calculateOutstandingBalance(scheduleModel);
}
@@ -401,6 +418,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
repaymentPeriods.forEach(period -> {
if (!finalEqualMonthlyInstallment.isLessThan(period.getTotalPaidAmount())) {
period.setEmi(finalEqualMonthlyInstallment);
period.setOriginalEmi(finalEqualMonthlyInstallment);
}
});
}