FINERACT-1450-fix (#2177)

This commit is contained in:
Manoj
2022-03-28 22:41:36 +05:30
committed by GitHub
parent 35d0c4fbce
commit bc0b5c00bb
7 changed files with 317 additions and 18 deletions
@@ -177,7 +177,6 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
isFirstRepayment = false;
}
while (!scheduleParams.getOutstandingBalance().isZero() || !scheduleParams.getDisburseDetailMap().isEmpty()) {
LocalDate previousRepaymentDate = scheduleParams.getActualRepaymentDate();
scheduleParams.setActualRepaymentDate(this.scheduledDateGenerator
@@ -2196,16 +2195,32 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
actualRepaymentDate = lastInstallmentDate;
}
isFirstRepayment = false;
LocalDate prevLastInstDate = lastInstallmentDate;
lastInstallmentDate = this.scheduledDateGenerator
.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms, holidayDetailDTO).getChangedScheduleDate();
LocalDate modifiedLastInstDate = null;
LoanTermVariationsData variation1 = null;
while (loanApplicationTerms.getLoanTermVariations().hasDueDateVariation(lastInstallmentDate)) {
LoanTermVariationsData variation = loanApplicationTerms.getLoanTermVariations().nextDueDateVariation();
if (!variation.isSpecificToInstallment()) {
actualRepaymentDate = variation.getDateValue();
lastInstallmentDate = actualRepaymentDate;
modifiedLastInstDate = variation.getDateValue();
variation1 = variation;
}
dueDateVariationsDataList.add(variation);
}
if (!lastInstallmentDate.isEqual(installment.getDueDate())
&& !installment.getDueDate().equals(modifiedLastInstDate)) {
lastInstallmentDate = prevLastInstDate;
actualRepaymentDate = lastInstallmentDate;
if (modifiedLastInstDate != null) {
loanApplicationTerms.getLoanTermVariations().previousDueDateVariation();
}
} else if (installment.getDueDate().equals(modifiedLastInstDate)) {
actualRepaymentDate = modifiedLastInstDate;
lastInstallmentDate = actualRepaymentDate;
dueDateVariationsDataList.add(variation1);
}
loanTermVariationParams = applyExceptionLoanTermVariations(loanApplicationTerms, lastInstallmentDate,
exceptionDataListIterator, instalmentNumber, totalCumulativePrincipal, totalCumulativeInterest, mc);
} while (loanTermVariationParams != null && loanTermVariationParams.isSkipPeriod());
@@ -338,11 +338,12 @@ public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
generatedDate = loanApplicationTerms.getNewScheduledDueDateStart();
isFirstRepayment = false;
}
while (!generatedDate.isAfter(lastRepaymentDate)) {
LocalDate adjustedDate = generatedDate;
while (!adjustedDate.isAfter(lastRepaymentDate)) {
generatedDate = generateNextRepaymentDate(generatedDate, loanApplicationTerms, isFirstRepayment);
adjustedDate = adjustRepaymentDate(generatedDate, loanApplicationTerms, holidayDetailDTO).getChangedScheduleDate();
isFirstRepayment = false;
}
generatedDate = adjustRepaymentDate(generatedDate, loanApplicationTerms, holidayDetailDTO).getChangedScheduleDate();
return generatedDate;
return adjustedDate;
}
}
@@ -185,8 +185,8 @@ public class LoanRescheduleOnDecliningBalanceLoanTest {
LOG.info("Sucessfully created loan (ID: {} )", this.loanId);
this.approveLoanApplication();
this.disburseLoan();
this.approveLoanApplication(this.dateString);
this.disburseLoan(this.dateString);
}
private void addCollaterals(List<HashMap> collaterals, Integer collateralId, BigDecimal quantity) {
@@ -203,10 +203,10 @@ public class LoanRescheduleOnDecliningBalanceLoanTest {
/**
* approve the loan application
**/
private void approveLoanApplication() {
private void approveLoanApplication(String approveDate) {
if (this.loanId != null) {
this.loanTransactionHelper.approveLoan(this.dateString, this.loanId);
this.loanTransactionHelper.approveLoan(approveDate, this.loanId);
LOG.info("Successfully approved loan (ID: {} )", this.loanId);
}
}
@@ -214,11 +214,11 @@ public class LoanRescheduleOnDecliningBalanceLoanTest {
/**
* disburse the newly created loan
**/
private void disburseLoan() {
private void disburseLoan(String disburseDate) {
if (this.loanId != null) {
String loanDetails = this.loanTransactionHelper.getLoanDetails(this.requestSpec, this.responseSpec, this.loanId);
this.loanTransactionHelper.disburseLoan(this.dateString, this.loanId,
this.loanTransactionHelper.disburseLoan(disburseDate, this.loanId,
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
LOG.info("Successfully disbursed loan (ID: {} )", this.loanId);
}
@@ -485,8 +485,8 @@ public class LoanRescheduleOnDecliningBalanceLoanTest {
LOG.info("Sucessfully created loan (ID: {} )", this.loanId);
this.approveLoanApplication();
this.disburseLoan();
this.approveLoanApplication(this.dateString);
this.disburseLoan(this.dateString);
}
/**
@@ -529,5 +529,4 @@ public class LoanRescheduleOnDecliningBalanceLoanTest {
LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);
}
}
@@ -0,0 +1,258 @@
/**
* 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.assertTrue;
import io.restassured.builder.RequestSpecBuilder;
import io.restassured.builder.ResponseSpecBuilder;
import io.restassured.http.ContentType;
import io.restassured.path.json.JsonPath;
import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
import org.apache.fineract.integrationtests.common.LoanRescheduleRequestHelper;
import org.apache.fineract.integrationtests.common.Utils;
import org.apache.fineract.integrationtests.common.WorkingDaysHelper;
import org.apache.fineract.integrationtests.common.loans.LoanApplicationTestBuilder;
import org.apache.fineract.integrationtests.common.loans.LoanProductTestBuilder;
import org.apache.fineract.integrationtests.common.loans.LoanRescheduleRequestTestBuilder;
import org.apache.fineract.integrationtests.common.loans.LoanTransactionHelper;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LoanRescheduleWithAdvancePaymentTest {
private static final Logger LOG = LoggerFactory.getLogger(LoanRescheduleWithAdvancePaymentTest.class);
private ResponseSpecification responseSpec;
private ResponseSpecification generalResponseSpec;
private RequestSpecification requestSpec;
private LoanTransactionHelper loanTransactionHelper;
private LoanRescheduleRequestHelper loanRescheduleRequestHelper;
private Integer clientId;
private Integer loanProductId;
private Integer loanId;
private Integer loanRescheduleRequestId;
private final String loanPrincipalAmount = "100000.00";
private final String numberOfRepayments = "12";
private final String interestRatePerPeriod = "18";
@BeforeEach
public void initialize() {
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.loanRescheduleRequestHelper = new LoanRescheduleRequestHelper(this.requestSpec, this.responseSpec);
this.generalResponseSpec = new ResponseSpecBuilder().build();
}
@AfterEach
public void tearDown() {
disableConfig();
}
/**
* enables the configuration `is-interest-to-be-recovered-first-when-greater-than-emi`
**/
private void enableConfig() {
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "42", true);
}
/**
* disables the configuration `is-interest-to-be-recovered-first-when-greater-than-emi`
**/
private void disableConfig() {
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "42", false);
}
/**
* enables the configuration `is-principal-compounding-disabled-for-overdue-loans`
**/
private void enablePrincipalCompoundingConfig() {
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "43", true);
}
/**
* disables the configuration `is-principal-compounding-disabled-for-overdue-loans`
**/
private void disablePrincipalCompoundingConfig() {
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "43", false);
}
/**
* approve the loan application
**/
private void approveLoanApplication(String approveDate) {
if (this.loanId != null) {
this.loanTransactionHelper.approveLoan(approveDate, this.loanId);
LOG.info("Successfully approved loan (ID: {} )", this.loanId);
}
}
/**
* disburse the newly created loan
**/
private void disburseLoan(String disburseDate) {
if (this.loanId != null) {
String loanDetails = this.loanTransactionHelper.getLoanDetails(this.requestSpec, this.responseSpec, this.loanId);
this.loanTransactionHelper.disburseLoan(disburseDate, this.loanId,
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
LOG.info("Successfully disbursed loan (ID: {} )", this.loanId);
}
}
/* FINERACT-1450 */
@Test
public void testRescheduleAfterLatePayment() {
this.enableConfig();
this.enablePrincipalCompoundingConfig();
WorkingDaysHelper.updateWorkingDaysWeekDays(this.requestSpec, this.responseSpec);
// create all required entities
this.createRequiredEntitiesWithLatePayment();
this.createApproveLoanRescheduleRequestAfterLatePayment();
WorkingDaysHelper.updateWorkingDays(this.requestSpec, this.responseSpec);
this.disablePrincipalCompoundingConfig();
this.disableConfig();
}
/**
* create a new client
**/
private void createClientEntity() {
this.clientId = ClientHelper.createClient(this.requestSpec, this.responseSpec);
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, this.clientId);
}
private void createRequiredEntitiesWithLatePayment() {
this.createClientEntity();
this.createLoanProductWithInterestRecalculation();
this.createLoanEntityWithEntitiesForTestResceduleWithLatePayment();
}
private void createLoanProductWithInterestRecalculation() {
LOG.info(
"---------------------------------CREATING LOAN PRODUCT WITH RECALULATION ENABLED ------------------------------------------");
final String interestRecalculationCompoundingMethod = LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE;
final String rescheduleStrategyMethod = LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS;
final String recalculationRestFrequencyType = LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_DAILY;
final String recalculationRestFrequencyInterval = "0";
final String preCloseInterestCalculationStrategy = LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE;
final String recalculationCompoundingFrequencyType = null;
final String recalculationCompoundingFrequencyInterval = null;
final Integer recalculationCompoundingFrequencyOnDayType = null;
final Integer recalculationCompoundingFrequencyDayOfWeekType = null;
final Integer recalculationRestFrequencyOnDayType = null;
final Integer recalculationRestFrequencyDayOfWeekType = null;
final String loanProductJSON = new LoanProductTestBuilder().withPrincipal(loanPrincipalAmount)
.withNumberOfRepayments(numberOfRepayments).withinterestRatePerPeriod(interestRatePerPeriod)
.withInterestRateFrequencyTypeAsYear().withInterestTypeAsDecliningBalance().withInterestCalculationPeriodTypeAsDays()
.withInterestRecalculationDetails(interestRecalculationCompoundingMethod, rescheduleStrategyMethod,
preCloseInterestCalculationStrategy)
.withInterestRecalculationRestFrequencyDetails(recalculationRestFrequencyType, recalculationRestFrequencyInterval,
recalculationRestFrequencyOnDayType, recalculationRestFrequencyDayOfWeekType)
.withInterestRecalculationCompoundingFrequencyDetails(recalculationCompoundingFrequencyType,
recalculationCompoundingFrequencyInterval, recalculationCompoundingFrequencyOnDayType,
recalculationCompoundingFrequencyDayOfWeekType)
.build(null);
this.loanProductId = this.loanTransactionHelper.getLoanProductId(loanProductJSON);
LOG.info("Successfully created loan product (ID:{}) ", this.loanProductId);
}
private void createLoanEntityWithEntitiesForTestResceduleWithLatePayment() {
String firstRepaymentDate = "14 June 2021";
String submittedDate = "10 May 2021";
LOG.info("---------------------------------NEW LOAN APPLICATION------------------------------------------");
final String loanApplicationJSON = new LoanApplicationTestBuilder().withPrincipal("15000").withLoanTermFrequency("12")
.withLoanTermFrequencyAsMonths().withNumberOfRepayments("12").withRepaymentEveryAfter("1")
.withRepaymentFrequencyTypeAsMonths().withAmortizationTypeAsEqualInstallments().withInterestCalculationPeriodTypeAsDays()
.withInterestRatePerPeriod("12").withInterestTypeAsDecliningBalance().withSubmittedOnDate(submittedDate)
.withExpectedDisbursementDate(submittedDate).withFirstRepaymentDate(firstRepaymentDate)
.withwithRepaymentStrategy(LoanApplicationTestBuilder.INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY)
.withinterestChargedFromDate(submittedDate).build(this.clientId.toString(), this.loanProductId.toString(), null);
this.loanId = this.loanTransactionHelper.getLoanId(loanApplicationJSON);
LOG.info("Sucessfully created loan (ID: {} )", this.loanId);
this.approveLoanApplication(submittedDate);
this.disburseLoan(submittedDate);
}
private void createApproveLoanRescheduleRequestAfterLatePayment() {
LOG.info("-------------Make repayment 1-----------");
this.loanTransactionHelper.makeRepayment("14 June 2021", Float.parseFloat("1331.58"), loanId);
LOG.info("-------------Make repayment 2-----------");
this.loanTransactionHelper.makeRepayment("15 July 2021", Float.parseFloat("1331.58"), loanId);
LOG.info(
"---------------------------------CREATING LOAN RESCHEDULE REQUEST FOR LOAN WITH RECALCULATION------------------------------------");
final String requestJSON = new LoanRescheduleRequestTestBuilder().updateGraceOnPrincipal(null).updateGraceOnInterest(null)
.updateExtraTerms(null).updateRescheduleFromDate("16 August 2021").updateAdjustedDueDate("31 August 2021")
.updateRecalculateInterest(false).updateSubmittedOnDate("16 August 2022").build(this.loanId.toString());
LOG.info("Reschedule request : {}", requestJSON);
this.loanRescheduleRequestId = this.loanRescheduleRequestHelper.createLoanRescheduleRequest(requestJSON);
this.loanRescheduleRequestHelper.verifyCreationOfLoanRescheduleRequest(this.loanRescheduleRequestId);
LOG.info("Successfully created loan reschedule request (ID: {} )", this.loanRescheduleRequestId);
final String aproveRequestJSON = new LoanRescheduleRequestTestBuilder().updateSubmittedOnDate("16 August 2022")
.getApproveLoanRescheduleRequestJSON();
this.loanRescheduleRequestHelper.approveLoanRescheduleRequest(this.loanRescheduleRequestId, aproveRequestJSON);
final HashMap response = (HashMap) this.loanRescheduleRequestHelper.getLoanRescheduleRequest(loanRescheduleRequestId, "statusEnum");
assertTrue((Boolean) response.get("approved"));
LOG.info("Successfully approved loan reschedule request (ID: {})", this.loanRescheduleRequestId);
final Map repaymentSchedule = (Map) this.loanTransactionHelper.getLoanDetailExcludeFutureSchedule(requestSpec, generalResponseSpec,
this.loanId, "repaymentSchedule");
LOG.info("Repayment Schedule for id {} : {}", this.loanId, repaymentSchedule);
final ArrayList periods = (ArrayList) repaymentSchedule.get("periods");
HashMap period = (HashMap) periods.get(4);
LOG.info("period {}", period);
assertEquals(new ArrayList<>(Arrays.asList(2021, 8, 31)), period.get("dueDate"), "Checking for Due Date for 1st Month");
}
}
@@ -51,6 +51,24 @@ public final class WorkingDaysHelper {
jsonAttributeToGetback);
}
public static Object updateWorkingDaysWeekDays(final RequestSpecification requestSpec, final ResponseSpecification responseSpec) {
final String UPDATE_WORKINGDAYS_URL = WORKINGDAYS_URL + "?" + Utils.TENANT_IDENTIFIER;
LOG.info("---------------------------------UPDATE WORKINGDAY---------------------------------------------");
return Utils.performServerPut(requestSpec, responseSpec, UPDATE_WORKINGDAYS_URL, updateWorkingWeekDaysDaysAsJson(), "");
}
@SuppressFBWarnings(value = {
"DMI_RANDOM_USED_ONLY_ONCE" }, justification = "False positive for random object created and used only once")
public static String updateWorkingWeekDaysDaysAsJson() {
final HashMap<String, Object> map = new HashMap<>();
map.put("recurrence", "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR");
map.put("locale", "en");
map.put("repaymentRescheduleType", 2);
map.put("extendTermForDailyRepayments", false);
LOG.info("map : {}", map);
return new Gson().toJson(map);
}
@SuppressFBWarnings(value = {
"DMI_RANDOM_USED_ONLY_ONCE" }, justification = "False positive for random object created and used only once")
public static String updateWorkingDaysAsJson() {
@@ -56,8 +56,9 @@ public class LoanRescheduleRequestTestBuilder {
if (extraTerms != null) {
map.put("extraTerms", extraTerms);
}
map.put("recalculateInterest", recalculateInterest);
if (recalculateInterest) {
map.put("recalculateInterest", recalculateInterest);
}
if (newInterestRate != null) {
map.put("newInterestRate", newInterestRate);
@@ -144,6 +144,13 @@ public class LoanTransactionHelper {
return Utils.performServerGet(requestSpec, responseSpec, URL, param);
}
public Object getLoanDetailExcludeFutureSchedule(final RequestSpecification requestSpec, final ResponseSpecification responseSpec,
final Integer loanID, final String param) {
final String URL = "/fineract-provider/api/v1/loans/" + loanID + "?associations=all&exclude=guarantors,futureSchedule&"
+ Utils.TENANT_IDENTIFIER;
return Utils.performServerGet(requestSpec, responseSpec, URL, param);
}
public String getLoanDetails(final RequestSpecification requestSpec, final ResponseSpecification responseSpec, final Integer loanID) {
final String URL = "/fineract-provider/api/v1/loans/" + loanID + "?associations=all&" + Utils.TENANT_IDENTIFIER;
return Utils.performServerGet(requestSpec, responseSpec, URL, null);