FINERACT-2181: Prevent Infinite Loop on MIR when last installment principal got updated to null

This commit is contained in:
Soma Sörös
2025-06-18 19:43:06 +02:00
committed by Adam Saghy
parent a21fe48c79
commit 74d079bc3d
@@ -1649,14 +1649,12 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
Money penaltyChargesPortion = Money.zero(transactionCtx.getCurrency()); Money penaltyChargesPortion = Money.zero(transactionCtx.getCurrency());
if (transactionCtx.getInstallments().stream().anyMatch(this::isNotObligationsMet)) { if (transactionCtx.getInstallments().stream().anyMatch(this::isNotObligationsMet)) {
if (loanTransaction.getLoan().isProgressiveSchedule()) { if (LoanChargeOffBehaviour.ZERO_INTEREST
if (LoanChargeOffBehaviour.ZERO_INTEREST .equals(loanTransaction.getLoan().getLoanProductRelatedDetail().getChargeOffBehaviour())) {
.equals(loanTransaction.getLoan().getLoanProductRelatedDetail().getChargeOffBehaviour())) { handleZeroInterestChargeOff(loanTransaction, transactionCtx);
handleZeroInterestChargeOff(loanTransaction, transactionCtx); } else if (LoanChargeOffBehaviour.ACCELERATE_MATURITY
} else if (LoanChargeOffBehaviour.ACCELERATE_MATURITY .equals(loanTransaction.getLoan().getLoanProductRelatedDetail().getChargeOffBehaviour())) {
.equals(loanTransaction.getLoan().getLoanProductRelatedDetail().getChargeOffBehaviour())) { handleAccelerateMaturityDate(loanTransaction, transactionCtx);
handleAccelerateMaturityDate(loanTransaction, transactionCtx);
}
} }
final BigDecimal newInterest = getInterestTillChargeOffForPeriod(loanTransaction.getLoan(), final BigDecimal newInterest = getInterestTillChargeOffForPeriod(loanTransaction.getLoan(),
@@ -1809,8 +1807,9 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
final BigDecimal interestOutstanding = installment.getInterestOutstanding(currency).getAmount(); final BigDecimal interestOutstanding = installment.getInterestOutstanding(currency).getAmount();
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(), final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(),
installment.getDueDate(), transactionDate, true).getAmount(); installment.getDueDate(), transactionDate, true).getAmount();
if (interestOutstanding.compareTo(BigDecimal.ZERO) > 0 || newInterest.compareTo(BigDecimal.ZERO) > 0) { if (MathUtil.isGreaterThanZero(interestOutstanding) || MathUtil.isGreaterThanZero(newInterest)) {
final BigDecimal interestRemoved = installment.getInterestCharged().subtract(newInterest); final BigDecimal interestRemoved = MathUtil.subtract(MathUtil.nullToZero(installment.getInterestCharged()),
newInterest);
installment.updatePrincipal(MathUtil.nullToZero(installment.getPrincipal()).add(interestRemoved)); installment.updatePrincipal(MathUtil.nullToZero(installment.getPrincipal()).add(interestRemoved));
installment.updateInterestCharged(newInterest); installment.updateInterestCharged(newInterest);
} }
@@ -1845,6 +1844,9 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
final BigDecimal installmentPrincipal = MathUtil.nullToZero(installment.getPrincipal()); final BigDecimal installmentPrincipal = MathUtil.nullToZero(installment.getPrincipal());
installment.updatePrincipal(MathUtil.negativeToZero(installmentPrincipal.add(principalBalance))); installment.updatePrincipal(MathUtil.negativeToZero(installmentPrincipal.add(principalBalance)));
if (!installment.isObligationsMet()) {
installment.checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
}
if (MathUtil.isLessThanOrEqualTo(MathUtil.abs(principalBalance), installmentPrincipal)) { if (MathUtil.isLessThanOrEqualTo(MathUtil.abs(principalBalance), installmentPrincipal)) {
principalBalance = BigDecimal.ZERO; principalBalance = BigDecimal.ZERO;
} else { } else {
@@ -2215,10 +2217,9 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
paymentAllocationContext.setTransactionAmountUnprocessed(transactionAmountUnprocessed); paymentAllocationContext.setTransactionAmountUnprocessed(transactionAmountUnprocessed);
boolean interestBearingAndInterestRecalculationEnabled = loanTransaction.getLoan() boolean interestBearingAndInterestRecalculationEnabled = loanTransaction.getLoan()
.isInterestBearingAndInterestRecalculationEnabled(); .isInterestBearingAndInterestRecalculationEnabled();
boolean isProgressiveCtx = ctx instanceof ProgressiveTransactionCtx;
if (isProgressiveCtx && interestBearingAndInterestRecalculationEnabled) { if (interestBearingAndInterestRecalculationEnabled && ctx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
ProgressiveTransactionCtx progressiveTransactionCtx = (ProgressiveTransactionCtx) ctx; && !progressiveTransactionCtx.isChargedOff() && !progressiveTransactionCtx.isContractTerminated()) {
// Clear any previously skipped installments before re-evaluating // Clear any previously skipped installments before re-evaluating
progressiveTransactionCtx.getSkipRepaymentScheduleInstallments().clear(); progressiveTransactionCtx.getSkipRepaymentScheduleInstallments().clear();
paymentAllocationContext paymentAllocationContext