Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ public enum DefaultLoanProduct implements LoanProduct {
LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_INTEREST_REFUND_INTEREST_RECALCULATION_MULTIDISB, //
LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_ZERO_INTEREST_CHARGE_OFF_BEHAVIOUR, //
LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALCULATION_ZERO_INTEREST_CHARGE_OFF, //
LP2_ADV_PYMNT_INT_DAILY_EMI_ACTUAL_ACTUAL_INT_REFUND_FULL_ZERO_INT_CHARGE_OFF_ACC_LAST_INSTALLMENT, //
LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF_BEHAVIOUR, //
LP2_ADV_PYMNT_ZERO_INTEREST_CHARGE_OFF, //
LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL_ZERO_CHARGE_OFF, //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2169,12 +2169,20 @@ public void loanOverpaid(double totalOverpaidExpected) {
() -> fineractClient.loans().retrieveLoan(loanId, Map.of("staffInSelectedOfficeOnly", "false")));
testContext().set(TestContextKey.LOAN_RESPONSE, loanDetailsResponse);

Double totalOverpaidActual = loanDetailsResponse.getTotalOverpaid().doubleValue();
Double totalOutstandingActual = loanDetailsResponse.getSummary().getTotalOutstanding().doubleValue();
double totalOutstandingExpected = 0.0;
assertThat(totalOutstandingActual)
.as(ErrorMessageHelper.wrongAmountInTotalOutstanding(totalOutstandingActual, totalOutstandingExpected))
.isEqualTo(totalOutstandingExpected);
// Loan API omits `totalOverpaid` when the loan has no overpayment (CLOSED_OBLIGATIONS_MET with zero
// overpayment). Tolerate that omission only when the scenario expects 0.0 - otherwise fail loudly so
// a regression where the API stops emitting the field is caught immediately.
double totalOverpaidActual = Optional.ofNullable(loanDetailsResponse.getTotalOverpaid()).map(BigDecimal::doubleValue)
.orElseGet(() -> {
assertThat(totalOverpaidExpected)
.as("Loan API returned null totalOverpaid but scenario expected %s", totalOverpaidExpected).isEqualTo(0.0);
return 0.0;
});
assertThat(totalOverpaidActual)
.as(ErrorMessageHelper.wrongAmountInTransactionsOverpayment(totalOverpaidActual, totalOverpaidExpected))
.isEqualTo(totalOverpaidExpected);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ public abstract class TestContextKey {
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_ACTUAL_ACTUAL_NO_INTEREST_RECALC_REFUND_FULL = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmiActualActualNoInterestRecalcRefundFull";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_ACTUAL_ACTUAL_INT_REFUND_FULL_ZERO_INT_CHARGE_OFF = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmiActualActualInterestRefundFullZeroInterestChargeOff";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_ACTUAL_ACTUAL_INT_REFUND_FULL_ZERO_INT_CHARGE_OFF_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmiActualActualInterestRefundFullZeroInterestChargeOffAccrualActivity";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_ACTUAL_ACTUAL_INT_REFUND_FULL_ZERO_INT_CHARGE_OFF_ACC_LAST_INSTALLMENT = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmiActualActualInterestRefundFullZeroInterestChargeOffAccrualActivityLastInstallment";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_ACTUAL_ACTUAL_NO_INTEREST_RECALC_INT_REFUND_FULL_ZERO_INT_CHARGE_OFF = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmiActualActualNoInterestRecalcInterestRefundFullZeroInterestChargeOff";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_ACTUAL_ACTUAL_INT_REFUND_FULL_ACCELERATE_MATURITY_CHARGE_OFF = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmiActualActualInterestRefundFullAccelerateMaturityChargeOff";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_ACTUAL_ACTUAL_NO_INTEREST_RECALC_INT_REFUND_FULL_ACC_MATUR_CHARGE_OFF = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmiActualActualNoInterestRecalcInterestRefundFullAccelerateMaturityChargeOff";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4961,6 +4961,47 @@ public void initialize() throws Exception {
responseLoanProductsRequestLP2AdvPaymentIntEmiActualActualIntRefundFullZeroIntChargeOffAccrualActivity);
});

tasks.add(() -> {
// LP2 + zero-interest chargeOff behaviour + progressive loan schedule + horizontal + interest recalculation
// + accrual activity posting, with LAST_INSTALLMENT future-installment allocation rule baked in for every
// advanced-allocation transaction type - exercises code paths that the more common NEXT_INSTALLMENT
// configuration does not reach.
// (LP2_ADV_PYMNT_INT_DAILY_EMI_ACTUAL_ACTUAL_INT_REFUND_FULL_ZERO_INT_CHARGE_OFF_ACC_LAST_INSTALLMENT)
final String nameLastInst = DefaultLoanProduct.LP2_ADV_PYMNT_INT_DAILY_EMI_ACTUAL_ACTUAL_INT_REFUND_FULL_ZERO_INT_CHARGE_OFF_ACC_LAST_INSTALLMENT
.getName();
final PostLoanProductsRequest loanProductsRequestLP2AdvPaymentIntEmiActualActualIntRefundFullZeroIntChargeOffAccLastInstallment = loanProductsRequestFactory
.defaultLoanProductsRequestLP2Emi()//
.multiDisburseLoan(true)//
.disallowExpectedDisbursements(true)//
.maxTrancheCount(500)//
.isInterestRecalculationEnabled(true)//
.preClosureInterestCalculationStrategy(1)//
.rescheduleStrategyMethod(4)//
.interestRecalculationCompoundingMethod(0)//
.recalculationRestFrequencyType(2)//
.recalculationRestFrequencyInterval(1)//
.supportedInterestRefundTypes(supportedInterestRefundTypes).paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "LAST_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "LAST_INSTALLMENT"), //
createPaymentAllocation("PAYOUT_REFUND", "LAST_INSTALLMENT"), //
createPaymentAllocation("INTEREST_REFUND", "LAST_INSTALLMENT")))
.name(nameLastInst)//
.shortName(loanProductsRequestFactory.generateShortNameSafely())//
.enableAccrualActivityPosting(true)//
.allowApprovedDisbursedAmountsOverApplied(true)//
.overAppliedCalculationType(OverAppliedCalculationType.FIXED_SIZE.value)//
.overAppliedNumber(1000)//
.enableInstallmentLevelDelinquency(true)//
.interestRecognitionOnDisbursementDate(true)//
.chargeOffBehaviour("ZERO_INTEREST");//
final PostLoanProductsResponse responseLoanProductsRequestLP2AdvPaymentIntEmiActualActualIntRefundFullZeroIntChargeOffAccLastInstallment = createLoanProductIdempotent(
loanProductsRequestLP2AdvPaymentIntEmiActualActualIntRefundFullZeroIntChargeOffAccLastInstallment);
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INT_DAILY_EMI_ACTUAL_ACTUAL_INT_REFUND_FULL_ZERO_INT_CHARGE_OFF_ACC_LAST_INSTALLMENT,
responseLoanProductsRequestLP2AdvPaymentIntEmiActualActualIntRefundFullZeroIntChargeOffAccLastInstallment);
});

tasks.add(() -> {
// LP2 with progressive loan schedule + horizontal + interest recalculation daily EMI + 360/30 +
// multidisbursement with full term tranche enabled
Expand Down
Loading
Loading