From b1cde8a809af890df8394fa755d29953efc116c7 Mon Sep 17 00:00:00 2001 From: taskain7 Date: Tue, 15 Aug 2023 15:13:04 +0200 Subject: [PATCH] [FINERACT-1958] Repayment schedule for down payment --- .../service/LoanReadPlatformServiceImpl.java | 7 +- ...nRepaymentScheduleWithDownPaymentTest.java | 85 ++++++++++++++++--- 2 files changed, 80 insertions(+), 12 deletions(-) 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 5e187f2a4..831631693 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 @@ -31,11 +31,13 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; import lombok.AllArgsConstructor; import org.apache.commons.lang3.StringUtils; import org.apache.fineract.accounting.common.AccountingRuleType; @@ -1191,6 +1193,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa totalOutstanding = totalOutstanding.plus(disbursementPeriod.getFeeChargesDue()).minus(disbursementPeriod.getFeeChargesPaid()); Integer loanTermInDays = 0; + Set disbursementPeriodIds = new HashSet<>(); while (rs.next()) { final Long loanId = rs.getLong("loanId"); @@ -1203,7 +1206,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa BigDecimal principal = BigDecimal.ZERO; if (!isAdditional) { for (final DisbursementData data : disbursementData) { - if (fromDate.equals(this.disbursement.disbursementDate()) && data.disbursementDate().equals(fromDate)) { + if (fromDate.equals(this.disbursement.disbursementDate()) && data.disbursementDate().equals(fromDate) + && !disbursementPeriodIds.contains(data.getId())) { principal = principal.add(data.getPrincipal()); LoanSchedulePeriodData periodData = null; if (data.getChargeAmount() == null) { @@ -1216,6 +1220,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)) { if (!excludePastUndisbursed || data.isDisbursed() || !data.disbursementDate().isBefore(DateUtils.getBusinessLocalDate())) { 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 b99b998c1..1adda179d 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 @@ -86,7 +86,7 @@ public class LoanRepaymentScheduleWithDownPaymentTest { .compareTo(disbursedAmountPercentageForDownPayment)); assertEquals(enableAutoRepaymentForDownPayment, getLoanProductsProductResponse.getEnableAutoRepaymentForDownPayment()); - final Integer loanId = createApproveAndDisburseLoanAccount(clientId, loanProductId.longValue(), loanExternalIdStr); + final Integer loanId = createApproveAndDisburseLoanAccount(clientId, loanProductId.longValue(), loanExternalIdStr, "1"); GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); @@ -132,7 +132,7 @@ public class LoanRepaymentScheduleWithDownPaymentTest { .compareTo(disbursedAmountPercentageForDownPayment)); assertEquals(enableAutoRepaymentForDownPayment, getLoanProductsProductResponse.getEnableAutoRepaymentForDownPayment()); - final Integer loanId = createApproveAndDisburseLoanAccount(clientId, loanProductId.longValue(), loanExternalIdStr); + final Integer loanId = createApproveAndDisburseLoanAccount(clientId, loanProductId.longValue(), loanExternalIdStr, "1"); GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); @@ -179,7 +179,7 @@ public class LoanRepaymentScheduleWithDownPaymentTest { .compareTo(disbursedAmountPercentageForDownPayment)); assertEquals(enableAutoRepaymentForDownPayment, getLoanProductsProductResponse.getEnableAutoRepaymentForDownPayment()); - final Integer loanId = createApproveAndDisburseLoanAccount(clientId, loanProductId.longValue(), loanExternalIdStr); + final Integer loanId = createApproveAndDisburseLoanAccount(clientId, loanProductId.longValue(), loanExternalIdStr, "1"); GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); @@ -225,7 +225,7 @@ public class LoanRepaymentScheduleWithDownPaymentTest { .compareTo(disbursedAmountPercentageForDownPayment)); assertEquals(enableAutoRepaymentForDownPayment, getLoanProductsProductResponse.getEnableAutoRepaymentForDownPayment()); - final Integer loanId = createApproveAndDisburseTwiceLoanAccount(clientId, loanProductId.longValue(), loanExternalIdStr); + final Integer loanId = createApproveAndDisburseTwiceLoanAccount(clientId, loanProductId.longValue(), loanExternalIdStr, "1"); GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); @@ -276,7 +276,7 @@ public class LoanRepaymentScheduleWithDownPaymentTest { .compareTo(disbursedAmountPercentageForDownPayment)); assertEquals(enableAutoRepaymentForDownPayment, getLoanProductsProductResponse.getEnableAutoRepaymentForDownPayment()); - final Integer loanId = createApproveAndDisburseTwiceLoanAccount(clientId, loanProductId.longValue(), loanExternalIdStr); + final Integer loanId = createApproveAndDisburseTwiceLoanAccount(clientId, loanProductId.longValue(), loanExternalIdStr, "1"); GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); @@ -304,6 +304,67 @@ public class LoanRepaymentScheduleWithDownPaymentTest { assertEquals(expectedRepaymentAmount, loanDetails.getSummary().getTotalOutstanding()); } + @Test + public void loanRepaymentScheduleWithMultiDisbursementProductOneDisbursementAndThreeRepaymentsAndDownPayment() { + 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 = createApproveAndDisburseLoanAccount(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()); + + List periods = loanDetails.getRepaymentSchedule().getPeriods(); + Double expectedDownPaymentAmount = 250.00; + LocalDate expectedDownPaymentDueDate = LocalDate.of(2022, 9, 3); + 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; + + 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()))); + } + private Integer createLoanProductWithDownPaymentConfiguration(final LoanTransactionHelper loanTransactionHelper, final Integer delinquencyBucketId, Boolean enableDownPayment, String disbursedAmountPercentageForDownPayment, Boolean enableAutoRepaymentForDownPayment, boolean multiDisbursement) { @@ -325,10 +386,11 @@ public class LoanRepaymentScheduleWithDownPaymentTest { return loanProductId; } - private Integer createApproveAndDisburseLoanAccount(final Integer clientID, final Long loanProductID, final String externalId) { + private Integer createApproveAndDisburseLoanAccount(final Integer clientID, final Long loanProductID, final String externalId, + final String numberOfRepayments) { - String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1") - .withLoanTermFrequencyAsMonths().withNumberOfRepayments("1").withRepaymentEveryAfter("1") + String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency(numberOfRepayments) + .withLoanTermFrequencyAsMonths().withNumberOfRepayments(numberOfRepayments).withRepaymentEveryAfter("1") .withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance() .withAmortizationTypeAsEqualPrincipalPayments().withInterestCalculationPeriodTypeSameAsRepaymentPeriod() .withExpectedDisbursementDate("03 September 2022").withSubmittedOnDate("01 September 2022").withLoanType("individual") @@ -336,14 +398,15 @@ public class LoanRepaymentScheduleWithDownPaymentTest { final Integer loanId = loanTransactionHelper.getLoanId(loanApplicationJSON); loanTransactionHelper.approveLoan("02 September 2022", "1000", loanId, null); - loanTransactionHelper.disburseLoanWithTransactionAmount("03 September 2022", loanId, "1000"); + HashMap hashMap = loanTransactionHelper.disburseLoanWithTransactionAmount("03 September 2022", loanId, "1000"); return loanId; } - private Integer createApproveAndDisburseTwiceLoanAccount(final Integer clientID, final Long loanProductID, final String externalId) { + private Integer createApproveAndDisburseTwiceLoanAccount(final Integer clientID, final Long loanProductID, final String externalId, + final String numberOfRepayments) { String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1") - .withLoanTermFrequencyAsMonths().withNumberOfRepayments("1").withRepaymentEveryAfter("1") + .withLoanTermFrequencyAsMonths().withNumberOfRepayments(numberOfRepayments).withRepaymentEveryAfter("1") .withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance() .withAmortizationTypeAsEqualPrincipalPayments().withInterestCalculationPeriodTypeSameAsRepaymentPeriod() .withExpectedDisbursementDate("04 September 2022").withSubmittedOnDate("01 September 2022").withLoanType("individual")