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

arnold 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 8457b31c0  FINERACT-1829-Store-Overpaid-Date-for-Loan
8457b31c0 is described below

commit 8457b31c0f7594f742c3a25fe9534a79f0c3ae51
Author: Ruchi Dhamankar <[email protected]>
AuthorDate: Mon Dec 12 13:55:02 2022 +0530

     FINERACT-1829-Store-Overpaid-Date-for-Loan
---
 .../loanaccount/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(-)

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<LoanProductData> 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<Long, Integer> 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<LoanAccountData> 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 @@
     <include file="parts/0074_add_last_cob_business_date_column_to_loan.xml" 
relativeToChangelogFile="true"/>
     <include file="parts/0075_add_processed_commands_purge_job.xml" 
relativeToChangelogFile="true" />
     <include file="parts/0076_add_loan_transaction_enum_values.xml" 
relativeToChangelogFile="true" />
+    <include file="parts/0077_add_overpaid_date_for_loan.xml" 
relativeToChangelogFile="true" />
 </databaseChangeLog>
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog";
+                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+                   
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog 
http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.3.xsd";>
+    <changeSet author="fineract" id="1">
+        <addColumn tableName="m_loan">
+            <column name="overpaidon_date" type="date">
+                <constraints nullable="true"/>
+            </column>
+        </addColumn>
+    </changeSet>
+</databaseChangeLog>
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<String, Object> 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;
+    }
+}

Reply via email to