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);
+ }
+
+}