FINERACT-1971: Fix EMI Calculator: Last Unpaid Repayment Period Handling.

call calculateLastUnpaidRepaymentPeriodEMI recursively when emi would be less than already paid to adjust remaining emi correction for previous period.
Fixing AdvancedPaymentScheduleTransactionProcessor to handle division by installment count adjustment part in case of IN_ADVANCED payment
This commit is contained in:
Soma Sörös
2025-01-14 13:24:02 +00:00
committed by Adam Saghy
parent d2bdbf843a
commit 2151b19e27
3 changed files with 121 additions and 6 deletions
@@ -1689,16 +1689,16 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
transactionMappings, loanTransaction, inAdvanceInstallment, currency);
Loan loan = loanTransaction.getLoan();
// Adjust the portion for the last installment
if (inAdvanceInstallment.equals(inAdvanceInstallments.get(numberOfInstallments - 1))) {
evenPortion = evenPortion.add(balanceAdjustment);
}
if (transactionCtx instanceof ProgressiveTransactionCtx ctx && loan.isInterestBearing()
&& loan.getLoanProductRelatedDetail().isInterestRecalculationEnabled() && !ctx.isChargedOff()) {
paidPortion = handlingPaymentAllocationForInterestBearingProgressiveLoan(loanTransaction, evenPortion,
balances, paymentAllocationType, inAdvanceInstallment, ctx,
loanTransactionToRepaymentScheduleMapping, inAdvanceInstallmentCharges);
} else {
// Adjust the portion for the last installment
if (inAdvanceInstallment.equals(inAdvanceInstallments.get(numberOfInstallments - 1))) {
evenPortion = evenPortion.add(balanceAdjustment);
}
paidPortion = processPaymentAllocation(paymentAllocationType, inAdvanceInstallment, loanTransaction,
evenPortion, loanTransactionToRepaymentScheduleMapping, inAdvanceInstallmentCharges, balances,
LoanRepaymentScheduleInstallment.PaymentAction.PAY);
@@ -309,8 +309,8 @@ public final class ProgressiveEMICalculator implements EMICalculator {
.reduce((first, second) -> second);
findLastUnpaidRepaymentPeriod.ifPresent(repaymentPeriod -> {
repaymentPeriod.setEmi(repaymentPeriod.getEmi().add(diff, mc));
if (repaymentPeriod.getEmi().isLessThanZero()) {
repaymentPeriod.setEmi(repaymentPeriod.getEmi().zero());
if (repaymentPeriod.getEmi().isLessThan(repaymentPeriod.getTotalPaidAmount())) {
repaymentPeriod.setEmi(repaymentPeriod.getTotalPaidAmount());
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel);
}
});
@@ -30,6 +30,7 @@ import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import java.math.BigDecimal;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
@@ -128,6 +129,120 @@ public class LoanInterestRefundTest extends BaseLoanIntegrationTest {
});
}
@Test
public void verifyFullMerchantIssuedRefundWithReAmortizationOnDay0HighInterest6month() {
runAt("1 January 2021", () -> {
PostLoanProductsResponse loanProduct = loanProductHelper
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
.daysInYearType(DaysInYearType.ACTUAL) //
.addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.MERCHANT_ISSUED_REFUND) //
.paymentAllocation(List.of(createDefaultPaymentAllocation("REAMORTIZATION")))//
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
);
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 600.0, 60.0,
6, null);
Assertions.assertNotNull(loanId);
disburseLoan(loanId, BigDecimal.valueOf(600), "1 January 2021");
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
.makeLoanRepayment("MerchantIssuedRefund", "1 January 2021", 600F, loanId.intValue());
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
verifyTransactions(loanId, transaction(600.0, "Disbursement", "01 January 2021"), //
transaction(600.0, "Merchant Issued Refund", "01 January 2021") //
);
verifyRepaymentSchedule(loanId, installment(600.0, null, "01 January 2021"), //
fullyRepaidInstallment(100.0, 0.0, "01 February 2021"), //
fullyRepaidInstallment(100.0, 0.0, "01 March 2021"), //
fullyRepaidInstallment(100.0, 0.0, "01 April 2021"), //
fullyRepaidInstallment(100.0, 0.0, "01 May 2021"), //
fullyRepaidInstallment(100.0, 0.0, "01 June 2021"), //
fullyRepaidInstallment(100.0, 0.0, "01 July 2021") //
);
});
}
@Test
public void verifyAlmostFullMerchantIssuedRefundWithReAmortizationOnDay0HighInterest12month() {
runAt("1 January 2021", () -> {
PostLoanProductsResponse loanProduct = loanProductHelper
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
.daysInYearType(DaysInYearType.ACTUAL) //
.addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.MERCHANT_ISSUED_REFUND) //
.paymentAllocation(List.of(createDefaultPaymentAllocation("REAMORTIZATION")))//
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
);
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 26.0,
12, null);
Assertions.assertNotNull(loanId);
disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
.makeLoanRepayment("MerchantIssuedRefund", "1 January 2021", 980F, loanId.intValue());
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
transaction(980.0, "Merchant Issued Refund", "01 January 2021") //
);
verifyRepaymentSchedule(loanId, installment(1000.0, null, "01 January 2021"), //
installment(95.04, 0.44, 13.81, false, "01 February 2021"), //
installment(88.30, 0.13, 6.76, false, "01 March 2021"), //
fullyRepaidInstallment(81.67, 0.0, "01 April 2021"), //
fullyRepaidInstallment(81.67, 0.0, "01 May 2021"), //
fullyRepaidInstallment(81.67, 0.0, "01 June 2021"), //
fullyRepaidInstallment(81.67, 0.0, "01 July 2021"), //
fullyRepaidInstallment(81.67, 0.0, "01 August 2021"), //
fullyRepaidInstallment(81.67, 0.0, "01 September 2021"), //
fullyRepaidInstallment(81.67, 0.0, "01 October 2021"), //
fullyRepaidInstallment(81.67, 0.0, "01 November 2021"), //
fullyRepaidInstallment(81.67, 0.0, "01 December 2021"), //
fullyRepaidInstallment(81.63, 0.0, "01 January 2022") //
);
});
}
@Test
public void verifyFullMerchantIssuedRefundWithReAmortizationOnDay0HighInterest12month() {
runAt("1 January 2021", () -> {
PostLoanProductsResponse loanProduct = loanProductHelper
.createLoanProduct(create4IProgressive().daysInMonthType(DaysInMonthType.ACTUAL) //
.daysInYearType(DaysInYearType.ACTUAL) //
.addSupportedInterestRefundTypesItem(SupportedInterestRefundTypesItem.MERCHANT_ISSUED_REFUND) //
.paymentAllocation(List.of(createDefaultPaymentAllocation("REAMORTIZATION")))//
.recalculationRestFrequencyType(RecalculationRestFrequencyType.DAILY) //
);
Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProduct.getResourceId(), "1 January 2021", 1000.0, 26.0,
12, null);
Assertions.assertNotNull(loanId);
disburseLoan(loanId, BigDecimal.valueOf(1000), "1 January 2021");
PostLoansLoanIdTransactionsResponse postLoansLoanIdTransactionsResponse = loanTransactionHelper
.makeLoanRepayment("MerchantIssuedRefund", "1 January 2021", 1000F, loanId.intValue());
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse);
Assertions.assertNotNull(postLoansLoanIdTransactionsResponse.getResourceId());
verifyTransactions(loanId, transaction(1000.0, "Disbursement", "01 January 2021"), //
transaction(1000.0, "Merchant Issued Refund", "01 January 2021") //
);
verifyRepaymentSchedule(loanId, installment(1000.0, null, "01 January 2021"), //
fullyRepaidInstallment(83.33, 0.0, "01 February 2021"), //
fullyRepaidInstallment(83.33, 0.0, "01 March 2021"), //
fullyRepaidInstallment(83.33, 0.0, "01 April 2021"), //
fullyRepaidInstallment(83.33, 0.0, "01 May 2021"), //
fullyRepaidInstallment(83.33, 0.0, "01 June 2021"), //
fullyRepaidInstallment(83.33, 0.0, "01 July 2021"), //
fullyRepaidInstallment(83.33, 0.0, "01 August 2021"), //
fullyRepaidInstallment(83.33, 0.0, "01 September 2021"), //
fullyRepaidInstallment(83.33, 0.0, "01 October 2021"), //
fullyRepaidInstallment(83.33, 0.0, "01 November 2021"), //
fullyRepaidInstallment(83.33, 0.0, "01 December 2021"), //
fullyRepaidInstallment(83.37, 0.0, "01 January 2022") //
);
});
}
@Test
public void verifyInterestRefundCreatedForPayoutRefund() {
AtomicReference<Long> loanIdRef = new AtomicReference<>();