FINERACT-2260: Fix incorrect interest handling in case of early repayment

This commit is contained in:
Adam Saghy
2025-04-12 09:12:08 +02:00
committed by Arnold Gálovics
parent 76a5ec89cc
commit d947183529
4 changed files with 121 additions and 21 deletions
@@ -129,7 +129,7 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
.multiply(getRateFactorTillPeriodDueDate(), mc) //
.divide(BigDecimal.valueOf(lengthTillPeriodDueDate), mc) //
.multiply(BigDecimal.valueOf(getLength()), mc); //
return MathUtil.add(chargebackInterest.getAmount(), interestDueTillRepaymentDueDate, mc);
return MathUtil.negativeToZero(MathUtil.add(chargebackInterest.getAmount(), interestDueTillRepaymentDueDate, mc));
}
public long getLength() {
@@ -145,18 +145,18 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
Optional<RepaymentPeriod> previousRepaymentPeriod = getRepaymentPeriod().getPrevious();
if (previousRepaymentPeriod.isPresent()) {
InterestPeriod previousInterestPeriod = previousRepaymentPeriod.get().getLastInterestPeriod();
this.outstandingLoanBalance = previousInterestPeriod.getOutstandingLoanBalance()//
this.outstandingLoanBalance = MathUtil.negativeToZero(previousInterestPeriod.getOutstandingLoanBalance()//
.plus(previousInterestPeriod.getDisbursementAmount(), mc)//
.plus(previousInterestPeriod.getBalanceCorrectionAmount(), mc)//
.minus(previousRepaymentPeriod.get().getDuePrincipal(), mc)//
.plus(previousRepaymentPeriod.get().getPaidPrincipal(), mc);//
.plus(previousRepaymentPeriod.get().getPaidPrincipal(), mc), mc);//
}
} else {
int index = getRepaymentPeriod().getInterestPeriods().indexOf(this);
InterestPeriod previousInterestPeriod = getRepaymentPeriod().getInterestPeriods().get(index - 1);
this.outstandingLoanBalance = previousInterestPeriod.getOutstandingLoanBalance() //
this.outstandingLoanBalance = MathUtil.negativeToZero(previousInterestPeriod.getOutstandingLoanBalance() //
.plus(previousInterestPeriod.getBalanceCorrectionAmount(), mc) //
.plus(previousInterestPeriod.getDisbursementAmount(), mc); //
.plus(previousInterestPeriod.getDisbursementAmount(), mc)); //
}
}
@@ -305,7 +305,7 @@ public class ProgressiveLoanInterestScheduleModel {
* @return
*/
public Money getTotalDueInterest() {
return repaymentPeriods().stream().map(RepaymentPeriod::getDueInterest).reduce(zero(), Money::plus);
return MathUtil.negativeToZero(repaymentPeriods().stream().map(RepaymentPeriod::getDueInterest).reduce(zero(), Money::plus), mc);
}
/**
@@ -315,7 +315,7 @@ public class ProgressiveLoanInterestScheduleModel {
* @return
*/
public Money getTotalDuePrincipal() {
return repaymentPeriods.stream().map(RepaymentPeriod::getCreditedAmounts).reduce(zero(), Money::plus);
return MathUtil.negativeToZero(repaymentPeriods.stream().map(RepaymentPeriod::getCreditedAmounts).reduce(zero(), Money::plus), mc);
}
/**
@@ -324,7 +324,7 @@ public class ProgressiveLoanInterestScheduleModel {
* @return
*/
public Money getTotalPaidInterest() {
return repaymentPeriods().stream().map(RepaymentPeriod::getPaidInterest).reduce(zero, Money::plus);
return MathUtil.negativeToZero(repaymentPeriods().stream().map(RepaymentPeriod::getPaidInterest).reduce(zero, Money::plus), mc);
}
/**
@@ -333,7 +333,7 @@ public class ProgressiveLoanInterestScheduleModel {
* @return
*/
public Money getTotalPaidPrincipal() {
return repaymentPeriods().stream().map(RepaymentPeriod::getPaidPrincipal).reduce(zero, Money::plus);
return MathUtil.negativeToZero(repaymentPeriods().stream().map(RepaymentPeriod::getPaidPrincipal).reduce(zero, Money::plus), mc);
}
/**
@@ -342,7 +342,8 @@ public class ProgressiveLoanInterestScheduleModel {
* @return
*/
public Money getTotalChargebackPrincipal() {
return repaymentPeriods().stream().map(RepaymentPeriod::getChargebackPrincipal).reduce(zero, Money::plus);
return MathUtil.negativeToZero(repaymentPeriods().stream().map(RepaymentPeriod::getChargebackPrincipal).reduce(zero, Money::plus),
mc);
}
public Money getTotalOutstandingPrincipal() {
@@ -165,7 +165,7 @@ public final class RepaymentPeriod {
if (getPrevious().isPresent()) {
calculatedDueInterest = calculatedDueInterest.add(getPrevious().get().getUnrecognizedInterest(), mc);
}
return calculatedDueInterest;
return MathUtil.negativeToZero(calculatedDueInterest, mc);
}
/**
@@ -176,9 +176,10 @@ public final class RepaymentPeriod {
public Money getDueInterest() {
if (dueInterestCalculation == null) {
// Due interest might be the maximum paid if there is pay-off or early repayment
dueInterestCalculation = Memo.of(() -> MathUtil.max(
getPaidPrincipal().isGreaterThan(getCalculatedDuePrincipal()) ? getPaidInterest() : getCalculatedDueInterest(),
getPaidInterest(), false), () -> new Object[] { paidPrincipal, paidInterest, interestPeriods });
dueInterestCalculation = Memo.of(
() -> MathUtil.max(getPaidPrincipal().isGreaterThan(getCalculatedDuePrincipal()) ? getPaidInterest()
: MathUtil.min(getCalculatedDueInterest(), getEmiPlusChargeback(), false), getPaidInterest(), false),
() -> new Object[] { paidPrincipal, paidInterest, interestPeriods });
}
return dueInterestCalculation.get();
}
@@ -198,7 +199,7 @@ public final class RepaymentPeriod {
* @return
*/
public Money getCalculatedDuePrincipal() {
return getEmiPlusChargeback().minus(getCalculatedDueInterest(), mc);
return MathUtil.negativeToZero(getEmiPlusChargeback().minus(getCalculatedDueInterest(), mc), mc);
}
/**
@@ -207,9 +208,9 @@ public final class RepaymentPeriod {
* @return
*/
public Money getChargebackPrincipal() {
return interestPeriods.stream() //
return MathUtil.negativeToZero(interestPeriods.stream() //
.map(InterestPeriod::getChargebackPrincipal) //
.reduce(getZero(mc), (value, previous) -> value.plus(previous, mc)); //
.reduce(getZero(mc), (value, previous) -> value.plus(previous, mc)), mc); //
}
/**
@@ -218,9 +219,9 @@ public final class RepaymentPeriod {
* @return
*/
public Money getChargebackInterest() {
return interestPeriods.stream() //
return MathUtil.negativeToZero(interestPeriods.stream() //
.map(InterestPeriod::getChargebackInterest) //
.reduce(getZero(mc), (value, previous) -> value.plus(previous, mc)); //
.reduce(getZero(mc), (value, previous) -> value.plus(previous, mc)), mc); //
}
/**
@@ -230,7 +231,7 @@ public final class RepaymentPeriod {
*/
public Money getDuePrincipal() {
// Due principal might be the maximum paid if there is pay-off or early repayment
return MathUtil.max(getEmiPlusChargeback().minus(getDueInterest(), mc), getPaidPrincipal(), false);
return MathUtil.max(MathUtil.negativeToZero(getEmiPlusChargeback().minus(getDueInterest(), mc), mc), getPaidPrincipal(), false);
}
/**
@@ -331,6 +332,6 @@ public final class RepaymentPeriod {
}
public Money getOutstandingPrincipal() {
return getDuePrincipal().minus(getPaidPrincipal());
return MathUtil.negativeToZero(getDuePrincipal().minus(getPaidPrincipal()), mc);
}
}