From 8457b31c0f7594f742c3a25fe9534a79f0c3ae51 Mon Sep 17 00:00:00 2001 From: Ruchi Dhamankar Date: Mon, 12 Dec 2022 13:55:02 +0530 Subject: [PATCH] FINERACT-1829-Store-Overpaid-Date-for-Loan --- .../api/LoansApiResourceSwagger.java | 2 + .../loanaccount/data/LoanAccountData.java | 24 ++- .../portfolio/loanaccount/domain/Loan.java | 16 +- .../service/LoanReadPlatformServiceImpl.java | 5 +- .../db/changelog/tenant/changelog-tenant.xml | 1 + .../parts/0077_add_overpaid_date_for_loan.xml | 32 +++ .../LoanAccountOverpaidDateStatusTest.java | 189 ++++++++++++++++++ 7 files changed, 255 insertions(+), 14 deletions(-) create mode 100644 fineract-provider/src/main/resources/db/changelog/tenant/parts/0077_add_overpaid_date_for_loan.xml create mode 100644 integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountOverpaidDateStatusTest.java diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java index fa32ec58a..0e25608b9 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/api/LoansApiResourceSwagger.java @@ -1005,6 +1005,8 @@ final class LoansApiResourceSwagger { @Schema(example = "250.000000") public Double totalOverpaid; public LocalDate lastClosedBusinessDate; + @Schema(example = "[2013, 11, 1]") + public LocalDate overpaidOnDate; } @Schema(description = "GetLoansResponse") diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java index caf3dc0f8..4ddca73c0 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/data/LoanAccountData.java @@ -247,6 +247,7 @@ public class LoanAccountData { private String linkAccountId; private Long groupId; private LocalDate expectedDisbursementDate; + private LocalDate overpaidOnDate; private CollectionData delinquent; private DelinquencyRangeData delinquencyRange; @@ -379,7 +380,7 @@ public class LoanAccountData { .setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled) .setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent) .setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements) - .setFraud(acc.fraud); + .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate); } /** @@ -450,7 +451,7 @@ public class LoanAccountData { .setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled) .setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent) .setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements) - .setFraud(acc.fraud); + .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate); } public static LoanAccountData loanProductWithTemplateDefaults(final LoanProductData product, @@ -617,7 +618,8 @@ public class LoanAccountData { .setClosureLoanId(acc.closureLoanId).setClosureLoanAccountNo(acc.closureLoanAccountNo).setTopupAmount(acc.topupAmount) .setIsEqualAmortization(product.isEqualAmortization()).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled) .setFixedPrincipalPercentagePerInstallment(product.getFixedPrincipalPercentagePerInstallment()).setDelinquent(delinquent) - .setDisallowExpectedDisbursements(product.getDisallowExpectedDisbursements()).setFraud(acc.fraud); + .setDisallowExpectedDisbursements(product.getDisallowExpectedDisbursements()).setFraud(acc.fraud) + .setOverpaidOnDate(acc.overpaidOnDate); } /* @@ -650,7 +652,7 @@ public class LoanAccountData { final boolean canUseForTopup, final boolean isTopup, final Long closureLoanId, final String closureLoanAccountNo, final BigDecimal topupAmount, final boolean isEqualAmortization, final BigDecimal fixedPrincipalPercentagePerInstallment, final DelinquencyRangeData delinquencyRange, final boolean disallowExpectedDisbursements, final boolean fraud, - LocalDate lastClosedBusinessDate) { + LocalDate lastClosedBusinessDate, LocalDate overpaidOnDate) { final CollectionData delinquent = CollectionData.template(); @@ -690,7 +692,7 @@ public class LoanAccountData { .setClosureLoanAccountNo(closureLoanAccountNo).setTopupAmount(topupAmount).setIsEqualAmortization(isEqualAmortization) .setFixedPrincipalPercentagePerInstallment(fixedPrincipalPercentagePerInstallment).setDelinquent(delinquent) .setDelinquencyRange(delinquencyRange).setDisallowExpectedDisbursements(disallowExpectedDisbursements).setFraud(fraud) - .setLastClosedBusinessDate(lastClosedBusinessDate); + .setLastClosedBusinessDate(lastClosedBusinessDate).setOverpaidOnDate(overpaidOnDate); } /* @@ -774,7 +776,7 @@ public class LoanAccountData { .setIsEqualAmortization(acc.isEqualAmortization) .setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment) .setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements) - .setFraud(acc.fraud).setLastClosedBusinessDate(acc.getLastClosedBusinessDate()); + .setFraud(acc.fraud).setLastClosedBusinessDate(acc.getLastClosedBusinessDate()).setOverpaidOnDate(acc.overpaidOnDate); } public static LoanAccountData associationsAndTemplate(final LoanAccountData acc, final Collection productOptions, @@ -849,7 +851,7 @@ public class LoanAccountData { .setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled) .setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent) .setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements) - .setFraud(acc.fraud); + .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate); } public static LoanAccountData associateMemberVariations(final LoanAccountData acc, final Map memberLoanCycle) { @@ -946,7 +948,7 @@ public class LoanAccountData { .setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled) .setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent) .setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements) - .setFraud(acc.fraud); + .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate); } public static LoanAccountData withInterestRecalculationCalendarData(final LoanAccountData acc, final CalendarData calendarData, @@ -1011,7 +1013,7 @@ public class LoanAccountData { .setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled) .setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent) .setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements) - .setFraud(acc.fraud); + .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate); } public static LoanAccountData withLoanCalendarData(final LoanAccountData acc, final CalendarData calendarData) { @@ -1069,7 +1071,7 @@ public class LoanAccountData { .setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled) .setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent) .setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements) - .setFraud(acc.fraud); + .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate); } public static LoanAccountData withOriginalSchedule(final LoanAccountData acc, final LoanScheduleData originalSchedule) { @@ -1130,7 +1132,7 @@ public class LoanAccountData { .setIsEqualAmortization(acc.isEqualAmortization).setRates(acc.rates).setIsRatesEnabled(acc.isRatesEnabled) .setFixedPrincipalPercentagePerInstallment(acc.fixedPrincipalPercentagePerInstallment).setDelinquent(acc.delinquent) .setDelinquencyRange(acc.delinquencyRange).setDisallowExpectedDisbursements(acc.disallowExpectedDisbursements) - .setFraud(acc.fraud); + .setFraud(acc.fraud).setOverpaidOnDate(acc.overpaidOnDate); } public static final Comparator ClientNameComparator = (loan1, loan2) -> { diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index ac4b0a997..e6b3f10ad 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -314,6 +314,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { @Column(name = "total_overpaid_derived", scale = 6, precision = 19) private BigDecimal totalOverpaid; + @Column(name = "overpaidon_date") + private LocalDate overpaidOnDate; + @Column(name = "loan_counter") private Integer loanCounter; @@ -3410,6 +3413,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { handleLoanRepaymentInFull(transactionDate, loanLifecycleStateMachine); statusChanged = true; } + if (this.totalOverpaid == null || BigDecimal.ZERO.compareTo(this.totalOverpaid) == 0) { + this.overpaidOnDate = null; + } return statusChanged; } @@ -3429,6 +3435,9 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { this.closedOnDate = transactionDate; this.actualMaturityDate = transactionDate; } else if (LoanStatus.fromInt(this.loanStatus).isOverpaid()) { + if (this.totalOverpaid == null || BigDecimal.ZERO.compareTo(this.totalOverpaid) == 0) { + this.overpaidOnDate = null; + } loanLifecycleStateMachine.transition(LoanEvent.LOAN_REPAYMENT_OR_WAIVER, this); } processIncomeAccrualTransactionOnLoanClosure(); @@ -3512,7 +3521,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { private void handleLoanOverpayment(final LoanLifecycleStateMachine loanLifecycleStateMachine) { loanLifecycleStateMachine.transition(LoanEvent.LOAN_OVERPAYMENT, this); - + this.overpaidOnDate = DateUtils.getBusinessLocalDate(); this.closedOnDate = null; this.actualMaturityDate = null; } @@ -5280,6 +5289,10 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { return this.totalOverpaid; } + public LocalDate getOverpaidOnDate() { + return this.overpaidOnDate; + } + public void updateIsInterestRecalculationEnabled() { this.loanRepaymentScheduleDetail.updateIsInterestRecalculationEnabled(isInterestRecalculationEnabledForProduct()); } @@ -6086,6 +6099,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom { updateLoanSummaryDerivedFields(); if (this.totalOverpaid == null || BigDecimal.ZERO.compareTo(this.totalOverpaid) == 0) { + this.overpaidOnDate = null; defaultLoanLifecycleStateMachine.transition(LoanEvent.LOAN_CREDIT_BALANCE_REFUND, this); } 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 16ecd7028..530274e5c 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 @@ -661,7 +661,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { + " lpvi.minimum_gap as minimuminstallmentgap, lpvi.maximum_gap as maximuminstallmentgap, " + " lp.can_use_for_topup as canUseForTopup, l.is_topup as isTopup, topup.closure_loan_id as closureLoanId, " + " l.total_recovered_derived as totalRecovered, topuploan.account_no as closureLoanAccountNo, " - + " topup.topup_amount as topupAmount, l.last_closed_business_date as lastClosedBusinessDate from m_loan l" // + + " topup.topup_amount as topupAmount, l.last_closed_business_date as lastClosedBusinessDate,l.overpaidon_date as overpaidOnDate from m_loan l" // + " join m_product_loan lp on lp.id = l.product_id" // + " left join m_loan_recalculation_details lir on lir.loan_id = l.id join m_currency rc on rc." + sqlGenerator.escape("code") + " = l.currency_code" // @@ -988,6 +988,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { final boolean isFraud = rs.getBoolean("isFraud"); final LocalDate lastClosedBusinessDate = JdbcSupport.getLocalDate(rs, "lastClosedBusinessDate"); + final LocalDate overpaidOnDate = JdbcSupport.getLocalDate(rs, "overpaidOnDate"); return LoanAccountData.basicLoanDetails(id, accountNo, status, externalId, clientId, clientAccountNo, clientName, clientOfficeId, clientExternalId, groupData, loanType, loanProductId, loanProductName, loanProductDescription, @@ -1004,7 +1005,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService { createStandingInstructionAtDisbursement, isvariableInstallmentsAllowed, minimumGap, maximumGap, loanSubStatus, canUseForTopup, isTopup, closureLoanId, closureLoanAccountNo, topupAmount, isEqualAmortization, fixedPrincipalPercentagePerInstallment, delinquencyRange, disallowExpectedDisbursements, isFraud, - lastClosedBusinessDate); + lastClosedBusinessDate, overpaidOnDate); } } diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml index 19b969f99..fd5e528f8 100644 --- a/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml +++ b/fineract-provider/src/main/resources/db/changelog/tenant/changelog-tenant.xml @@ -96,4 +96,5 @@ + diff --git a/fineract-provider/src/main/resources/db/changelog/tenant/parts/0077_add_overpaid_date_for_loan.xml b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0077_add_overpaid_date_for_loan.xml new file mode 100644 index 000000000..b40a2b43c --- /dev/null +++ b/fineract-provider/src/main/resources/db/changelog/tenant/parts/0077_add_overpaid_date_for_loan.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountOverpaidDateStatusTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountOverpaidDateStatusTest.java new file mode 100644 index 000000000..544388edd --- /dev/null +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountOverpaidDateStatusTest.java @@ -0,0 +1,189 @@ +/** + * 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.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +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.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.util.HashMap; +import java.util.UUID; +import org.apache.fineract.client.models.GetDelinquencyBucketsResponse; +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.loans.LoanApplicationTestBuilder; +import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder; +import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper; +import org.apache.fineract.integrationtests.common.products.DelinquencyBucketsHelper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class LoanAccountOverpaidDateStatusTest { + + private ResponseSpecification responseSpec; + private ResponseSpecification responseSpecErr400; + private ResponseSpecification responseSpecErr503; + private RequestSpecification requestSpec; + private ClientHelper clientHelper; + private LoanTransactionHelper loanTransactionHelper; + private DateTimeFormatter dateFormatter = 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.responseSpecErr400 = new ResponseSpecBuilder().expectStatusCode(400).build(); + this.responseSpecErr503 = new ResponseSpecBuilder().expectStatusCode(503).build(); + this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec); + this.clientHelper = new ClientHelper(this.requestSpec, this.responseSpec); + } + + @Test + public void loanOverpaidDateStatusTest() { + // Set business date + final LocalDate todaysDate = Utils.getLocalDateOfTenant(); + GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE); + BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, todaysDate); + + // Loan ExternalId + String loanExternalIdStr = UUID.randomUUID().toString(); + + // Delinquency Bucket + final Integer delinquencyBucketId = DelinquencyBucketsHelper.createDelinquencyBucket(requestSpec, responseSpec); + final GetDelinquencyBucketsResponse delinquencyBucket = DelinquencyBucketsHelper.getDelinquencyBucket(requestSpec, responseSpec, + delinquencyBucketId); + + // Client and Loan account creation + + final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue(); + final GetLoanProductsProductIdResponse getLoanProductsProductResponse = createLoanProduct(loanTransactionHelper, + delinquencyBucketId); + assertNotNull(getLoanProductsProductResponse); + + final Integer loanId = createLoanAccount(clientId, getLoanProductsProductResponse.getId(), loanExternalIdStr); + + // make Repayments + final PostLoansLoanIdTransactionsResponse repaymentTransaction_1 = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr, + new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("5 September 2022").locale("en") + .transactionAmount(200.0)); + + final PostLoansLoanIdTransactionsResponse repaymentTransaction_2 = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr, + new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("6 September 2022").locale("en") + .transactionAmount(200.0)); + + final PostLoansLoanIdTransactionsResponse repaymentTransaction_3 = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr, + new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("7 September 2022").locale("en") + .transactionAmount(500.0)); + + // make repayment to make loan overpaid + final PostLoansLoanIdTransactionsResponse repaymentTransaction_4 = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr, + new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("9 September 2022").locale("en") + .transactionAmount(200.0)); + + // check loan overpaid date is not null and is set as Business date and loan status + GetLoansLoanIdResponse loanDetailsOverpaid = loanTransactionHelper.getLoanDetails((long) loanId); + assertTrue(loanDetailsOverpaid.getStatus().getOverpaid()); + assertNotNull(loanDetailsOverpaid.getOverpaidOnDate()); + assertEquals(loanDetailsOverpaid.getOverpaidOnDate(), todaysDate); + + // reverse repayment to make loan not overpaid and overpaid date is reset + loanTransactionHelper.reverseRepayment(loanId, repaymentTransaction_4.getResourceId().intValue(), "10 September 2022"); + GetLoansLoanIdResponse loanDetailsNotOverpaidAfterReversal = loanTransactionHelper.getLoanDetails((long) loanId); + assertFalse(loanDetailsNotOverpaidAfterReversal.getStatus().getOverpaid()); + assertNull(loanDetailsNotOverpaidAfterReversal.getOverpaidOnDate()); + + // make repayment to make loan overpaid again + final PostLoansLoanIdTransactionsResponse repaymentTransaction_5 = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr, + new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("11 September 2022").locale("en") + .transactionAmount(200.0)); + + // check loan overpaid date is not null and is set as Business date and loan status + GetLoansLoanIdResponse loanDetailsOverpaid_1 = loanTransactionHelper.getLoanDetails((long) loanId); + assertTrue(loanDetailsOverpaid_1.getStatus().getOverpaid()); + assertNotNull(loanDetailsOverpaid_1.getOverpaidOnDate()); + assertEquals(loanDetailsOverpaid_1.getOverpaidOnDate(), todaysDate); + + // Credit balance refund to reset overpaid status + loanTransactionHelper.creditBalanceRefund("12 September 2022", Float.valueOf(100), null, loanId, ""); + GetLoansLoanIdResponse loanDetailsNotOverpaidAfterCBR = loanTransactionHelper.getLoanDetails((long) loanId); + assertFalse(loanDetailsNotOverpaidAfterCBR.getStatus().getOverpaid()); + assertNull(loanDetailsNotOverpaidAfterCBR.getOverpaidOnDate()); + + // reverse repayment to make loan active again + loanTransactionHelper.reverseRepayment(loanId, repaymentTransaction_2.getResourceId().intValue(), "13 September 2022"); + GetLoansLoanIdResponse loanDetailsNotOverpaidAfterReversal_1 = loanTransactionHelper.getLoanDetails((long) loanId); + assertFalse(loanDetailsNotOverpaidAfterReversal_1.getStatus().getOverpaid()); + assertNull(loanDetailsNotOverpaidAfterReversal_1.getOverpaidOnDate()); + + // make repayment to make loan overpaid again + final PostLoansLoanIdTransactionsResponse repaymentTransaction_6 = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr, + new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("14 September 2022").locale("en") + .transactionAmount(300.0)); + + // check loan overpaid date is not null and is set as Business date and loan status + GetLoansLoanIdResponse loanDetailsOverpaid_3 = loanTransactionHelper.getLoanDetails((long) loanId); + assertTrue(loanDetailsOverpaid_3.getStatus().getOverpaid()); + assertNotNull(loanDetailsOverpaid_3.getOverpaidOnDate()); + assertEquals(loanDetailsOverpaid_3.getOverpaidOnDate(), todaysDate); + + GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE); + + } + + private GetLoanProductsProductIdResponse createLoanProduct(final LoanTransactionHelper loanTransactionHelper, + final Integer delinquencyBucketId) { + final HashMap loanProductMap = new LoanProductTestBuilder().build(null, delinquencyBucketId); + final Integer loanProductId = loanTransactionHelper.getLoanProductId(Utils.convertToJson(loanProductMap)); + return loanTransactionHelper.getLoanProduct(loanProductId); + } + + private Integer createLoanAccount(final Integer clientID, final Long loanProductID, final String externalId) { + + String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("1000").withLoanTermFrequency("1") + .withLoanTermFrequencyAsMonths().withNumberOfRepayments("1").withRepaymentEveryAfter("1") + .withRepaymentFrequencyTypeAsMonths().withInterestRatePerPeriod("0").withInterestTypeAsFlatBalance() + .withAmortizationTypeAsEqualPrincipalPayments().withInterestCalculationPeriodTypeSameAsRepaymentPeriod() + .withExpectedDisbursementDate("03 September 2022").withSubmittedOnDate("01 September 2022").withLoanType("individual") + .withExternalId(externalId).build(clientID.toString(), loanProductID.toString(), null); + + final Integer loanId = loanTransactionHelper.getLoanId(loanApplicationJSON); + loanTransactionHelper.approveLoan("02 September 2022", "1000", loanId, null); + loanTransactionHelper.disburseLoanWithNetDisbursalAmount("03 September 2022", loanId, "1000"); + return loanId; + } +}