You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@fineract.apache.org by ad...@apache.org on 2026/02/05 12:45:24 UTC
(fineract) branch develop updated (8d064ddf6c -> 0904c4f530)
This is an automated email from the ASF dual-hosted git repository.
adamsaghy pushed a change to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
from 8d064ddf6c FINERACT-2452: New command processing - audit
new 41d859e68a FINERACT-2421: Add pastDueDate to loan summary and event
new 0904c4f530 FINERACT-2421: Add pastDueDate to loan summary and event - E2E test
The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails. The revisions
listed as "add" were already present in the repository and have only
been added to this reference.
Summary of changes:
.../src/main/avro/loan/v1/CollectionDataV1.avsc | 8 ++
.../test/stepdef/loan/InlineCOBStepDef.java | 23 +++
.../test/stepdef/loan/LoanDelinquencyStepDef.java | 160 +++++++++++----------
.../fineract/test/stepdef/loan/LoanStepDef.java | 1 +
.../resources/features/LoanDelinquency.feature | 93 ++++++++++++
.../service/LoanDelinquencyDomainServiceImpl.java | 3 +
.../portfolio/loanaccount/data/CollectionData.java | 4 +-
.../loanaccount/api/LoansApiResourceSwagger.java | 2 +
...cyWritePlatformServiceRangeChangeEventTest.java | 41 +++---
.../LoanDelinquencyDomainServiceTest.java | 9 ++
10 files changed, 246 insertions(+), 98 deletions(-)
(fineract) 02/02: FINERACT-2421: Add pastDueDate to loan summary and event - E2E test
Posted by ad...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
commit 0904c4f5302952da832135d8368724f088f8ba9d
Author: mariiaKraievska <ri...@gmail.com>
AuthorDate: Tue Feb 3 12:07:49 2026 +0200
FINERACT-2421: Add pastDueDate to loan summary and event - E2E test
---
.../test/stepdef/loan/InlineCOBStepDef.java | 23 +++++++++
.../fineract/test/stepdef/loan/LoanStepDef.java | 1 +
.../resources/features/LoanDelinquency.feature | 54 ++++++++++++++++++++--
3 files changed, 75 insertions(+), 3 deletions(-)
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/InlineCOBStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/InlineCOBStepDef.java
index 82f82e8a26..032dd96a4c 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/InlineCOBStepDef.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/InlineCOBStepDef.java
@@ -19,6 +19,7 @@
package org.apache.fineract.test.stepdef.loan;
import static org.apache.fineract.client.feign.util.FeignCalls.ok;
+import static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
@@ -30,10 +31,12 @@ import org.apache.fineract.client.models.InlineJobRequest;
import org.apache.fineract.client.models.PostLoansResponse;
import org.apache.fineract.test.messaging.EventAssertion;
import org.apache.fineract.test.messaging.event.assetexternalization.LoanAccountCustomSnapshotEvent;
+import org.apache.fineract.test.messaging.event.loan.LoanBalanceChangedEvent;
import org.apache.fineract.test.messaging.event.loan.repayment.LoanRepaymentDueEvent;
import org.apache.fineract.test.messaging.event.loan.repayment.LoanRepaymentOverdueEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
+import org.junit.jupiter.api.Assertions;
import org.springframework.beans.factory.annotation.Autowired;
public class InlineCOBStepDef extends AbstractStepDef {
@@ -49,6 +52,7 @@ public class InlineCOBStepDef extends AbstractStepDef {
@When("Admin runs inline COB job for Loan")
public void runInlineCOB() throws IOException {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
+ Assertions.assertNotNull(loanResponse);
long loanId = loanResponse.getLoanId();
InlineJobRequest inlineJobRequest = new InlineJobRequest().addLoanIdsItem(loanId);
@@ -59,6 +63,7 @@ public class InlineCOBStepDef extends AbstractStepDef {
@Then("Loan Repayment Due Business Event is created")
public void checkLoanRepaymentDueBusinessEventCreated() {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
+ Assertions.assertNotNull(loanResponse);
long loanId = loanResponse.getLoanId();
eventAssertion.assertEventRaised(LoanRepaymentDueEvent.class, loanId);
@@ -67,6 +72,7 @@ public class InlineCOBStepDef extends AbstractStepDef {
@Then("Loan Repayment Overdue Business Event is created")
public void checkLoanRepaymentOverdueBusinessEventCreated() {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
+ Assertions.assertNotNull(loanResponse);
long loanId = loanResponse.getLoanId();
eventAssertion.assertEventRaised(LoanRepaymentOverdueEvent.class, loanId);
@@ -75,9 +81,26 @@ public class InlineCOBStepDef extends AbstractStepDef {
@Then("LoanAccountCustomSnapshotBusinessEvent is created with business date {string}")
public void checkLoanRepaymentDueBusinessEventCreatedWithBusinessDate(String expectedBusinessDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
+ Assertions.assertNotNull(loanResponse);
long loanId = loanResponse.getLoanId();
LocalDate expectedBusinessDateParsed = LocalDate.parse(expectedBusinessDate, FORMATTER);
eventAssertion.assertEvent(LoanAccountCustomSnapshotEvent.class, loanId).isRaisedOnBusinessDate(expectedBusinessDateParsed);
}
+
+ @Then("LoanBalanceChangedBusinessEvent has pastDueDate {string}")
+ public void checkLoanBalanceChangedBusinessEventHasPastDueDate(final String expectedPastDueDate) {
+ final PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
+ Assertions.assertNotNull(loanResponse);
+ final long loanId = loanResponse.getLoanId();
+
+ final String expectedPastDueDateValue = "null".equals(expectedPastDueDate) ? null : expectedPastDueDate;
+
+ eventAssertion.assertEvent(LoanBalanceChangedEvent.class, loanId).extractingData(loanAccountDataV1 -> {
+ final String actualPastDueDate = loanAccountDataV1.getDelinquent() == null ? null
+ : loanAccountDataV1.getDelinquent().getPastDueDate();
+ assertThat(actualPastDueDate).as("pastDueDate in LoanBalanceChangedBusinessEvent").isEqualTo(expectedPastDueDateValue);
+ return null;
+ });
+ }
}
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
index ecfd1bd889..c7c1887d7f 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanStepDef.java
@@ -1622,6 +1622,7 @@ public class LoanStepDef extends AbstractStepDef {
.isEqualTo(statusExpected);//
eventCheckHelper.disburseLoanEventCheck(loanId);
eventCheckHelper.loanDisbursalTransactionEventCheck(loanDisburseResponse);
+ eventCheckHelper.loanBalanceChangedEventCheck(loanId);
}
@And("Admin successfully add disbursement detail to the loan on {string} with {double} EUR transaction amount")
diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanDelinquency.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanDelinquency.feature
index e0479d43fd..6204135ce6 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/LoanDelinquency.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanDelinquency.feature
@@ -2177,11 +2177,12 @@ Feature: LoanDelinquency
| classification | delinquentAmount | delinquentDate | delinquentDays | pastDueDays |
| RANGE_90 | 666.68 | 06 February 2025 | 98 | 103 |
+ @TestRailId:C4619
Scenario: Verify that pastDueDate is returned correctly for overdue loan
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
- | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
+ | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL_INSTALLMENT_LEVEL_DELINQUENCY | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
@@ -2191,11 +2192,12 @@ Feature: LoanDelinquency
| classification | delinquentAmount | delinquentDate | pastDueDate | delinquentDays | pastDueDays |
| RANGE_3 | 250.0 | 04 October 2023 | 01 October 2023 | 6 | 9 |
+ @TestRailId:C4620
Scenario: Verify that pastDueDate is null when loan has no overdue
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
- | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
+ | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL_INSTALLMENT_LEVEL_DELINQUENCY | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
@@ -2205,11 +2207,12 @@ Feature: LoanDelinquency
| classification | delinquentAmount | delinquentDate | pastDueDate | delinquentDays | pastDueDays |
| NO_DELINQUENCY | 0.0 | null | null | 0 | 0 |
+ @TestRailId:C4621
Scenario: Verify that pastDueDate equals chargeback date when chargeback creates overdue
When Admin sets the business date to "01 October 2023"
When Admin creates a client with random data
When Admin creates a fully customized loan with the following data:
- | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
+ | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
| LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL_INSTALLMENT_LEVEL_DELINQUENCY | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
@@ -2221,3 +2224,48 @@ Feature: LoanDelinquency
Then Loan has the following LOAN level delinquency data:
| classification | delinquentAmount | delinquentDate | pastDueDate | delinquentDays | pastDueDays |
| RANGE_1 | 250.0 | 08 October 2023 | 05 October 2023 | 2 | 5 |
+
+ @TestRailId:C4622
+ Scenario: Verify that pastDueDate is present in LoanBalanceChangedBusinessEvent for overdue loan
+ When Admin sets the business date to "01 October 2023"
+ When Admin creates a client with random data
+ When Admin creates a fully customized loan with the following data:
+ | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
+ | LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 October 2023 | 1000 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
+ And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
+ When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
+ When Admin sets the business date to "20 October 2023"
+ And Create an interest pause period with start date "25 October 2023" and end date "30 October 2023"
+ And LoanBalanceChangedBusinessEvent has pastDueDate "2023-10-16"
+
+ @TestRailId:C4623
+ Scenario: Verify that pastDueDate is null in LoanBalanceChangedBusinessEvent when loan has no overdue
+ When Admin sets the business date to "01 October 2023"
+ When Admin creates a client with random data
+ When Admin creates a fully customized loan with the following data:
+ | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
+ | LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 October 2023 | 1000 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
+ And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
+ When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
+ When Admin sets the business date to "16 October 2023"
+ And Customer makes "AUTOPAY" repayment on "16 October 2023" with 335.28 EUR transaction amount
+ When Admin sets the business date to "20 October 2023"
+ And Create an interest pause period with start date "25 October 2023" and end date "30 October 2023"
+ And LoanBalanceChangedBusinessEvent has pastDueDate "null"
+
+ @TestRailId:C4624
+ Scenario: Verify that pastDueDate is present in LoanBalanceChangedBusinessEvent and equals chargeback date when chargeback creates overdue
+ When Admin sets the business date to "01 October 2023"
+ When Admin creates a client with random data
+ When Admin creates a fully customized loan with the following data:
+ | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
+ | LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 01 October 2023 | 1000 | 7 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
+ And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
+ When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
+ When Admin sets the business date to "16 October 2023"
+ And Customer makes "AUTOPAY" repayment on "16 October 2023" with 335.28 EUR transaction amount
+ When Admin sets the business date to "20 October 2023"
+ When Admin makes "REPAYMENT_ADJUSTMENT_CHARGEBACK" chargeback with 335.28 EUR transaction amount
+ When Admin sets the business date to "25 October 2023"
+ And Create an interest pause period with start date "25 October 2023" and end date "30 October 2023"
+ Then LoanBalanceChangedBusinessEvent has pastDueDate "2023-10-20"
(fineract) 01/02: FINERACT-2421: Add pastDueDate to loan summary and event
Posted by ad...@apache.org.
This is an automated email from the ASF dual-hosted git repository.
adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
commit 41d859e68a29f81f2cbcd4b9e38dd1ff70d51cd6
Author: Oleksii Novikov <al...@onix-systems.com>
AuthorDate: Wed Jan 28 13:41:10 2026 +0200
FINERACT-2421: Add pastDueDate to loan summary and event
---
.../src/main/avro/loan/v1/CollectionDataV1.avsc | 8 ++
.../test/stepdef/loan/LoanDelinquencyStepDef.java | 160 +++++++++++----------
.../resources/features/LoanDelinquency.feature | 45 ++++++
.../service/LoanDelinquencyDomainServiceImpl.java | 3 +
.../portfolio/loanaccount/data/CollectionData.java | 4 +-
.../loanaccount/api/LoansApiResourceSwagger.java | 2 +
...cyWritePlatformServiceRangeChangeEventTest.java | 41 +++---
.../LoanDelinquencyDomainServiceTest.java | 9 ++
8 files changed, 174 insertions(+), 98 deletions(-)
diff --git a/fineract-avro-schemas/src/main/avro/loan/v1/CollectionDataV1.avsc b/fineract-avro-schemas/src/main/avro/loan/v1/CollectionDataV1.avsc
index af515e5930..22c3c086ab 100644
--- a/fineract-avro-schemas/src/main/avro/loan/v1/CollectionDataV1.avsc
+++ b/fineract-avro-schemas/src/main/avro/loan/v1/CollectionDataV1.avsc
@@ -27,6 +27,14 @@
"int"
]
},
+ {
+ "default": null,
+ "name": "pastDueDate",
+ "type": [
+ "null",
+ "string"
+ ]
+ },
{
"default": null,
"name": "nextPaymentDueDate",
diff --git a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanDelinquencyStepDef.java b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanDelinquencyStepDef.java
index 786d754341..23e9735751 100644
--- a/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanDelinquencyStepDef.java
+++ b/fineract-e2e-tests-core/src/test/java/org/apache/fineract/test/stepdef/loan/LoanDelinquencyStepDef.java
@@ -25,7 +25,6 @@ import static org.assertj.core.api.Assertions.assertThat;
import io.cucumber.datatable.DataTable;
import io.cucumber.java.en.Then;
import io.cucumber.java.en.When;
-import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.format.DateTimeFormatter;
@@ -34,6 +33,7 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
+import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.avro.loan.v1.LoanAccountDelinquencyRangeDataV1;
import org.apache.fineract.avro.loan.v1.LoanInstallmentDelinquencyBucketDataV1;
@@ -59,27 +59,19 @@ import org.apache.fineract.test.messaging.event.EventCheckHelper;
import org.apache.fineract.test.messaging.event.loan.delinquency.LoanDelinquencyRangeChangeEvent;
import org.apache.fineract.test.stepdef.AbstractStepDef;
import org.apache.fineract.test.support.TestContextKey;
-import org.springframework.beans.factory.annotation.Autowired;
@Slf4j
+@RequiredArgsConstructor
public class LoanDelinquencyStepDef extends AbstractStepDef {
public static final String DATE_FORMAT = "dd MMMM yyyy";
public static final String DEFAULT_LOCALE = "en";
public static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern(DATE_FORMAT);
- private static final String PWD_USER_WITH_ROLE = "1234567890Aa!";
- @Autowired
- private FineractFeignClient fineractClient;
-
- @Autowired
- private ApiProperties apiProperties;
-
- @Autowired
- private EventAssertion eventAssertion;
-
- @Autowired
- private EventCheckHelper eventCheckHelper;
+ private final FineractFeignClient fineractClient;
+ private final ApiProperties apiProperties;
+ private final EventAssertion eventAssertion;
+ private final EventCheckHelper eventCheckHelper;
private FineractFeignClient createClientForUser(String username, String password) {
String baseUrl = apiProperties.getBaseUrl();
@@ -93,9 +85,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Admin checks that delinquency range is: {string} and has delinquentDate {string}")
- public void checkDelinquencyRange(String range, String delinquentDateExpected) throws IOException {
+ public void checkDelinquencyRange(String range, String delinquentDateExpected) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
Integer loanStatus = loanDetails.getStatus().getId();
@@ -122,11 +114,10 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Admin checks that {string}th delinquency range is: {string} and added on: {string} and has delinquentDate {string}")
- public void checkDelinquencyRange(String nthInList, String range, String addedOnDate, String delinquentDateExpected)
- throws IOException {
+ public void checkDelinquencyRange(String nthInList, String range, String addedOnDate, String delinquentDateExpected) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
String delinquentDateExpectedValue = "".equals(delinquentDateExpected) ? null : delinquentDateExpected;
eventAssertion.assertEvent(LoanDelinquencyRangeChangeEvent.class, loanId)
@@ -155,12 +146,12 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Loan delinquency history has the following details:")
- public void delinquencyHistoryCheck(DataTable table) throws IOException {
+ public void delinquencyHistoryCheck(DataTable table) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_FORMAT);
List<List<String>> dataExpected = table.asLists();
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
List<GetDelinquencyTagHistoryResponse> body = ok(() -> fineractClient.loans().getDelinquencyTagHistory(loanId));
@@ -188,9 +179,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@When("Admin initiate a DELINQUENCY PAUSE with startDate: {string} and endDate: {string}")
- public void delinquencyPause(String startDate, String endDate) throws IOException {
+ public void delinquencyPause(String startDate, String endDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
@@ -205,9 +196,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@When("Created user with CREATE_DELINQUENCY_ACTION permission initiate a DELINQUENCY PAUSE with startDate: {string} and endDate: {string}")
- public void delinquencyPauseWithCreatedUser(String startDate, String endDate) throws IOException {
+ public void delinquencyPauseWithCreatedUser(String startDate, String endDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
@@ -226,9 +217,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Created user with no CREATE_DELINQUENCY_ACTION permission gets an error when initiate a DELINQUENCY PAUSE with startDate: {string} and endDate: {string}")
- public void delinquencyPauseWithCreatedUserNOPermissionError(String startDate, String endDate) throws IOException {
+ public void delinquencyPauseWithCreatedUserNOPermissionError(String startDate, String endDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
int errorCodeExpected = 403;
String errorMessageExpected = "User has no authority to CREATE delinquency_actions";
@@ -257,9 +248,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@When("Admin initiate a DELINQUENCY RESUME with startDate: {string}")
- public void delinquencyResume(String startDate) throws IOException {
+ public void delinquencyResume(String startDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
@@ -273,10 +264,10 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@When("Admin initiate a DELINQUENCY PAUSE by loanExternalId with startDate: {string} and endDate: {string}")
- public void delinquencyPauseByLoanExternalId(String startDate, String endDate) throws IOException {
+ public void delinquencyPauseByLoanExternalId(String startDate, String endDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.getResourceExternalId();
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
@@ -292,10 +283,10 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@When("Admin initiate a DELINQUENCY RESUME by loanExternalId with startDate: {string}")
- public void delinquencyResumeByLoanExternalId(String startDate) throws IOException {
+ public void delinquencyResumeByLoanExternalId(String startDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
String loanExternalId = loanResponse.getResourceExternalId();
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
@@ -310,9 +301,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Delinquency-actions have the following data:")
- public void getDelinquencyActionData(DataTable table) throws IOException {
+ public void getDelinquencyActionData(DataTable table) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
List<List<String>> data = table.asLists();
int nrOfLinesExpected = data.size() - 1;
@@ -341,9 +332,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Initiating a delinquency-action other than PAUSE or RESUME in action field results an error - startDate: {string}, endDate: {string}")
- public void actionFieldError(String startDate, String endDate) throws IOException {
+ public void actionFieldError(String startDate, String endDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("TEST")//
@@ -358,9 +349,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Initiating a DELINQUENCY PAUSE with startDate before the actual business date results an error - startDate: {string}, endDate: {string}")
- public void delinquencyPauseStartDateError(String startDate, String endDate) throws IOException {
+ public void delinquencyPauseStartDateError(String startDate, String endDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
@@ -375,9 +366,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Initiating a DELINQUENCY PAUSE on a non-active loan results an error - startDate: {string}, endDate: {string}")
- public void delinquencyPauseNonActiveLoanError(String startDate, String endDate) throws IOException {
+ public void delinquencyPauseNonActiveLoanError(String startDate, String endDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
@@ -392,9 +383,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Initiating a DELINQUENCY RESUME on a non-active loan results an error - startDate: {string}")
- public void delinquencyResumeNonActiveLoanError(String startDate) throws IOException {
+ public void delinquencyResumeNonActiveLoanError(String startDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
@@ -408,9 +399,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Overlapping PAUSE periods result an error - startDate: {string}, endDate: {string}")
- public void delinquencyPauseOverlappingError(String startDate, String endDate) throws IOException {
+ public void delinquencyPauseOverlappingError(String startDate, String endDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("pause")//
@@ -425,9 +416,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Initiating a DELINQUENCY RESUME without an active PAUSE period results an error - startDate: {string}")
- public void delinquencyResumeWithoutPauseError(String startDate) throws IOException {
+ public void delinquencyResumeWithoutPauseError(String startDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
@@ -441,9 +432,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Initiating a DELINQUENCY RESUME with start date other than actual business date results an error - startDate: {string}")
- public void delinquencyResumeStartDateError(String startDate) throws IOException {
+ public void delinquencyResumeStartDateError(String startDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
@@ -457,9 +448,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Initiating a DELINQUENCY RESUME with an endDate results an error - startDate: {string}, endDate: {string}")
- public void delinquencyResumeWithEndDateError(String startDate, String endDate) throws IOException {
+ public void delinquencyResumeWithEndDateError(String startDate, String endDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
PostLoansDelinquencyActionRequest request = new PostLoansDelinquencyActionRequest()//
.action("resume")//
@@ -474,17 +465,17 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Installment level delinquency event has correct data")
- public void installmentLevelDelinquencyEventCheck() throws IOException {
+ public void installmentLevelDelinquencyEventCheck() {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
eventCheckHelper.installmentLevelDelinquencyRangeChangeEventCheck(loanId);
}
@Then("INSTALLMENT level delinquency is null")
- public void installmentLevelDelinquencyNull() throws IOException {
+ public void installmentLevelDelinquencyNull() {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
List<GetLoansLoanIdLoanInstallmentLevelDelinquency> installmentLevelDelinquency = loanDetails.getDelinquent()
@@ -493,12 +484,12 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Loan has the following LOAN level delinquency data:")
- public void loanDelinquencyDataCheck(DataTable table) throws IOException {
+ public void loanDelinquencyDataCheck(DataTable table) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
List<String> expectedValuesList = table.asLists().get(1);
- DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedValuesList.get(0));
+ DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedValuesList.getFirst());
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
expectedValuesList.set(0, expectedDelinquencyRangeValue);
@@ -506,23 +497,37 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
String actualDelinquencyRangeValue = loanDetails.getDelinquencyRange() == null ? "NO_DELINQUENCY"
: loanDetails.getDelinquencyRange().getClassification();
GetLoansLoanIdDelinquencySummary delinquent = loanDetails.getDelinquent();
+ assertThat(delinquent).isNotNull();
String delinquentAmount = delinquent.getDelinquentAmount() == null ? null
: new Utils.DoubleFormatter(delinquent.getDelinquentAmount().doubleValue()).format();
- List<String> actualValuesList = List.of(actualDelinquencyRangeValue, delinquentAmount,
- delinquent.getDelinquentDate() == null ? "null" : FORMATTER.format(delinquent.getDelinquentDate()),
- delinquent.getDelinquentDays().toString(), delinquent.getPastDueDays().toString());
+
+ assertThat(actualDelinquencyRangeValue).isNotNull();
+ assertThat(delinquentAmount).isNotNull();
+ assertThat(delinquent.getDelinquentDays()).isNotNull();
+ assertThat(delinquent.getPastDueDays()).isNotNull();
+ List<String> actualValuesList;
+ if (expectedValuesList.size() == 6) {
+ actualValuesList = List.of(actualDelinquencyRangeValue, delinquentAmount,
+ delinquent.getDelinquentDate() == null ? "null" : FORMATTER.format(delinquent.getDelinquentDate()),
+ delinquent.getPastDueDate() == null ? "null" : FORMATTER.format(delinquent.getPastDueDate()),
+ delinquent.getDelinquentDays().toString(), delinquent.getPastDueDays().toString());
+ } else {
+ actualValuesList = List.of(actualDelinquencyRangeValue, delinquentAmount,
+ delinquent.getDelinquentDate() == null ? "null" : FORMATTER.format(delinquent.getDelinquentDate()),
+ delinquent.getDelinquentDays().toString(), delinquent.getPastDueDays().toString());
+ }
assertThat(actualValuesList).as(ErrorMessageHelper.wrongValueInLoanLevelDelinquencyData(actualValuesList, expectedValuesList))
.isEqualTo(expectedValuesList);
}
@Then("Loan has the following LOAN level next payment due data:")
- public void loanNextPaymentDataCheck(DataTable table) throws IOException {
+ public void loanNextPaymentDataCheck(DataTable table) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
List<String> expectedValuesList = table.asLists().get(1);
- DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedValuesList.get(0));
+ DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedValuesList.getFirst());
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
expectedValuesList.set(0, expectedDelinquencyRangeValue);
@@ -543,9 +548,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Loan has the following INSTALLMENT level delinquency data:")
- public void loanDelinquencyInstallmentLevelDataCheck(DataTable table) throws IOException {
+ public void loanDelinquencyInstallmentLevelDataCheck(DataTable table) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
List<GetLoansLoanIdLoanInstallmentLevelDelinquency> installmentLevelDelinquency = loanDetails.getDelinquent()
@@ -572,9 +577,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Loan Delinquency pause periods has the following data:")
- public void loanDelinquencyPauseDataCheck(DataTable table) throws IOException {
+ public void loanDelinquencyPauseDataCheck(DataTable table) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
List<List<String>> expectedData = table.asLists();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
@@ -598,9 +603,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("Loan details delinquent.nextPaymentDueDate will be {string}")
- public void nextPaymentDueDateCheck(String expectedDate) throws IOException {
+ public void nextPaymentDueDateCheck(String expectedDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
String actualDate = FORMATTER.format(loanDetails.getDelinquent().getNextPaymentDueDate());
@@ -611,7 +616,7 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
@Then("LoanAccountDelinquencyRangeDataV1 has delinquencyRange field with value {string}")
public void checkDelinquencyRangeInEvent(String expectedRange) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
DelinquencyRange expectedDelinquencyRange = DelinquencyRange.valueOf(expectedRange);
String expectedDelinquencyRangeValue = expectedDelinquencyRange.getValue();
@@ -627,9 +632,9 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("LoanDelinquencyRangeChangeBusinessEvent has the same Delinquency range, date and amount as in LoanDetails on both loan- and installment-level")
- public void checkDelinquencyRangeInEvent() throws IOException {
+ public void checkDelinquencyRangeInEvent() {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
DelinquencyRangeData delinquencyRange = loanDetails.getDelinquencyRange();
@@ -699,18 +704,17 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
}
@Then("In Loan details delinquent.lastRepaymentAmount is {int} EUR with lastRepaymentDate {string}")
- public void delinquentLastRepaymentAmountCheck(int expectedLastRepaymentAmount, String expectedLastRepaymentDate) throws IOException {
+ public void delinquentLastRepaymentAmountCheck(int expectedLastRepaymentAmount, String expectedLastRepaymentDate) {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
GetLoansLoanIdResponse loanDetails = ok(() -> fineractClient.loans().retrieveLoan(loanId, Map.of("associations", "collection")));
- Double expectedLastRepaymentAmount1 = Double.valueOf(expectedLastRepaymentAmount);
Double actualLastRepaymentAmount = loanDetails.getDelinquent().getLastRepaymentAmount().doubleValue();
String actualLastRepaymentDate = FORMATTER.format(loanDetails.getDelinquent().getLastRepaymentDate());
assertThat(actualLastRepaymentAmount)//
- .as(ErrorMessageHelper.wrongDataInDelinquentLastRepaymentAmount(actualLastRepaymentAmount, expectedLastRepaymentAmount1))//
+ .as(ErrorMessageHelper.wrongDataInDelinquentLastRepaymentAmount(actualLastRepaymentAmount, actualLastRepaymentAmount))//
.isEqualTo(expectedLastRepaymentAmount);//
assertThat(actualLastRepaymentDate)//
.as(ErrorMessageHelper.wrongDataInDelinquentLastRepaymentDate(actualLastRepaymentDate, expectedLastRepaymentDate))//
@@ -751,7 +755,7 @@ public class LoanDelinquencyStepDef extends AbstractStepDef {
@Then("LoanDelinquencyRangeChangeBusinessEvent is created")
public void checkLoanDelinquencyRangeChangeBusinessEventCreated() {
PostLoansResponse loanResponse = testContext().get(TestContextKey.LOAN_CREATE_RESPONSE);
- long loanId = loanResponse.getLoanId();
+ Long loanId = loanResponse.getLoanId();
eventAssertion.assertEventRaised(LoanDelinquencyRangeChangeEvent.class, loanId);
}
diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanDelinquency.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanDelinquency.feature
index b3444a6d68..e0479d43fd 100644
--- a/fineract-e2e-tests-runner/src/test/resources/features/LoanDelinquency.feature
+++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanDelinquency.feature
@@ -2176,3 +2176,48 @@ Feature: LoanDelinquency
And Loan has the following LOAN level delinquency data:
| classification | delinquentAmount | delinquentDate | delinquentDays | pastDueDays |
| RANGE_90 | 666.68 | 06 February 2025 | 98 | 103 |
+
+ Scenario: Verify that pastDueDate is returned correctly for overdue loan
+ When Admin sets the business date to "01 October 2023"
+ When Admin creates a client with random data
+ When Admin creates a fully customized loan with the following data:
+ | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
+ | LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL_INSTALLMENT_LEVEL_DELINQUENCY | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
+ And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
+ When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
+ When Admin sets the business date to "10 October 2023"
+ When Admin runs inline COB job for Loan
+ Then Loan has the following LOAN level delinquency data:
+ | classification | delinquentAmount | delinquentDate | pastDueDate | delinquentDays | pastDueDays |
+ | RANGE_3 | 250.0 | 04 October 2023 | 01 October 2023 | 6 | 9 |
+
+ Scenario: Verify that pastDueDate is null when loan has no overdue
+ When Admin sets the business date to "01 October 2023"
+ When Admin creates a client with random data
+ When Admin creates a fully customized loan with the following data:
+ | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
+ | LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL_INSTALLMENT_LEVEL_DELINQUENCY | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
+ And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
+ When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
+ And Customer makes "AUTOPAY" repayment on "01 October 2023" with 250 EUR transaction amount
+ When Admin runs inline COB job for Loan
+ Then Loan has the following LOAN level delinquency data:
+ | classification | delinquentAmount | delinquentDate | pastDueDate | delinquentDays | pastDueDays |
+ | NO_DELINQUENCY | 0.0 | null | null | 0 | 0 |
+
+ Scenario: Verify that pastDueDate equals chargeback date when chargeback creates overdue
+ When Admin sets the business date to "01 October 2023"
+ When Admin creates a client with random data
+ When Admin creates a fully customized loan with the following data:
+ | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy |
+ | LP2_DOWNPAYMENT_ADV_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL_INSTALLMENT_LEVEL_DELINQUENCY | 01 October 2023 | 1000 | 0 | FLAT | SAME_AS_REPAYMENT_PERIOD | EQUAL_INSTALLMENTS | 45 | DAYS | 15 | DAYS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION |
+ And Admin successfully approves the loan on "01 October 2023" with "1000" amount and expected disbursement date on "01 October 2023"
+ When Admin successfully disburse the loan on "01 October 2023" with "1000" EUR transaction amount
+ And Customer makes "AUTOPAY" repayment on "01 October 2023" with 250 EUR transaction amount
+ When Admin sets the business date to "05 October 2023"
+ When Admin makes "REPAYMENT_ADJUSTMENT_CHARGEBACK" chargeback with 250 EUR transaction amount
+ When Admin sets the business date to "10 October 2023"
+ When Admin runs inline COB job for Loan
+ Then Loan has the following LOAN level delinquency data:
+ | classification | delinquentAmount | delinquentDate | pastDueDate | delinquentDays | pastDueDays |
+ | RANGE_1 | 250.0 | 08 October 2023 | 05 October 2023 | 2 | 5 |
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
index e4fc1eb187..53d3f244d7 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
@@ -126,6 +126,7 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
overdueDays = 0L;
}
collectionData.setPastDueDays(overdueDays);
+ collectionData.setPastDueDate(overdueSinceDate);
LocalDate delinquentStartDate = overdueSinceDate.plusDays(graceDays.longValue());
collectionData.setDelinquentDate(delinquentStartDate);
}
@@ -205,6 +206,7 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
overdueDays = 0L;
}
collectionData.setPastDueDays(overdueDays);
+ collectionData.setPastDueDate(overdueSinceDate);
LocalDate delinquentStartDate = overdueSinceDate.plusDays(graceDays.longValue());
collectionData.setDelinquentDate(delinquentStartDate);
}
@@ -244,6 +246,7 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
overdueDays = 0L;
}
collectionData.setPastDueDays(overdueDays);
+ collectionData.setPastDueDate(overdueSinceDate);
collectionData.setDelinquentDate(overdueSinceDate);
}
collectionData.setDelinquentAmount(outstandingAmount);
diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java
index 4d22a3f275..8317db7b08 100644
--- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java
+++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/data/CollectionData.java
@@ -31,6 +31,7 @@ public final class CollectionData {
private BigDecimal availableDisbursementAmount;
private BigDecimal availableDisbursementAmountWithOverApplied;
private Long pastDueDays;
+ private LocalDate pastDueDate;
private LocalDate nextPaymentDueDate;
private BigDecimal nextPaymentAmount;
private Long delinquentDays;
@@ -52,7 +53,8 @@ public final class CollectionData {
public static CollectionData template() {
final BigDecimal zero = BigDecimal.ZERO;
- return new CollectionData(zero, zero, 0L, null, zero, 0L, null, zero, null, zero, null, zero, null, null, zero, zero, zero, zero);
+ return new CollectionData(zero, zero, 0L, null, null, zero, 0L, null, zero, null, zero, null, zero, null, null, zero, zero, zero,
+ zero);
}
}
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
index a2349178a5..45bb2367fb 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java
@@ -1027,6 +1027,8 @@ final class LoansApiResourceSwagger {
@Schema(example = "12")
public Integer pastDueDays;
@Schema(example = "[2022, 07, 01]")
+ public LocalDate pastDueDate;
+ @Schema(example = "[2022, 07, 01]")
public LocalDate nextPaymentDueDate;
@Schema(example = "123.23")
public BigDecimal nextPaymentAmount;
diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
index 5e05029bcb..d692d1d967 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/DelinquencyWritePlatformServiceRangeChangeEventTest.java
@@ -186,8 +186,8 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
LoanScheduleDelinquencyData loanScheduleDelinquencyData = new LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
loanForProcessing);
final BigDecimal zero = BigDecimal.ZERO;
- CollectionData collectionData = new CollectionData(zero, zero, 2L, null, zero, 2L, overDueSinceDate, zero, null, null, null, null,
- null, null, zero, zero, zero, zero);
+ CollectionData collectionData = new CollectionData(zero, zero, 2L, overDueSinceDate, null, zero, 2L, overDueSinceDate, zero, null,
+ null, null, null, null, null, zero, zero, zero, zero);
Map<Long, CollectionData> installmentsCollection = new HashMap<>();
@@ -240,11 +240,12 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
LocalDate overDueSinceDate = DateUtils.getBusinessLocalDate().minusDays(2);
LoanScheduleDelinquencyData loanScheduleDelinquencyData = new LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
loanForProcessing);
- CollectionData collectionData = new CollectionData(zeroAmount, zeroAmount, 2L, null, zeroAmount, 2L, overDueSinceDate, zeroAmount,
- null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
+ CollectionData collectionData = new CollectionData(zeroAmount, zeroAmount, 2L, overDueSinceDate, null, zeroAmount, 2L,
+ overDueSinceDate, zeroAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
- CollectionData installmentCollectionData = new CollectionData(zeroAmount, zeroAmount, 2L, null, zeroAmount, 2L, overDueSinceDate,
- installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
+ CollectionData installmentCollectionData = new CollectionData(zeroAmount, zeroAmount, 2L, overDueSinceDate, null, zeroAmount, 2L,
+ overDueSinceDate, installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount,
+ zeroAmount);
Map<Long, CollectionData> installmentsCollection = new HashMap<>();
installmentsCollection.put(1L, installmentCollectionData);
@@ -369,11 +370,12 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
LoanScheduleDelinquencyData loanScheduleDelinquencyData = new LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
loanForProcessing);
- CollectionData collectionData = new CollectionData(zeroAmount, zeroAmount, 2L, null, zeroAmount, 2L, overDueSinceDate, zeroAmount,
- null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
+ CollectionData collectionData = new CollectionData(zeroAmount, zeroAmount, 2L, overDueSinceDate, null, zeroAmount, 2L,
+ overDueSinceDate, zeroAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
- CollectionData installmentCollectionData = new CollectionData(zeroAmount, zeroAmount, 2L, null, zeroAmount, 2L, overDueSinceDate,
- installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
+ CollectionData installmentCollectionData = new CollectionData(zeroAmount, zeroAmount, 2L, overDueSinceDate, null, zeroAmount, 2L,
+ overDueSinceDate, installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount,
+ zeroAmount);
Map<Long, CollectionData> installmentsCollection = new HashMap<>();
installmentsCollection.put(1L, installmentCollectionData);
@@ -446,11 +448,12 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
LocalDate overDueSinceDate = DateUtils.getBusinessLocalDate().minusDays(29);
LoanScheduleDelinquencyData loanScheduleDelinquencyData = new LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
loanForProcessing);
- CollectionData collectionData = new CollectionData(zeroAmount, zeroAmount, 29L, null, zeroAmount, 29L, overDueSinceDate, zeroAmount,
- null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
+ CollectionData collectionData = new CollectionData(zeroAmount, zeroAmount, 29L, overDueSinceDate, null, zeroAmount, 29L,
+ overDueSinceDate, zeroAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
- CollectionData installmentCollectionData = new CollectionData(zeroAmount, zeroAmount, 29L, null, zeroAmount, 29L, overDueSinceDate,
- installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
+ CollectionData installmentCollectionData = new CollectionData(zeroAmount, zeroAmount, 29L, overDueSinceDate, null, zeroAmount, 29L,
+ overDueSinceDate, installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount,
+ zeroAmount);
Map<Long, CollectionData> installmentsCollection = new HashMap<>();
installmentsCollection.put(1L, installmentCollectionData);
@@ -534,14 +537,14 @@ public class DelinquencyWritePlatformServiceRangeChangeEventTest {
LocalDate overDueSinceDate = DateUtils.getBusinessLocalDate().minusDays(29);
LoanScheduleDelinquencyData loanScheduleDelinquencyData = new LoanScheduleDelinquencyData(1L, overDueSinceDate, 1L,
loanForProcessing);
- CollectionData collectionData = new CollectionData(zeroAmount, zeroAmount, 29L, null, zeroAmount, 29L, overDueSinceDate, zeroAmount,
- null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
+ CollectionData collectionData = new CollectionData(zeroAmount, zeroAmount, 29L, overDueSinceDate, null, zeroAmount, 29L,
+ overDueSinceDate, zeroAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
- CollectionData installmentCollectionData_1 = new CollectionData(zeroAmount, zeroAmount, 29L, null, zeroAmount, 29L,
- overDueSinceDate, installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount,
+ CollectionData installmentCollectionData_1 = new CollectionData(zeroAmount, zeroAmount, 29L, overDueSinceDate, null, zeroAmount,
+ 29L, overDueSinceDate, installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount,
zeroAmount);
- CollectionData installmentCollectionData_2 = new CollectionData(zeroAmount, zeroAmount, 0L, null, zeroAmount, 0L, null,
+ CollectionData installmentCollectionData_2 = new CollectionData(zeroAmount, zeroAmount, 0L, null, null, zeroAmount, 0L, null,
installmentPrincipalAmount, null, null, null, null, null, null, zeroAmount, zeroAmount, zeroAmount, zeroAmount);
Map<Long, CollectionData> installmentsCollection = new HashMap<>();
diff --git a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/LoanDelinquencyDomainServiceTest.java b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/LoanDelinquencyDomainServiceTest.java
index cd213cbf46..112d9b984f 100644
--- a/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/LoanDelinquencyDomainServiceTest.java
+++ b/fineract-provider/src/test/java/org/apache/fineract/portfolio/deliquency/LoanDelinquencyDomainServiceTest.java
@@ -139,6 +139,7 @@ public class LoanDelinquencyDomainServiceTest {
// then
assertEquals(0L, collectionData.getDelinquentDays());
assertEquals(null, collectionData.getDelinquentDate());
+ assertEquals(null, collectionData.getPastDueDate());
assertEquals(collectionData.getDelinquentDays(), collectionData.getPastDueDays());
}
@@ -170,6 +171,7 @@ public class LoanDelinquencyDomainServiceTest {
// then
assertEquals(daysDiff, collectionData.getDelinquentDays());
assertEquals(dueDate, collectionData.getDelinquentDate());
+ assertEquals(dueDate, collectionData.getPastDueDate());
assertEquals(collectionData.getDelinquentDays(), collectionData.getPastDueDays());
}
@@ -206,6 +208,7 @@ public class LoanDelinquencyDomainServiceTest {
// then
assertEquals(0L, collectionData.getDelinquentDays());
assertEquals(null, collectionData.getDelinquentDate());
+ assertEquals(null, collectionData.getPastDueDate());
assertEquals(collectionData.getDelinquentDays(), collectionData.getPastDueDays());
}
@@ -248,10 +251,12 @@ public class LoanDelinquencyDomainServiceTest {
assertEquals(daysDiff, loanCollectionData.getDelinquentDays());
assertEquals(dueDate, loanCollectionData.getDelinquentDate());
+ assertEquals(dueDate, loanCollectionData.getPastDueDate());
assertEquals(loanCollectionData.getDelinquentDays(), loanCollectionData.getPastDueDays());
assertEquals(daysDiff, installmentCollectionData.getDelinquentDays());
assertEquals(dueDate, installmentCollectionData.getDelinquentDate());
+ assertEquals(dueDate, installmentCollectionData.getPastDueDate());
assertEquals(installmentCollectionData.getDelinquentDays(), installmentCollectionData.getPastDueDays());
}
@@ -301,11 +306,13 @@ public class LoanDelinquencyDomainServiceTest {
assertEquals(daysDiff, loanCollectionData.getDelinquentDays());
assertEquals(transactionDate, loanCollectionData.getDelinquentDate());
+ assertEquals(transactionDate, loanCollectionData.getPastDueDate());
assertEquals(loanCollectionData.getDelinquentDays(), loanCollectionData.getPastDueDays());
// then
assertEquals(daysDiff, installmentCollectionData.getDelinquentDays());
assertEquals(transactionDate, installmentCollectionData.getDelinquentDate());
+ assertEquals(transactionDate, installmentCollectionData.getPastDueDate());
assertEquals(installmentCollectionData.getDelinquentDays(), installmentCollectionData.getPastDueDays());
assertEquals(0, principal.compareTo(installmentCollectionData.getDelinquentAmount()));
@@ -353,6 +360,7 @@ public class LoanDelinquencyDomainServiceTest {
CollectionData loanCollectionData = collectionData.getLoanCollectionData();
assertEquals(35L, loanCollectionData.getDelinquentDays());
assertEquals(LocalDate.of(2022, 1, 16), loanCollectionData.getDelinquentDate());
+ assertEquals(LocalDate.of(2022, 1, 16), loanCollectionData.getPastDueDate());
Map<Long, CollectionData> installments = collectionData.getLoanInstallmentsCollectionData();
assertNotNull(installments);
@@ -408,6 +416,7 @@ public class LoanDelinquencyDomainServiceTest {
CollectionData loanCollectionData = delinquencyData.getLoanCollectionData();
assertEquals(16L, loanCollectionData.getDelinquentDays());
assertEquals(LocalDate.of(2022, 1, 10), loanCollectionData.getDelinquentDate());
+ assertEquals(LocalDate.of(2022, 1, 10), loanCollectionData.getPastDueDate());
Map<Long, CollectionData> installmentData = delinquencyData.getLoanInstallmentsCollectionData();
assertEquals(3, installmentData.size());