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 2023/06/05 07:40:52 UTC

[fineract] branch develop updated: FINERACT-1724: Arrears configuration at loan creation is not considered correctly

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


The following commit(s) were added to refs/heads/develop by this push:
     new b1af8a3f8 FINERACT-1724: Arrears configuration at loan creation is not considered correctly
b1af8a3f8 is described below

commit b1af8a3f833503b65ebf42717b0d69de9c0e2891
Author: Jose Alberto Hernandez <al...@blue-daemon.local>
AuthorDate: Thu Jun 1 01:50:50 2023 -0600

    FINERACT-1724: Arrears configuration at loan creation is not considered correctly
---
 .../service/LoanDelinquencyDomainServiceImpl.java  |   7 +-
 .../loanaccount/api/LoansApiResourceSwagger.java   |   2 +
 .../api/LoanProductsApiResourceSwagger.java        |   2 +
 .../LoanDelinquencyDomainServiceTest.java          |   9 +-
 .../DelinquencyBucketsIntegrationTest.java         | 115 +++++++++++++++++++--
 5 files changed, 116 insertions(+), 19 deletions(-)

diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
index 90e036f82..13507cf59 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/LoanDelinquencyDomainServiceImpl.java
@@ -120,11 +120,8 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
         }
 
         Integer graceDays = 0;
-        if (loan.getLoanProduct().getLoanProductRelatedDetail().getGraceOnArrearsAgeing() != null) {
-            graceDays = loan.getLoanProduct().getLoanProductRelatedDetail().getGraceOnArrearsAgeing();
-            if (graceDays == null) {
-                graceDays = 0;
-            }
+        if (loan.getLoanProductRelatedDetail().getGraceOnArrearsAgeing() != null) {
+            graceDays = loan.getLoanProductRelatedDetail().getGraceOnArrearsAgeing();
         }
         log.debug("Loan id {} with overdue since date {} and outstanding amount {}", loan.getId(), overdueSinceDate, outstandingAmount);
 
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 96bf96f27..2c636e45f 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
@@ -1072,6 +1072,8 @@ final class LoansApiResourceSwagger {
         public LocalDate overpaidOnDate;
         @Schema(example = "false")
         public Boolean chargedOff;
+        @Schema(example = "3")
+        public Integer inArrearsTolerance;
     }
 
     @Schema(description = "GetLoansResponse")
diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
index 1f2afab08..2626ac977 100644
--- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
+++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanproduct/api/LoanProductsApiResourceSwagger.java
@@ -1199,6 +1199,8 @@ final class LoanProductsApiResourceSwagger {
         public Integer dueDaysForRepaymentEvent;
         @Schema(example = "3")
         public Integer overDueDaysForRepaymentEvent;
+        @Schema(example = "3")
+        public Integer inArrearsTolerance;
     }
 
     @Schema(description = "PutLoanProductsProductIdRequest")
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 054021e56..c75580724 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
@@ -112,8 +112,7 @@ public class LoanDelinquencyDomainServiceTest {
 
         // when
         when(loanProductRelatedDetail.getGraceOnArrearsAgeing()).thenReturn(0);
-        when(loanProduct.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
-        when(loan.getLoanProduct()).thenReturn(loanProduct);
+        when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
         when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
         when(loan.getCurrency()).thenReturn(currency);
 
@@ -137,8 +136,7 @@ public class LoanDelinquencyDomainServiceTest {
 
         // when
         when(loanProductRelatedDetail.getGraceOnArrearsAgeing()).thenReturn(0);
-        when(loanProduct.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
-        when(loan.getLoanProduct()).thenReturn(loanProduct);
+        when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
         when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
         when(loan.getLoanTransactions(Mockito.any(Predicate.class))).thenReturn(Collections.emptyList());
         when(loan.getLastLoanRepaymentScheduleInstallment()).thenReturn(repaymentScheduleInstallments.get(0));
@@ -174,8 +172,7 @@ public class LoanDelinquencyDomainServiceTest {
 
         // when
         when(loanProductRelatedDetail.getGraceOnArrearsAgeing()).thenReturn(0);
-        when(loanProduct.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
-        when(loan.getLoanProduct()).thenReturn(loanProduct);
+        when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
         when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
         when(loan.getCurrency()).thenReturn(currency);
 
diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
index 252c7ec64..dcac50af7 100644
--- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
+++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/DelinquencyBucketsIntegrationTest.java
@@ -50,6 +50,8 @@ import org.apache.fineract.client.models.PostDelinquencyRangeResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
 import org.apache.fineract.client.models.PutDelinquencyBucketResponse;
 import org.apache.fineract.client.models.PutDelinquencyRangeResponse;
+import org.apache.fineract.client.models.PutLoanProductsProductIdRequest;
+import org.apache.fineract.client.models.PutLoanProductsProductIdResponse;
 import org.apache.fineract.cob.data.JobBusinessStepConfigData;
 import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
 import org.apache.fineract.integrationtests.common.BusinessDateHelper;
@@ -294,7 +296,7 @@ public class DelinquencyBucketsIntegrationTest {
 
             // Create Loan Account
             final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
-                    getLoanProductsProductResponse.getId().toString(), operationDate);
+                    getLoanProductsProductResponse.getId().toString(), operationDate, null);
 
             GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
             assertNotNull(getLoansLoanIdResponse);
@@ -390,7 +392,7 @@ public class DelinquencyBucketsIntegrationTest {
 
             // Create Loan Account
             final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
-                    getLoanProductsProductResponse.getId().toString(), operationDate);
+                    getLoanProductsProductResponse.getId().toString(), operationDate, null);
 
             GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
             log.info("Loan Delinquency Range after Disbursement {}", getLoansLoanIdResponse.getDelinquencyRange().getClassification());
@@ -488,7 +490,7 @@ public class DelinquencyBucketsIntegrationTest {
 
         // Create Loan Account
         final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
-                getLoanProductsProductResponse.getId().toString(), operationDate);
+                getLoanProductsProductResponse.getId().toString(), operationDate, null);
 
         GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
         assertNotNull(getLoansLoanIdResponse);
@@ -583,7 +585,7 @@ public class DelinquencyBucketsIntegrationTest {
 
         // Create Loan Account
         final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
-                getLoanProductsProductResponse.getId().toString(), operationDate);
+                getLoanProductsProductResponse.getId().toString(), operationDate, null);
 
         GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
         loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
@@ -689,7 +691,7 @@ public class DelinquencyBucketsIntegrationTest {
 
             // Create Loan Account
             final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
-                    getLoanProductsProductResponse.getId().toString(), operationDate);
+                    getLoanProductsProductResponse.getId().toString(), operationDate, null);
 
             // Run first time the Job
             final String jobName = "Loan Delinquency Classification";
@@ -777,7 +779,7 @@ public class DelinquencyBucketsIntegrationTest {
 
             // Create Loan Account
             final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
-                    getLoanProductsProductResponse.getId().toString(), operationDate);
+                    getLoanProductsProductResponse.getId().toString(), operationDate, null);
 
             // COB Step Validation
             final JobBusinessStepConfigData jobBusinessStepConfigData = BusinessStepConfigurationHelper
@@ -879,7 +881,7 @@ public class DelinquencyBucketsIntegrationTest {
 
             // Create Loan Account
             final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
-                    getLoanProductsProductResponse.getId().toString(), operationDate);
+                    getLoanProductsProductResponse.getId().toString(), operationDate, null);
 
             // Get loan details expecting to have a delinquency classification
             GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
@@ -911,6 +913,95 @@ public class DelinquencyBucketsIntegrationTest {
         }
     }
 
+    @Test
+    public void testLoanClassificationUsingAgeingArrears() {
+        try {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
+
+            LocalDate bussinesLocalDate = Utils.getDateAsLocalDate("01 January 2012");
+            log.info("Current date {}", bussinesLocalDate);
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
+
+            // Given
+            final LoanTransactionHelper loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
+            final SchedulerJobHelper schedulerJobHelper = new SchedulerJobHelper(requestSpec);
+
+            ArrayList<Integer> rangeIds = new ArrayList<>();
+            String jsonRange = DelinquencyRangesHelper.getAsJSON(1, 3);
+            PostDelinquencyRangeResponse delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec,
+                    responseSpec, jsonRange);
+            rangeIds.add(delinquencyRangeResponse.getResourceId());
+            final GetDelinquencyRangesResponse range = DelinquencyRangesHelper.getDelinquencyRange(requestSpec, responseSpec,
+                    delinquencyRangeResponse.getResourceId());
+            final String classificationExpected = range.getClassification();
+            log.info("Expected Delinquency Range classification {}", classificationExpected);
+
+            jsonRange = DelinquencyRangesHelper.getAsJSON(4, 60);
+            delinquencyRangeResponse = DelinquencyRangesHelper.createDelinquencyRange(requestSpec, responseSpec, jsonRange);
+            rangeIds.add(delinquencyRangeResponse.getResourceId());
+
+            String jsonBucket = DelinquencyBucketsHelper.getAsJSON(rangeIds);
+            PostDelinquencyBucketResponse delinquencyBucketResponse = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec,
+                    responseSpec, jsonBucket);
+            final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                    delinquencyBucketResponse.getResourceId());
+
+            // Client and Loan account creation
+            final Integer clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec, "01 January 2012");
+            final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper,
+                    delinquencyBucket.getId(), "3");
+            assertNotNull(getLoanProductsProductResponse);
+            log.info("Loan Product Arrears: {}", getLoanProductsProductResponse.getInArrearsTolerance());
+            assertEquals(3, getLoanProductsProductResponse.getInArrearsTolerance());
+
+            // Older date to have more than one overdue installment
+            final LocalDate transactionDate = bussinesLocalDate;
+            String operationDate = Utils.dateFormatter.format(transactionDate);
+
+            // Create Loan Account
+            final Integer loanId = createLoanAccount(loanTransactionHelper, clientId.toString(),
+                    getLoanProductsProductResponse.getId().toString(), operationDate, "3");
+
+            // Get loan details expecting to have a delinquency classification
+            GetLoansLoanIdResponse getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+            final GetDelinquencyRangesResponse firstTestCase = getLoansLoanIdResponse.getDelinquencyRange();
+            log.info("Loan Delinquency Range is null {}", (firstTestCase == null));
+            loanTransactionHelper.printRepaymentSchedule(getLoansLoanIdResponse);
+            log.info("Loan Account Arrears {}", getLoansLoanIdResponse.getInArrearsTolerance());
+            assertEquals(3, getLoansLoanIdResponse.getInArrearsTolerance());
+
+            // Update the Loan Product
+            updateLoanProduct(loanTransactionHelper, getLoanProductsProductResponse.getId(), 0);
+            GetLoanProductsProductIdResponse loanProductsProductIdResponseUpd = loanTransactionHelper
+                    .getLoanProduct(getLoanProductsProductResponse.getId().intValue());
+            assertNotNull(loanProductsProductIdResponseUpd);
+            log.info("Loan Product Arrears: {}", loanProductsProductIdResponseUpd.getInArrearsTolerance());
+            assertEquals(0, loanProductsProductIdResponseUpd.getInArrearsTolerance());
+
+            final String jobName = "Loan COB";
+
+            bussinesLocalDate = Utils.getDateAsLocalDate("31 January 2012");
+            LocalDate lastLoanCOBBusinessDate = bussinesLocalDate.minusDays(1);
+            schedulerJobHelper.fastForwardTime(lastLoanCOBBusinessDate, bussinesLocalDate, jobName, responseSpec);
+            log.info("Current date {}", bussinesLocalDate);
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, bussinesLocalDate);
+            // Run Second time the Job
+            schedulerJobHelper.executeAndAwaitJob(jobName);
+
+            // Get loan details expecting to have a delinquency classification
+            getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId);
+            loanTransactionHelper.printDelinquencyData(getLoansLoanIdResponse);
+
+            GetLoansLoanIdCollectionData getLoansLoanIdCollectionData = getLoansLoanIdResponse.getDelinquent();
+            assertNotNull(getLoansLoanIdCollectionData);
+            assertEquals(0, getLoansLoanIdCollectionData.getDelinquentDays());
+            assertEquals(0, getLoansLoanIdCollectionData.getPastDueDays());
+
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
+        }
+    }
+
     private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper,
             final Integer delinquencyBucketId, final String inArrearsTolerance) {
         final HashMap<String, Object> loanProductMap = new LoanProductTestBuilder().withInArrearsTolerance(inArrearsTolerance).build(null,
@@ -919,8 +1010,15 @@ public class DelinquencyBucketsIntegrationTest {
         return loanTransactionHelper.getLoanProduct(loanProductId);
     }
 
+    private PutLoanProductsProductIdResponse updateLoanProduct(LoanTransactionHelper loanTransactionHelper, Long id,
+            final Integer inArrearsTolerance) {
+        final PutLoanProductsProductIdRequest requestModifyLoan = new PutLoanProductsProductIdRequest()
+                .inArrearsTolerance(inArrearsTolerance);
+        return loanTransactionHelper.updateLoanProduct(id, requestModifyLoan);
+    }
+
     private Integer createLoanAccount(final LoanTransactionHelper loanTransactionHelper, final String clientId, final String loanProductId,
-            final String operationDate) {
+            final String operationDate, final String inArrearsTolerance) {
         final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal(principalAmount).withLoanTermFrequency("12")
                 .withLoanTermFrequencyAsMonths().withNumberOfRepayments("12").withRepaymentEveryAfter("1")
                 .withRepaymentFrequencyTypeAsMonths() //
@@ -928,6 +1026,7 @@ public class DelinquencyBucketsIntegrationTest {
                 .withExpectedDisbursementDate(operationDate) //
                 .withInterestTypeAsDecliningBalance() //
                 .withSubmittedOnDate(operationDate) //
+                .withInArrearsTolerance(inArrearsTolerance) //
                 .build(clientId, loanProductId, null);
         final Integer loanId = loanTransactionHelper.getLoanId(loanApplicationJSON);
         loanTransactionHelper.approveLoan(operationDate, principalAmount, loanId, null);