From 94cc68ba7c27421f3f91ccdca42dc4a7b31a631a Mon Sep 17 00:00:00 2001 From: Jose Alberto Hernandez Date: Tue, 27 May 2025 07:49:43 -0500 Subject: [PATCH] FINERACT-2181: Fix Consult Loan repayment schedule with disbursement harge using amount plus interest --- .../service/LoanScheduleAssembler.java | 10 +++-- .../ClientLoanIntegrationTest.java | 38 +++++++++++++++++++ 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java index 7afe2bb2c..5a87e0e90 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/service/LoanScheduleAssembler.java @@ -749,7 +749,8 @@ public class LoanScheduleAssembler { LoanScheduleModel loanScheduleModel = loanScheduleGenerator.generate(mc, loanApplicationTerms, loanCharges, detailDTO); if (!nonCompoundingCharges.isEmpty()) { - updateDisbursementWithCharges(loanScheduleModel.getPeriods(), nonCompoundingCharges); + updateDisbursementWithCharges(loanApplicationTerms.getPrincipal().getAmount(), loanScheduleModel.getPeriods(), + nonCompoundingCharges); } return loanScheduleModel; } @@ -1564,14 +1565,17 @@ public class LoanScheduleAssembler { return interestCharges; } - private void updateDisbursementWithCharges(final Collection periods, + private void updateDisbursementWithCharges(final BigDecimal principal, final Collection periods, final Set nonCompoundingCharges) { final BigDecimal totalInterest = periods.stream().filter(p -> p.isRepaymentPeriod()).map(LoanScheduleModelPeriod::interestDue) .reduce(BigDecimal.ZERO, BigDecimal::add); for (LoanScheduleModelPeriod loanScheduleModelPeriod : periods) { if (loanScheduleModelPeriod instanceof LoanScheduleModelDisbursementPeriod) { for (final LoanCharge loanCharge : nonCompoundingCharges) { - loanChargeService.populateDerivedFields(loanCharge, totalInterest, loanCharge.amountOrPercentage(), null, + final BigDecimal amountAppliedTo = loanCharge.getChargeCalculation().isPercentageOfAmountAndInterest() + ? principal.add(totalInterest) + : totalInterest; + loanChargeService.populateDerivedFields(loanCharge, amountAppliedTo, loanCharge.amountOrPercentage(), null, BigDecimal.ZERO); loanScheduleModelPeriod.addLoanCharges(loanCharge.getAmountOutstanding(), BigDecimal.ZERO); } diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java index 970a19533..586d7c0e7 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanIntegrationTest.java @@ -7062,6 +7062,44 @@ public class ClientLoanIntegrationTest extends BaseLoanIntegrationTest { } } + @Test + public void testLoanCharges_DISBURSEMENT_WITH_AMOUNT_AND_INTEREST() { + + Calendar fourMonthsfromNowCalendar = Calendar.getInstance(Utils.getTimeZoneOfTenant()); + fourMonthsfromNowCalendar.add(Calendar.MONTH, -4); + if (fourMonthsfromNowCalendar.get(Calendar.DAY_OF_MONTH) > 27) { + fourMonthsfromNowCalendar.add(Calendar.DAY_OF_MONTH, 4); + } + + String fourMonthsfromNow = Utils.convertDateToURLFormat(fourMonthsfromNowCalendar); + final Integer clientID = ClientHelper.createClient(REQUEST_SPEC, RESPONSE_SPEC); + ClientHelper.verifyClientCreatedOnServer(REQUEST_SPEC, RESPONSE_SPEC, clientID); + final Integer loanProductID = createLoanProduct(false, NONE); + + List charges = new ArrayList<>(); + Integer disbursementFee = ChargesHelper.createCharges(REQUEST_SPEC, RESPONSE_SPEC, + ChargesHelper.getLoanDisbursementJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_PERCENTAGE_AMOUNT_AND_INTEREST, "2")); + addCharges(charges, disbursementFee, "2", null); + + List collaterals = new ArrayList<>(); + final Integer loanID = applyForLoanApplicationWithPaymentStrategyAndPastMonth(clientID, loanProductID, charges, null, "1000", + LoanApplicationTestBuilder.DEFAULT_STRATEGY, fourMonthsfromNow, collaterals); + Assertions.assertNotNull(loanID); + + LOAN_TRANSACTION_HELPER.approveLoan(fourMonthsfromNow, loanID); + + String loanDetails = LOAN_TRANSACTION_HELPER.getLoanDetails(REQUEST_SPEC, RESPONSE_SPEC, loanID); + LOAN_TRANSACTION_HELPER.disburseLoanWithNetDisbursalAmount(fourMonthsfromNow, loanID, + JsonPath.from(loanDetails).get("netDisbursalAmount").toString()); + + // check for disbursement fee: Principal 1,000 with 24% Annual Rate for 6 Months we have Total Interest of: + // 120.00 + ArrayList loanSchedule = LOAN_TRANSACTION_HELPER.getLoanRepaymentSchedule(REQUEST_SPEC, RESPONSE_SPEC, loanID); + HashMap disbursementDetail = loanSchedule.get(0); + // Disbursement Fee: 2% of 1,120.00 = 22.40 + validateNumberForEqual("22.40", String.valueOf(disbursementDetail.get("feeChargesDue"))); + } + private void checkLoanTransactionOrder(Long loanId, String... transactionUUIDs) { LOG.info("-------------------------------CHECK LOAN TRANSACTION ORDER-------------------------------------------"); GetLoansLoanIdResponse loanDetailsResult = LOAN_TRANSACTION_HELPER.getLoanDetails(loanId);