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<>();