Skip to content
Merged
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 @@ -35,6 +35,8 @@
import org.apache.fineract.client.models.PostWorkingCapitalLoansDelinquencyActionRequest;
import org.apache.fineract.client.models.PostWorkingCapitalLoansDelinquencyActionResponse;
import org.apache.fineract.client.models.PostWorkingCapitalLoansResponse;
import org.apache.fineract.client.models.WorkingCapitalCollection;
import org.apache.fineract.client.models.WorkingCapitalCollectionRangeScheduleDelinquency;
import org.apache.fineract.client.models.WorkingCapitalLoanDelinquencyActionData;
import org.apache.fineract.client.models.WorkingCapitalLoanDelinquencyRangeScheduleData;
import org.apache.fineract.test.api.FineractClientConfiguration;
Expand Down Expand Up @@ -202,6 +204,52 @@ public void verifyDelinquencyRangeSchedule(List<WorkingCapitalLoanDelinquencyRan
log.info("Successfully verified actual delinquency range schedule {} range schedule entries", actualRangeSchedule.size());
}

@Then("Working Capital loan installment level delinquency has the following data:")
public void verifyInstallmentLevelDelinquency(final DataTable dataTable) {
final Long loanId = extractLoanId();
final List<WorkingCapitalCollectionRangeScheduleDelinquency> actual = retrieveInstallmentLevelDelinquency(loanId);

final List<List<String>> rows = dataTable.asLists();
final List<String> headers = rows.getFirst();
final List<List<String>> expectedData = rows.subList(1, rows.size());

assertThat(actual).as("Installment level delinquency size should match expected data").hasSize(expectedData.size());

for (int i = 0; i < expectedData.size(); i++) {
final List<String> expectedRow = expectedData.get(i);
final WorkingCapitalCollectionRangeScheduleDelinquency actualRow = actual.get(i);
for (int j = 0; j < headers.size(); j++) {
verifyInstallmentLevelDelinquencyField(actualRow, headers.get(j), expectedRow.get(j), i + 1);
}
}

log.info("Successfully verified {} installment level delinquency entries", actual.size());
}

private List<WorkingCapitalCollectionRangeScheduleDelinquency> retrieveInstallmentLevelDelinquency(final Long loanId) {
final WorkingCapitalCollection delinquent = ok(() -> fineractClient.workingCapitalLoans().retrieveWorkingCapitalLoanById(loanId))
.getDelinquent();
final List<WorkingCapitalCollectionRangeScheduleDelinquency> installmentLevelDelinquency = delinquent == null ? null
: delinquent.getInstallmentLevelDelinquency();
log.debug("Installment level delinquency for loan {}: {}", loanId, installmentLevelDelinquency);
return installmentLevelDelinquency == null ? List.of() : installmentLevelDelinquency;
}

private void verifyInstallmentLevelDelinquencyField(final WorkingCapitalCollectionRangeScheduleDelinquency actual,
final String fieldName, final String expectedValue, final int rowNumber) {
switch (fieldName) {
case "classification" ->
assertThat(actual.getClassification()).as("Classification for row %d", rowNumber).isEqualTo(expectedValue);
case "minimumAgeDays" -> assertThat(actual.getMinimumAgeDays()).as("Minimum age days for row %d", rowNumber)
.isEqualTo(Integer.parseInt(expectedValue));
case "maximumAgeDays" -> assertThat(actual.getMaximumAgeDays()).as("Maximum age days for row %d", rowNumber)
.isEqualTo(Integer.parseInt(expectedValue));
case "delinquentAmount" ->
verifyNullableBigDecimal(actual.getDelinquentAmount(), expectedValue, "Delinquent amount", rowNumber);
default -> throw new IllegalArgumentException("Unknown field name: " + fieldName);
}
}

public Long extractLoanId() {
final PostWorkingCapitalLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
return loanResponse.getLoanId();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,9 @@ Feature: Working Capital Delinquency
| 3 | 2026-04-01 | | D00 | 1 | 30 |
| 2 | 2026-04-01 | | D30 | 31 | 60 |
| 1 | 2026-04-01 | | D60 | 61 | 90 |
| 2 | 2026-03-02 | | D00 | 1 | 30 |
| 1 | 2026-03-02 | | D30 | 31 | 60 |
| 1 | 2026-01-31 | | D00 | 1 | 30 |
| 2 | 2026-03-02 | 2026-04-01 | D00 | 1 | 30 |
| 1 | 2026-03-02 | 2026-04-01 | D30 | 31 | 60 |
| 1 | 2026-01-31 | 2026-03-02 | D00 | 1 | 30 |
Then Admin closes the Working Capital loan with all obligations met with a full repayment on "01 April 2026"

@TestRailId:C74526
Expand Down Expand Up @@ -1055,6 +1055,118 @@ Feature: Working Capital Delinquency
| 1 | 2026-01-01 | 2026-01-30 | 270.0 | 0.0 | 270.0 | null | null | null |
Then Admin closes the Working Capital loan with all obligations met with a full repayment on "02 January 2026"

@TestRailId:C110958
Scenario: Verify working capital loan delinquency tag history - one active tag per period and installment level delinquency aggregated by range with its delinquent amount
When Admin sets the business date to "01 January 2026"
And Admin creates a client with random data
And Admin creates WC Delinquency Bucket with frequency 15 DAYS and minimumPayment 3 PERCENTAGE
And Admin creates a new Working Capital Loan Product with delinquency bucket
And Admin creates a working capital loan with the following data:
| LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPaymentVolume | periodPaymentRate | discount |
| WCLP_DELINQUENCY | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 |
Then Working capital loan creation was successful
And Admin successfully approves the working capital loan on "01 January 2026" with "9000" amount and expected disbursement date on "01 January 2026"
When Admin successfully disburse the Working Capital loan on "01 January 2026" with "9000" EUR transaction amount
Then Working Capital loan status will be "ACTIVE"
# --- Period 1 becomes delinquent: a single D00 tag carrying the period's delinquent amount ---
When Admin sets the business date to "16 January 2026"
And Admin runs inline COB job for Working Capital Loan by loanId
Then Working Capital loan delinquency range schedule has the following data:
| periodNumber | fromDate | toDate | expectedAmount | paidAmount | outstandingAmount | minPaymentCriteriaMet | delinquentAmount | delinquentDays |
| 1 | 2026-01-01 | 2026-01-15 | 270.0 | 0.0 | 270.0 | false | 270.0 | 1 |
| 2 | 2026-01-16 | 2026-01-30 | 270.0 | 0.0 | 270.0 | null | null | null |
And Delinquency Tag History for Working Capital loan has lines:
| periodNumber | addedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays |
| 1 | 2026-01-16 | | D00 | 1 | 30 |
And Working Capital loan installment level delinquency has the following data:
| classification | minimumAgeDays | maximumAgeDays | delinquentAmount |
| D00 | 1 | 30 | 270.0 |
# --- Period 1 ages into D30 while periods 2 and 3 are in D00: the D00 tag of period 1 is lifted on the escalation day ---
# --- and the two D00 periods are reported as one aggregated D00 entry; the loan keeps period 1's delinquency start ---
When Admin sets the business date to "16 February 2026"
And Admin runs inline COB job for Working Capital Loan by loanId
Then Working Capital loan delinquency range schedule has the following data:
| periodNumber | fromDate | toDate | expectedAmount | paidAmount | outstandingAmount | minPaymentCriteriaMet | delinquentAmount | delinquentDays |
| 1 | 2026-01-01 | 2026-01-15 | 270.0 | 0.0 | 270.0 | false | 270.0 | 32 |
| 2 | 2026-01-16 | 2026-01-30 | 270.0 | 0.0 | 270.0 | false | 270.0 | 17 |
| 3 | 2026-01-31 | 2026-02-14 | 270.0 | 0.0 | 270.0 | false | 270.0 | 2 |
| 4 | 2026-02-15 | 2026-03-01 | 270.0 | 0.0 | 270.0 | null | null | null |
And Delinquency Tag History for Working Capital loan has lines:
| periodNumber | addedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays |
| 3 | 2026-02-15 | | D00 | 1 | 30 |
| 1 | 2026-02-15 | | D30 | 31 | 60 |
| 2 | 2026-01-31 | | D00 | 1 | 30 |
| 1 | 2026-01-16 | 2026-02-15 | D00 | 1 | 30 |
And Working Capital loan installment level delinquency has the following data:
| classification | minimumAgeDays | maximumAgeDays | delinquentAmount |
| D00 | 1 | 30 | 540.0 |
| D30 | 31 | 60 | 270.0 |
And Working capital loan details has the following field values:
| delinquent.delinquentDate | 2026-01-16 |
| delinquent.delinquentDays | 32 |
| delinquent.delinquentAmount | 810.0 |
| delinquent.pastDueDays | 32 |

@TestRailId:C110959
Scenario: Verify working capital loan delinquency tag history - a repayment curing the escalated period lifts its only active tag and moves the loan delinquency start to the next period
When Admin sets the business date to "01 January 2026"
And Admin creates a client with random data
And Admin creates WC Delinquency Bucket with frequency 15 DAYS and minimumPayment 3 PERCENTAGE
And Admin creates a new Working Capital Loan Product with delinquency bucket
And Admin creates a working capital loan with the following data:
| LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPaymentVolume | periodPaymentRate | discount |
| WCLP_DELINQUENCY | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 |
And Admin successfully approves the working capital loan on "01 January 2026" with "9000" amount and expected disbursement date on "01 January 2026"
When Admin successfully disburse the Working Capital loan on "01 January 2026" with "9000" EUR transaction amount
When Admin sets the business date to "16 January 2026"
And Admin runs inline COB job for Working Capital Loan by loanId
When Admin sets the business date to "16 February 2026"
And Admin runs inline COB job for Working Capital Loan by loanId
# --- 300 pays period 1 in full (270) and 30 of period 2: the D30 tag of period 1 is lifted, nothing else changes ---
And Customer makes repayment on "16 February 2026" with 300.0 transaction amount on Working Capital loan
Then Delinquency Tag History for Working Capital loan has lines:
| periodNumber | addedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays |
| 3 | 2026-02-15 | | D00 | 1 | 30 |
| 1 | 2026-02-15 | 2026-02-16 | D30 | 31 | 60 |
| 2 | 2026-01-31 | | D00 | 1 | 30 |
| 1 | 2026-01-16 | 2026-02-15 | D00 | 1 | 30 |
And Working Capital loan installment level delinquency has the following data:
| classification | minimumAgeDays | maximumAgeDays | delinquentAmount |
| D00 | 1 | 30 | 510.0 |
And Working capital loan details has the following field values:
| delinquent.delinquentDate | 2026-01-31 |
| delinquent.delinquentDays | 17 |
| delinquent.delinquentAmount | 510.0 |
| delinquent.pastDueDays | 17 |

@TestRailId:C110960
Scenario: Verify working capital loan delinquency tag history - a repayment value-dated before the business date keeps the escalated tag active
When Admin sets the business date to "01 January 2026"
And Admin creates a client with random data
And Admin creates WC Delinquency Bucket with frequency 15 DAYS and minimumPayment 3 PERCENTAGE
And Admin creates a new Working Capital Loan Product with delinquency bucket
And Admin creates a working capital loan with the following data:
| LoanProduct | submittedOnDate | expectedDisbursementDate | principalAmount | totalPaymentVolume | periodPaymentRate | discount |
| WCLP_DELINQUENCY | 01 January 2026 | 01 January 2026 | 9000 | 100000 | 18 | 0 |
And Admin successfully approves the working capital loan on "01 January 2026" with "9000" amount and expected disbursement date on "01 January 2026"
When Admin successfully disburse the Working Capital loan on "01 January 2026" with "9000" EUR transaction amount
When Admin sets the business date to "16 January 2026"
And Admin runs inline COB job for Working Capital Loan by loanId
When Admin sets the business date to "16 February 2026"
And Admin runs inline COB job for Working Capital Loan by loanId
# --- 100 entered on 16 February with value date 20 January: period 1 is still 32 days overdue, so it stays D30 ---
# --- and no tag may be lifted before it was added ---
And Customer makes repayment on "20 January 2026" with 100.0 transaction amount on Working Capital loan
Then Delinquency Tag History for Working Capital loan has lines:
| periodNumber | addedOnDate | liftedOnDate | classification | minimumAgeDays | maximumAgeDays |
| 3 | 2026-02-15 | | D00 | 1 | 30 |
| 1 | 2026-02-15 | | D30 | 31 | 60 |
| 2 | 2026-01-31 | | D00 | 1 | 30 |
| 1 | 2026-01-16 | 2026-02-15 | D00 | 1 | 30 |
And Working Capital loan installment level delinquency has the following data:
| classification | minimumAgeDays | maximumAgeDays | delinquentAmount |
| D00 | 1 | 30 | 540.0 |
| D30 | 31 | 60 | 170.0 |

@TestRailId:C106839
Scenario: Verify delinquencyEffectiveStartDate - UC1: first delinquent period with grace days
Expand Down
Loading
Loading