|
54 | 54 | import org.apache.fineract.client.models.GetLoansLoanIdResponse; |
55 | 55 | import org.apache.fineract.client.models.GetLoansLoanIdTransactions; |
56 | 56 | import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTemplateResponse; |
| 57 | +import org.apache.fineract.client.models.GetLoansLoanIdTransactionsTransactionIdResponse; |
57 | 58 | import org.apache.fineract.client.models.LoanProduct; |
58 | 59 | import org.apache.fineract.client.models.PaymentAllocationOrder; |
59 | 60 | import org.apache.fineract.client.models.PostClientsResponse; |
|
69 | 70 | import org.apache.fineract.client.models.PostLoansRequest; |
70 | 71 | import org.apache.fineract.client.models.PostLoansResponse; |
71 | 72 | import org.apache.fineract.client.models.PostUpdateRescheduleLoansRequest; |
| 73 | +import org.apache.fineract.client.models.PutGlobalConfigurationsRequest; |
72 | 74 | import org.apache.fineract.client.models.PutLoansLoanIdRequest; |
73 | 75 | import org.apache.fineract.client.util.CallFailedRuntimeException; |
74 | 76 | import org.apache.fineract.infrastructure.configuration.api.GlobalConfigurationConstants; |
75 | 77 | import org.apache.fineract.integrationtests.common.BusinessDateHelper; |
| 78 | +import org.apache.fineract.integrationtests.common.BusinessStepHelper; |
76 | 79 | import org.apache.fineract.integrationtests.common.ClientHelper; |
77 | 80 | import org.apache.fineract.integrationtests.common.CommonConstants; |
78 | 81 | import org.apache.fineract.integrationtests.common.LoanRescheduleRequestHelper; |
@@ -136,6 +139,10 @@ public static void setup() { |
136 | 139 | commonLoanProductId = createLoanProduct("500", "15", "4", true, "25", true, LoanScheduleType.PROGRESSIVE, |
137 | 140 | LoanScheduleProcessingType.HORIZONTAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount); |
138 | 141 | client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()); |
| 142 | + // setup COB Business Steps to prevent test failing due other integration test configurations |
| 143 | + new BusinessStepHelper().updateSteps("LOAN_CLOSE_OF_BUSINESS", "APPLY_CHARGE_TO_OVERDUE_LOANS", "LOAN_DELINQUENCY_CLASSIFICATION", |
| 144 | + "CHECK_LOAN_REPAYMENT_DUE", "CHECK_LOAN_REPAYMENT_OVERDUE", "UPDATE_LOAN_ARREARS_AGING", "ADD_PERIODIC_ACCRUAL_ENTRIES", |
| 145 | + "EXTERNAL_ASSET_OWNER_TRANSFER", "CHECK_DUE_INSTALLMENTS", "ACCRUAL_ACTIVITY_POSTING", "LOAN_INTEREST_RECALCULATION"); |
139 | 146 | } |
140 | 147 |
|
141 | 148 | // UC1: Simple payments |
@@ -6146,6 +6153,104 @@ public void uc156() { |
6146 | 6153 | }); |
6147 | 6154 | } |
6148 | 6155 |
|
| 6156 | + // UC157: Progressive loan with Accrual Activity reverse-replay |
| 6157 | + @Test |
| 6158 | + public void uc157() { |
| 6159 | + globalConfigurationHelper.updateGlobalConfiguration(GlobalConfigurationConstants.ENABLE_AUTO_GENERATED_EXTERNAL_ID, |
| 6160 | + new PutGlobalConfigurationsRequest().enabled(true)); |
| 6161 | + final String operationDate = "13 September 2025"; |
| 6162 | + AtomicLong createdLoanId = new AtomicLong(); |
| 6163 | + GetLoansLoanIdTransactions[] accrualActivityId = new GetLoansLoanIdTransactions[1]; |
| 6164 | + final BigDecimal interestRatePerPeriod = BigDecimal.valueOf(11.32); |
| 6165 | + final BigDecimal principalAmount = BigDecimal.valueOf(135.94); |
| 6166 | + final Integer delinquencyBucketId = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec, List.of(// |
| 6167 | + Pair.of(1, 10), // |
| 6168 | + Pair.of(11, 30), // |
| 6169 | + Pair.of(31, 60), // |
| 6170 | + Pair.of(61, null)// |
| 6171 | + )); |
| 6172 | + |
| 6173 | + runAt(operationDate, () -> { |
| 6174 | + final ArrayList<String> interestRefundTypes = new ArrayList<String>(); |
| 6175 | + interestRefundTypes.add("PAYOUT_REFUND"); |
| 6176 | + interestRefundTypes.add("MERCHANT_ISSUED_REFUND"); |
| 6177 | + Long clientId = ClientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId(); |
| 6178 | + PostLoanProductsRequest product = createOnePeriod30DaysLongNoInterestPeriodicAccrualProductWithAdvancedPaymentAllocation() |
| 6179 | + .interestRatePerPeriod(interestRatePerPeriod.doubleValue()).interestRateFrequencyType(YEARS)// |
| 6180 | + .daysInMonthType(DaysInMonthType.DAYS_30)// |
| 6181 | + .daysInYearType(DaysInYearType.DAYS_360)// |
| 6182 | + .numberOfRepayments(6)// |
| 6183 | + .repaymentEvery(1)// |
| 6184 | + .repaymentFrequencyType(2L)// |
| 6185 | + .chargeOffBehaviour("ZERO_INTEREST")// |
| 6186 | + .repaymentFrequencyType(RepaymentFrequencyType.MONTHS.longValue())// |
| 6187 | + .repaymentStartDateType(LoanProduct.RepaymentStartDateTypeEnum.SUBMITTED_ON_DATE.ordinal())// |
| 6188 | + .enableDownPayment(false)// |
| 6189 | + .enableAccrualActivityPosting(true)// |
| 6190 | + .allowPartialPeriodInterestCalcualtion(null)// |
| 6191 | + .enableAutoRepaymentForDownPayment(null)// |
| 6192 | + .isInterestRecalculationEnabled(true)// |
| 6193 | + .delinquencyBucketId(delinquencyBucketId.longValue())// |
| 6194 | + .enableInstallmentLevelDelinquency(true)// |
| 6195 | + .interestRecalculationCompoundingMethod(0)// |
| 6196 | + .interestCalculationPeriodType(InterestCalculationPeriodType.DAILY)// |
| 6197 | + .installmentAmountInMultiplesOf(null)// |
| 6198 | + .supportedInterestRefundTypes(interestRefundTypes) // |
| 6199 | + .rescheduleStrategyMethod(LoanRescheduleStrategyMethod.ADJUST_LAST_UNPAID_PERIOD.getValue())// |
| 6200 | + .recalculationRestFrequencyType(2)// |
| 6201 | + .recalculationRestFrequencyInterval(1)// |
| 6202 | + .enableAccrualActivityPosting(true); |
| 6203 | + PostLoanProductsResponse loanProductResponse = loanProductHelper.createLoanProduct(product); |
| 6204 | + PostLoansRequest applicationRequest = applyLoanRequest(clientId, loanProductResponse.getResourceId(), operationDate, |
| 6205 | + principalAmount.doubleValue(), 6).interestCalculationPeriodType(DAYS)// |
| 6206 | + .transactionProcessingStrategyCode(LoanProductTestBuilder.ADVANCED_PAYMENT_ALLOCATION_STRATEGY)// |
| 6207 | + .interestRatePerPeriod(interestRatePerPeriod)// |
| 6208 | + .repaymentEvery(1)// |
| 6209 | + .repaymentFrequencyType(MONTHS)// |
| 6210 | + .loanTermFrequency(6)// |
| 6211 | + .loanTermFrequencyType(MONTHS); |
| 6212 | + |
| 6213 | + PostLoansResponse loanResponse = loanTransactionHelper.applyLoan(applicationRequest); |
| 6214 | + createdLoanId.set(loanResponse.getLoanId()); |
| 6215 | + |
| 6216 | + loanTransactionHelper.approveLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest().approvedLoanAmount(principalAmount) |
| 6217 | + .dateFormat(DATETIME_PATTERN).approvedOnDate(operationDate).locale("en")); |
| 6218 | + |
| 6219 | + loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), new PostLoansLoanIdRequest().actualDisbursementDate(operationDate) |
| 6220 | + .dateFormat(DATETIME_PATTERN).locale("en").transactionAmount(principalAmount)); |
| 6221 | + |
| 6222 | + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get()); |
| 6223 | + assertTrue(loanDetails.getStatus().getActive()); |
| 6224 | + }); |
| 6225 | + |
| 6226 | + runAt("22 October 2025", () -> { |
| 6227 | + |
| 6228 | + executeInlineCOB(createdLoanId.get()); |
| 6229 | + verifyTransactions(createdLoanId.get(), // |
| 6230 | + transaction(135.94, "Disbursement", "13 September 2025", 135.94, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), |
| 6231 | + transaction(1.28, "Accrual Activity", "13 October 2025", 0.0, 0.0, 1.28, 0.0, 0.0, 0.0, 0.0), |
| 6232 | + transaction(1.61, "Accrual", "21 October 2025", 0.0, 0.0, 1.61, 0.0, 0.0, 0.0, 0.0)); |
| 6233 | + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(createdLoanId.get()); |
| 6234 | + loanDetails.getTransactions().stream().filter(t -> "loanTransactionType.accrualActivity".equals(t.getType().getCode())) |
| 6235 | + .findFirst().ifPresent(t -> { |
| 6236 | + accrualActivityId[0] = t; |
| 6237 | + }); |
| 6238 | + assertNotNull(accrualActivityId[0]); |
| 6239 | + assertNotNull(accrualActivityId[0].getExternalId()); |
| 6240 | + |
| 6241 | + loanTransactionHelper.makeLoanRepayment(createdLoanId.get(), new PostLoansLoanIdTransactionsRequest() // |
| 6242 | + .transactionDate("13 September 2025") // |
| 6243 | + .transactionAmount(135.94) // |
| 6244 | + .locale("en") // |
| 6245 | + .dateFormat(DATETIME_PATTERN)); // |
| 6246 | + |
| 6247 | + GetLoansLoanIdTransactionsTransactionIdResponse loanTransactionDetails = loanTransactionHelper |
| 6248 | + .getLoanTransactionDetails(createdLoanId.get(), accrualActivityId[0].getId()); |
| 6249 | + assertNotNull(loanTransactionDetails.getExternalId()); |
| 6250 | + assertEquals(LocalDate.of(2025, 10, 22), loanTransactionDetails.getReversedOnDate()); |
| 6251 | + }); |
| 6252 | + } |
| 6253 | + |
6149 | 6254 | private Long applyAndApproveLoanProgressiveAdvancedPaymentAllocationStrategyMonthlyRepayments(Long clientId, Long loanProductId, |
6150 | 6255 | Integer numberOfRepayments, String loanDisbursementDate, double amount) { |
6151 | 6256 | LOG.info("------------------------------APPLY AND APPROVE LOAN ---------------------------------------"); |
|
0 commit comments