diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanTransactionValidatorImpl.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanTransactionValidatorImpl.java index 483763405..54754195f 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanTransactionValidatorImpl.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/ProgressiveLoanTransactionValidatorImpl.java @@ -47,6 +47,7 @@ import org.apache.fineract.portfolio.loanaccount.domain.LoanCapitalizedIncomeBal import org.apache.fineract.portfolio.loanaccount.domain.LoanDisbursementDetails; import org.apache.fineract.portfolio.loanaccount.domain.LoanEvent; import org.apache.fineract.portfolio.loanaccount.domain.LoanRepositoryWrapper; +import org.apache.fineract.portfolio.loanaccount.domain.LoanStatus; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRepository; import org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType; @@ -95,9 +96,10 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("income.capitalization.not.enabled"); } - // Validate loan is active - if (!loan.getStatus().isActive()) { - baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.active"); + // Validate loan is active, or closed or overpaid + final LoanStatus loanStatus = loan.getStatus(); + if (!loanStatus.isActive() && !loanStatus.isClosed() && !loanStatus.isOverpaid()) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.valid.loan.status"); } final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed("transactionDate", element); @@ -164,9 +166,10 @@ public class ProgressiveLoanTransactionValidatorImpl implements ProgressiveLoanT baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("income.capitalization.not.enabled"); } - // Validate loan is active - if (!loan.getStatus().isActive()) { - baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.active"); + // Validate loan is active, or closed or overpaid + final LoanStatus loanStatus = loan.getStatus(); + if (!loanStatus.isActive() && !loanStatus.isClosed() && !loanStatus.isOverpaid()) { + baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode("not.valid.loan.status"); } final LocalDate transactionDate = this.fromApiJsonHelper.extractLocalDateNamed("transactionDate", element); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCapitalizedIncomeTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCapitalizedIncomeTest.java index 756f13012..b92341ec2 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCapitalizedIncomeTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanCapitalizedIncomeTest.java @@ -18,6 +18,8 @@ */ package org.apache.fineract.integrationtests; +import static org.junit.jupiter.api.Assertions.assertTrue; + import java.math.BigDecimal; import java.util.Optional; import java.util.concurrent.atomic.AtomicReference; @@ -26,6 +28,7 @@ import org.apache.fineract.client.models.GetLoansLoanIdTransactions; import org.apache.fineract.client.models.PostClientsResponse; import org.apache.fineract.client.models.PostLoanProductsRequest; import org.apache.fineract.client.models.PostLoanProductsResponse; +import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse; import org.apache.fineract.client.util.CallFailedRuntimeException; import org.apache.fineract.integrationtests.common.BusinessStepHelper; @@ -617,4 +620,67 @@ public class LoanCapitalizedIncomeTest extends BaseLoanIntegrationTest { }); }); } + + @Test + public void testLoanCapitalizedIncomeOnLoanClosed() { + final AtomicReference loanIdRef = new AtomicReference<>(); + final AtomicReference capitalizedIncomeTransactionIdRef = new AtomicReference<>(); + + final PostClientsResponse client = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()); + + final PostLoanProductsResponse loanProductsResponse = loanProductHelper + .createLoanProduct(create4IProgressive().enableIncomeCapitalization(true) + .capitalizedIncomeCalculationType(PostLoanProductsRequest.CapitalizedIncomeCalculationTypeEnum.FLAT) + .capitalizedIncomeStrategy(PostLoanProductsRequest.CapitalizedIncomeStrategyEnum.EQUAL_AMORTIZATION) + .deferredIncomeLiabilityAccountId(deferredIncomeLiabilityAccount.getAccountID().longValue()) + .incomeFromCapitalizationAccountId(feeIncomeAccount.getAccountID().longValue()) + .capitalizedIncomeType(PostLoanProductsRequest.CapitalizedIncomeTypeEnum.FEE)); + + runAt("1 January 2024", () -> { + Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), loanProductsResponse.getResourceId(), "1 January 2024", + 500.0, 7.0, 3, null); + loanIdRef.set(loanId); + + disburseLoan(loanId, BigDecimal.valueOf(100), "1 January 2024"); + Long capitalizedIncomeTransactionId = loanTransactionHelper.addCapitalizedIncome(loanId, "1 January 2024", 50.0) + .getResourceId(); + capitalizedIncomeTransactionIdRef.set(capitalizedIncomeTransactionId); + }); + runAt("1 February 2024", () -> { + Long loanId = loanIdRef.get(); + executeInlineCOB(loanId); + + addRepaymentForLoan(loanId, 50.58, "1 February 2024"); + }); + runAt("1 March 2024", () -> { + Long loanId = loanIdRef.get(); + executeInlineCOB(loanId); + + addRepaymentForLoan(loanId, 50.58, "1 March 2024"); + }); + runAt("15 March 2024", () -> { + Long loanId = loanIdRef.get(); + executeInlineCOB(loanId); + + addRepaymentForLoan(loanId, 50.58, "15 March 2024"); + + loanTransactionHelper.makeCreditBalanceRefund(loanId, new PostLoansLoanIdTransactionsRequest().dateFormat(DATETIME_PATTERN) + .transactionDate("15 March 2024").locale("en").transactionAmount(0.15)); + + // Validate Loan is Closed + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId); + assertTrue(loanDetails.getStatus().getClosedObligationsMet()); + }); + runAt("16 March 2024", () -> { + Long loanId = loanIdRef.get(); + executeInlineCOB(loanId); + + Long capitalizedIncomeTransactionId = loanTransactionHelper.addCapitalizedIncome(loanId, "16 March 2024", 50.0).getResourceId(); + + verifyTRJournalEntries(capitalizedIncomeTransactionId, journalEntry(50, loansReceivableAccount, "DEBIT"), // + journalEntry(50, deferredIncomeLiabilityAccount, "CREDIT") // + ); + }); + } + }