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 5d0152c26 FINERACT-1971: Disable delinquency calculation for non 
active loans
5d0152c26 is described below

commit 5d0152c262e372fef71e3d99c3ffc2460c8e0cde
Author: Ruchi Dhamankar <[email protected]>
AuthorDate: Wed May 8 14:07:37 2024 +0530

    FINERACT-1971: Disable delinquency calculation for non active loans
---
 .../portfolio/loanaccount/domain/Loan.java         |   3 +-
 .../DelinquencyReadPlatformServiceImpl.java        |   7 +-
 .../DelinquencyWritePlatformServiceImpl.java       |   4 +-
 .../service/LoanDelinquencyDomainServiceImpl.java  |  16 ++
 .../LoanDelinquencyDomainServiceTest.java          |   6 +
 .../integrationtests/BaseLoanIntegrationTest.java  |   9 +
 .../LoanDelinquencyForNonActiveAccountsTest.java   | 272 +++++++++++++++++++++
 .../common/loans/LoanTransactionHelper.java        |   4 +
 8 files changed, 316 insertions(+), 5 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index 172709b9d..0769c1c26 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -4186,7 +4186,7 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
         return getStatus().isClosedWithOutsandingAmountMarkedForReschedule();
     }
 
-    private boolean isCancelled() {
+    public boolean isCancelled() {
         return isRejected() || isWithdrawn();
     }
 
@@ -7273,5 +7273,4 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
     public void updateEnableInstallmentLevelDelinquency(boolean 
enableInstallmentLevelDelinquency) {
         this.enableInstallmentLevelDelinquency = 
enableInstallmentLevelDelinquency;
     }
-
 }
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
index 876609bea..103152965 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyReadPlatformServiceImpl.java
@@ -126,8 +126,8 @@ public class DelinquencyReadPlatformServiceImpl implements 
DelinquencyReadPlatfo
         if (optLoan.isPresent()) {
             final Loan loan = optLoan.get();
 
-            // If the Loan is not Active yet, return template data
-            if (loan.isSubmittedAndPendingApproval() || loan.isApproved()) {
+            // If the Loan is not Active yet or is cancelled (rejected or 
withdrawn), return template data
+            if (loan.isSubmittedAndPendingApproval() || loan.isApproved() || 
loan.isCancelled()) {
                 return CollectionData.template();
             }
 
@@ -137,6 +137,9 @@ public class DelinquencyReadPlatformServiceImpl implements 
DelinquencyReadPlatfo
 
             final String nextPaymentDueDateConfig = 
configurationDomainService.getNextPaymentDateConfigForLoan();
 
+            // Below method calculates delinquency for active loans only and 
returns template data for Closed or
+            // Overpaid
+            // loans
             collectionData = 
loanDelinquencyDomainService.getOverdueCollectionData(loan, 
effectiveDelinquencyList);
             
collectionData.setAvailableDisbursementAmount(loan.getApprovedPrincipal().subtract(loan.getDisbursedAmount()));
             
collectionData.setNextPaymentDueDate(loan.possibleNextRepaymentDate(nextPaymentDueDateConfig));
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java
index 185ef2875..2cda8e753 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/delinquency/service/DelinquencyWritePlatformServiceImpl.java
@@ -162,7 +162,9 @@ public class DelinquencyWritePlatformServiceImpl implements 
DelinquencyWritePlat
         }
         CollectionData collectionData = null;
         // If the Loan is not Active yet, return template data
-        if (loan.isSubmittedAndPendingApproval() || loan.isApproved()) {
+        // If the Loan is Rejected, Closed written-off, Withdrawn by Client, 
Closed with outstanding marked for
+        // reschedule, Closed obligation met, Overpaid, return template data
+        if (loan.isSubmittedAndPendingApproval() || loan.isApproved() || 
loan.isClosed() || loan.getStatus().isOverpaid()) {
             collectionData = CollectionData.template();
         } else {
             collectionData = 
loanDelinquencyDomainService.getOverdueCollectionData(loan, 
effectiveDelinquencyList);
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 8b048e7af..d6ad0e5fc 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
@@ -56,6 +56,14 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
         boolean overdueSinceDateWasSet = false;
         boolean firstNotYetDueInstallment = false;
         log.debug("Loan id {} with {} installments", loan.getId(), 
loan.getRepaymentScheduleInstallments().size());
+
+        // If the Loan is not Active yet, return template data
+        // If the Loan is Rejected, Closed written-off, Withdrawn by Client, 
Closed with outstanding marked for
+        // reschedule, Closed obligation met, Overpaid return template data
+        if (loan.isSubmittedAndPendingApproval() || loan.isApproved() || 
loan.isClosed() || loan.getStatus().isOverpaid()) {
+            return CollectionData.template();
+        }
+
         // Get the oldest overdue installment if exists one
         for (LoanRepaymentScheduleInstallment installment : 
loan.getRepaymentScheduleInstallments()) {
             if (!installment.isObligationsMet()) {
@@ -124,6 +132,14 @@ public class LoanDelinquencyDomainServiceImpl implements 
LoanDelinquencyDomainSe
         boolean overdueSinceDateWasSet = false;
         boolean firstNotYetDueInstallment = false;
         log.debug("Loan id {} with {} installments", loan.getId(), 
loan.getRepaymentScheduleInstallments().size());
+
+        // If the Loan is not Active yet, return template data
+        // If the Loan is Rejected, Closed written-off, Withdrawn by Client, 
Closed with outstanding marked for
+        // reschedule, Closed obligation met, Overpaid, return template data
+        if (loan.isSubmittedAndPendingApproval() || loan.isApproved() || 
loan.isClosed() || loan.getStatus().isOverpaid()) {
+            return new LoanDelinquencyData(collectionData, 
loanInstallmentsCollectionData);
+        }
+
         for (LoanRepaymentScheduleInstallment installment : 
loan.getRepaymentScheduleInstallments()) {
             CollectionData installmentCollectionData = 
CollectionData.template();
             if (!installment.isObligationsMet()) {
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 67cd74e71..dcd3dd42f 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
@@ -49,6 +49,7 @@ import 
org.apache.fineract.portfolio.loanaccount.data.CollectionData;
 import org.apache.fineract.portfolio.loanaccount.data.LoanDelinquencyData;
 import org.apache.fineract.portfolio.loanaccount.domain.Loan;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
+import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus;
 import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
 import 
org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionToRepaymentScheduleMapping;
 import org.apache.fineract.portfolio.loanproduct.domain.LoanProduct;
@@ -123,6 +124,7 @@ public class LoanDelinquencyDomainServiceTest {
         
when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
         
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
         when(loan.getCurrency()).thenReturn(currency);
+        when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
 
         CollectionData collectionData = 
underTest.getOverdueCollectionData(loan, effectiveDelinquencyList);
 
@@ -150,6 +152,7 @@ public class LoanDelinquencyDomainServiceTest {
         
when(loan.getLoanTransactions(Mockito.any(Predicate.class))).thenReturn(Collections.emptyList());
         
when(loan.getLastLoanRepaymentScheduleInstallment()).thenReturn(repaymentScheduleInstallments.get(0));
         when(loan.getCurrency()).thenReturn(currency);
+        when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
         
when(delinquencyEffectivePauseHelper.getPausedDaysBeforeDate(effectiveDelinquencyList,
 businessDate)).thenReturn(0L);
 
         CollectionData collectionData = 
underTest.getOverdueCollectionData(loan, effectiveDelinquencyList);
@@ -186,6 +189,7 @@ public class LoanDelinquencyDomainServiceTest {
         
when(loan.getLoanProductRelatedDetail()).thenReturn(loanProductRelatedDetail);
         
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
         when(loan.getCurrency()).thenReturn(currency);
+        when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
 
         CollectionData collectionData = 
underTest.getOverdueCollectionData(loan, effectiveDelinquencyList);
 
@@ -217,6 +221,7 @@ public class LoanDelinquencyDomainServiceTest {
         
when(loan.getLastLoanRepaymentScheduleInstallment()).thenReturn(repaymentScheduleInstallments.get(0));
         when(loan.getCurrency()).thenReturn(currency);
         when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true);
+        when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
         
when(delinquencyEffectivePauseHelper.getPausedDaysBeforeDate(effectiveDelinquencyList,
 businessDate)).thenReturn(0L);
 
         LoanDelinquencyData collectionData = 
underTest.getLoanDelinquencyData(loan, effectiveDelinquencyList);
@@ -266,6 +271,7 @@ public class LoanDelinquencyDomainServiceTest {
         
when(loan.getRepaymentScheduleInstallments()).thenReturn(repaymentScheduleInstallments);
         when(loan.isEnableInstallmentLevelDelinquency()).thenReturn(true);
         when(loan.getCurrency()).thenReturn(currency);
+        when(loan.getStatus()).thenReturn(LoanStatus.ACTIVE);
         
when(loan.getLoanTransactions(Mockito.any(Predicate.class))).thenReturn(Arrays.asList(loanTransaction));
         
when(delinquencyEffectivePauseHelper.getPausedDaysBeforeDate(effectiveDelinquencyList,
 businessDate)).thenReturn(0L);
 
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
index a0eedefb4..6b56a36b6 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java
@@ -851,6 +851,15 @@ public abstract class BaseLoanIntegrationTest {
         assertEquals(loanStatus.getCode(), loanDetails.getStatus().getCode());
     }
 
+    protected void undoLoanApproval(Long loanId) {
+        loanTransactionHelper.undoApprovalForLoan(loanId, new 
PostLoansLoanIdRequest());
+    }
+
+    protected void rejectLoan(Long loanId, String rejectedOnDate) {
+        loanTransactionHelper.rejectLoan(loanId,
+                new 
PostLoansLoanIdRequest().rejectedOnDate(rejectedOnDate).locale("en").dateFormat(DATETIME_PATTERN));
+    }
+
     @RequiredArgsConstructor
     public static class BatchRequestBuilder {
 
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDelinquencyForNonActiveAccountsTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDelinquencyForNonActiveAccountsTest.java
new file mode 100644
index 000000000..754a67678
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDelinquencyForNonActiveAccountsTest.java
@@ -0,0 +1,272 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.math.BigDecimal;
+import java.util.List;
+import lombok.AllArgsConstructor;
+import org.apache.commons.lang3.tuple.Pair;
+import 
org.apache.fineract.client.models.GetLoansLoanIdLoanInstallmentLevelDelinquency;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.PostLoanProductsRequest;
+import org.apache.fineract.client.models.PostLoanProductsResponse;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
+import 
org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class LoanDelinquencyForNonActiveAccountsTest extends 
BaseLoanIntegrationTest {
+
+    private SchedulerJobHelper schedulerJobHelper = new 
SchedulerJobHelper(this.requestSpec);
+
+    @Test
+    public void testDelinquencyCalculationsForRejectedLoanAccount() {
+        runAt("06 May 2024", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create DelinquencyBuckets
+            Integer delinquencyBucketId = 
DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec, 
List.of(//
+                    Pair.of(1, 10), //
+                    Pair.of(11, 30), //
+                    Pair.of(31, 60), //
+                    Pair.of(61, null)//
+            ));
+
+            // Create Loan Product
+            PostLoanProductsRequest loanProductsRequest = 
create1InstallmentAmountInMultiplesOf4Period1MonthLongWithInterestAndAmortizationProduct(
+                    InterestType.FLAT, AmortizationType.EQUAL_INSTALLMENTS);
+            loanProductsRequest.setEnableInstallmentLevelDelinquency(true);
+            
loanProductsRequest.setDelinquencyBucketId(delinquencyBucketId.longValue());
+            PostLoanProductsResponse loanProductResponse = 
loanProductHelper.createLoanProduct(loanProductsRequest);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, 
loanProductResponse.getResourceId(), "06 May 2024", 1000.0, 4);
+
+            // Delinquency Calculations
+            verifyDelinquency(loanId, 0, "0.0", null, null);
+
+            // Update Business Date
+            updateBusinessDate("17 June 2024");
+
+            // Undo Approval
+            undoLoanApproval(loanId);
+
+            // Delinquency Calculations
+            verifyDelinquency(loanId, 0, "0.0", null, null);
+
+            // Reject Loan
+            rejectLoan(loanId, "17 June 2024");
+
+            // Delinquency Calculations
+            verifyDelinquency(loanId, 0, "0.0", null, null);
+        });
+    }
+
+    @Test
+    public void testDelinquencyCalculationsForRejectedLoanAccountCOBTest() {
+        runAt("06 May 2024", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create DelinquencyBuckets
+            Integer delinquencyBucketId = 
DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec, 
List.of(//
+                    Pair.of(1, 10), //
+                    Pair.of(11, 30), //
+                    Pair.of(31, 60), //
+                    Pair.of(61, null)//
+            ));
+
+            // Create Loan Product
+            PostLoanProductsRequest loanProductsRequest = 
create1InstallmentAmountInMultiplesOf4Period1MonthLongWithInterestAndAmortizationProduct(
+                    InterestType.FLAT, AmortizationType.EQUAL_INSTALLMENTS);
+            loanProductsRequest.setEnableInstallmentLevelDelinquency(true);
+            
loanProductsRequest.setDelinquencyBucketId(delinquencyBucketId.longValue());
+            PostLoanProductsResponse loanProductResponse = 
loanProductHelper.createLoanProduct(loanProductsRequest);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, 
loanProductResponse.getResourceId(), "06 May 2024", 1000.0, 4);
+
+            // Delinquency Calculations
+            verifyDelinquency(loanId, 0, "0.0", null, null);
+
+            // Update Business Date
+            updateBusinessDate("17 June 2024");
+
+            // Undo Approval
+            undoLoanApproval(loanId);
+
+            // Delinquency Calculations
+            verifyDelinquency(loanId, 0, "0.0", null, null);
+
+            // Reject Loan
+            rejectLoan(loanId, "17 June 2024");
+
+            // Delinquency Calculations
+            verifyDelinquency(loanId, 0, "0.0", null, null);
+
+            // Update Business Date
+            updateBusinessDate("18 June 2024");
+
+            // execute COB
+            schedulerJobHelper.executeAndAwaitJob("Loan COB");
+
+            // Delinquency Calculations
+            verifyDelinquency(loanId, 0, "0.0", null, null);
+
+        });
+    }
+
+    @Test
+    public void testDelinquencyCalculationsForClosedLoanAccount() {
+        runAt("06 May 2024", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create DelinquencyBuckets
+            Integer delinquencyBucketId = 
DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec, 
List.of(//
+                    Pair.of(1, 10), //
+                    Pair.of(11, 30), //
+                    Pair.of(31, 60), //
+                    Pair.of(61, null)//
+            ));
+
+            // Create Loan Product
+            PostLoanProductsRequest loanProductsRequest = 
create1InstallmentAmountInMultiplesOf4Period1MonthLongWithInterestAndAmortizationProduct(
+                    InterestType.FLAT, AmortizationType.EQUAL_INSTALLMENTS);
+            loanProductsRequest.setEnableInstallmentLevelDelinquency(true);
+            
loanProductsRequest.setDelinquencyBucketId(delinquencyBucketId.longValue());
+            PostLoanProductsResponse loanProductResponse = 
loanProductHelper.createLoanProduct(loanProductsRequest);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, 
loanProductResponse.getResourceId(), "06 May 2024", 1000.0, 4);
+
+            // Delinquency Calculations
+            verifyDelinquency(loanId, 0, "0.0", null, null);
+
+            // Update Business Date
+            updateBusinessDate("17 June 2024");
+
+            // disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000), "06 May 2024");
+
+            verifyDelinquency(loanId, 12, "250.0", null, null, //
+                    delinquency(11, 30, "250.0"));
+
+            // re-pay Loan
+            addRepaymentForLoan(loanId, 1000.0, "17 June 2024");
+
+            // Delinquency Calculations
+            verifyDelinquency(loanId, 0, "0.0", "17 June 2024", "1000.0");
+        });
+    }
+
+    @Test
+    public void testDelinquencyCalculationsForOverPaidLoanAccount() {
+        runAt("06 May 2024", () -> {
+            // Create Client
+            Long clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId();
+
+            // Create DelinquencyBuckets
+            Integer delinquencyBucketId = 
DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec, 
List.of(//
+                    Pair.of(1, 10), //
+                    Pair.of(11, 30), //
+                    Pair.of(31, 60), //
+                    Pair.of(61, null)//
+            ));
+
+            // Create Loan Product
+            PostLoanProductsRequest loanProductsRequest = 
create1InstallmentAmountInMultiplesOf4Period1MonthLongWithInterestAndAmortizationProduct(
+                    InterestType.FLAT, AmortizationType.EQUAL_INSTALLMENTS);
+            loanProductsRequest.setEnableInstallmentLevelDelinquency(true);
+            
loanProductsRequest.setDelinquencyBucketId(delinquencyBucketId.longValue());
+            PostLoanProductsResponse loanProductResponse = 
loanProductHelper.createLoanProduct(loanProductsRequest);
+
+            // Apply and Approve Loan
+            Long loanId = applyAndApproveLoan(clientId, 
loanProductResponse.getResourceId(), "06 May 2024", 1000.0, 4);
+
+            // Delinquency Calculations
+            verifyDelinquency(loanId, 0, "0.0", null, null);
+
+            // Update Business Date
+            updateBusinessDate("17 June 2024");
+
+            // disburse Loan
+            disburseLoan(loanId, BigDecimal.valueOf(1000), "06 May 2024");
+
+            verifyDelinquency(loanId, 12, "250.0", null, null, //
+                    delinquency(11, 30, "250.0"));
+
+            // over-pay Loan
+            addRepaymentForLoan(loanId, 1200.0, "17 June 2024");
+
+            // Delinquency Calculations
+            verifyDelinquency(loanId, 0, "0.0", "17 June 2024", "1200.0");
+        });
+    }
+
+    private void verifyDelinquency(Long loanId, Integer 
loanLevelDelinquentDays, String loanLevelDelinquentAmount,
+            String expectedLastRepaymentDate, String 
expectedLastRepaymentAmount,
+            InstallmentLevelDelinquencyAPIIntegrationTests.DelinquencyData... 
expectedInstallmentLevelDelinquencyData) {
+        GetLoansLoanIdResponse loan = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanId.intValue());
+        assertThat(loan.getDelinquent()).isNotNull();
+        List<GetLoansLoanIdLoanInstallmentLevelDelinquency> 
installmentLevelDelinquency = loan.getDelinquent()
+                .getInstallmentLevelDelinquency();
+
+        
assertThat(loan.getDelinquent().getDelinquentDays()).isEqualTo(loanLevelDelinquentDays);
+        
assertThat(loan.getDelinquent().getDelinquentAmount()).isEqualByComparingTo(Double.valueOf(loanLevelDelinquentAmount));
+        if (expectedLastRepaymentDate != null && expectedLastRepaymentAmount 
!= null) {
+            
assertThat(loan.getDelinquent().getLastRepaymentDate()).isNotNull();
+            Assertions.assertEquals(expectedLastRepaymentDate, 
loan.getDelinquent().getLastRepaymentDate().format(dateTimeFormatter));
+            
assertThat(loan.getDelinquent().getLastRepaymentAmount()).isNotNull();
+            
assertThat(loan.getDelinquent().getLastRepaymentAmount()).isEqualByComparingTo(Double.valueOf(expectedLastRepaymentAmount));
+        }
+
+        if (expectedInstallmentLevelDelinquencyData != null && 
expectedInstallmentLevelDelinquencyData.length > 0) {
+            assertThat(installmentLevelDelinquency).isNotNull();
+            
assertThat(installmentLevelDelinquency).hasSize(expectedInstallmentLevelDelinquencyData.length);
+            for (int i = 0; i < 
expectedInstallmentLevelDelinquencyData.length; i++) {
+                
assertThat(installmentLevelDelinquency.get(i).getMaximumAgeDays())
+                        
.isEqualTo(expectedInstallmentLevelDelinquencyData[i].maxAgeDays);
+                
assertThat(installmentLevelDelinquency.get(i).getMinimumAgeDays())
+                        
.isEqualTo(expectedInstallmentLevelDelinquencyData[i].minAgeDays);
+                
assertThat(installmentLevelDelinquency.get(i).getDelinquentAmount())
+                        
.isEqualByComparingTo(expectedInstallmentLevelDelinquencyData[i].delinquentAmount);
+            }
+        } else {
+            assertThat(installmentLevelDelinquency).isNull();
+        }
+    }
+
+    @AllArgsConstructor
+    public static class DelinquencyData {
+
+        Integer minAgeDays;
+        Integer maxAgeDays;
+        BigDecimal delinquentAmount;
+    }
+
+    private static 
InstallmentLevelDelinquencyAPIIntegrationTests.DelinquencyData 
delinquency(Integer minAgeDays, Integer maxAgeDays,
+            String delinquentAmount) {
+        return new 
InstallmentLevelDelinquencyAPIIntegrationTests.DelinquencyData(minAgeDays, 
maxAgeDays, new BigDecimal(delinquentAmount));
+    }
+}
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
index a81c20804..03a2e81f8 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java
@@ -2015,4 +2015,8 @@ public class LoanTransactionHelper extends 
IntegrationTest {
         return Utils.performServerPut(requestSpec, responseSpec, 
UPDATE_LOAN_PRODUCT_URL, request, null);
     }
 
+    public PostLoansLoanIdResponse undoApprovalForLoan(Long loanId, 
PostLoansLoanIdRequest request) {
+        return ok(fineract().loans.stateTransitions(loanId, request, 
"undoapproval"));
+    }
+
 }

Reply via email to