This is an automated email from the ASF dual-hosted git repository.

taskain 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 388510476 [FINERACT-1958] Repayment schedule recalculation fix for 
multidisburse down payment
388510476 is described below

commit 38851047670860020a0f215d0047c45f4695b8a5
Author: taskain7 <[email protected]>
AuthorDate: Wed Aug 23 09:20:03 2023 +0200

    [FINERACT-1958] Repayment schedule recalculation fix for multidisburse down 
payment
---
 .../domain/AbstractLoanScheduleGenerator.java      |   9 ++
 .../service/LoanReadPlatformServiceImpl.java       |  18 ++-
 .../LoanRepaymentScheduleWithDownPaymentTest.java  | 122 ++++++++++++++++++---
 3 files changed, 132 insertions(+), 17 deletions(-)

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 e152c78da..a5edd7abc 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
@@ -284,6 +284,15 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             // backup for pre-close transaction
             updateCompoundingDetails(scheduleParams, 
periodStartDateApplicableForInterest);
 
+            if (loanApplicationTerms.isMultiDisburseLoan() && 
loanApplicationTerms.isDownPaymentEnabled()) {
+                long numberOfDownPaymentPeriods = periods.stream() //
+                        
.filter(LoanScheduleModelPeriod::isDownPaymentPeriod).count();
+                Long 
notApplicableForInstallmentRecalculationDownPaymentPeriods = 
numberOfDownPaymentPeriods - 1;
+                // Only the first down payment installment counts for the 
number of repayments
+                scheduleParams.setPeriodNumber(
+                        scheduleParams.getPeriodNumber() - 
notApplicableForInstallmentRecalculationDownPaymentPeriods.intValue());
+            }
+
             // 5 determine principal,interest of repayment period
             PrincipalInterest principalInterestForThisPeriod = 
calculatePrincipalInterestComponentsForPeriod(
                     this.paymentPeriodsInOneYearCalculator, 
currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(),
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
index 831631693..1ce71ca2b 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java
@@ -1221,7 +1221,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                             periods.add(periodData);
                             this.outstandingLoanPrincipalBalance = 
this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
                             disbursementPeriodIds.add(data.getId());
-                        } else if (data.isDueForDisbursement(fromDate, 
dueDate)) {
+                        } else if (data.isDueForDisbursement(fromDate, 
dueDate) && !disbursementPeriodIds.contains(data.getId())) {
                             if (!excludePastUndisbursed || data.isDisbursed()
                                     || 
!data.disbursementDate().isBefore(DateUtils.getBusinessLocalDate())) {
                                 principal = principal.add(data.getPrincipal());
@@ -1235,7 +1235,23 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
                                 }
                                 periods.add(periodData);
                                 this.outstandingLoanPrincipalBalance = 
this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
+                                disbursementPeriodIds.add(data.getId());
                             }
+                        } else if (fromDate.equals(dueDate) && 
data.disbursementDate().equals(fromDate)
+                                && 
!disbursementPeriodIds.contains(data.getId())) {
+                            principal = principal.add(data.getPrincipal());
+                            LoanSchedulePeriodData periodData = null;
+                            if (data.getChargeAmount() == null) {
+                                periodData = 
LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), 
data.getPrincipal(),
+                                        disbursementChargeAmount, 
data.isDisbursed());
+                            } else {
+                                periodData = 
LoanSchedulePeriodData.disbursementOnlyPeriod(data.disbursementDate(), 
data.getPrincipal(),
+                                        
disbursementChargeAmount.add(data.getChargeAmount()).subtract(waivedChargeAmount),
+                                        data.isDisbursed());
+                            }
+                            periods.add(periodData);
+                            this.outstandingLoanPrincipalBalance = 
this.outstandingLoanPrincipalBalance.add(data.getPrincipal());
+                            disbursementPeriodIds.add(data.getId());
                         }
                     }
                 }
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRepaymentScheduleWithDownPaymentTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRepaymentScheduleWithDownPaymentTest.java
index 1adda179d..99d0d7194 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRepaymentScheduleWithDownPaymentTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanRepaymentScheduleWithDownPaymentTest.java
@@ -338,6 +338,7 @@ public class LoanRepaymentScheduleWithDownPaymentTest {
         assertEquals(enableAutoRepaymentForDownPayment, 
loanDetails.getEnableAutoRepaymentForDownPayment());
 
         List<GetLoansLoanIdRepaymentPeriod> periods = 
loanDetails.getRepaymentSchedule().getPeriods();
+        Double expectedOutstandingLoanBalanceOnDisbursement = 1000.00;
         Double expectedDownPaymentAmount = 250.00;
         LocalDate expectedDownPaymentDueDate = LocalDate.of(2022, 9, 3);
         Double expectedRepaymentAmount = 250.00;
@@ -348,21 +349,110 @@ public class LoanRepaymentScheduleWithDownPaymentTest {
         LocalDate expectedThirdRepaymentDueDate = LocalDate.of(2022, 12, 3);
         Double outstandingBalanceOnThirdRepayment = 0.00;
 
-        assertTrue(periods.stream() //
-                .anyMatch(period -> 
expectedDownPaymentAmount.equals(period.getTotalDueForPeriod()) //
-                        && 
expectedDownPaymentDueDate.equals(period.getDueDate())));
-        assertTrue(periods.stream()
-                .anyMatch(period -> 
expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
-                        && 
expectedFirstRepaymentDueDate.equals(period.getDueDate())
-                        && 
outstandingBalanceOnFirstRepayment.equals(period.getPrincipalLoanBalanceOutstanding())));
-        assertTrue(periods.stream()
-                .anyMatch(period -> 
expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
-                        && 
expectedSecondRepaymentDueDate.equals(period.getDueDate())
-                        && 
outstandingBalanceOnSecondRepayment.equals(period.getPrincipalLoanBalanceOutstanding())));
-        assertTrue(periods.stream()
-                .anyMatch(period -> 
expectedRepaymentAmount.equals(period.getTotalDueForPeriod())
-                        && 
expectedThirdRepaymentDueDate.equals(period.getDueDate())
-                        && 
outstandingBalanceOnThirdRepayment.equals(period.getPrincipalLoanBalanceOutstanding())));
+        GetLoansLoanIdRepaymentPeriod firstDisbursementPeriod = periods.get(0);
+        assertEquals(expectedDownPaymentDueDate, 
firstDisbursementPeriod.getDueDate());
+        assertEquals(expectedOutstandingLoanBalanceOnDisbursement, 
firstDisbursementPeriod.getPrincipalLoanBalanceOutstanding());
+
+        GetLoansLoanIdRepaymentPeriod firstDownPaymentPeriod = periods.get(1);
+        assertEquals(expectedDownPaymentAmount, 
firstDownPaymentPeriod.getTotalDueForPeriod());
+        assertEquals(expectedDownPaymentDueDate, 
firstDownPaymentPeriod.getDueDate());
+
+        GetLoansLoanIdRepaymentPeriod firstRepaymentPeriod = periods.get(2);
+        assertEquals(expectedRepaymentAmount, 
firstRepaymentPeriod.getTotalDueForPeriod());
+        assertEquals(expectedFirstRepaymentDueDate, 
firstRepaymentPeriod.getDueDate());
+        assertEquals(outstandingBalanceOnFirstRepayment, 
firstRepaymentPeriod.getPrincipalLoanBalanceOutstanding());
+
+        GetLoansLoanIdRepaymentPeriod secondRepaymentPeriod = periods.get(3);
+        assertEquals(expectedRepaymentAmount, 
secondRepaymentPeriod.getTotalDueForPeriod());
+        assertEquals(expectedSecondRepaymentDueDate, 
secondRepaymentPeriod.getDueDate());
+        assertEquals(outstandingBalanceOnSecondRepayment, 
secondRepaymentPeriod.getPrincipalLoanBalanceOutstanding());
+
+        GetLoansLoanIdRepaymentPeriod thirdRepaymentPeriod = periods.get(4);
+        assertEquals(expectedRepaymentAmount, 
thirdRepaymentPeriod.getTotalDueForPeriod());
+        assertEquals(expectedThirdRepaymentDueDate, 
thirdRepaymentPeriod.getDueDate());
+        assertEquals(outstandingBalanceOnThirdRepayment, 
thirdRepaymentPeriod.getPrincipalLoanBalanceOutstanding());
+    }
+
+    @Test
+    public void 
loanRepaymentScheduleWithMultiDisbursementProductTwoDisbursementAndThreeRepaymentsAndDownPayment()
 {
+        String loanExternalIdStr = UUID.randomUUID().toString();
+
+        final Integer delinquencyBucketId = 
DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec);
+        final GetDelinquencyBucketsResponse delinquencyBucket = 
DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec,
+                delinquencyBucketId);
+
+        Boolean enableDownPayment = true;
+        BigDecimal disbursedAmountPercentageForDownPayment = 
BigDecimal.valueOf(25);
+        Boolean enableAutoRepaymentForDownPayment = true;
+
+        final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+
+        Integer loanProductId = 
createLoanProductWithDownPaymentConfiguration(loanTransactionHelper, 
delinquencyBucketId, enableDownPayment,
+                "25", enableAutoRepaymentForDownPayment, true);
+
+        final GetLoanProductsProductIdResponse getLoanProductsProductResponse 
= loanTransactionHelper.getLoanProduct(loanProductId);
+        assertNotNull(getLoanProductsProductResponse);
+        assertEquals(enableDownPayment, 
getLoanProductsProductResponse.getEnableDownPayment());
+        assertEquals(0, 
getLoanProductsProductResponse.getDisbursedAmountPercentageForDownPayment()
+                .compareTo(disbursedAmountPercentageForDownPayment));
+        assertEquals(enableAutoRepaymentForDownPayment, 
getLoanProductsProductResponse.getEnableAutoRepaymentForDownPayment());
+
+        final Integer loanId = 
createApproveAndDisburseTwiceLoanAccount(clientId, loanProductId.longValue(), 
loanExternalIdStr, "4");
+
+        GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+        assertNotNull(loanDetails);
+        assertEquals(enableDownPayment, loanDetails.getEnableDownPayment());
+        assertEquals(0, 
loanDetails.getDisbursedAmountPercentageForDownPayment().compareTo(disbursedAmountPercentageForDownPayment));
+        assertEquals(enableAutoRepaymentForDownPayment, 
loanDetails.getEnableAutoRepaymentForDownPayment());
+
+        loanTransactionHelper.printRepaymentSchedule(loanDetails);
+
+        List<GetLoansLoanIdRepaymentPeriod> periods = 
loanDetails.getRepaymentSchedule().getPeriods();
+        Double expectedOutstandingLoanBalanceOnFirstDisbursement = 700.00;
+        Double expectedFirstDownPaymentAmount = 175.00;
+        LocalDate expectedFirstDownPaymentDueDate = LocalDate.of(2022, 9, 3);
+        Double expectedOutstandingLoanBalanceOnSecondDisbursement = 300.00;
+        Double expectedSecondDownPaymentAmount = 75.00;
+        LocalDate expectedSecondDownPaymentDueDate = LocalDate.of(2022, 9, 4);
+        Double expectedRepaymentAmount = 250.00;
+        LocalDate expectedFirstRepaymentDueDate = LocalDate.of(2022, 10, 3);
+        Double outstandingBalanceOnFirstRepayment = 500.00;
+        LocalDate expectedSecondRepaymentDueDate = LocalDate.of(2022, 11, 3);
+        Double outstandingBalanceOnSecondRepayment = 250.00;
+        LocalDate expectedThirdRepaymentDueDate = LocalDate.of(2022, 12, 3);
+        Double outstandingBalanceOnThirdRepayment = 0.00;
+
+        GetLoansLoanIdRepaymentPeriod firstDisbursementPeriod = periods.get(0);
+        assertEquals(expectedFirstDownPaymentDueDate, 
firstDisbursementPeriod.getDueDate());
+        assertEquals(expectedOutstandingLoanBalanceOnFirstDisbursement, 
firstDisbursementPeriod.getPrincipalLoanBalanceOutstanding());
+
+        GetLoansLoanIdRepaymentPeriod firstDownPaymentPeriod = periods.get(1);
+        assertEquals(expectedFirstDownPaymentAmount, 
firstDownPaymentPeriod.getTotalDueForPeriod());
+        assertEquals(expectedFirstDownPaymentDueDate, 
firstDownPaymentPeriod.getDueDate());
+
+        GetLoansLoanIdRepaymentPeriod secondDisbursementPeriod = 
periods.get(2);
+        assertEquals(expectedSecondDownPaymentDueDate, 
secondDisbursementPeriod.getDueDate());
+        assertEquals(expectedOutstandingLoanBalanceOnSecondDisbursement, 
secondDisbursementPeriod.getPrincipalLoanBalanceOutstanding());
+
+        GetLoansLoanIdRepaymentPeriod secondDownPaymentPeriod = periods.get(3);
+        assertEquals(expectedSecondDownPaymentAmount, 
secondDownPaymentPeriod.getTotalDueForPeriod());
+        assertEquals(expectedSecondDownPaymentDueDate, 
secondDownPaymentPeriod.getDueDate());
+
+        GetLoansLoanIdRepaymentPeriod firstRepaymentPeriod = periods.get(4);
+        assertEquals(expectedRepaymentAmount, 
firstRepaymentPeriod.getTotalDueForPeriod());
+        assertEquals(expectedFirstRepaymentDueDate, 
firstRepaymentPeriod.getDueDate());
+        assertEquals(outstandingBalanceOnFirstRepayment, 
firstRepaymentPeriod.getPrincipalLoanBalanceOutstanding());
+
+        GetLoansLoanIdRepaymentPeriod secondRepaymentPeriod = periods.get(5);
+        assertEquals(expectedRepaymentAmount, 
secondRepaymentPeriod.getTotalDueForPeriod());
+        assertEquals(expectedSecondRepaymentDueDate, 
secondRepaymentPeriod.getDueDate());
+        assertEquals(outstandingBalanceOnSecondRepayment, 
secondRepaymentPeriod.getPrincipalLoanBalanceOutstanding());
+
+        GetLoansLoanIdRepaymentPeriod thirdRepaymentPeriod = periods.get(6);
+        assertEquals(expectedRepaymentAmount, 
thirdRepaymentPeriod.getTotalDueForPeriod());
+        assertEquals(expectedThirdRepaymentDueDate, 
thirdRepaymentPeriod.getDueDate());
+        assertEquals(outstandingBalanceOnThirdRepayment, 
thirdRepaymentPeriod.getPrincipalLoanBalanceOutstanding());
     }
 
     private Integer createLoanProductWithDownPaymentConfiguration(final 
LoanTransactionHelper loanTransactionHelper,
@@ -405,7 +495,7 @@ public class LoanRepaymentScheduleWithDownPaymentTest {
     private Integer createApproveAndDisburseTwiceLoanAccount(final Integer 
clientID, final Long loanProductID, final String externalId,
             final String numberOfRepayments) {
 
-        String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1")
+        String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency(numberOfRepayments)
                 
.withLoanTermFrequencyAsMonths().withNumberOfRepayments(numberOfRepayments).withRepaymentEveryAfter("1")
                 
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance()
                 
.withAmortizationTypeAsEqualPrincipalPayments().withInterestCalculationPeriodTypeSameAsRepaymentPeriod()

Reply via email to