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 7e893eac7 FINERACT-1958-Loan-repayment-calculation
7e893eac7 is described below

commit 7e893eac780ebaf19b14a5a3d826ecf633e83f32
Author: Ruchi Dhamankar <[email protected]>
AuthorDate: Wed Sep 27 21:02:39 2023 +0530

    FINERACT-1958-Loan-repayment-calculation
---
 .../organisation/monetary/domain/Money.java        |   9 +
 .../loanschedule/domain/LoanApplicationTerms.java  |   4 +
 .../domain/AbstractLoanScheduleGenerator.java      |  19 +-
 .../LoanAccountRepaymentCalculationTest.java       | 447 +++++++++++++++++++++
 4 files changed, 477 insertions(+), 2 deletions(-)

diff --git 
a/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/Money.java
 
b/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/Money.java
index 6f955882e..b8e2fc0fe 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/Money.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/organisation/monetary/domain/Money.java
@@ -120,6 +120,15 @@ public class Money implements Comparable<Money> {
         return amountScaled;
     }
 
+    public static BigDecimal roundToMultiplesOf(final BigDecimal existingVal, 
final Integer inMultiplesOf) {
+        BigDecimal amountScaled = existingVal;
+        BigDecimal inMultiplesOfValue = 
BigDecimal.valueOf(inMultiplesOf.intValue());
+        if (inMultiplesOfValue.compareTo(BigDecimal.ZERO) > 0) {
+            amountScaled = existingVal.divide(inMultiplesOfValue, 0, 
RoundingMode.HALF_UP).multiply(inMultiplesOfValue);
+        }
+        return amountScaled;
+    }
+
     public static double ceiling(final double n, final double s) {
         double c;
 
diff --git 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
index 6a48d8d2d..4ea52809d 100644
--- 
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
+++ 
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/LoanApplicationTerms.java
@@ -1774,4 +1774,8 @@ public final class LoanApplicationTerms {
     public boolean isScheduleExtensionForDownPaymentDisabled() {
         return isScheduleExtensionForDownPaymentDisabled;
     }
+
+    public Integer getInstallmentAmountInMultiplesOf() {
+        return installmentAmountInMultiplesOf;
+    }
 }
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 0290c404f..d1633a7a4 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
@@ -141,6 +141,11 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             if (loanApplicationTerms.isDownPaymentEnabled()) {
                 downPaymentAmount = 
MathUtil.percentageOf(scheduleParams.getOutstandingBalance().getAmount(),
                         
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), 19);
+                if (loanApplicationTerms.getInstallmentAmountInMultiplesOf() 
!= null) {
+                    downPaymentAmount = 
Money.roundToMultiplesOf(downPaymentAmount,
+                            
loanApplicationTerms.getInstallmentAmountInMultiplesOf());
+                }
+
             }
             Money calculatedAmortizableAmount = 
loanApplicationTerms.getPrincipal().minus(downPaymentAmount);
             scheduleParams.setOutstandingBalance(calculatedAmortizableAmount);
@@ -155,6 +160,9 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                 if (loanApplicationTerms.isDownPaymentEnabled()) {
                     downPaymentAmt = MathUtil.percentageOf(disburseAmt, 
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(),
                             19);
+                    if 
(loanApplicationTerms.getInstallmentAmountInMultiplesOf() != null) {
+                        downPaymentAmt = 
Money.roundToMultiplesOf(downPaymentAmt, 
loanApplicationTerms.getInstallmentAmountInMultiplesOf());
+                    }
                 }
                 BigDecimal remainingPrincipalAmt = 
disburseAmt.subtract(downPaymentAmt);
                 scheduleParams.setPrincipalToBeScheduled(Money.of(currency, 
remainingPrincipalAmt));
@@ -2022,6 +2030,9 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
             LoanScheduleParams scheduleParams, LocalDate date, BigDecimal 
periodBaseAmount) {
         BigDecimal downPaymentAmount = MathUtil.percentageOf(periodBaseAmount,
                 
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), 19);
+        if (loanApplicationTerms.getInstallmentAmountInMultiplesOf() != null) {
+            downPaymentAmount = Money.roundToMultiplesOf(downPaymentAmount, 
loanApplicationTerms.getInstallmentAmountInMultiplesOf());
+        }
         Money downPayment = Money.of(loanApplicationTerms.getCurrency(), 
downPaymentAmount);
         LoanScheduleModelDownPaymentPeriod installment = 
LoanScheduleModelDownPaymentPeriod
                 .downPayment(scheduleParams.getInstalmentNumber(), date, 
downPayment, scheduleParams.getOutstandingBalance());
@@ -2238,8 +2249,12 @@ public abstract class AbstractLoanScheduleGenerator 
implements LoanScheduleGener
                     : loanApplicationTerms.getSubmittedOnDate();
             BigDecimal downPaymentAmount = BigDecimal.ZERO;
             if (loanApplicationTerms.isDownPaymentEnabled()) {
-                downPaymentAmount = 
MathUtil.percentageOf(loanScheduleParams.getOutstandingBalance().getAmount(),
-                        
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), 19);
+                double downPaymentAmt = 
MathUtil.percentageOf(loanScheduleParams.getOutstandingBalance().getAmount(),
+                        
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), 
19).doubleValue();
+                if (loanApplicationTerms.getInstallmentAmountInMultiplesOf() 
!= null) {
+                    downPaymentAmt = Money.roundToMultiplesOf(downPaymentAmt, 
loanApplicationTerms.getInstallmentAmountInMultiplesOf());
+                }
+                downPaymentAmount = BigDecimal.valueOf(downPaymentAmt);
             }
             Money calculatedAmortizableAmount = 
principalToBeScheduled.minus(downPaymentAmount);
             
loanScheduleParams.setOutstandingBalance(calculatedAmortizableAmount);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountRepaymentCalculationTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountRepaymentCalculationTest.java
new file mode 100644
index 000000000..cb0c4a53e
--- /dev/null
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountRepaymentCalculationTest.java
@@ -0,0 +1,447 @@
+/**
+ * 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 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.util.UUID;
+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.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.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 LoanAccountRepaymentCalculationTest {
+
+    private ResponseSpecification responseSpec;
+    private RequestSpecification requestSpec;
+    private LoanTransactionHelper loanTransactionHelper;
+    private ClientHelper clientHelper;
+
+    @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 
loanAccountWithEnableDownPaymentWithInstallmentsInMultipleOfNullRepaymentScheduleCalculationTest()
 {
+        try {
+
+            // 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();
+
+            // Loan Product creation with down-payment configuration and 
installmentAmountInMultiplesOf as null
+            final GetLoanProductsProductIdResponse 
getLoanProductsProductResponse = 
createLoanProductWithEnableDownPaymentAndMultipleDisbursements(
+                    loanTransactionHelper, enableDownPayment, "25", 
enableAutoRepaymentForDownPayment, null);
+
+            assertNotNull(getLoanProductsProductResponse);
+            assertEquals(enableDownPayment, 
getLoanProductsProductResponse.getEnableDownPayment());
+            assertEquals(0, 
getLoanProductsProductResponse.getDisbursedAmountPercentageForDownPayment()
+                    .compareTo(disbursedAmountPercentageForDownPayment));
+            assertEquals(enableAutoRepaymentForDownPayment, 
getLoanProductsProductResponse.getEnableAutoRepaymentForDownPayment());
+
+            // create loan account with amount 1250
+
+            final Integer loanId = 
createLoanAccountMultipleRepaymentsDisbursement(clientId, 
getLoanProductsProductResponse.getId(), "1250",
+                    loanExternalIdStr, 
LoanProductTestBuilder.DEFAULT_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());
+
+            // verify loan schedule
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // first period [2023-03-03 to 2023-03-03] down payment installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 
312.5, 1, LocalDate.of(2023, 3, 3),
+                    LocalDate.of(2023, 3, 3), false);
+
+            // second period [2023-03-03 to 2023-04-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(2), 
312.5, 2, LocalDate.of(2023, 3, 3),
+                    LocalDate.of(2023, 4, 3), false);
+
+            // third period [2023-04-03 to 2023-05-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(3), 
312.5, 3, LocalDate.of(2023, 4, 3),
+                    LocalDate.of(2023, 5, 3), false);
+
+            // fourth period [2023-05-03 to 2023-06-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 
312.5, 4, LocalDate.of(2023, 5, 3),
+                    LocalDate.of(2023, 6, 3), false);
+
+            // disbursement
+            loanTransactionHelper.disburseLoanWithTransactionAmount("03 March 
2023", loanId, "1250");
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // first period [2023-03-03 to 2023-03-03] down payment installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 
312.5, 1, LocalDate.of(2023, 3, 3),
+                    LocalDate.of(2023, 3, 3), false);
+
+            // second period [2023-03-03 to 2023-04-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(2), 
312.5, 2, LocalDate.of(2023, 3, 3),
+                    LocalDate.of(2023, 4, 3), false);
+
+            // third period [2023-04-03 to 2023-05-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(3), 
312.5, 3, LocalDate.of(2023, 4, 3),
+                    LocalDate.of(2023, 5, 3), false);
+
+            // fourth period [2023-05-03 to 2023-06-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 
312.5, 4, LocalDate.of(2023, 5, 3),
+                    LocalDate.of(2023, 6, 3), false);
+
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+        }
+
+    }
+
+    @Test
+    public void 
loanAccountWithEnableDownPaymentWithInstallmentsInMultipleOfSetAsOneRepaymentScheduleCalculationTest()
 {
+        try {
+
+            // 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();
+
+            // Loan Product creation with down-payment configuration and 
installmentAmountInMultiplesOf as 1
+            final GetLoanProductsProductIdResponse 
getLoanProductsProductResponse = 
createLoanProductWithEnableDownPaymentAndMultipleDisbursements(
+                    loanTransactionHelper, enableDownPayment, "25", 
enableAutoRepaymentForDownPayment, "1");
+
+            assertNotNull(getLoanProductsProductResponse);
+            assertEquals(enableDownPayment, 
getLoanProductsProductResponse.getEnableDownPayment());
+            assertEquals(0, 
getLoanProductsProductResponse.getDisbursedAmountPercentageForDownPayment()
+                    .compareTo(disbursedAmountPercentageForDownPayment));
+            assertEquals(enableAutoRepaymentForDownPayment, 
getLoanProductsProductResponse.getEnableAutoRepaymentForDownPayment());
+
+            // create loan account with amount 1250
+
+            final Integer loanId = 
createLoanAccountMultipleRepaymentsDisbursement(clientId, 
getLoanProductsProductResponse.getId(), "1250",
+                    loanExternalIdStr, 
LoanProductTestBuilder.DEFAULT_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());
+
+            // verify loan schedule
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // first period [2023-03-03 to 2023-03-03] down payment installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 
313.0, 1, LocalDate.of(2023, 3, 3),
+                    LocalDate.of(2023, 3, 3), false);
+
+            // second period [2023-03-03 to 2023-04-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(2), 
312.0, 2, LocalDate.of(2023, 3, 3),
+                    LocalDate.of(2023, 4, 3), false);
+
+            // third period [2023-04-03 to 2023-05-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(3), 
312.0, 3, LocalDate.of(2023, 4, 3),
+                    LocalDate.of(2023, 5, 3), false);
+
+            // fourth period [2023-05-03 to 2023-06-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 
313.0, 4, LocalDate.of(2023, 5, 3),
+                    LocalDate.of(2023, 6, 3), false);
+
+            // disbursement
+            loanTransactionHelper.disburseLoanWithTransactionAmount("03 March 
2023", loanId, "1250");
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // first period [2023-03-03 to 2023-03-03] down payment installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 
313.0, 1, LocalDate.of(2023, 3, 3),
+                    LocalDate.of(2023, 3, 3), false);
+
+            // second period [2023-03-03 to 2023-04-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(2), 
312.0, 2, LocalDate.of(2023, 3, 3),
+                    LocalDate.of(2023, 4, 3), false);
+
+            // third period [2023-04-03 to 2023-05-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(3), 
312.0, 3, LocalDate.of(2023, 4, 3),
+                    LocalDate.of(2023, 5, 3), false);
+
+            // fourth period [2023-05-03 to 2023-06-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(4), 
313.0, 4, LocalDate.of(2023, 5, 3),
+                    LocalDate.of(2023, 6, 3), false);
+
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+        }
+
+    }
+
+    @Test
+    public void 
loanAccountWithDisableDownPaymentWithInstallmentsInMultipleOfNullRepaymentScheduleCalculationTest()
 {
+        try {
+
+            // 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 = false;
+
+            final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+
+            // Loan Product creation with down-payment configuration and 
installmentAmountInMultiplesOf as null
+            final GetLoanProductsProductIdResponse 
getLoanProductsProductResponse = 
createLoanProductWithEnableDownPaymentAndMultipleDisbursements(
+                    loanTransactionHelper, enableDownPayment, null, false, 
null);
+
+            assertNotNull(getLoanProductsProductResponse);
+            assertEquals(enableDownPayment, 
getLoanProductsProductResponse.getEnableDownPayment());
+
+            // create loan account with amount 1250
+
+            final Integer loanId = 
createLoanAccountMultipleRepaymentsDisbursement(clientId, 
getLoanProductsProductResponse.getId(), "1250",
+                    loanExternalIdStr, 
LoanProductTestBuilder.DEFAULT_STRATEGY);
+
+            // Retrieve Loan with loanId
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            // verify down-payment details for Loan
+            assertNotNull(loanDetails);
+            assertEquals(enableDownPayment, 
loanDetails.getEnableDownPayment());
+
+            // verify loan schedule
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // first period [2023-03-03 to 2023-04-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 
416.67, 1, LocalDate.of(2023, 3, 3),
+                    LocalDate.of(2023, 4, 3), false);
+
+            // second period [2023-04-03 to 2023-05-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(2), 
416.67, 2, LocalDate.of(2023, 4, 3),
+                    LocalDate.of(2023, 5, 3), false);
+
+            // third period [2023-05-03 to 2023-06-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(3), 
416.66, 3, LocalDate.of(2023, 5, 3),
+                    LocalDate.of(2023, 6, 3), false);
+
+            // disbursement
+            loanTransactionHelper.disburseLoanWithTransactionAmount("03 March 
2023", loanId, "1250");
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // first period [2023-03-03 to 2023-04-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 
416.67, 1, LocalDate.of(2023, 3, 3),
+                    LocalDate.of(2023, 4, 3), false);
+
+            // second period [2023-04-03 to 2023-05-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(2), 
416.67, 2, LocalDate.of(2023, 4, 3),
+                    LocalDate.of(2023, 5, 3), false);
+
+            // third period [2023-05-03 to 2023-06-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(3), 
416.66, 3, LocalDate.of(2023, 5, 3),
+                    LocalDate.of(2023, 6, 3), false);
+
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+        }
+
+    }
+
+    @Test
+    public void 
loanAccountWithDisableDownPaymentWithInstallmentsInMultipleOfSetAsOneRepaymentScheduleCalculationTest()
 {
+        try {
+
+            // 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 = false;
+
+            final Integer clientId = 
clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue();
+
+            // Loan Product creation with down-payment configuration and 
installmentAmountInMultiplesOf as 1
+            final GetLoanProductsProductIdResponse 
getLoanProductsProductResponse = 
createLoanProductWithEnableDownPaymentAndMultipleDisbursements(
+                    loanTransactionHelper, enableDownPayment, null, false, 
"1");
+
+            assertNotNull(getLoanProductsProductResponse);
+            assertEquals(enableDownPayment, 
getLoanProductsProductResponse.getEnableDownPayment());
+
+            // create loan account with amount 1250
+
+            final Integer loanId = 
createLoanAccountMultipleRepaymentsDisbursement(clientId, 
getLoanProductsProductResponse.getId(), "1250",
+                    loanExternalIdStr, 
LoanProductTestBuilder.DEFAULT_STRATEGY);
+
+            // Retrieve Loan with loanId
+
+            GetLoansLoanIdResponse loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            // verify down-payment details for Loan
+            assertNotNull(loanDetails);
+            assertEquals(enableDownPayment, 
loanDetails.getEnableDownPayment());
+
+            // verify loan schedule
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // first period [2023-03-03 to 2023-04-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 
417.0, 1, LocalDate.of(2023, 3, 3),
+                    LocalDate.of(2023, 4, 3), false);
+
+            // second period [2023-04-03 to 2023-05-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(2), 
417.0, 2, LocalDate.of(2023, 4, 3),
+                    LocalDate.of(2023, 5, 3), false);
+
+            // third period [2023-05-03 to 2023-06-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(3), 
416.0, 3, LocalDate.of(2023, 5, 3),
+                    LocalDate.of(2023, 6, 3), false);
+
+            // disbursement
+            loanTransactionHelper.disburseLoanWithTransactionAmount("03 March 
2023", loanId, "1250");
+
+            loanDetails = 
loanTransactionHelper.getLoanDetails(loanId.longValue());
+
+            assertNotNull(loanDetails.getRepaymentSchedule());
+
+            // first period [2023-03-03 to 2023-04-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(1), 
417.0, 1, LocalDate.of(2023, 3, 3),
+                    LocalDate.of(2023, 4, 3), false);
+
+            // second period [2023-04-03 to 2023-05-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(2), 
417.0, 2, LocalDate.of(2023, 4, 3),
+                    LocalDate.of(2023, 5, 3), false);
+
+            // third period [2023-05-03 to 2023-06-03] regular installment
+            
verifyPeriodDetails(loanDetails.getRepaymentSchedule().getPeriods().get(3), 
416.0, 3, LocalDate.of(2023, 5, 3),
+                    LocalDate.of(2023, 6, 3), false);
+
+        } finally {
+            GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+        }
+
+    }
+
+    private void verifyPeriodDetails(GetLoansLoanIdRepaymentPeriod period, 
double expectedAmount, int expectedPeriodNumber,
+            LocalDate expectedPeriodFromDate, LocalDate expectedPeriodDueDate, 
boolean isComplete) {
+        assertEquals(expectedPeriodNumber, period.getPeriod());
+        assertEquals(expectedPeriodFromDate, period.getFromDate());
+        assertEquals(expectedPeriodDueDate, period.getDueDate());
+        assertEquals(expectedAmount, 
period.getTotalInstallmentAmountForPeriod());
+        assertEquals(isComplete, period.getComplete());
+    }
+
+    private Integer createLoanAccountMultipleRepaymentsDisbursement(final 
Integer clientID, final Long loanProductID,
+            final String principalAmount, final String externalId, final 
String repaymentStartegy) {
+
+        String loanApplicationJSON = new 
LoanApplicationTestBuilder().withPrincipal(principalAmount).withLoanTermFrequency("3")
+                
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("3").withRepaymentEveryAfter("1")
+                
.withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("0").withInterestTypeAsDecliningBalance()
+                
.withAmortizationTypeAsEqualInstallments().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", "1250", loanId, 
null);
+        return loanId;
+    }
+
+    private GetLoanProductsProductIdResponse 
createLoanProductWithEnableDownPaymentAndMultipleDisbursements(
+            LoanTransactionHelper loanTransactionHelper, Boolean 
enableDownPayment, String disbursedAmountPercentageForDownPayment,
+            boolean enableAutoRepaymentForDownPayment, String 
installmentAmountInMultiplesOf) {
+        final String loanProductJSON = new 
LoanProductTestBuilder().withPrincipal("1000").withRepaymentTypeAsMonth()
+                
.withRepaymentAfterEvery("1").withNumberOfRepayments("3").withRepaymentTypeAsMonth().withinterestRatePerPeriod("0")
+                
.withInterestRateFrequencyTypeAsMonths().withInterestTypeAsDecliningBalance().withAmortizationTypeAsEqualInstallments()
+                
.withInterestCalculationPeriodTypeAsRepaymentPeriod(true).withDaysInMonth("30").withDaysInYear("365")
+                .withMoratorium("0", 
"0").withMultiDisburse().withDisallowExpectedDisbursements(true)
+                .withEnableDownPayment(enableDownPayment, 
disbursedAmountPercentageForDownPayment, enableAutoRepaymentForDownPayment)
+                
.withInstallmentAmountInMultiplesOf(installmentAmountInMultiplesOf).build(null);
+        final Integer loanProductId = 
loanTransactionHelper.getLoanProductId(loanProductJSON);
+        return loanTransactionHelper.getLoanProduct(loanProductId);
+    }
+
+}

Reply via email to