From 5d0152c262e372fef71e3d99c3ffc2460c8e0cde Mon Sep 17 00:00:00 2001 From: Ruchi Dhamankar Date: Wed, 8 May 2024 14:07:37 +0530 Subject: [PATCH] FINERACT-1971: Disable delinquency calculation for non active loans --- .../portfolio/loanaccount/domain/Loan.java | 3 +- .../DelinquencyReadPlatformServiceImpl.java | 7 +- .../DelinquencyWritePlatformServiceImpl.java | 4 +- .../LoanDelinquencyDomainServiceImpl.java | 16 ++ .../LoanDelinquencyDomainServiceTest.java | 6 + .../BaseLoanIntegrationTest.java | 9 + ...anDelinquencyForNonActiveAccountsTest.java | 272 ++++++++++++++++++ .../common/loans/LoanTransactionHelper.java | 4 + 8 files changed, 316 insertions(+), 5 deletions(-) create mode 100644 integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanDelinquencyForNonActiveAccountsTest.java 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 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")); + } + }