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 060cdb5685 FINERACT-2181: Loan disbursement amount exceed loan 
principal amount when we have Capitalized Income
060cdb5685 is described below

commit 060cdb56854448101689be9b13fb26855c9d627b
Author: Jose Alberto Hernandez <[email protected]>
AuthorDate: Fri May 30 08:08:48 2025 -0500

    FINERACT-2181: Loan disbursement amount exceed loan principal amount when 
we have Capitalized Income
---
 .../serialization/LoanDisbursementValidator.java   |  6 ++--
 .../LoanTransactionValidatorImpl.java              |  5 +++-
 .../LoanCapitalizedIncomeTest.java                 | 33 ++++++++++++++++++++++
 3 files changed, 41 insertions(+), 3 deletions(-)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanDisbursementValidator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanDisbursementValidator.java
index e6aef72591..2de131c565 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanDisbursementValidator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanDisbursementValidator.java
@@ -35,9 +35,10 @@ public final class LoanDisbursementValidator {
 
     public void compareDisbursedToApprovedOrProposedPrincipal(final Loan loan, 
final BigDecimal disbursedAmount,
             final BigDecimal totalDisbursed) {
+        final BigDecimal capitalizedIncome = 
loan.getSummary().getTotalCapitalizedIncome();
         if (loan.loanProduct().isDisallowExpectedDisbursements() && 
loan.loanProduct().isAllowApprovedDisbursedAmountsOverApplied()) {
             final BigDecimal maxDisbursedAmount = 
loanApplicationValidator.getOverAppliedMax(loan);
-            if (totalDisbursed.compareTo(maxDisbursedAmount) > 0) {
+            if 
(totalDisbursed.add(capitalizedIncome).compareTo(maxDisbursedAmount) > 0) {
                 final String errorMessage = String.format(
                         "Loan disbursal amount can't be greater than maximum 
applied loan amount calculation. "
                                 + "Total disbursed amount: %s  Maximum 
disbursal amount: %s",
@@ -47,7 +48,8 @@ public final class LoanDisbursementValidator {
                         maxDisbursedAmount);
             }
         } else {
-            if (totalDisbursed.compareTo(loan.getApprovedPrincipal()) > 0) {
+            if ((totalDisbursed.compareTo(loan.getApprovedPrincipal()) > 0)
+                    || 
(totalDisbursed.add(capitalizedIncome).compareTo(loan.getApprovedPrincipal()) > 
0)) {
                 final String errorMsg = "Loan can't be disbursed,disburse 
amount is exceeding approved principal ";
                 throw new LoanDisbursalException(errorMsg, 
"disburse.amount.must.be.less.than.approved.principal", totalDisbursed,
                         loan.getApprovedPrincipal());
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanTransactionValidatorImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanTransactionValidatorImpl.java
index 3e6911a8b9..e91852a6aa 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanTransactionValidatorImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/serialization/LoanTransactionValidatorImpl.java
@@ -106,6 +106,7 @@ public final class LoanTransactionValidatorImpl implements 
LoanTransactionValida
     private final EntityDatatableChecksWritePlatformService 
entityDatatableChecksWritePlatformService;
     private final CalendarInstanceRepository calendarInstanceRepository;
     private final LoanDownPaymentTransactionValidator 
loanDownPaymentTransactionValidator;
+    private final LoanDisbursementValidator loanDisbursementValidator;
 
     private void throwExceptionIfValidationWarningsExist(final 
List<ApiParameterError> dataValidationErrors) {
         if (!dataValidationErrors.isEmpty()) {
@@ -157,6 +158,9 @@ public final class LoanTransactionValidatorImpl implements 
LoanTransactionValida
             validateLoanClientIsActive(loan);
             validateLoanGroupIsActive(loan);
 
+            final BigDecimal disbursedAmount = loan.getDisbursedAmount();
+            
loanDisbursementValidator.compareDisbursedToApprovedOrProposedPrincipal(loan, 
principal, disbursedAmount);
+
             if (loan.isChargedOff()) {
                 throw new 
GeneralPlatformDomainRuleException("error.msg.loan.disbursal.not.allowed.on.charged.off",
                         "Loan: " + loan.getId() + " disbursement is not 
allowed on charged-off loan.");
@@ -173,7 +177,6 @@ public final class LoanTransactionValidatorImpl implements 
LoanTransactionValida
                 baseDataValidator.getDataValidationErrors().add(error);
             }
 
-            final BigDecimal disbursedAmount = loan.getDisbursedAmount();
             final Set<LoanCollateralManagement> loanCollateralManagements = 
loan.getLoanCollateralManagements();
 
             if ((loanCollateralManagements != null && 
!loanCollateralManagements.isEmpty()) && 
loan.getLoanType().isIndividualAccount()) {
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 43b9390a98..087bde1af2 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
@@ -27,6 +27,7 @@ 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.PostLoansLoanIdTransactionsResponse;
+import org.apache.fineract.client.util.CallFailedRuntimeException;
 import org.apache.fineract.integrationtests.common.BusinessStepHelper;
 import org.apache.fineract.integrationtests.common.ClientHelper;
 import org.junit.jupiter.api.Assertions;
@@ -102,6 +103,38 @@ public class LoanCapitalizedIncomeTest extends 
BaseLoanIntegrationTest {
         });
     }
 
+    @Test
+    public void testLoanDisbursementWithCapitalizedIncome() {
+        final AtomicReference<Long> loanIdRef = new AtomicReference<>();
+        final AtomicReference<Long> capitalizedIncomeIdRef = 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).overAppliedNumber(3));
+
+        runAt("1 April 2024", () -> {
+            Long loanId = applyAndApproveProgressiveLoan(client.getClientId(), 
loanProductsResponse.getResourceId(), "1 January 2024",
+                    500.0, 7.0, 3, null);
+            loanIdRef.set(loanId);
+
+            disburseLoan(loanId, BigDecimal.valueOf(300), "1 January 2024");
+            PostLoansLoanIdTransactionsResponse capitalizedIncomeResponse = 
loanTransactionHelper.addCapitalizedIncome(loanId,
+                    "1 January 2024", 50.0);
+
+            CallFailedRuntimeException callFailedRuntimeException = 
Assertions.assertThrows(CallFailedRuntimeException.class,
+                    () -> disburseLoan(loanId, BigDecimal.valueOf(200), "1 
February 2024"));
+
+            Assertions.assertTrue(callFailedRuntimeException.getMessage()
+                    .contains("Loan disbursal amount can't be greater than 
maximum applied loan amount calculation"));
+        });
+    }
+
     @Test
     public void testLoanCapitalizedIncomeAdjustment() {
         final AtomicReference<Long> loanIdRef = new AtomicReference<>();

Reply via email to