From 2f38d284df59ec441852f8297048408cdd781e71 Mon Sep 17 00:00:00 2001 From: Adam Saghy Date: Tue, 2 Jan 2024 21:53:23 +0100 Subject: [PATCH] FINERACT-1968: Fix when multiple disbursement happens on the 1st day of the loan --- ...tractProgressiveLoanScheduleGenerator.java | 11 ++-- ...ntAllocationLoanRepaymentScheduleTest.java | 57 +++++++++++++++++++ ...ncedPaymentAllocationIntegrationTests.java | 14 ++--- 3 files changed, 70 insertions(+), 12 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractProgressiveLoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractProgressiveLoanScheduleGenerator.java index a5738aec0..98d75ffc7 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractProgressiveLoanScheduleGenerator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractProgressiveLoanScheduleGenerator.java @@ -77,7 +77,8 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc : loanApplicationTerms.getSubmittedOnDate(); LoanScheduleParams scheduleParams = LoanScheduleParams.createLoanScheduleParams(currency, - Money.of(currency, chargesDueAtTimeOfDisbursement), periodStartDate, getPrincipalToBeScheduled(loanApplicationTerms)); + Money.of(currency, chargesDueAtTimeOfDisbursement), periodStartDate, + getPrincipalToBeScheduled(loanApplicationTerms, periodStartDate)); List periods = createNewLoanScheduleListWithDisbursementDetails(loanApplicationTerms, scheduleParams, chargesDueAtTimeOfDisbursement); @@ -233,12 +234,14 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc /** * this method calculates the principal amount for generating the repayment schedule. */ - private Money getPrincipalToBeScheduled(final LoanApplicationTerms loanApplicationTerms) { + private Money getPrincipalToBeScheduled(final LoanApplicationTerms loanApplicationTerms, LocalDate periodStartDate) { Money principalToBeScheduled; if (loanApplicationTerms.isMultiDisburseLoan()) { if (loanApplicationTerms.getTotalDisbursedAmount().isGreaterThanZero()) { - principalToBeScheduled = Money.of(loanApplicationTerms.getCurrency(), - loanApplicationTerms.getDisbursementDatas().get(0).getPrincipal()); + BigDecimal totalDisbursalAmountsOnThe = loanApplicationTerms.getDisbursementDatas().stream() + .filter(d -> d.getActualDisbursementDate().equals(periodStartDate)).map(DisbursementData::getPrincipal) + .reduce(BigDecimal.ZERO, BigDecimal::add); + principalToBeScheduled = Money.of(loanApplicationTerms.getCurrency(), totalDisbursalAmountsOnThe); } else if (loanApplicationTerms.getApprovedPrincipal().isGreaterThanZero()) { principalToBeScheduled = loanApplicationTerms.getApprovedPrincipal(); } else { diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java index dcf631239..380da4921 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java @@ -3033,6 +3033,63 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan }); } + // UC120: Advanced payment allocation with auto down payment and multiple disbursement on the first day + // ADVANCED_PAYMENT_ALLOCATION_STRATEGY + // 1. Create a Loan product with Adv. Pment. Alloc., and auto down payment + // 2. Submit Loan and approve + // 3. Disburse only 100 from 1000 + // 4. Disburse again on the same day but now 901 + @Test + public void uc120() { + runAt("22 November 2023", () -> { + final Account assetAccount = accountHelper.createAssetAccount(); + final Account incomeAccount = accountHelper.createIncomeAccount(); + final Account expenseAccount = accountHelper.createExpenseAccount(); + final Account overpaymentAccount = accountHelper.createLiabilityAccount(); + Integer localLoanProductId = createLoanProduct("1000", "15", "3", true, "25", true, LoanScheduleType.PROGRESSIVE, + LoanScheduleProcessingType.HORIZONTAL, assetAccount, incomeAccount, expenseAccount, overpaymentAccount); + final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId, + BigDecimal.valueOf(1000.0), 45, 15, 3, BigDecimal.ZERO, "22 November 2023", "01 January 2023"); + + loanTransactionHelper.approveLoan(loanResponse.getLoanId(), + new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(1000)).dateFormat(DATETIME_PATTERN) + .approvedOnDate("22 November 2023").locale("en")); + + loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), + new PostLoansLoanIdRequest().actualDisbursementDate("22 November 2023").dateFormat(DATETIME_PATTERN) + .transactionAmount(BigDecimal.valueOf(100.0)).locale("en")); + + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId()); + validateLoanSummaryBalances(loanDetails, 75.0, 25.0, 75.0, 25.0, null); + validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 11, 22), 25.0, 25.0, 0.0, 0.0, 0.0); + validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 12, 7), 25.0, 0.0, 25.0, 0.0, 0.0); + validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 12, 22), 25.0, 0.0, 25.0, 0.0, 0.0); + validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2024, 1, 6), 25.0, 0.0, 25.0, 0.0, 0.0); + assertTrue(loanDetails.getStatus().getActive()); + + loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), + new PostLoansLoanIdRequest().actualDisbursementDate("22 November 2023").dateFormat(DATETIME_PATTERN) + .transactionAmount(BigDecimal.valueOf(901.0)).locale("en")); + loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId()); + validateLoanSummaryBalances(loanDetails, 750.75, 250.25, 750.75, 250.25, null); + validatePeriod(loanDetails, 0, LocalDate.of(2023, 11, 22), null, 100.0, null, null, null, 0.0, 0.0, null, null, null, null, + null, null, null, null, null); + validatePeriod(loanDetails, 1, LocalDate.of(2023, 11, 22), null, 901.0, null, null, null, 0.0, 0.0, null, null, null, null, + null, null, null, null, null); + validatePeriod(loanDetails, 2, LocalDate.of(2023, 11, 22), LocalDate.of(2023, 11, 22), 976.0, 25.0, 25.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + validatePeriod(loanDetails, 3, LocalDate.of(2023, 11, 22), LocalDate.of(2023, 11, 22), 750.75, 225.25, 225.25, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0); + validatePeriod(loanDetails, 4, LocalDate.of(2023, 12, 7), null, 500.50, 250.25, 0.0, 250.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0); + validatePeriod(loanDetails, 5, LocalDate.of(2023, 12, 22), null, 250.25, 250.25, 0.0, 250.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0); + validatePeriod(loanDetails, 6, LocalDate.of(2024, 1, 6), null, 0.0, 250.25, 0.0, 250.25, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0); + assertTrue(loanDetails.getStatus().getActive()); + }); + } + private static void validateLoanSummaryBalances(GetLoansLoanIdResponse loanDetails, Double totalOutstanding, Double totalRepayment, Double principalOutstanding, Double principalPaid, Double totalOverpaid) { assertEquals(totalOutstanding, loanDetails.getSummary().getTotalOutstanding()); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanWithAdvancedPaymentAllocationIntegrationTests.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanWithAdvancedPaymentAllocationIntegrationTests.java index 371cf9e92..2d57ea6fa 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanWithAdvancedPaymentAllocationIntegrationTests.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanWithAdvancedPaymentAllocationIntegrationTests.java @@ -57,8 +57,6 @@ import org.junit.jupiter.api.extension.ExtendWith; public class LoanWithAdvancedPaymentAllocationIntegrationTests { private static ClientHelper CLIENT_HELPER; - private static ResponseSpecification RESPONSE_SPEC; - private static RequestSpecification REQUEST_SPEC; private static Account ASSET_ACCOUNT; private static Account FEE_PENALTY_ACCOUNT; private static Account EXPENSE_ACCOUNT; @@ -69,12 +67,12 @@ public class LoanWithAdvancedPaymentAllocationIntegrationTests { @BeforeAll public static void setupTests() { Utils.initializeRESTAssured(); - REQUEST_SPEC = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); - REQUEST_SPEC.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); - RESPONSE_SPEC = new ResponseSpecBuilder().expectStatusCode(200).build(); - AccountHelper accountHelper = new AccountHelper(REQUEST_SPEC, RESPONSE_SPEC); - LOAN_TRANSACTION_HELPER = new LoanTransactionHelper(REQUEST_SPEC, RESPONSE_SPEC); - CLIENT_HELPER = new ClientHelper(REQUEST_SPEC, RESPONSE_SPEC); + RequestSpecification requestSpec = new RequestSpecBuilder().setContentType(ContentType.JSON).build(); + requestSpec.header("Authorization", "Basic " + Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey()); + ResponseSpecification responseSpec = new ResponseSpecBuilder().expectStatusCode(200).build(); + AccountHelper accountHelper = new AccountHelper(requestSpec, responseSpec); + LOAN_TRANSACTION_HELPER = new LoanTransactionHelper(requestSpec, responseSpec); + CLIENT_HELPER = new ClientHelper(requestSpec, responseSpec); ASSET_ACCOUNT = accountHelper.createAssetAccount(); FEE_PENALTY_ACCOUNT = accountHelper.createAssetAccount();