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

adamsaghy 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 e7013309c FINERACT-1958-Overlapping-installment-payment-allocation
e7013309c is described below

commit e7013309ca0b9d67cbc11c6c512e16376ddd7c58
Author: Ruchi Dhamankar <[email protected]>
AuthorDate: Mon Aug 28 14:45:39 2023 +0530

    FINERACT-1958-Overlapping-installment-payment-allocation
---
 .../portfolio/loanaccount/domain/Loan.java         |   4 -
 .../domain/AbstractLoanScheduleGenerator.java      |  62 +-
 .../service/LoanReadPlatformServiceImpl.java       |   9 +-
 ...nWithOverlappingDownPaymentInstallmentTest.java | 697 +++++++++++++++++++++
 4 files changed, 757 insertions(+), 15 deletions(-)

diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
index 90b52b80d..25a479a84 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java
@@ -2972,10 +2972,6 @@ public class Loan extends 
AbstractAuditableWithUTCDateTimeCustom {
                     MathUtil.percentageOf(disbursedAmount, 
disbursedAmountPercentageForDownPayment, 19));
             LoanTransaction downPaymentTransaction = 
LoanTransaction.downPayment(getOffice(), downPaymentMoney, null, disbursedOn,
                     externalId);
-            /*
-             * Currently mapping down payment transaction as repayment 
transaction to loan repayment schedule for
-             * consistency. Need to replace below logic for creating new 
installment for down payment and mapping.
-             */
 
             LoanEvent event = LoanEvent.LOAN_REPAYMENT_OR_WAIVER;
             validateRepaymentTypeAccountStatus(downPaymentTransaction, event);
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 a5edd7abc..d76f75e3f 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
@@ -367,6 +367,10 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
 
             periods.add(installment);
 
+            if (loanApplicationTerms.isMultiDisburseLoan() && 
loanApplicationTerms.isDownPaymentEnabled()) {
+                
createDownPaymentInstallmentForOverlappingInstallments(loanApplicationTerms, 
scheduleParams, periods, scheduledDueDate);
+            }
+
             // Updates principal paid map with efective date for reducing
             // the amount from outstanding balance(interest calculation)
             updateAmountsWithEffectiveDate(loanApplicationTerms, 
holidayDetailDTO, scheduleParams, scheduledDueDate, currentPeriodParams,
@@ -427,6 +431,29 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                 totalOutstanding);
     }
 
+    private void 
createDownPaymentInstallmentForOverlappingInstallments(LoanApplicationTerms 
loanApplicationTerms,
+            LoanScheduleParams scheduleParams, 
Collection<LoanScheduleModelPeriod> periods, LocalDate scheduledDueDate) {
+        for (Map.Entry<LocalDate, Money> disburseDetail : 
scheduleParams.getDisburseDetailMap().entrySet()) {
+            if 
(disburseDetail.getKey().isAfter(scheduleParams.getPeriodStartDate()) && 
disburseDetail.getKey().isEqual(scheduledDueDate)) {
+                scheduleParams.incrementPeriodNumber();
+                scheduleParams.incrementInstalmentNumber();
+
+                BigDecimal downPaymentAmount = 
MathUtil.percentageOf(disburseDetail.getValue().getAmount(),
+                        
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), 19);
+                Money downPayment = 
Money.of(loanApplicationTerms.getCurrency(), downPaymentAmount);
+                LoanScheduleModelDownPaymentPeriod installment = 
LoanScheduleModelDownPaymentPeriod.downPayment(
+                        scheduleParams.getInstalmentNumber(), 
disburseDetail.getKey(), downPayment, scheduleParams.getOutstandingBalance());
+
+                
addLoanRepaymentScheduleInstallment(scheduleParams.getInstallments(), 
installment);
+                periods.add(installment);
+                scheduleParams.reduceOutstandingBalance(downPayment);
+                scheduleParams.addTotalCumulativePrincipal(downPayment);
+                scheduleParams.addTotalRepaymentExpected(downPayment);
+
+            }
+        }
+    }
+
     private void updateCompoundingDetails(final 
Collection<LoanScheduleModelPeriod> periods, final LoanScheduleParams params,
             final LoanApplicationTerms loanApplicationTerms) {
         final Map<LocalDate, Map<LocalDate, Money>> compoundingDetails = 
params.getCompoundingDateVariations();
@@ -998,8 +1025,10 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                 periods.add(disbursementPeriod);
 
                 if (loanApplicationTerms.isDownPaymentEnabled()) {
-                    createDownPaymentPeriod(loanApplicationTerms, 
scheduleParams, periods, disburseDetail.getKey(),
-                            disburseDetail.getValue().getAmount());
+                    if (!disburseDetail.getKey().isEqual(scheduledDueDate)) {
+                        createDownPaymentPeriod(loanApplicationTerms, 
scheduleParams, periods, disburseDetail.getKey(),
+                                disburseDetail.getValue().getAmount());
+                    }
                 }
 
                 // updates actual outstanding balance with new
@@ -2348,11 +2377,14 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                                 .disbursement(disburseDetail.getKey(), 
disburseDetail.getValue(), chargesDueAtTimeOfDisbursement);
                         periods.add(disbursementPeriod);
                         if (loanApplicationTerms.isDownPaymentEnabled()) {
-                            loanScheduleParams = 
LoanScheduleParams.createLoanScheduleParams(currency,
-                                    Money.of(currency, 
chargesDueAtTimeOfDisbursement), 
loanApplicationTerms.getExpectedDisbursementDate(),
-                                    
getPrincipalToBeScheduled(loanApplicationTerms));
-                            createDownPaymentPeriod(loanApplicationTerms, 
loanScheduleParams, periods, disburseDetail.getKey(),
-                                    disbursementPeriod.principalDue());
+                            if 
(!disburseDetail.getKey().isEqual(installment.getDueDate())) {
+                                loanScheduleParams = 
LoanScheduleParams.createLoanScheduleParams(currency,
+                                        Money.of(currency, 
chargesDueAtTimeOfDisbursement),
+                                        
loanApplicationTerms.getExpectedDisbursementDate(),
+                                        
getPrincipalToBeScheduled(loanApplicationTerms));
+                                createDownPaymentPeriod(loanApplicationTerms, 
loanScheduleParams, periods, disburseDetail.getKey(),
+                                        disbursementPeriod.principalDue());
+                            }
                         }
                         // updates actual outstanding balance with new
                         // disbursement detail
@@ -2369,6 +2401,22 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                 outstandingBalance = 
outstandingBalance.minus(installment.getPrincipal(currency));
                 final LoanScheduleModelPeriod loanScheduleModelPeriod = 
createLoanScheduleModelPeriod(installment, outstandingBalance);
                 periods.add(loanScheduleModelPeriod);
+
+                // downpayment installment for overlapping case
+
+                if (loanApplicationTerms.isMultiDisburseLoan() && 
loanApplicationTerms.isDownPaymentEnabled()) {
+                    for (Map.Entry<LocalDate, Money> disburseDetail : 
disburseDetailMap.entrySet()) {
+                        if 
(disburseDetail.getKey().isAfter(installment.getFromDate())
+                                && 
disburseDetail.getKey().isEqual(installment.getDueDate())) {
+                            loanScheduleParams = 
LoanScheduleParams.createLoanScheduleParams(currency,
+                                    Money.of(currency, 
chargesDueAtTimeOfDisbursement), 
loanApplicationTerms.getExpectedDisbursementDate(),
+                                    
getPrincipalToBeScheduled(loanApplicationTerms));
+                            createDownPaymentPeriod(loanApplicationTerms, 
loanScheduleParams, periods, disburseDetail.getKey(),
+                                    disburseDetail.getValue().getAmount());
+                        }
+                    }
+                }
+
                 totalCumulativePrincipal = 
totalCumulativePrincipal.plus(installment.getPrincipal(currency));
                 totalCumulativeInterest = 
totalCumulativeInterest.plus(installment.getInterestCharged(currency));
                 totalFeeChargesCharged = 
totalFeeChargesCharged.plus(installment.getFeeChargesCharged(currency));
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 1ce71ca2b..6834d360a 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
@@ -447,7 +447,8 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
         RepaymentTransactionTemplateMapper mapper = new 
RepaymentTransactionTemplateMapper(sqlGenerator);
         String sql = "select " + mapper.schema();
         LoanTransactionData loanTransactionData = 
this.jdbcTemplate.queryForObject(sql, mapper, // NOSONAR
-                LoanTransactionType.REPAYMENT.getValue(), 
LoanTransactionType.REPAYMENT.getValue(), loanId, loanId);
+                LoanTransactionType.REPAYMENT.getValue(), 
LoanTransactionType.DOWN_PAYMENT.getValue(),
+                LoanTransactionType.REPAYMENT.getValue(), 
LoanTransactionType.DOWN_PAYMENT.getValue(), loanId, loanId);
         final Collection<PaymentTypeData> paymentOptions = 
this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
         return LoanTransactionData.templateOnTop(loanTransactionData, 
paymentOptions);
     }
@@ -2494,9 +2495,9 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
             StringBuilder sqlBuilder = new StringBuilder();
             sqlBuilder.append("(CASE ");
             sqlBuilder.append(
-                    "WHEN (select max(tr.transaction_date) as transaction_date 
from m_loan_transaction tr where tr.loan_id = l.id AND tr.transaction_type_enum 
= ? AND tr.is_reversed = false) > ls.dueDate ");
+                    "WHEN (select max(tr.transaction_date) as transaction_date 
from m_loan_transaction tr where tr.loan_id = l.id AND tr.transaction_type_enum 
in (?,?) AND tr.is_reversed = false) > ls.dueDate ");
             sqlBuilder.append(
-                    "THEN (select max(tr.transaction_date) as transaction_date 
from m_loan_transaction tr where tr.loan_id = l.id AND tr.transaction_type_enum 
= ? AND tr.is_reversed = false) ");
+                    "THEN (select max(tr.transaction_date) as transaction_date 
from m_loan_transaction tr where tr.loan_id = l.id AND tr.transaction_type_enum 
in (?,?) AND tr.is_reversed = false) ");
             sqlBuilder.append("ELSE ls.dueDate END) as transactionDate, ");
             sqlBuilder.append(
                     "ls.principal_amount - 
coalesce(ls.principal_writtenoff_derived, 0) - 
coalesce(ls.principal_completed_derived, 0) as principalDue, ");
@@ -2516,7 +2517,7 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService, Loa
             sqlBuilder.append(
                     "JOIN((SELECT ls.loan_id, ls.duedate as datedue FROM 
m_loan_repayment_schedule ls WHERE ls.loan_id = ? and ls.completed_derived = 
false ORDER BY ls.duedate LIMIT 1)) asq on asq.loan_id = ls.loan_id ");
             sqlBuilder.append("AND asq.datedue = ls.duedate ");
-            sqlBuilder.append("WHERE l.id = ?");
+            sqlBuilder.append("WHERE l.id = ? LIMIT 1");
             return sqlBuilder.toString();
         }
 
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
new file mode 100644
index 000000000..f427c198e
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTest.java
@@ -0,0 +1,697 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.fineract.integrationtests;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import io.restassured.builder.RequestSpecBuilder;
+import io.restassured.builder.ResponseSpecBuilder;
+import io.restassured.http.ContentType;
+import io.restassured.specification.RequestSpecification;
+import io.restassured.specification.ResponseSpecification;
+import java.math.BigDecimal;
+import java.time.LocalDate;
+import java.time.format.DateTimeFormatter;
+import java.time.format.DateTimeFormatterBuilder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.UUID;
+import org.apache.fineract.client.models.GetLoanProductsProductIdResponse;
+import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
+import org.apache.fineract.client.models.PostLoansLoanIdTransactionsResponse;
+import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
+import org.apache.fineract.integrationtests.common.BusinessDateHelper;
+import org.apache.fineract.integrationtests.common.ClientHelper;
+import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
+import org.apache.fineract.integrationtests.common.Utils;
+import org.apache.fineract.integrationtests.common.charges.ChargesHelper;
+import 
org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
+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.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+@ExtendWith(LoanTestLifecycleExtension.class)
+public class 
LoanAccountPaymentAllocationWithOverlappingDownPaymentInstallmentTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+    private LoanTransactionHelper loanTransactionHelper;
+    private ClientHelper clientHelper;
+    private static final DateTimeFormatter DATE_FORMATTER = new 
DateTimeFormatterBuilder().appendPattern("dd MMMM yyyy").toFormatter();
+
+    @BeforeEach
+    public void setup() {
+        Utils.initializeRESTAssured();
+        this.requestSpec = new 
RequestSpecBuilder().setContentType(ContentType.JSON).build();
+        this.requestSpec.header("Authorization", "Basic " + 
Utils.loginIntoServerAndGetBase64EncodedAuthenticationKey());
+        this.responseSpec = new 
ResponseSpecBuilder().expectStatusCode(200).build();
+        this.loanTransactionHelper = new 
LoanTransactionHelper(this.requestSpec, this.responseSpec);
+        this.clientHelper = new ClientHelper(this.requestSpec, 
this.responseSpec);
+    }
+
+    @Test
+    public void 
loanAccountWithEnableDownPaymentAndEnableAutoRepaymentForDownPaymentWithOverlappingInstallmentPaymentAllocationTest()
 {
+        try {
+
+            // Test with
+            // Enable Down Payment
+            // Enable Auto Repayment For Down Payment
+            // Payment Strategy DEFAULT payment allocation strategy 
"Penalties, Fees, Interest, Principal order"
+            // Overlapping down payment and regular installment
+
+            // Set business date
+            LocalDate disbursementDate = LocalDate.of(2023, 03, 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 = true;
+
+            final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+
+            // Loan Product creation with down-payment configuration
+            final GetLoanProductsProductIdResponse 
getLoanProductsProductResponse = 
createLoanProductWithEnableDownPaymentAndMultipleDisbursements(
+                    loanTransactionHelper, enableDownPayment, "25", 
enableAutoRepaymentForDownPayment);
+
+            assertNotNull(getLoanProductsProductResponse);
+            assertEquals(enableDownPayment, 
getLoanProductsProductResponse.getEnableDownPayment());
+            assertEquals(0, 
getLoanProductsProductResponse.getDisbursedAmountPercentageForDownPayment()
+                    .compareTo(disbursedAmountPercentageForDownPayment));
+            assertEquals(enableAutoRepaymentForDownPayment, 
getLoanProductsProductResponse.getEnableAutoRepaymentForDownPayment());
+
+            // create loan account with DEFAULT payment allocation strategy 
"Penalties, Fees, Interest, Principal order"
+
+            final Integer loanId = 
createLoanAccountMultipleRepaymentsDisbursement(clientId, 
getLoanProductsProductResponse.getId(),
+                    loanExternalIdStr, 
LoanProductTestBuilder.DEFAULT_STRATEGY);
+
+            // add charge PENALTY with due date as overlapping installment due 
date
+
+            Integer penalty = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                    
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", true));
+
+            LocalDate targetDate = LocalDate.of(2023, 4, 3);
+            final String penaltyCharge1AddedDate = 
DATE_FORMATTER.format(targetDate);
+
+            Integer penalty1LoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                    
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
 penaltyCharge1AddedDate, "10"));
+
+            assertNotNull(penalty1LoanChargeId);
+
+            // add charge FEE with due date as overlapping installment due date
+            Integer fee = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                    
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", false));
+
+            targetDate = LocalDate.of(2023, 4, 3);
+            final String feeCharge1AddedDate = 
DATE_FORMATTER.format(targetDate);
+
+            Integer feeLoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                    
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee),
 feeCharge1AddedDate, "5.15"));
+
+            assertNotNull(feeLoanChargeId);
+
+            // 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, "500");
+
+            // verify down-payment transaction created
+            checkDownPaymentTransaction(disbursementDate, 125.0f, 0.0f, 0.0f, 
0.0f, loanId);
+
+            // verify loan schedule
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // first period [2023-03-03 to 2023-03-03] down payment installment
+            assertEquals(1, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalPaidForPeriod());
+            assertEquals(true, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getComplete());
+
+            // second period [2023-03-03 to 2023-04-03] regular installment
+            assertEquals(2, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
+            assertEquals(166.67, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalInstallmentAmountForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalPaidForPeriod());
+            assertEquals(5.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFeeChargesDue());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFeeChargesPaid());
+            assertEquals(10.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPenaltyChargesDue());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPenaltyChargesPaid());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getComplete());
+
+            // third period [2023-04-03 to 2023-05-03] regular installment
+            assertEquals(3, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+            assertEquals(LocalDate.of(2023, 05, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+            assertEquals(208.33, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalPaidForPeriod());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getComplete());
+
+            // second disbursement with overlapping installment i.e same due 
date as regular repayment due date
+
+            disbursementDate = LocalDate.of(2023, 04, 3);
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, disbursementDate);
+            loanTransactionHelper.disburseLoanWithTransactionAmount("03 April 
2023", loanId, "1000");
+
+            checkDownPaymentTransaction(disbursementDate, 234.85f, 0.0f, 
5.15f, 10.0f, loanId);
+
+            // verify loan schedule
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // verify that overlapping down payment installment is created 
after regular installment
+
+            // and verify regular repayment type installment gets paid before 
down payment installment according to
+            // payment
+
+            // allocation strategy [DEFAULT]
+
+            // first period [2023-03-03 to 2023-03-03] first down payment 
installment
+            assertEquals(1, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalPaidForPeriod());
+            assertEquals(true, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getComplete());
+
+            // second period [2023-03-03 to 2023-04-03] regular installment, 
penalty and fee charges gets paid first
+            assertEquals(2, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+            assertEquals(500.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
+            assertEquals(250.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalPaidForPeriod());
+            assertEquals(265.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalOutstandingForPeriod());
+            assertEquals(5.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFeeChargesDue());
+            assertEquals(5.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFeeChargesPaid());
+            assertEquals(10.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPenaltyChargesDue());
+            assertEquals(10.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPenaltyChargesPaid());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getComplete());
+
+            // third period [2023-04-03 to 2023-04-03] overlapping down 
payment installment
+            assertEquals(3, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getDueDate());
+            assertEquals(250.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalInstallmentAmountForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalPaidForPeriod());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getComplete());
+
+            // fourth period [2023-04-03 to 2023-05-03] regular installment
+            assertEquals(4, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getFromDate());
+            assertEquals(LocalDate.of(2023, 05, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getDueDate());
+            assertEquals(625.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getTotalInstallmentAmountForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getTotalPaidForPeriod());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getComplete());
+
+            // make repayment for fully paying and verify that regular 
installment gets fully paid on 3rd april
+
+            final PostLoansLoanIdTransactionsResponse repaymentTransaction_1 = 
loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
+                    new PostLoansLoanIdTransactionsRequest().dateFormat("dd 
MMMM yyyy").transactionDate("3 April 2023").locale("en")
+                            .transactionAmount(265.15));
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // verify regular installment gets paid off
+
+            // first period [2023-03-03 to 2023-03-03] first down payment 
installment
+            assertEquals(1, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalPaidForPeriod());
+            assertEquals(true, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getComplete());
+
+            // second period [2023-03-03 to 2023-04-03] regular installment, 
penalty and fee charges gets paid first
+            assertEquals(2, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+            assertEquals(500.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
+            assertEquals(515.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalPaidForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalOutstandingForPeriod());
+            assertEquals(5.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFeeChargesDue());
+            assertEquals(5.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFeeChargesPaid());
+            assertEquals(10.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPenaltyChargesDue());
+            assertEquals(10.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPenaltyChargesPaid());
+            assertEquals(true, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getComplete());
+
+            // third period [2023-04-03 to 2023-04-03] overlapping down 
payment installment
+            assertEquals(3, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getDueDate());
+            assertEquals(250.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalInstallmentAmountForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalPaidForPeriod());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getComplete());
+
+            // fourth period [2023-04-03 to 2023-05-03] regular installment
+            assertEquals(4, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getFromDate());
+            assertEquals(LocalDate.of(2023, 05, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getDueDate());
+            assertEquals(625.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getTotalInstallmentAmountForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getTotalPaidForPeriod());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getComplete());
+
+            // make another repayment of 250 and verify downpayment 
installment gets paid off on 3rd april
+            final PostLoansLoanIdTransactionsResponse repaymentTransaction_2 = 
loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
+                    new PostLoansLoanIdTransactionsRequest().dateFormat("dd 
MMMM yyyy").transactionDate("3 April 2023").locale("en")
+                            .transactionAmount(250.00));
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // verify down payment installment gets paid off
+
+            // first period [2023-03-03 to 2023-03-03] first down payment 
installment
+            assertEquals(1, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalPaidForPeriod());
+            assertEquals(true, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getComplete());
+
+            // second period [2023-03-03 to 2023-04-03] regular installment, 
penalty and fee charges gets paid first and
+            // then principal
+            assertEquals(2, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+            assertEquals(500.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
+            assertEquals(515.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalPaidForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalOutstandingForPeriod());
+            assertEquals(5.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFeeChargesDue());
+            assertEquals(5.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFeeChargesPaid());
+            assertEquals(10.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPenaltyChargesDue());
+            assertEquals(10.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPenaltyChargesPaid());
+            assertEquals(true, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getComplete());
+
+            // third period [2023-04-03 to 2023-04-03] down payment 
installment gets paid
+            assertEquals(3, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getDueDate());
+            assertEquals(250.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalInstallmentAmountForPeriod());
+            assertEquals(250.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalPaidForPeriod());
+            assertEquals(true, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getComplete());
+
+            // fourth period [2023-04-03 to 2023-05-03] regular installment
+            assertEquals(4, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getFromDate());
+            assertEquals(LocalDate.of(2023, 05, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getDueDate());
+            assertEquals(625.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getTotalInstallmentAmountForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getTotalPaidForPeriod());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getComplete());
+
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+        }
+
+    }
+
+    @Test
+    public void 
loanAccountWithEnableDownPaymentAndDisableAutoRepaymentForDownPaymentWithOverlappingInstallmentPaymentAllocationTest()
 {
+        try {
+
+            // Test with
+            // Enable Down Payment
+            // Disable Auto Repayment For Down Payment
+            // Payment Strategy 
INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY =
+            // "interest-principal-penalties-fees-order-strategy"
+            // Overlapping down payment and regular installment
+
+            // Set business date
+            LocalDate disbursementDate = LocalDate.of(2023, 03, 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();
+
+            // Loan Product creation with down-payment configuration
+            final GetLoanProductsProductIdResponse 
getLoanProductsProductResponse = 
createLoanProductWithEnableDownPaymentAndMultipleDisbursements(
+                    loanTransactionHelper, enableDownPayment, "25", 
enableAutoRepaymentForDownPayment);
+
+            assertNotNull(getLoanProductsProductResponse);
+            assertEquals(enableDownPayment, 
getLoanProductsProductResponse.getEnableDownPayment());
+            assertEquals(0, 
getLoanProductsProductResponse.getDisbursedAmountPercentageForDownPayment()
+                    .compareTo(disbursedAmountPercentageForDownPayment));
+            assertEquals(enableAutoRepaymentForDownPayment, 
getLoanProductsProductResponse.getEnableAutoRepaymentForDownPayment());
+
+            // create loan account with 
"interest-principal-penalties-fees-order-strategy"
+
+            final Integer loanId = 
createLoanAccountMultipleRepaymentsDisbursement(clientId, 
getLoanProductsProductResponse.getId(),
+                    loanExternalIdStr, 
LoanProductTestBuilder.INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY);
+
+            // add charge PENALTY with due date as overlapping installment due 
date
+
+            Integer penalty = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                    
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", true));
+
+            LocalDate targetDate = LocalDate.of(2023, 4, 3);
+            final String penaltyCharge1AddedDate = 
DATE_FORMATTER.format(targetDate);
+
+            Integer penalty1LoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                    
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty),
 penaltyCharge1AddedDate, "10"));
+
+            assertNotNull(penalty1LoanChargeId);
+
+            // add charge FEE with due date as overlapping installment due date
+            Integer fee = ChargesHelper.createCharges(requestSpec, 
responseSpec,
+                    
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", false));
+
+            targetDate = LocalDate.of(2023, 4, 3);
+            final String feeCharge1AddedDate = 
DATE_FORMATTER.format(targetDate);
+
+            Integer feeLoanChargeId = 
this.loanTransactionHelper.addChargesForLoan(loanId,
+                    
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee),
 feeCharge1AddedDate, "5.15"));
+
+            assertNotNull(feeLoanChargeId);
+
+            // 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, "500");
+
+            // make repayment on 3rd March
+            final PostLoansLoanIdTransactionsResponse repaymentTransaction = 
loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
+                    new PostLoansLoanIdTransactionsRequest().dateFormat("dd 
MMMM yyyy").transactionDate("3 March 2023").locale("en")
+                            .transactionAmount(125.0));
+
+            // verify loan schedule
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // first period [2023-03-03 to 2023-03-03] down payment installment
+            assertEquals(1, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalPaidForPeriod());
+            assertEquals(true, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getComplete());
+
+            // second period [2023-03-03 to 2023-04-03] regular installment
+            assertEquals(2, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getDueDate());
+            assertEquals(166.67, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalInstallmentAmountForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getTotalPaidForPeriod());
+            assertEquals(5.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFeeChargesDue());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getFeeChargesPaid());
+            assertEquals(10.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPenaltyChargesDue());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getPenaltyChargesPaid());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(2).getComplete());
+
+            // third period [2023-04-03 to 2023-05-03] regular installment
+            assertEquals(3, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+            assertEquals(LocalDate.of(2023, 05, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+            assertEquals(208.33, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalPaidForPeriod());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getComplete());
+
+            // second disbursement with overlapping installment i.e same due 
date as regular repayment due date
+
+            disbursementDate = LocalDate.of(2023, 04, 3);
+            BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.BUSINESS_DATE, disbursementDate);
+            loanTransactionHelper.disburseLoanWithTransactionAmount("03 April 
2023", loanId, "1000");
+
+            // 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(250.0));
+
+            // verify loan schedule
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // verify that overlapping down payment installment is created 
after regular installment
+
+            // and verify regular repayment type installment gets paid before 
down payment installment according to
+            // payment
+
+            // allocation strategy
+
+            // first period [2023-03-03 to 2023-03-03] first down payment 
installment
+            assertEquals(1, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalPaidForPeriod());
+            assertEquals(true, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getComplete());
+
+            // second period [2023-03-03 to 2023-04-03] regular installment, 
principal gets paid first
+            assertEquals(2, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+            assertEquals(500.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
+            assertEquals(250.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalPaidForPeriod());
+            assertEquals(250.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPrincipalPaid());
+            assertEquals(265.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalOutstandingForPeriod());
+            assertEquals(5.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFeeChargesDue());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFeeChargesPaid());
+            assertEquals(10.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPenaltyChargesDue());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPenaltyChargesPaid());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getComplete());
+
+            // third period [2023-04-03 to 2023-04-03] overlapping down 
payment installment
+            assertEquals(3, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getDueDate());
+            assertEquals(250.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalInstallmentAmountForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalPaidForPeriod());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getComplete());
+
+            // fourth period [2023-04-03 to 2023-05-03] regular installment
+            assertEquals(4, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getFromDate());
+            assertEquals(LocalDate.of(2023, 05, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getDueDate());
+            assertEquals(625.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getTotalInstallmentAmountForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getTotalPaidForPeriod());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getComplete());
+
+            // make repayment for fully paying and verify that regular 
installment gets fully paid on 3rd april
+
+            final PostLoansLoanIdTransactionsResponse repaymentTransaction_2 = 
loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
+                    new PostLoansLoanIdTransactionsRequest().dateFormat("dd 
MMMM yyyy").transactionDate("3 April 2023").locale("en")
+                            .transactionAmount(265.15));
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // verify regular installment gets paid off
+
+            // first period [2023-03-03 to 2023-03-03] first down payment 
installment
+            assertEquals(1, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalPaidForPeriod());
+            assertEquals(true, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getComplete());
+
+            // second period [2023-03-03 to 2023-04-03] regular installment, 
principal gets paid first
+            assertEquals(2, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+            assertEquals(500.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
+            assertEquals(515.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalPaidForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalOutstandingForPeriod());
+            assertEquals(500.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPrincipalPaid());
+            assertEquals(5.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFeeChargesDue());
+            assertEquals(5.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFeeChargesPaid());
+            assertEquals(10.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPenaltyChargesDue());
+            assertEquals(10.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPenaltyChargesPaid());
+            assertEquals(true, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getComplete());
+
+            // third period [2023-04-03 to 2023-04-03] overlapping down 
payment installment
+            assertEquals(3, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getDueDate());
+            assertEquals(250.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalInstallmentAmountForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalPaidForPeriod());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getComplete());
+
+            // fourth period [2023-04-03 to 2023-05-03] regular installment
+            assertEquals(4, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getFromDate());
+            assertEquals(LocalDate.of(2023, 05, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getDueDate());
+            assertEquals(625.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getTotalInstallmentAmountForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getTotalPaidForPeriod());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getComplete());
+
+            // make another repayment of 250 and verify downpayment 
installment gets paid off on 3rd april
+            final PostLoansLoanIdTransactionsResponse repaymentTransaction_3 = 
loanTransactionHelper.makeLoanRepayment(loanExternalIdStr,
+                    new PostLoansLoanIdTransactionsRequest().dateFormat("dd 
MMMM yyyy").transactionDate("3 April 2023").locale("en")
+                            .transactionAmount(250.00));
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // verify down payment installment gets paid off
+
+            // first period [2023-03-03 to 2023-03-03] first down payment 
installment
+            assertEquals(1, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getFromDate());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getDueDate());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalInstallmentAmountForPeriod());
+            assertEquals(125.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalPaidForPeriod());
+            assertEquals(true, 
loanDetails.getRepaymentSchedule().getPeriods().get(1).getComplete());
+
+            // second period [2023-03-03 to 2023-04-03] regular installment, 
principal gets paid first
+            assertEquals(2, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPeriod());
+            assertEquals(LocalDate.of(2023, 03, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getDueDate());
+            assertEquals(500.00, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalInstallmentAmountForPeriod());
+            assertEquals(515.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalPaidForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getTotalOutstandingForPeriod());
+            assertEquals(5.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFeeChargesDue());
+            assertEquals(5.15, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getFeeChargesPaid());
+            assertEquals(10.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPenaltyChargesDue());
+            assertEquals(10.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getPenaltyChargesPaid());
+            assertEquals(true, 
loanDetails.getRepaymentSchedule().getPeriods().get(3).getComplete());
+
+            // third period [2023-04-03 to 2023-04-03] down payment 
installment gets paid
+            assertEquals(3, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getFromDate());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getDueDate());
+            assertEquals(250.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalInstallmentAmountForPeriod());
+            assertEquals(250.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getTotalPaidForPeriod());
+            assertEquals(true, 
loanDetails.getRepaymentSchedule().getPeriods().get(4).getComplete());
+
+            // fourth period [2023-04-03 to 2023-05-03] regular installment
+            assertEquals(4, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getPeriod());
+            assertEquals(LocalDate.of(2023, 04, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getFromDate());
+            assertEquals(LocalDate.of(2023, 05, 3), 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getDueDate());
+            assertEquals(625.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getTotalInstallmentAmountForPeriod());
+            assertEquals(0.0, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getTotalPaidForPeriod());
+            assertEquals(false, 
loanDetails.getRepaymentSchedule().getPeriods().get(5).getComplete());
+
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+        }
+
+    }
+
+    private Integer createLoanAccountMultipleRepaymentsDisbursement(final 
Integer clientID, final Long loanProductID,
+            final String externalId, final String repaymentStartegy) {
+
+        String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("3")
+                
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("3").withRepaymentEveryAfter("1")
+                
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance()
+                
.withAmortizationTypeAsEqualPrincipalPayments().withInterestCalculationPeriodTypeSameAsRepaymentPeriod()
+                .withExpectedDisbursementDate("03 March 
2023").withSubmittedOnDate("03 March 2023").withLoanType("individual")
+                
.withExternalId(externalId).withRepaymentStrategy(repaymentStartegy)
+                .build(clientID.toString(), loanProductID.toString(), null);
+
+        final Integer loanId = 
loanTransactionHelper.getLoanId(loanApplicationJSON);
+        loanTransactionHelper.approveLoan("03 March 2023", "1000", loanId, 
null);
+        return loanId;
+    }
+
+    private GetLoanProductsProductIdResponse 
createLoanProductWithEnableDownPaymentAndMultipleDisbursements(
+            LoanTransactionHelper loanTransactionHelper, Boolean 
enableDownPayment, String disbursedAmountPercentageForDownPayment,
+            boolean enableAutoRepaymentForDownPayment) {
+        final String loanProductJSON = new 
LoanProductTestBuilder().withPrincipal("1000").withRepaymentTypeAsMonth()
+                
.withRepaymentAfterEvery("1").withNumberOfRepayments("3").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0")
+                
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualPrincipalPayment().withInterestTypeAsDecliningBalance()
+                
.withInterestCalculationPeriodTypeAsRepaymentPeriod(true).withDaysInMonth("30").withDaysInYear("365")
+                .withMoratorium("0", 
"0").withMultiDisburse().withDisallowExpectedDisbursements(true)
+                .withEnableDownPayment(enableDownPayment, 
disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment)
+                .build(null);
+        final Integer loanProductId = 
loanTransactionHelper.getLoanProductId(loanProductJSON);
+        return loanTransactionHelper.getLoanProduct(loanProductId);
+    }
+
+    private void checkDownPaymentTransaction(final LocalDate transactionDate, 
final Float principalPortion, final Float interestPortion,
+            final Float feePortion, final Float penaltyPortion, final Integer 
loanID) {
+        ArrayList<HashMap> transactions = (ArrayList<HashMap>) 
loanTransactionHelper.getLoanTransactions(this.requestSpec,
+                this.responseSpec, loanID);
+        boolean isTransactionFound = false;
+        for (int i = 0; i < transactions.size(); i++) {
+            HashMap transactionType = (HashMap) 
transactions.get(i).get("type");
+            boolean isDownPaymentTransaction = (Boolean) 
transactionType.get("downPayment");
+
+            if (isDownPaymentTransaction) {
+                ArrayList<Integer> downPaymentDateAsArray = 
(ArrayList<Integer>) transactions.get(i).get("date");
+                LocalDate downPaymentEntryDate = 
LocalDate.of(downPaymentDateAsArray.get(0), downPaymentDateAsArray.get(1),
+                        downPaymentDateAsArray.get(2));
+
+                if (transactionDate.isEqual(downPaymentEntryDate)) {
+                    isTransactionFound = true;
+                    assertEquals(principalPortion, 
Float.valueOf(String.valueOf(transactions.get(i).get("principalPortion"))),
+                            "Mismatch in transaction amounts");
+                    assertEquals(interestPortion, 
Float.valueOf(String.valueOf(transactions.get(i).get("interestPortion"))),
+                            "Mismatch in transaction amounts");
+                    assertEquals(feePortion, 
Float.valueOf(String.valueOf(transactions.get(i).get("feeChargesPortion"))),
+                            "Mismatch in transaction amounts");
+                    assertEquals(penaltyPortion, 
Float.valueOf(String.valueOf(transactions.get(i).get("penaltyChargesPortion"))),
+                            "Mismatch in transaction amounts");
+                    break;
+                }
+            }
+        }
+        assertTrue(isTransactionFound, "No Down Payment entries are posted");
+    }
+}

Reply via email to