diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java index c72bb2b7f..1465baaf4 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractLoanScheduleGenerator.java @@ -1906,27 +1906,40 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener private BigDecimal getDisbursementAmount(final LoanApplicationTerms loanApplicationTerms, LocalDate disbursementDate, final Collection periods, final BigDecimal chargesDueAtTimeOfDisbursement, final Map disurseDetail, final boolean excludePastUndisbursed) { + + // this method relates to multi-disbursement loans BigDecimal principal = BigDecimal.ZERO; - MonetaryCurrency currency = loanApplicationTerms.getPrincipal().getCurrency(); - for (DisbursementData disbursementData : loanApplicationTerms.getDisbursementDatas()) { - if (disbursementData.disbursementDate().equals(disbursementDate)) { - final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod.disbursement( - disbursementData.disbursementDate(), Money.of(currency, disbursementData.amount()), chargesDueAtTimeOfDisbursement); - periods.add(disbursementPeriod); - principal = principal.add(disbursementData.amount()); - } else if (!excludePastUndisbursed || disbursementData.isDisbursed() - || !disbursementData.disbursementDate().isBefore(DateUtils.getBusinessLocalDate())) { - /* - * JW: sums up amounts by disbursal date in case of side-effect issues. Original assumed that there were - * no duplicate disbursal dates and 'put' each amount into the map keyed by date - */ - Money prevsum = disurseDetail.get(disbursementData.disbursementDate()); - BigDecimal sumToNow = BigDecimal.ZERO; - if (prevsum != null) { - sumToNow = prevsum.getAmount(); + if (loanApplicationTerms.getDisbursementDatas().size() == 0) { + // non tranche loans have no disbursement data entries in submitted and approved status + // the appropriate approved amount or applied for amount is used to show a proposed schedule + if (loanApplicationTerms.getApprovedPrincipal().getAmount().compareTo(BigDecimal.ZERO) > 0) { + principal = loanApplicationTerms.getApprovedPrincipal().getAmount(); + } else { + principal = loanApplicationTerms.getPrincipal().getAmount(); + } + } else { + MonetaryCurrency currency = loanApplicationTerms.getPrincipal().getCurrency(); + for (DisbursementData disbursementData : loanApplicationTerms.getDisbursementDatas()) { + if (disbursementData.disbursementDate().equals(disbursementDate)) { + final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod.disbursement( + disbursementData.disbursementDate(), Money.of(currency, disbursementData.amount()), + chargesDueAtTimeOfDisbursement); + periods.add(disbursementPeriod); + principal = principal.add(disbursementData.amount()); + } else if (!excludePastUndisbursed || disbursementData.isDisbursed() + || !disbursementData.disbursementDate().isBefore(DateUtils.getLocalDateOfTenant())) { + /* + * JW: sums up amounts by disbursal date in case of side-effect issues. Original assumed that there + * were no duplicate disbursal dates and 'put' each amount into the map keyed by date + */ + Money prevsum = disurseDetail.get(disbursementData.disbursementDate()); + BigDecimal sumToNow = BigDecimal.ZERO; + if (prevsum != null) { + sumToNow = prevsum.getAmount(); + } + sumToNow = sumToNow.add(disbursementData.amount()); + disurseDetail.put(disbursementData.disbursementDate(), Money.of(currency, sumToNow)); } - sumToNow = sumToNow.add(disbursementData.amount()); - disurseDetail.put(disbursementData.disbursementDate(), Money.of(currency, sumToNow)); } } return principal; @@ -1937,7 +1950,12 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener Collection periods = null; if (loanApplicationTerms.isMultiDisburseLoan()) { - periods = new ArrayList<>(numberOfRepayments + loanApplicationTerms.getDisbursementDatas().size()); + int multiDisbursalTrancheEntries = loanApplicationTerms.getDisbursementDatas().size(); + if (multiDisbursalTrancheEntries == 0) { + // will be zero for non-tranche multi-disbursal loan when submitted or approved + multiDisbursalTrancheEntries = 1; + } + periods = new ArrayList<>(numberOfRepayments + multiDisbursalTrancheEntries); } else { periods = new ArrayList<>(numberOfRepayments + 1); final LoanScheduleModelDisbursementPeriod disbursementPeriod = LoanScheduleModelDisbursementPeriod diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanNonTrancheMultipleDisbursementsIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanNonTrancheMultipleDisbursementsIntegrationTest.java index fecca0c2b..a33f7402c 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanNonTrancheMultipleDisbursementsIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanNonTrancheMultipleDisbursementsIntegrationTest.java @@ -203,4 +203,43 @@ public class ClientLoanNonTrancheMultipleDisbursementsIntegrationTest { } + @Test + public void checkThatNonTrancheMultiDisbursalsCreateAScheduleOnSubmitAndApprovalTest() { + this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); + + final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec); + ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID); + + /*** + * Create loan product allowing non-tranche multiple disbursals with interest recalculation + */ + boolean isInterestRecalculationEnabled = true; + final Integer loanProductID = createLoanProduct(isInterestRecalculationEnabled); + Assertions.assertNotNull(loanProductID); + + /*** + * Apply for loan application and verify loan status + */ + final String savingsId = null; + String submitDate = "01 January 2022"; + Integer repaymentsNo = 3; + final Integer loanID = applyForLoanApplication(clientID, loanProductID, savingsId, APPLIED_FOR_PRINCIPAL, submitDate, + repaymentsNo.toString()); + Assertions.assertNotNull(loanID); + HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID); + LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap); + ArrayList loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); + Integer loanScheduleLineCount = loanSchedule.size() - 1; // exclude disbursement line + Assertions.assertEquals(repaymentsNo, loanScheduleLineCount); + + LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------"); + final Float approved = 9000.00f; + loanStatusHashMap = this.loanTransactionHelper.approveLoanWithApproveAmount(submitDate, null, approved.toString(), loanID, null); + LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap); + LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap); + loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID); + loanScheduleLineCount = loanSchedule.size() - 1; + Assertions.assertEquals(repaymentsNo, loanScheduleLineCount); + + } }