FINERACT-2405: totalUnpaidPayableNotDueInterest resets to 0 on Charge-Off

This commit is contained in:
Jose Alberto Hernandez
2025-11-19 19:58:33 +01:00
committed by Adam Saghy
parent 2681ee3518
commit 47e8674fa0
7 changed files with 177 additions and 4 deletions
@@ -177,6 +177,7 @@ public enum DefaultLoanProduct implements LoanProduct {
LP2_ADV_CUSTOM_PMT_ALLOC_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_RECALC_ZERO_CHARGE_OFF_ACCRUAL, //
LP1_INTEREST_FLAT_DAILY_RECALCULATION_SAR_MULTIDISB_EXPECT_TRANCHES, //
LP1_INTEREST_FLAT_DAILY_ACTUAL_ACTUAL_MULTIDISB_EXPECT_TRANCHES, //
LP2_ADV_PYMNT_360_30_ZERO_INTEREST_CHARGE_OFF_ACCRUAL_ACTIVITY, //
;
@Override
@@ -403,6 +403,14 @@ public final class ErrorMessageHelper {
actualToStr, expectedToStr);
}
public static String wrongAmountInTotalUnpaidPayableNotDueInterest(Double actual, Double expected) {
String actualToStr = actual.toString();
String expectedToStr = expected.toString();
return String.format(
"Wrong amount in Loan total unpaid payable not due interest. Actual amount is: %s - But expected amount is: %s",
actualToStr, expectedToStr);
}
public static String wrongAmountInTotalOverdue(Double actual, Double expected) {
String actualToStr = actual.toString();
String expectedToStr = expected.toString();
@@ -4172,10 +4172,9 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ
// LP2 advanced + progressive loan schedule + horizontal + interest recalculation
// Frequency for recalculate Outstanding Principal: Daily, Frequency Interval for recalculation: 1
String name150 = DefaultLoanProduct.LP2_ADV_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_RECALC_ZERO_CHARGE_OF_ACCRUAL.getName();
PostLoanProductsRequest loanProductsResponseAdvCustomPaymentAllocationProgressiveLoanInterestDailyEmiActualInterestRecalcZeroChargeOffChargebackAccruals = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(name150)//
.name(DefaultLoanProduct.LP2_ADV_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_RECALC_ZERO_CHARGE_OF_ACCRUAL.getName())//
.supportedInterestRefundTypes(supportedInterestRefundTypes).installmentAmountInMultiplesOf(null) //
.daysInYearType(DaysInYearType.ACTUAL.value)//
.daysInMonthType(DaysInMonthType.ACTUAL.value)//
@@ -4218,6 +4217,23 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ
TestContext.INSTANCE.set(TestContextKey.LP1_INTEREST_FLAT_DAILY_RECALCULATION_SAR_MULTIDISB_EXPECT_TRANCHES,
responseLoanProductsRequestInterestFlatSaRRecalculationSameAsRepaymentMultiDisbursementExpectsTranches);
// LP2 + zero-interest chargeOff behaviour + progressive loan schedule + horizontal
// (LP2_ADV_PYMNT_360_30_ZERO_INTEREST_CHARGE_OFF_ACCRUAL_ACTIVITY)
final PostLoanProductsRequest loanProductsRequestAdvZeroInterestChargeOffBehaviourAccrualActivity = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.name(DefaultLoanProduct.LP2_ADV_PYMNT_360_30_ZERO_INTEREST_CHARGE_OFF_ACCRUAL_ACTIVITY.getName())//
.daysInYearType(DaysInYearType.DAYS360.value)//
.daysInMonthType(DaysInMonthType.DAYS30.value)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT")))
.enableAccrualActivityPosting(true)//
.chargeOffBehaviour("ZERO_INTEREST");//
final Response<PostLoanProductsResponse> responseLoanProductsRequestAdvZeroInterestChargeOffBehaviourAccrualActivity = loanProductsApi
.createLoanProduct(loanProductsRequestAdvZeroInterestChargeOffBehaviourAccrualActivity).execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_360_30_ZERO_INTEREST_CHARGE_OFF_ACCRUAL_ACTIVITY,
responseLoanProductsRequestAdvZeroInterestChargeOffBehaviourAccrualActivity);
// LP1 with 12% Flat interest, interest period: Daily, Interest recalculation- Actual
// Multi-disbursement that expects tranches
PostLoanProductsRequest loanProductsRequestInterestFlatActualActualMultiDisbursementExpectsTranches = loanProductsRequestFactory
@@ -6060,6 +6060,23 @@ public class LoanStepDef extends AbstractStepDef {
checkLoanAmortizationAllocationMappingData(resourceId, loanAmortizationAllocationResponse.body(), table);
}
@Then("Loan has {double} total unpaid payable not due interest")
public void loanTotalUnpaidPayableNotDueInterest(double totalUnpaidPayableNotDueInterestExpected) throws IOException {
Response<PostLoansResponse> loanCreateResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
long loanId = loanCreateResponse.body().getLoanId();
Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "repaymentSchedule", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
testContext().set(TestContextKey.LOAN_RESPONSE, loanDetailsResponse);
Double totalUnpaidPayableNotDueInterestActual = loanDetailsResponse.body().getSummary().getTotalUnpaidPayableNotDueInterest()
.doubleValue();
assertThat(totalUnpaidPayableNotDueInterestActual)
.as(ErrorMessageHelper.wrongAmountInTotalUnpaidPayableNotDueInterest(totalUnpaidPayableNotDueInterestActual,
totalUnpaidPayableNotDueInterestExpected))
.isEqualTo(totalUnpaidPayableNotDueInterestExpected);
}
private void checkLoanAmortizationAllocationMappingData(final String resourceId,
final LoanAmortizationAllocationResponse amortizationAllocationResponse, final DataTable table) {
final List<List<String>> data = table.asLists();
@@ -284,4 +284,5 @@ public abstract class TestContextKey {
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_CUSTOM_PMT_ALLOC_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_RECALC_ZERO_CHARGE_OFF_ACCRUAL = "loanProductCreateResponseLP2AdvancedPaymentCustomAllocationInterestDailyEmiActualInterestRecalcChargeOffAccruals";
public static final String LP1_INTEREST_FLAT_DAILY_RECALCULATION_SAR_MULTIDISB_EXPECT_TRANCHES = "loanProductCreateResponseLP1InterestFlatDailyRecalculationSameAsRepaymentMultiDisbursementExpectTranches";
public static final String LP1_INTEREST_FLAT_DAILY_ACTUAL_ACTUAL_MULTIDISB_EXPECT_TRANCHES = "loanProductCreateResponseLP1InterestFlatDailyActualActualMultiDisbursementExpectTranches";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_360_30_ZERO_INTEREST_CHARGE_OFF_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP2AdvancedPaymentZeroInterestChargeOffBehaviourAccrualActivity";
}
@@ -9949,3 +9949,130 @@ Feature: Charge-off
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 546.14 | 24.69 | 0.0 | 10.0 | 580.83 | 308.59 | 308.59 | 0.0 | 272.24 |
@TestRailId:C4153
Scenario: Verify that totalUnpaidPayableNotDueInterest doesn't get reset to 0 on the charge-off date
When Admin sets the business date to "01 May 2025"
And Admin creates a client with random data
And Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| LP2_ADV_PYMNT_360_30_ZERO_INTEREST_CHARGE_OFF_ACCRUAL_ACTIVITY | 01 May 2025 | 423.38 | 12.25 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 24 | MONTHS | 1 | MONTHS | 24 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 May 2025" with "423.38" amount and expected disbursement date on "01 May 2025"
And Admin successfully disburse the loan on "01 May 2025" with "423.38" EUR transaction amount
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 423.38 | 56.13 | 0.0 | 0.0 | 479.51 | 0.0 | 0.0 | 0.0 | 479.51 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 May 2025 | Disbursement | 423.38 | 0.0 | 0.0 | 0.0 | 0.0 | 423.38 |
When Admin sets the business date to "01 June 2025"
When Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "01 June 2025" with 19.98 EUR transaction amount and system-generated Idempotency key
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 423.38 | 56.13 | 0.0 | 0.0 | 479.51 | 19.98 | 0.0 | 0.0 | 459.53 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 May 2025 | Disbursement | 423.38 | 0.0 | 0.0 | 0.0 | 0.0 | 423.38 |
| 01 June 2025 | Repayment | 19.98 | 15.66 | 4.32 | 0.0 | 0.0 | 407.72 |
When Admin sets the business date to "01 July 2025"
When Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "01 July 2025" with 19.98 EUR transaction amount and system-generated Idempotency key
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 423.38 | 56.13 | 0.0 | 0.0 | 479.51 | 39.96 | 0.0 | 0.0 | 439.55 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 May 2025 | Disbursement | 423.38 | 0.0 | 0.0 | 0.0 | 0.0 | 423.38 |
| 01 June 2025 | Repayment | 19.98 | 15.66 | 4.32 | 0.0 | 0.0 | 407.72 |
| 01 July 2025 | Repayment | 19.98 | 15.82 | 4.16 | 0.0 | 0.0 | 391.9 |
When Admin sets the business date to "08 October 2025"
When Admin runs inline COB job for Loan
Then Loan has 11.51 total unpaid payable due interest
Then Loan has 0.79 total unpaid payable not due interest
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 423.38 | 56.13 | 0.0 | 0.0 | 479.51 | 39.96 | 0.0 | 0.0 | 439.55 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 May 2025 | Disbursement | 423.38 | 0.0 | 0.0 | 0.0 | 0.0 | 423.38 |
| 01 June 2025 | Repayment | 19.98 | 15.66 | 4.32 | 0.0 | 0.0 | 407.72 |
| 01 June 2025 | Accrual Activity | 4.32 | 0.0 | 4.32 | 0.0 | 0.0 | 0.0 |
| 01 July 2025 | Repayment | 19.98 | 15.82 | 4.16 | 0.0 | 0.0 | 391.9 |
| 01 July 2025 | Accrual Activity | 4.16 | 0.0 | 4.16 | 0.0 | 0.0 | 0.0 |
| 01 August 2025 | Accrual Activity | 4.0 | 0.0 | 4.0 | 0.0 | 0.0 | 0.0 |
| 01 September 2025 | Accrual Activity | 3.84 | 0.0 | 3.84 | 0.0 | 0.0 | 0.0 |
| 01 October 2025 | Accrual Activity | 3.67 | 0.0 | 3.67 | 0.0 | 0.0 | 0.0 |
| 07 October 2025 | Accrual | 20.67 | 0.0 | 20.67 | 0.0 | 0.0 | 0.0 |
And Admin does charge-off the loan on "08 October 2025"
Then Loan has 11.51 total unpaid payable due interest
Then Loan has 0.79 total unpaid payable not due interest
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 423.38 | 20.78 | 0.0 | 0.0 | 444.16 | 39.96 | 0.0 | 0.0 | 404.2 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 May 2025 | Disbursement | 423.38 | 0.0 | 0.0 | 0.0 | 0.0 | 423.38 |
| 01 June 2025 | Repayment | 19.98 | 15.66 | 4.32 | 0.0 | 0.0 | 407.72 |
| 01 June 2025 | Accrual Activity | 4.32 | 0.0 | 4.32 | 0.0 | 0.0 | 0.0 |
| 01 July 2025 | Repayment | 19.98 | 15.82 | 4.16 | 0.0 | 0.0 | 391.9 |
| 01 July 2025 | Accrual Activity | 4.16 | 0.0 | 4.16 | 0.0 | 0.0 | 0.0 |
| 01 August 2025 | Accrual Activity | 4.0 | 0.0 | 4.0 | 0.0 | 0.0 | 0.0 |
| 01 September 2025 | Accrual Activity | 3.84 | 0.0 | 3.84 | 0.0 | 0.0 | 0.0 |
| 01 October 2025 | Accrual Activity | 3.67 | 0.0 | 3.67 | 0.0 | 0.0 | 0.0 |
| 07 October 2025 | Accrual | 20.67 | 0.0 | 20.67 | 0.0 | 0.0 | 0.0 |
| 08 October 2025 | Accrual | 0.11 | 0.0 | 0.11 | 0.0 | 0.0 | 0.0 |
| 08 October 2025 | Charge-off | 404.2 | 391.9 | 12.3 | 0.0 | 0.0 | 0.0 |
@TestRailId:C4228
Scenario: Verify that totalUnpaidPayableNotDueInterest is correct when charge-off falls on a due date of an open repayment period
When Admin sets the business date to "01 May 2025"
And Admin creates a client with random data
And Admin creates a fully customized loan with the following data:
| LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| LP2_ADV_PYMNT_360_30_ZERO_INTEREST_CHARGE_OFF_ACCRUAL_ACTIVITY | 01 May 2025 | 423.38 | 12.25 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 24 | MONTHS | 1 | MONTHS | 24 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 May 2025" with "423.38" amount and expected disbursement date on "01 May 2025"
And Admin successfully disburse the loan on "01 May 2025" with "423.38" EUR transaction amount
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 423.38 | 56.13 | 0.0 | 0.0 | 479.51 | 0.0 | 0.0 | 0.0 | 479.51 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 May 2025 | Disbursement | 423.38 | 0.0 | 0.0 | 0.0 | 0.0 | 423.38 |
When Admin sets the business date to "01 June 2025"
When Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "01 June 2025" with 19.98 EUR transaction amount and system-generated Idempotency key
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 423.38 | 56.13 | 0.0 | 0.0 | 479.51 | 19.98 | 0.0 | 0.0 | 459.53 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 May 2025 | Disbursement | 423.38 | 0.0 | 0.0 | 0.0 | 0.0 | 423.38 |
| 01 June 2025 | Repayment | 19.98 | 15.66 | 4.32 | 0.0 | 0.0 | 407.72 |
When Admin sets the business date to "01 July 2025"
When Customer makes "REPAYMENT" transaction with "AUTOPAY" payment type on "01 July 2025" with 19.98 EUR transaction amount and system-generated Idempotency key
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 423.38 | 56.13 | 0.0 | 0.0 | 479.51 | 39.96 | 0.0 | 0.0 | 439.55 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 May 2025 | Disbursement | 423.38 | 0.0 | 0.0 | 0.0 | 0.0 | 423.38 |
| 01 June 2025 | Repayment | 19.98 | 15.66 | 4.32 | 0.0 | 0.0 | 407.72 |
| 01 July 2025 | Repayment | 19.98 | 15.82 | 4.16 | 0.0 | 0.0 | 391.9 |
When Admin sets the business date to "01 October 2025"
When Admin runs inline COB job for Loan
Then Loan has 11.51 total unpaid payable due interest
Then Loan has 0.0 total unpaid payable not due interest
Then Loan Repayment schedule has the following data in Total row:
| Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding |
| 423.38 | 56.13 | 0.0 | 0.0 | 479.51 | 39.96 | 0.0 | 0.0 | 439.55 |
Then Loan Transactions tab has the following data:
| Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance |
| 01 May 2025 | Disbursement | 423.38 | 0.0 | 0.0 | 0.0 | 0.0 | 423.38 |
| 01 June 2025 | Repayment | 19.98 | 15.66 | 4.32 | 0.0 | 0.0 | 407.72 |
| 01 June 2025 | Accrual Activity | 4.32 | 0.0 | 4.32 | 0.0 | 0.0 | 0.0 |
| 01 July 2025 | Repayment | 19.98 | 15.82 | 4.16 | 0.0 | 0.0 | 391.9 |
| 01 July 2025 | Accrual Activity | 4.16 | 0.0 | 4.16 | 0.0 | 0.0 | 0.0 |
| 01 August 2025 | Accrual Activity | 4.0 | 0.0 | 4.0 | 0.0 | 0.0 | 0.0 |
| 01 September 2025 | Accrual Activity | 3.84 | 0.0 | 3.84 | 0.0 | 0.0 | 0.0 |
| 30 September 2025 | Accrual | 19.87 | 0.0 | 19.87 | 0.0 | 0.0 | 0.0 |
And Admin does charge-off the loan on "01 October 2025"
Then Loan has 11.51 total unpaid payable due interest
Then Loan has 0.0 total unpaid payable not due interest
@@ -84,8 +84,11 @@ public class ProgressiveLoanSummaryDataProvider extends CommonLoanSummaryDataPro
if (currentRepaymentPeriod.isPresent()) {
if (loan.isChargedOff() || loan.hasContractTerminationTransaction()) {
return MathUtil.subtractToZero(currentRepaymentPeriod.get().getInterestOutstanding(loan.getCurrency()).getAmount(),
totalUnpaidPayableDueInterest);
if (currentRepaymentPeriod.get().getDueDate().isEqual(businessDate)) {
return BigDecimal.ZERO;
} else {
return currentRepaymentPeriod.get().getInterestOutstanding(loan.getCurrency()).getAmount();
}
} else {
Optional<ProgressiveLoanInterestScheduleModel> savedModel = modelRepository.getSavedModel(loan, businessDate);