Skip to content

Commit d353aa1

Browse files
committed
FINERACT-2455: WC - Delinquency Management - Delinquency days & Delinquency Bucket handling
- create SetWorkingCapitalLoanDelinquencyTagsBusinessStep - QA test for business step order - create ENABLE_INSTANT_DELINQUENCY_CALCULATION + integration tests - Delinquency History for Working Capital Loans - DTOs Entity & Repository - Get API - introduce WorkingCapitalLoanDelinquencyReadPlatformService - for read operations. - Cucumber StepDef + basic tests - WIP - WorkingCapitalLoanDelinquencyClassificationService - Handle loan level delinquency + grace day handling
1 parent 44617c9 commit d353aa1

File tree

32 files changed

+1394
-21
lines changed

32 files changed

+1394
-21
lines changed

fineract-core/src/main/java/org/apache/fineract/infrastructure/configuration/api/GlobalConfigurationConstants.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public final class GlobalConfigurationConstants {
8181
public static final String ALLOWED_LOAN_STATUSES_OF_DELAYED_SETTLEMENT_FOR_EXTERNAL_ASSET_TRANSFER = "allowed-loan-statuses-of-delayed-settlement-for-external-asset-transfer";
8282
public static final String MAX_LOGIN_RETRY_ATTEMPTS = "max-login-retry-attempts";
8383
public static final String ENABLE_ORIGINATOR_CREATION_DURING_LOAN_APPLICATION = "enable-originator-creation-during-loan-application";
84+
public static final String ENABLE_INSTANT_DELINQUENCY_CALCULATION = "enable-instant-delinquency-calculation";
8485
public static final String PASSWORD_REUSE_CHECK_HISTORY_COUNT = "password-reuse-check-history-count";
8586
public static final String FORCE_WITHDRAWAL_ON_SAVINGS_ACCOUNT = "allow-force-withdrawal-on-savings-account";
8687
public static final String FORCE_WITHDRAWAL_ON_SAVINGS_ACCOUNT_LIMIT = "force-withdrawal-on-savings-account-limit";

fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalProductLoanAccountStepDef.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,6 +562,9 @@ public void checkDisbursementData(String actualDisbursementDate, String transact
562562
.orElseThrow(() -> new RuntimeException(""));
563563
String formattedDate = disbursementDetails.getActualDisbursementDate().format(FORMATTER);
564564
assertThat(formattedDate).isEqualTo(actualDisbursementDate);
565+
log.info(
566+
"Verified working capital loan disbursement was successful for loan ID: {} with actual disbursement date: {} and amount: {}",
567+
loanId, actualDisbursementDate, disbursementDetails.getActualAmount());
565568
assertThat(disbursementDetails.getActualAmount().compareTo(new BigDecimal(transactionAmount))).isEqualTo(0);
566569
}
567570

@@ -639,6 +642,12 @@ private void createWorkingCapitalLoanAccount(final List<String> loanData) {
639642
final PostWorkingCapitalLoansResponse response = ok(
640643
() -> fineractClient.workingCapitalLoans().submitWorkingCapitalLoanApplication(loansRequest));
641644
testContext().set(TestContextKey.LOAN_CREATE_RESPONSE, response);
645+
List<Long> loanIds = testContext().get(TestContextKey.WC_LOAN_IDS);
646+
if (loanIds == null) {
647+
loanIds = new ArrayList<>();
648+
testContext().set(TestContextKey.WC_LOAN_IDS, loanIds);
649+
}
650+
loanIds.add(response.getLoanId());
642651
log.info("Working Capital Loan created with ID: {}", response.getLoanId());
643652
}
644653

fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/WorkingCapitalStepDef.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import io.cucumber.java.en.Then;
2727
import io.cucumber.java.en.When;
2828
import java.math.BigDecimal;
29+
import java.time.LocalDate;
2930
import java.util.List;
3031
import java.util.Map;
3132
import java.util.UUID;
@@ -37,11 +38,14 @@
3738
import org.apache.fineract.client.models.DeleteWorkingCapitalLoanProductsProductIdResponse;
3839
import org.apache.fineract.client.models.GetConfigurableAttributes;
3940
import org.apache.fineract.client.models.GetPaymentAllocation;
41+
import org.apache.fineract.client.models.GetWorkingCapitalLoanDelinquencyRangeScheduleTagHistoryResponse;
4042
import org.apache.fineract.client.models.GetWorkingCapitalLoanProductsProductIdResponse;
4143
import org.apache.fineract.client.models.GetWorkingCapitalLoanProductsTemplateResponse;
44+
import org.apache.fineract.client.models.InternalWorkingCapitalLoanPaymentRequest;
4245
import org.apache.fineract.client.models.PostAllowAttributeOverrides;
4346
import org.apache.fineract.client.models.PostWorkingCapitalLoanProductsRequest;
4447
import org.apache.fineract.client.models.PostWorkingCapitalLoanProductsResponse;
48+
import org.apache.fineract.client.models.PostWorkingCapitalLoansResponse;
4549
import org.apache.fineract.client.models.PutWorkingCapitalLoanProductsProductIdRequest;
4650
import org.apache.fineract.client.models.PutWorkingCapitalLoanProductsProductIdResponse;
4751
import org.apache.fineract.client.models.StringEnumOptionData;
@@ -53,6 +57,7 @@
5357
import org.apache.fineract.test.stepdef.AbstractStepDef;
5458
import org.apache.fineract.test.support.TestContextKey;
5559
import org.assertj.core.api.SoftAssertions;
60+
import org.junit.jupiter.api.Assertions;
5661
import org.springframework.beans.factory.annotation.Autowired;
5762

5863
@Slf4j
@@ -350,6 +355,40 @@ public void checkWorkingCapitalLoanProductIsDeletedViaExternalId() {
350355
.contains(ErrorMessageHelper.workingCapitalLoanProductIdentifiedDoesNotExistFailure(String.valueOf(externalId)));
351356
}
352357

358+
@When("make Internal Payment {string} on {string}")
359+
public void internalPayWCLoan(String amount, String transactionDate) {
360+
PostWorkingCapitalLoansResponse workingCapitalLoanProductsResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
361+
Long resourceId = workingCapitalLoanProductsResponse.getResourceId();
362+
fineractFeignClient.workingCapitalLoans().payment(resourceId, new InternalWorkingCapitalLoanPaymentRequest()
363+
.amount(BigDecimal.valueOf(Double.parseDouble(amount))).transactionDate(LocalDate.parse(transactionDate)));
364+
}
365+
366+
@Then("Delinquency Tag History for WC Loan has lines:")
367+
public void checkDelinquencyHistory(final DataTable table) {
368+
PostWorkingCapitalLoansResponse workingCapitalLoanProductsResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
369+
Long resourceId = workingCapitalLoanProductsResponse.getResourceId();
370+
List<GetWorkingCapitalLoanDelinquencyRangeScheduleTagHistoryResponse> actualLines = ok(
371+
() -> fineractFeignClient.workingCapitalLoans().getDelinquencyRangeScheduleTagHistoryById(resourceId));
372+
log.info("Loan {}", actualLines);
373+
List<List<String>> rows = table.asLists();
374+
Assertions.assertEquals(rows.size() - 1, actualLines.size());
375+
for (int i = 0; i < rows.size() - 1; i++) {
376+
GetWorkingCapitalLoanDelinquencyRangeScheduleTagHistoryResponse actual = actualLines.get(i);
377+
Assertions.assertNotNull(actual);
378+
List<String> expected = rows.get(i + 1);
379+
Assertions.assertEquals(expected.get(0), actual.getPeriodNumber() != null ? actual.getPeriodNumber().toString() : null);
380+
Assertions.assertEquals(expected.get(1), actual.getAddedOnDate() != null ? actual.getAddedOnDate().toString() : null);
381+
Assertions.assertEquals(expected.get(2), actual.getLiftedOnDate() != null ? actual.getLiftedOnDate().toString() : null);
382+
383+
Assertions.assertNotNull(actual.getDelinquencyRange());
384+
Assertions.assertEquals(expected.get(3), actual.getDelinquencyRange().getClassification());
385+
Assertions.assertEquals(expected.get(4), actual.getDelinquencyRange().getMinimumAgeDays() == null ? null
386+
: actual.getDelinquencyRange().getMinimumAgeDays().toString());
387+
Assertions.assertEquals(expected.get(5), actual.getDelinquencyRange().getMaximumAgeDays() == null ? null
388+
: actual.getDelinquencyRange().getMaximumAgeDays().toString());
389+
}
390+
}
391+
353392
public PostWorkingCapitalLoanProductsResponse createWorkingCapitalLoanProduct(
354393
PostWorkingCapitalLoanProductsRequest workingCapitalProductRequest) {
355394
String workingCapitalProductName = workingCapitalProductRequest.getName();

fineract-e2e-tests-runner/src/test/resources/features/WorkingCapitalProductLoanAccount.feature

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -851,3 +851,103 @@ Feature: WorkingCapitalProduct
851851
And Working capital loan account has the correct data:
852852
| product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount |
853853
| WCLP | 2026-01-01 | 2026-01-01 | Approved | 100.0 | 100.0 | 100.0 | 1.0 | 0.0 |
854+
855+
@TestRailId:CXXXXX1
856+
Scenario: Working Capital Loan COB + Delinquency UC1
857+
When Admin sets the business date to "01 January 2026"
858+
And Admin creates a client with random data
859+
And Admin creates a working capital loan with the following data:
860+
| LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount |
861+
| WCLP | 01 January 2026 | 01 January 2026 | 100 | 100 | 1 | 0 |
862+
Then Working capital loan creation was successful
863+
And Working capital loan account has the correct data:
864+
| product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount |
865+
| WCLP | 2026-01-01 | 2026-01-01 | Submitted and pending approval | 100.0 | 0.0 | 100.0 | 1.0 | 0.0 |
866+
Then Admin successfully approves the working capital loan on "01 January 2026" with "100" amount and expected disbursement date on "01 January 2026"
867+
Then Admin successfully disburse the Working Capital loan on "01 January 2026" with "100" EUR transaction amount
868+
Then Working Capital loan status will be "ACTIVE"
869+
Then Verify Working Capital loan disbursement was successful on "01 January 2026" with "100" EUR transaction amount
870+
And Working capital loan account has the correct data:
871+
| product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount |
872+
| WCLP | 2026-01-01 | 2026-01-01 | Active | 100.0 | 100.0 | 100.0 | 1.0 | 0.0 |
873+
When Admin sets the business date to "02 January 2026"
874+
And Admin runs inline COB job for Working Capital Loan
875+
## Should be ok
876+
Then Delinquency Tag History for WC Loan has lines:
877+
| rangeId | getAddedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays |
878+
879+
When Admin sets the business date to "30 January 2026"
880+
And Admin runs inline COB job for Working Capital Loan
881+
## Should be ok
882+
Then Delinquency Tag History for WC Loan has lines:
883+
| rangeId | getAddedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays |
884+
885+
When Admin sets the business date to "31 January 2026"
886+
And Admin runs inline COB job for Working Capital Loan
887+
## Should be delinquent for 1 days and Delinquency range 1
888+
Then Delinquency Tag History for WC Loan has lines:
889+
| rangeId | getAddedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays |
890+
| 1 | 2026-01-31 | | D00 | 1 | 30 |
891+
892+
When Admin sets the business date to "01 April 2026"
893+
And Admin runs inline COB job for Working Capital Loan
894+
Then Delinquency Tag History for WC Loan has lines:
895+
| rangeId | getAddedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays |
896+
| 1 | 2026-04-01 | | D60 | 61 | 90 |
897+
| 2 | 2026-04-01 | | D30 | 31 | 60 |
898+
| 3 | 2026-04-01 | | D00 | 1 | 30 |
899+
| 1 | 2026-03-02 | | D30 | 31 | 60 |
900+
| 2 | 2026-03-02 | | D00 | 1 | 30 |
901+
| 1 | 2026-01-31 | | D00 | 1 | 30 |
902+
903+
@TestRailId:CXXXXX2
904+
Scenario: Working Capital Loan COB + Delinquency UC2
905+
When Admin sets the business date to "01 December 2020"
906+
And Admin creates a client with random data
907+
And Admin creates a working capital loan with the following data:
908+
| LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPayment | periodPaymentRate | discount |
909+
| WCLP | 01 December 2020 | 01 December 2020 | 1800 | 1800 | 1 | 0 |
910+
Then Working capital loan creation was successful
911+
And Working capital loan account has the correct data:
912+
| product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount |
913+
| WCLP | 2020-12-01 | 2020-12-01 | Submitted and pending approval | 1800.0 | 0.0 | 1800.0 | 1.0 | 0.0 |
914+
Then Admin successfully approves the working capital loan on "01 December 2020" with "1800" amount and expected disbursement date on "01 December 2020"
915+
Then Admin successfully disburse the Working Capital loan on "01 December 2020" with "1800" EUR transaction amount
916+
Then Working Capital loan status will be "ACTIVE"
917+
Then Verify Working Capital loan disbursement was successful on "01 December 2020" with "1800" EUR transaction amount
918+
And Working capital loan account has the correct data:
919+
| product.name | submittedOnDate | expectedDisbursementDate | status | principal | approvedPrincipal | totalPayment | periodPaymentRate | discount |
920+
| WCLP |2020-12-01 | 2020-12-01 | Active | 1800.0 | 1800.0 | 1800.0 | 1.0 | 0.0 |
921+
When Admin sets the business date to "02 December 2020"
922+
And Admin runs inline COB job for Working Capital Loan
923+
## Should be ok
924+
When Admin sets the business date to "05 December 2020"
925+
And Admin runs inline COB job for Working Capital Loan
926+
When make Internal Payment "30.0" on "2020-12-05"
927+
928+
Then Delinquency Tag History for WC Loan has lines:
929+
| rangeId | getAddedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays |
930+
931+
When Admin sets the business date to "01 January 2021"
932+
And Admin runs inline COB job for Working Capital Loan
933+
## Should be ok
934+
Then Delinquency Tag History for WC Loan has lines:
935+
| rangeId | getAddedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays |
936+
| 1 | 2020-12-31 | | D00 | 1 | 30 |
937+
938+
When Admin sets the business date to "06 January 2021"
939+
And Admin runs inline COB job for Working Capital Loan
940+
Then Delinquency Tag History for WC Loan has lines:
941+
| rangeId | getAddedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays |
942+
| 1 | 2020-12-31 | | D00 | 1 | 30 |
943+
When make Internal Payment "54.0" on "2021-01-06"
944+
Then Delinquency Tag History for WC Loan has lines:
945+
| rangeId | getAddedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays |
946+
| 1 | 2020-12-31 | 2021-01-06 | D00 | 1 | 30 |
947+
948+
When Admin sets the business date to "07 January 2021"
949+
And Admin runs inline COB job for Working Capital Loan
950+
Then Delinquency Tag History for WC Loan has lines:
951+
| rangeId | getAddedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays |
952+
| 1 | 2020-12-31 | 2021-01-06 | D00 | 1 | 30 |
953+

fineract-e2e-tests-runner/src/test/resources/features/WorkingCapital_COB.feature

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,10 @@ Feature: Working Capital COB Job
88
Scenario: Verify WC COB job registration, default business step, and scheduler metadata
99
Then Admin checks that configured business jobs contain "WORKING_CAPITAL_LOAN_CLOSE_OF_BUSINESS"
1010
Then Admin verifies configured business steps for "WORKING_CAPITAL_LOAN_CLOSE_OF_BUSINESS" match:
11-
| stepName | order |
12-
| DUMMY_BUSINESS_STEP | 1 |
13-
| WC_DELINQUENCY_RANGE_SCHEDULE | 2 |
11+
| stepName | order |
12+
| DUMMY_BUSINESS_STEP | 1 |
13+
| WC_DELINQUENCY_RANGE_SCHEDULE | 2 |
14+
| WC_LOAN_DELINQUENCY_CLASSIFICATION | 3 |
1415
Then Admin verifies scheduler job "WC_COB" has display name "Working Capital Loan COB"
1516
Then Admin verifies scheduler job "WC_COB" has active status "false"
1617

0 commit comments

Comments
 (0)