diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java index 236ecb7ed..fd3bf5bf6 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/AbstractCumulativeLoanScheduleGenerator.java @@ -1004,10 +1004,16 @@ public abstract class AbstractCumulativeLoanScheduleGenerator implements LoanSch BigDecimal downPaymentAmt = BigDecimal.ZERO; if (loanApplicationTerms.isDownPaymentEnabled()) { - final LoanScheduleModelDownPaymentPeriod downPaymentPeriod = createDownPaymentPeriod(loanApplicationTerms, - scheduleParams, disburseDetail.getKey(), disburseDetail.getValue().getAmount()); - periods.add(downPaymentPeriod); - downPaymentAmt = downPaymentPeriod.principalDue(); + // get list of disbursements done on same day and create down payment periods + List disbursementsOnSameDate = loanApplicationTerms.getDisbursementDatas().stream() + .filter(disbursementData -> DateUtils.isEqual(disbursementData.disbursementDate(), disburseDetail.getKey())) + .toList(); + for (DisbursementData disbursementData : disbursementsOnSameDate) { + final LoanScheduleModelDownPaymentPeriod downPaymentPeriod = createDownPaymentPeriod(loanApplicationTerms, + scheduleParams, disbursementData.disbursementDate(), disbursementData.getPrincipal()); + periods.add(downPaymentPeriod); + downPaymentAmt = downPaymentAmt.add(downPaymentPeriod.principalDue()); + } } // updates actual outstanding balance with new // disbursement detail 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 d46b9aa1b..aa0645378 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 @@ -365,10 +365,16 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc BigDecimal downPaymentAmt = BigDecimal.ZERO; if (loanApplicationTerms.isDownPaymentEnabled()) { - final LoanScheduleModelDownPaymentPeriod downPaymentPeriod = createDownPaymentPeriod(loanApplicationTerms, - scheduleParams, disburseDetail.getKey(), disburseDetail.getValue().getAmount()); - periods.add(downPaymentPeriod); - downPaymentAmt = downPaymentPeriod.principalDue(); + // get list of disbursements done on same day and create down payment periods + List disbursementsOnSameDate = loanApplicationTerms.getDisbursementDatas().stream() + .filter(disbursementData -> DateUtils.isEqual(disbursementData.disbursementDate(), disburseDetail.getKey())) + .toList(); + for (DisbursementData disbursementData : disbursementsOnSameDate) { + final LoanScheduleModelDownPaymentPeriod downPaymentPeriod = createDownPaymentPeriod(loanApplicationTerms, + scheduleParams, disbursementData.disbursementDate(), disbursementData.getPrincipal()); + periods.add(downPaymentPeriod); + downPaymentAmt = downPaymentAmt.add(downPaymentPeriod.principalDue()); + } } // updates actual outstanding balance with new // disbursement detail diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTest.java index 4e560a925..9a9ec44a6 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTest.java @@ -32,11 +32,16 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; +import java.util.List; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; +import org.apache.fineract.client.models.AdvancedPaymentData; import org.apache.fineract.client.models.GetLoanProductsProductIdResponse; import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod; import org.apache.fineract.client.models.GetLoansLoanIdResponse; +import org.apache.fineract.client.models.PaymentAllocationOrder; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest; import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse; import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; @@ -50,6 +55,8 @@ import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuil import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder; import org.apache.fineract.integrationtests.common.loans.LoanTestLifecycleExtension; import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; +import org.apache.fineract.portfolio.loanaccount.loanschedule.domain.LoanScheduleType; +import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -549,7 +556,7 @@ public class LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTe assertNotNull(loanDetails.getRepaymentSchedule()); // periods - assertEquals(7, loanDetails.getRepaymentSchedule().getPeriods().size()); + assertEquals(8, loanDetails.getRepaymentSchedule().getPeriods().size()); // disbursement period [0] verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(0), LocalDate.of(2023, 3, 3), 200.0); // down payment period [1] @@ -560,13 +567,16 @@ public class LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTe // disbursement period [3] verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(3), LocalDate.of(2023, 4, 3), 200.0); // down payment period [4] - verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 2, 100.0, 100.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, - true, LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3)); - // regular installment [5] - verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(5), 3, 225.0, 0.0, 0.0, 225.0, 0.0, 0.0, 0.0, 0.0, - false, LocalDate.of(2023, 3, 3), LocalDate.of(2023, 4, 3)); + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 2, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, + LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3)); + // down payment period [5] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(5), 3, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, + LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3)); // regular installment [6] verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(6), 4, 225.0, 0.0, 0.0, 225.0, 0.0, 0.0, 0.0, 0.0, + false, LocalDate.of(2023, 3, 3), LocalDate.of(2023, 4, 3)); + // regular installment [7] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(7), 5, 225.0, 0.0, 0.0, 225.0, 0.0, 0.0, 0.0, 0.0, false, LocalDate.of(2023, 4, 3), LocalDate.of(2023, 5, 3)); // make repayment for fully paying and verify that regular installment gets fully paid on 3rd april @@ -579,7 +589,7 @@ public class LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTe assertNotNull(loanDetails.getRepaymentSchedule()); // periods - assertEquals(7, loanDetails.getRepaymentSchedule().getPeriods().size()); + assertEquals(8, loanDetails.getRepaymentSchedule().getPeriods().size()); // disbursement period [0] verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(0), LocalDate.of(2023, 3, 3), 200.0); // down payment period [1] @@ -590,13 +600,212 @@ public class LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTe // disbursement period [3] verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(3), LocalDate.of(2023, 4, 3), 200.0); // down payment period [4] - verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 2, 100.0, 100.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, - true, LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3)); - // regular installment [5] - verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(5), 3, 225.0, 225.0, 225.0, 0.0, 0.0, 0.0, 0.0, 0.0, + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 2, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, + LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3)); + // down payment period [5] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(5), 3, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, + LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3)); + // regular installment [6] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(6), 4, 225.0, 225.0, 225.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, LocalDate.of(2023, 3, 3), LocalDate.of(2023, 4, 3)); + // regular installment [7] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(7), 5, 225.0, 0.0, 0.0, 225.0, 0.0, 0.0, 0.0, 0.0, + false, LocalDate.of(2023, 4, 3), LocalDate.of(2023, 5, 3)); + + } finally { + GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE); + } + + } + + @Test + public void loanAccountWithEnableDownPaymentWithAdvancedPaymentAllocationWithProgressiveScheduleGenerationMultipleDisbursementsOnSameDayTest() { + try { + + // Test with + // Enable Down Payment + // Disable Auto Repayment For Down Payment + // Overlapping down payment and regular installment with multiple disbursements on same day + // Progressive Schedule generation with Advanced Payment Allocation + + // Set business date + LocalDate disbursementDate = LocalDate.of(2023, 3, 3); + + GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE); + BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, disbursementDate); + + // Loan ExternalId + String loanExternalIdStr = UUID.randomUUID().toString(); + + // down-payment configuration + Boolean enableDownPayment = true; + BigDecimal disbursedAmountPercentageForDownPayment = BigDecimal.valueOf(25); + Boolean enableAutoRepaymentForDownPayment = false; + + final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue(); + + String futureInstallmentAllocationRule = "NEXT_INSTALLMENT"; + AdvancedPaymentData defaultAllocation = createDefaultPaymentAllocation(futureInstallmentAllocationRule); + + // Loan Product creation with down-payment configuration and progressive schedule + final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProductWithAdvancedPaymentStrategyAndProgressiveLoanSchedule( + loanTransactionHelper, enableDownPayment, "25", enableAutoRepaymentForDownPayment, defaultAllocation); + + assertNotNull(getLoanProductsProductResponse); + assertEquals(enableDownPayment, getLoanProductsProductResponse.getEnableDownPayment()); + assertEquals(0, getLoanProductsProductResponse.getDisbursedAmountPercentageForDownPayment() + .compareTo(disbursedAmountPercentageForDownPayment)); + assertEquals(enableAutoRepaymentForDownPayment, getLoanProductsProductResponse.getEnableAutoRepaymentForDownPayment()); + + // create loan account + + final Integer loanId = createLoanAccountMultipleRepaymentsDisbursement(clientId, getLoanProductsProductResponse.getId(), + loanExternalIdStr, "advanced-payment-allocation-strategy"); + + // Retrieve Loan with loanId + + GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); + + // verify down-payment details for Loan + assertNotNull(loanDetails); + assertEquals(enableDownPayment, loanDetails.getEnableDownPayment()); + assertEquals(0, loanDetails.getDisbursedAmountPercentageForDownPayment().compareTo(disbursedAmountPercentageForDownPayment)); + assertEquals(enableAutoRepaymentForDownPayment, loanDetails.getEnableAutoRepaymentForDownPayment()); + + // first disbursement + loanTransactionHelper.disburseLoanWithTransactionAmount("03 March 2023", loanId, "200"); + + // make repayment on 3rd March + final PostLoansLoanIdTransactionsResponse repaymentTransaction = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr, + new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("3 March 2023").locale("en") + .transactionAmount(50.0)); + + // verify loan schedule + + loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); + + assertNotNull(loanDetails.getRepaymentSchedule()); + + // periods + assertEquals(4, loanDetails.getRepaymentSchedule().getPeriods().size()); + // disbursement period [0] + verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(0), LocalDate.of(2023, 3, 3), 200.0); + // down payment period [1] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 1, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, + LocalDate.of(2023, 3, 3), LocalDate.of(2023, 3, 3)); + // regular installment [2] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(2), 2, 75.0, 0.0, 0.0, 75.0, 0.0, 0.0, 0.0, 0.0, false, + LocalDate.of(2023, 3, 3), LocalDate.of(2023, 4, 3)); + // regular installment [3] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(3), 3, 75.0, 0.0, 0.0, 75.0, 0.0, 0.0, 0.0, 0.0, false, + LocalDate.of(2023, 4, 3), LocalDate.of(2023, 5, 3)); + + // second disbursement with overlapping installment i.e same due date as regular repayment due date + + disbursementDate = LocalDate.of(2023, 4, 3); + BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, disbursementDate); + loanTransactionHelper.disburseLoanWithTransactionAmount("03 April 2023", loanId, "200"); + + // make repayment on 3rd April + final PostLoansLoanIdTransactionsResponse repaymentTransaction_1 = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr, + new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("3 April 2023").locale("en") + .transactionAmount(50.0)); + + // verify loan schedule + + loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); + + assertNotNull(loanDetails.getRepaymentSchedule()); + + // periods + assertEquals(6, loanDetails.getRepaymentSchedule().getPeriods().size()); + // disbursement period [0] + verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(0), LocalDate.of(2023, 3, 3), 200.0); + // down payment period [1] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 1, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, + LocalDate.of(2023, 3, 3), LocalDate.of(2023, 3, 3)); + // disbursement period [2] + verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(2), LocalDate.of(2023, 4, 3), 200.0); + // down payment period [3] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(3), 2, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, + LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3)); + // regular installment [4] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 3, 150.0, 0.0, 0.0, 150.0, 0.0, 0.0, 0.0, 0.0, + false, LocalDate.of(2023, 3, 3), LocalDate.of(2023, 4, 3)); + // regular installment [5] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(5), 4, 150.0, 0.0, 0.0, 150.0, 0.0, 0.0, 0.0, 0.0, + false, LocalDate.of(2023, 4, 3), LocalDate.of(2023, 5, 3)); + + // same day third disbursement with overlapping installment i.e same due date as regular repayment due date + // 3-April + loanTransactionHelper.disburseLoanWithTransactionAmount("03 April 2023", loanId, "200"); + + // make repayment on 3rd April + final PostLoansLoanIdTransactionsResponse repaymentTransaction_2 = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr, + new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("3 April 2023").locale("en") + .transactionAmount(50.0)); + + // verify loan schedule + + loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); + + assertNotNull(loanDetails.getRepaymentSchedule()); + + // periods + assertEquals(8, loanDetails.getRepaymentSchedule().getPeriods().size()); + // disbursement period [0] + verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(0), LocalDate.of(2023, 3, 3), 200.0); + // down payment period [1] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 1, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, + LocalDate.of(2023, 3, 3), LocalDate.of(2023, 3, 3)); + // disbursement period [2] + verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(2), LocalDate.of(2023, 4, 3), 200.0); + // disbursement period [3] + verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(3), LocalDate.of(2023, 4, 3), 200.0); + // down payment period [4] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 2, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, + LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3)); + // down payment period [5] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(5), 3, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, + LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3)); // regular installment [6] verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(6), 4, 225.0, 0.0, 0.0, 225.0, 0.0, 0.0, 0.0, 0.0, + false, LocalDate.of(2023, 3, 3), LocalDate.of(2023, 4, 3)); + // regular installment [7] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(7), 5, 225.0, 0.0, 0.0, 225.0, 0.0, 0.0, 0.0, 0.0, + false, LocalDate.of(2023, 4, 3), LocalDate.of(2023, 5, 3)); + + // make repayment for fully paying and verify that regular installment gets fully paid on 3rd april + final PostLoansLoanIdTransactionsResponse repaymentTransaction_3 = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr, + new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("3 April 2023").locale("en") + .transactionAmount(225.0)); + + loanDetails = loanTransactionHelper.getLoanDetails(loanId.longValue()); + + assertNotNull(loanDetails.getRepaymentSchedule()); + + // periods + assertEquals(8, loanDetails.getRepaymentSchedule().getPeriods().size()); + // disbursement period [0] + verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(0), LocalDate.of(2023, 3, 3), 200.0); + // down payment period [1] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 1, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, + LocalDate.of(2023, 3, 3), LocalDate.of(2023, 3, 3)); + // disbursement period [2] + verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(2), LocalDate.of(2023, 4, 3), 200.0); + // disbursement period [3] + verifyDisbursementPeriod(loanDetails.getRepaymentSchedule().getPeriods().get(3), LocalDate.of(2023, 4, 3), 200.0); + // down payment period [4] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 2, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, + LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3)); + // down payment period [5] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(5), 3, 50.0, 50.0, 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, true, + LocalDate.of(2023, 4, 3), LocalDate.of(2023, 4, 3)); + // regular installment [6] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(6), 4, 225.0, 225.0, 225.0, 0.0, 0.0, 0.0, 0.0, 0.0, + true, LocalDate.of(2023, 3, 3), LocalDate.of(2023, 4, 3)); + // regular installment [7] + verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(7), 5, 225.0, 0.0, 0.0, 225.0, 0.0, 0.0, 0.0, 0.0, false, LocalDate.of(2023, 4, 3), LocalDate.of(2023, 5, 3)); } finally { @@ -657,6 +866,46 @@ public class LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTe return loanTransactionHelper.getLoanProduct(loanProductId); } + private GetLoanProductsProductIdResponse createLoanProductWithAdvancedPaymentStrategyAndProgressiveLoanSchedule( + LoanTransactionHelper loanTransactionHelper, Boolean enableDownPayment, String disbursedAmountPercentageForDownPayment, + boolean enableAutoRepaymentForDownPayment, AdvancedPaymentData... advancedPaymentData) { + + final String loanProductJSON = new LoanProductTestBuilder().withPrincipal("1000").withRepaymentTypeAsMonth() + .withRepaymentAfterEvery("1").withNumberOfRepayments("2").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0") + .withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsDecliningBalance() + .withInterestCalculationPeriodTypeAsRepaymentPeriod(true).withDaysInMonth("30").withDaysInYear("365") + .withMoratorium("0", "0").withMultiDisburse().withDisallowExpectedDisbursements(true) + .withEnableDownPayment(enableDownPayment, disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment) + .addAdvancedPaymentAllocation(advancedPaymentData).withLoanScheduleType(LoanScheduleType.PROGRESSIVE).build(null); + final Integer loanProductId = loanTransactionHelper.getLoanProductId(loanProductJSON); + return loanTransactionHelper.getLoanProduct(loanProductId); + } + + private AdvancedPaymentData createDefaultPaymentAllocation(String futureInstallmentAllocationRule) { + AdvancedPaymentData advancedPaymentData = new AdvancedPaymentData(); + advancedPaymentData.setTransactionType("DEFAULT"); + advancedPaymentData.setFutureInstallmentAllocationRule(futureInstallmentAllocationRule); + + List paymentAllocationOrders = getPaymentAllocationOrder(PaymentAllocationType.PAST_DUE_PENALTY, + PaymentAllocationType.PAST_DUE_FEE, PaymentAllocationType.PAST_DUE_PRINCIPAL, PaymentAllocationType.PAST_DUE_INTEREST, + PaymentAllocationType.DUE_PENALTY, PaymentAllocationType.DUE_FEE, PaymentAllocationType.DUE_PRINCIPAL, + PaymentAllocationType.DUE_INTEREST, PaymentAllocationType.IN_ADVANCE_PENALTY, PaymentAllocationType.IN_ADVANCE_FEE, + PaymentAllocationType.IN_ADVANCE_PRINCIPAL, PaymentAllocationType.IN_ADVANCE_INTEREST); + + advancedPaymentData.setPaymentAllocationOrder(paymentAllocationOrders); + return advancedPaymentData; + } + + private List getPaymentAllocationOrder(PaymentAllocationType... paymentAllocationTypes) { + AtomicInteger integer = new AtomicInteger(1); + return Arrays.stream(paymentAllocationTypes).map(pat -> { + PaymentAllocationOrder paymentAllocationOrder = new PaymentAllocationOrder(); + paymentAllocationOrder.setPaymentAllocationRule(pat.name()); + paymentAllocationOrder.setOrder(integer.getAndIncrement()); + return paymentAllocationOrder; + }).toList(); + } + private void checkDownPaymentTransaction(final LocalDate transactionDate, final Float principalPortion, final Float interestPortion, final Float feePortion, final Float penaltyPortion, final Integer loanID) { ArrayList transactions = (ArrayList) loanTransactionHelper.getLoanTransactions(this.requestSpec,