FINERACT-2317: Added e2e tests covering support for modifying loan approved amounts with validation and history tracking

This commit is contained in:
Rustam Zeinalov
2025-07-21 17:13:51 +02:00
committed by Adam Saghy
parent 6d65f07924
commit ecbefb04c4
6 changed files with 258 additions and 1 deletions
@@ -144,6 +144,7 @@ public enum DefaultLoanProduct implements LoanProduct {
LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_APPROVED_OVER_APPLIED_PERCENTAGE_CAPITALIZED_INCOME, //
LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_APPROVED_OVER_APPLIED_FLAT_CAPITALIZED_INCOME, //
LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_MULTIDISB_CAPITALIZED_INCOME_ADJ_CUSTOM_ALLOC, //
LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_MULTIDISB_APPROVED_OVER_APPLIED_CAPITALIZED_INCOME, //
LP2_ADV_PYMNT_360_30_INTEREST_RECALC_AUTO_DOWNPAYMENT_ZERO_INTEREST_CHARGE_OFF_ACCRUAL_ACTIVITY, //
LP2_ADV_PYMNT_INTEREST_DAILY_INTEREST_RECALC_DAILY_INSTALLMENT_FEE_FLAT_CHARGES, //
LP2_ADV_PYMNT_INTEREST_DAILY_INSTALLMENT_FEE_PERCENT_AMOUNT_CHARGES, //
@@ -151,6 +152,7 @@ public enum DefaultLoanProduct implements LoanProduct {
LP2_ADV_PYMNT_INTEREST_DAILY_INSTALLMENT_FEE_PERCENT_AMOUNT_INTEREST_CHARGES, //
LP2_ADV_PYMNT_INTEREST_DAILY_INSTALLMENT_FEE_ALL_CHARGES, //
LP2_ADV_PYMNT_INTEREST_DAILY_INSTALLMENT_FEE_FLAT_INTEREST_CHARGES_TRANCHE, //
LP1_MULTIDISBURSAL_EXPECTS_TRANCHES, //
;
@Override
@@ -982,6 +982,10 @@ public final class ErrorMessageHelper {
}
public static String updateApprovedLoanExceedPrincipalFailure() {
return "Failed data validation due to: can't.be.greater.than.maximum.applied.loan.amount.calculation.";
}
public static String updateApprovedLoanLessThanDisbursedPrincipalAndCapitalizedIncomeFailure() {
return "Failed data validation due to: less.than.disbursed.principal.and.capitalized.income.";
}
@@ -3361,6 +3361,67 @@ public class LoanProductGlobalInitializerStep implements FineractGlobalInitializ
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_PROGRESSIVE_ADV_PYMNT_BUYDOWN_FEES_CHARGE_OFF_REASON,
responseLoanProductsRequestLP2ProgressiveAdvPaymentBuyDownFeesWithChargeOffReason);
// LP2 with progressive loan schedule + horizontal + interest EMI + 360/30
// + interest recalculation, preClosureInterestCalculationStrategy= till preclose,
// Frequency for recalculate Outstanding Principal: Daily, Frequency Interval for recalculation: 1
// capitalized income enabled; allow approved/disbursed amount over applied amount is enabled with percentage
// type
final String name129 = DefaultLoanProduct.LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_MULTIDISB_APPROVED_OVER_APPLIED_CAPITALIZED_INCOME
.getName();
final PostLoanProductsRequest loanProductsRequestLP2ProgressiveAdvPymnt36030InterestRecalcMultidisbApprovedOverAppliedCapitalizedIncome = loanProductsRequestFactory
.defaultLoanProductsRequestLP2EmiCapitalizedIncome()//
.name(name129)//
.daysInYearType(DaysInYearType.DAYS360.value)//
.daysInMonthType(DaysInMonthType.DAYS30.value)//
.isInterestRecalculationEnabled(true)//
.preClosureInterestCalculationStrategy(1)//
.rescheduleStrategyMethod(4)//
.interestRecalculationCompoundingMethod(0)//
.recalculationRestFrequencyType(2)//
.recalculationRestFrequencyInterval(1)//
.paymentAllocation(List.of(//
createPaymentAllocation("DEFAULT", "NEXT_INSTALLMENT"), //
createPaymentAllocation("GOODWILL_CREDIT", "LAST_INSTALLMENT"), //
createPaymentAllocation("MERCHANT_ISSUED_REFUND", "REAMORTIZATION"), //
createPaymentAllocation("PAYOUT_REFUND", "NEXT_INSTALLMENT"))) //
.allowApprovedDisbursedAmountsOverApplied(true)//
.overAppliedCalculationType(OverAppliedCalculationType.PERCENTAGE.value)//
.overAppliedNumber(50)//
.multiDisburseLoan(true)//
.disallowExpectedDisbursements(true)//
.maxTrancheCount(10)//
.outstandingLoanBalance(10000.0);//
final Response<PostLoanProductsResponse> responseLoanProductsRequestLP2ProgressiveAdvPymnt36030InterestRecalcMultidisbApprovedOverAppliedCapitalizedIncome = loanProductsApi
.createLoanProduct(
loanProductsRequestLP2ProgressiveAdvPymnt36030InterestRecalcMultidisbApprovedOverAppliedCapitalizedIncome)
.execute();
TestContext.INSTANCE.set(
TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_MULTIDISB_APPROVED_OVER_APPLIED_CAPITALIZED_INCOME,
responseLoanProductsRequestLP2ProgressiveAdvPymnt36030InterestRecalcMultidisbApprovedOverAppliedCapitalizedIncome);
// LP1 with new due-penalty-fee-interest-principal-in-advance-principal-penalty-fee-interest-strategy payment
// strategy and with 12% FLAT interest
// multidisbursal that expects tranche(s)
// (LP1_PAYMENT_STRATEGY_DUE_IN_ADVANCE_INTEREST_FLAT)
String name130 = DefaultLoanProduct.LP1_MULTIDISBURSAL_EXPECTS_TRANCHES.getName();
PostLoanProductsRequest loanProductsRequestMultidisbursalExpectTranches = loanProductsRequestFactory
// .defaultLoanProductsRequestLP1InterestFlat()//
// .interestType(INTEREST_TYPE_DECLINING_BALANCE)//
.defaultLoanProductsRequestLP1() //
.interestCalculationPeriodType(0)//
// .allowApprovedDisbursedAmountsOverApplied(false)//
.name(name130)//
.transactionProcessingStrategyCode(
TransactionProcessingStrategyCode.DUE_PENALTY_FEE_INTEREST_PRINCIPAL_IN_ADVANCE_PRINCIPAL_PENALTY_FEE_INTEREST.value)//
.multiDisburseLoan(true)//
.disallowExpectedDisbursements(false)//
.maxTrancheCount(10)//
.outstandingLoanBalance(10000.0);//
Response<PostLoanProductsResponse> responseLoanProductMultidisbursalExpectTranches = loanProductsApi
.createLoanProduct(loanProductsRequestMultidisbursalExpectTranches).execute();
TestContext.INSTANCE.set(TestContextKey.DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_MULTIDISBURSAL_EXPECTS_TRANCHES,
responseLoanProductMultidisbursalExpectTranches);
}
public static AdvancedPaymentData createPaymentAllocation(String transactionType, String futureInstallmentAllocationRule,
@@ -4932,6 +4932,33 @@ public class LoanStepDef extends AbstractStepDef {
assertThat(developerMessage).isEqualTo(ErrorMessageHelper.updateApprovedLoanExceedPrincipalFailure());
}
@Then("Update loan approved amount is forbidden with amount {string} due to higher principal amount on loan")
public void updateLoanApprovedAmountForbiddenHigherPrincipalAmountOnLoan(final String amount) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
final long loanId = loanResponse.body().getLoanId();
final Response<GetLoansLoanIdResponse> loanDetailsResponse = loansApi.retrieveLoan(loanId, false, "transactions", "", "").execute();
ErrorHelper.checkSuccessfulApiCall(loanDetailsResponse);
final PutLoansApprovedAmountRequest modifyLoanApprovedAmountRequest = new PutLoansApprovedAmountRequest().locale(LOCALE_EN)
.amount(new BigDecimal(amount));
final Response<PutLoansApprovedAmountResponse> modifyLoanApprovedAmountResponse = loansApi
.modifyLoanApprovedAmount(loanId, modifyLoanApprovedAmountRequest).execute();
ErrorResponse errorDetails = ErrorResponse.from(modifyLoanApprovedAmountResponse);
assertThat(errorDetails.getHttpStatusCode()).isEqualTo(403);
Object errorArgs = errorDetails.getErrors().getFirst().getArgs().getFirst().getValue();
String developerMessage;
if (errorArgs instanceof Map errorArgsMap) {
developerMessage = (String) errorArgsMap.get("developerMessage");
} else {
developerMessage = errorDetails.getDeveloperMessage();
}
assertThat(developerMessage)
.isEqualTo(ErrorMessageHelper.updateApprovedLoanLessThanDisbursedPrincipalAndCapitalizedIncomeFailure());
}
@Then("Update loan approved amount is forbidden with amount {string} due to min allowed amount")
public void updateLoanApprovedAmountForbiddenMinAllowedAmount(final String amount) throws IOException {
final Response<PostLoansResponse> loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
@@ -4946,7 +4973,7 @@ public class LoanStepDef extends AbstractStepDef {
.modifyLoanApprovedAmount(loanId, modifyLoanApprovedAmountRequest).execute();
ErrorResponse errorDetails = ErrorResponse.from(modifyLoanApprovedAmountResponse);
assertThat(errorDetails.getHttpStatusCode()).isEqualTo(400);
assertThat(errorDetails.getHttpStatusCode()).isEqualTo(403);
Object errorArgs = errorDetails.getErrors().getFirst().getArgs().getFirst().getValue();
String developerMessage;
@@ -161,6 +161,7 @@ public abstract class TestContextKey {
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_MULTIDISB_OVER_APPLIED_PERCENTAGE_CAPITALIZED_INCOME = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyMultidisbursalApprovedOVerAppliedPercentageCapitalizedIncome";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_MULTIDISB_OVER_APPLIED_FLAT_CAPITALIZED_INCOME = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyMultidisbursalApprovedOVerAppliedFlatCapitalizedIncome";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_APPROVED_OVER_APPLIED_PERCENTAGE_CAPITALIZED_INCOME = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyApprovedOVerAppliedAmountPercentageCapitalizedIncome";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_MULTIDISB_APPROVED_OVER_APPLIED_CAPITALIZED_INCOME = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalcDailyMultidisbursalApprovedOVerAppliedAmountPercentageCapitalizedIncome";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_RECALC_EMI_360_30_APPROVED_OVER_APPLIED_FLAT_CAPITALIZED_INCOME = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyApprovedOVerAppliedAmountFlatCapitalizedIncome";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_CAPITALIZED_INCOME = "loanProductCreateResponseLP2ProgressiveAdvPayment36030InterestRecalcCapitalizedIncome";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_RECALC_DAILY_CAPITALIZED_INCOME_FEE = "loanProductCreateResponseLP2ProgressiveAdvPayment36030InterestRecalcCapitalizedIncomeFee";
@@ -176,6 +177,7 @@ public abstract class TestContextKey {
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_IR_DAILY_TILL_REST_FREQUENCY_DATE_LAST_INSTALLMENT = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRecalculationDailyTillRestFrequencyDateLastInstallment";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_REFUND_INTEREST_RECALC_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRefundInterestRecalculationAccrualActivity";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP2_ADV_PYMNT_INTEREST_DAILY_EMI_360_30_INTEREST_REFUND_INTEREST_RECALC_DOWNPAYMENT_ACCRUAL_ACTIVITY = "loanProductCreateResponseLP2AdvancedPaymentInterestDailyEmi36030InterestRefundInterestRecalculatioDownpaymentnAccrualActivity";
public static final String DEFAULT_LOAN_PRODUCT_CREATE_RESPONSE_LP1_MULTIDISBURSAL_EXPECTS_TRANCHES = "loanProductCreateResponseLP1MultidisbursalThatExpectTranches";
public static final String CHARGE_FOR_LOAN_PERCENT_LATE_CREATE_RESPONSE = "ChargeForLoanPercentLateCreateResponse";
public static final String CHARGE_FOR_LOAN_PERCENT_LATE_AMOUNT_PLUS_INTEREST_CREATE_RESPONSE = "ChargeForLoanPercentLateAmountPlusInterestCreateResponse";
public static final String CHARGE_FOR_LOAN_PERCENT_PROCESSING_CREATE_RESPONSE = "ChargeForLoanPercentProcessingCreateResponse";