This is an automated email from the ASF dual-hosted git repository.
arnold 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 76a03ecdbf FINERACT-2232: Capitalized income and Caplitalized income
adjustment in case of loan is overpaid
76a03ecdbf is described below
commit 76a03ecdbf23d86dbe98a50659bfe49267e8a013
Author: Jose Alberto Hernandez <[email protected]>
AuthorDate: Fri Jun 6 08:01:00 2025 -0500
FINERACT-2232: Capitalized income and Caplitalized income adjustment in
case of loan is overpaid
---
.../ProgressiveLoanTransactionValidatorImpl.java | 15 +++--
.../LoanCapitalizedIncomeTest.java | 66 ++++++++++++++++++++++
2 files changed, 75 insertions(+), 6 deletions(-)
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 4837634050..54754195fb 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 756f13012e..b92341ec2f 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<Long> loanIdRef = new AtomicReference<>();
+ final AtomicReference<Long> 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")
//
+ );
+ });
+ }
+
}