FINERACT-1748: Loan prepayment issue after advance payment
This commit is contained in:
committed by
Arnold Gálovics
parent
47c9b93d27
commit
ba1d0ce66c
+3
-1
@@ -2225,7 +2225,9 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
.adjustRepaymentDate(actualRepaymentDate, loanApplicationTerms, holidayDetailDTO).getChangedScheduleDate();
|
||||
LocalDate modifiedLastInstDate = null;
|
||||
LoanTermVariationsData variation1 = null;
|
||||
boolean hasDueDateVariation = false;
|
||||
while (loanApplicationTerms.getLoanTermVariations().hasDueDateVariation(lastInstallmentDate)) {
|
||||
hasDueDateVariation = true;
|
||||
LoanTermVariationsData variation = loanApplicationTerms.getLoanTermVariations().nextDueDateVariation();
|
||||
if (!variation.isSpecificToInstallment()) {
|
||||
modifiedLastInstDate = variation.getDateValue();
|
||||
@@ -2233,7 +2235,7 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
|
||||
}
|
||||
}
|
||||
|
||||
if (!lastInstallmentDate.isEqual(installment.getDueDate())
|
||||
if (hasDueDateVariation && !lastInstallmentDate.isEqual(installment.getDueDate())
|
||||
&& !installment.getDueDate().equals(modifiedLastInstDate)) {
|
||||
lastInstallmentDate = prevLastInstDate;
|
||||
actualRepaymentDate = lastInstallmentDate;
|
||||
|
||||
+207
@@ -45,6 +45,7 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||
import org.apache.fineract.integrationtests.common.CollateralManagementHelper;
|
||||
import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
|
||||
import org.apache.fineract.integrationtests.common.SchedulerJobHelper;
|
||||
import org.apache.fineract.integrationtests.common.Utils;
|
||||
import org.apache.fineract.integrationtests.common.accounting.Account;
|
||||
@@ -4712,6 +4713,82 @@ public class ClientLoanIntegrationTest {
|
||||
return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
}
|
||||
|
||||
private Integer createLoanProductWithInterestRecalculationAndCompoundingDetails(final String repaymentStrategy,
|
||||
final String interestRecalculationCompoundingMethod, final String rescheduleStrategyMethod,
|
||||
final String recalculationRestFrequencyType, final String preCloseInterestCalculationStrategy, final Account[] accounts,
|
||||
final String installmentMultipleOf) {
|
||||
final String recalculationCompoundingFrequencyType = null;
|
||||
final String recalculationCompoundingFrequencyInterval = null;
|
||||
final Integer recalculationCompoundingFrequencyOnDayType = null;
|
||||
final Integer recalculationCompoundingFrequencyDayOfWeekType = null;
|
||||
return createLoanProductWithInterestRecalculation(repaymentStrategy, interestRecalculationCompoundingMethod,
|
||||
rescheduleStrategyMethod, recalculationCompoundingFrequencyType, recalculationCompoundingFrequencyInterval,
|
||||
preCloseInterestCalculationStrategy, accounts, null, false, recalculationCompoundingFrequencyOnDayType,
|
||||
recalculationCompoundingFrequencyDayOfWeekType, installmentMultipleOf);
|
||||
}
|
||||
|
||||
private Integer createLoanProductWithInterestRecalculation(final String repaymentStrategy,
|
||||
final String interestRecalculationCompoundingMethod, final String rescheduleStrategyMethod,
|
||||
final String recalculationCompoundingFrequencyType, final String recalculationCompoundingFrequencyInterval,
|
||||
final String preCloseInterestCalculationStrategy, final Account[] accounts, final String chargeId,
|
||||
boolean isArrearsBasedOnOriginalSchedule, final Integer recalculationCompoundingFrequencyOnDayType,
|
||||
final Integer recalculationCompoundingFrequencyDayOfWeekType, final String installmentsMultiplesOf) {
|
||||
LOG.info("------------------------------CREATING NEW LOAN PRODUCT ---------------------------------------");
|
||||
LoanProductTestBuilder builder = new LoanProductTestBuilder().withPrincipal("10000.00").withNumberOfRepayments("12")
|
||||
.withRepaymentAfterEvery("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("19.9")
|
||||
.withInterestRateFrequencyTypeAsMonths().withRepaymentStrategy(repaymentStrategy).withAmortizationTypeAsEqualInstallments()
|
||||
.withInterestTypeAsDecliningBalance().withInterestCalculationPeriodTypeAsDays()
|
||||
.withInterestRecalculationDetails(interestRecalculationCompoundingMethod, rescheduleStrategyMethod,
|
||||
preCloseInterestCalculationStrategy)
|
||||
.withInterestRecalculationDetails(interestRecalculationCompoundingMethod, rescheduleStrategyMethod,
|
||||
preCloseInterestCalculationStrategy)
|
||||
.withInterestRecalculationCompoundingFrequencyDetails(recalculationCompoundingFrequencyType,
|
||||
recalculationCompoundingFrequencyInterval, recalculationCompoundingFrequencyOnDayType,
|
||||
recalculationCompoundingFrequencyDayOfWeekType)
|
||||
.withDefineInstallmentAmount(true).withInstallmentAmountInMultiplesOf(installmentsMultiplesOf);
|
||||
if (accounts != null) {
|
||||
builder = builder.withAccountingRulePeriodicAccrual(accounts);
|
||||
}
|
||||
|
||||
if (isArrearsBasedOnOriginalSchedule) {
|
||||
builder = builder.withArrearsConfiguration();
|
||||
}
|
||||
|
||||
final String loanProductJSON = builder.build(chargeId);
|
||||
return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||
}
|
||||
|
||||
private Integer applyForLoanApplicationForInterestRecalculation(final Integer clientID, final Integer loanProductID,
|
||||
final String disbursementDate, final String repaymentStrategy, final String firstRepaymentDate) {
|
||||
LOG.info("--------------------------------APPLYING FOR LOAN APPLICATION--------------------------------");
|
||||
final Integer collateralId = CollateralManagementHelper.createCollateralProduct(this.requestSpec, this.responseSpec);
|
||||
Assertions.assertNotNull(collateralId);
|
||||
List<HashMap> collaterals = new ArrayList<>();
|
||||
|
||||
final Integer clientCollateralId = CollateralManagementHelper.createClientCollateral(this.requestSpec, this.responseSpec,
|
||||
String.valueOf(clientID), collateralId);
|
||||
Assertions.assertNotNull(clientCollateralId);
|
||||
addCollaterals(collaterals, clientCollateralId, BigDecimal.valueOf(1));
|
||||
|
||||
final String loanApplicationJSON = new LoanApplicationTestBuilder() //
|
||||
.withPrincipal("10000.00") //
|
||||
.withLoanTermFrequency("12") //
|
||||
.withLoanTermFrequencyAsMonths() //
|
||||
.withNumberOfRepayments("12") //
|
||||
.withRepaymentEveryAfter("1") //
|
||||
.withLoanTermFrequencyAsMonths() //
|
||||
.withInterestRatePerPeriod("19.9") //
|
||||
.withAmortizationTypeAsEqualInstallments() //
|
||||
.withInterestTypeAsDecliningBalance() //
|
||||
.withInterestCalculationPeriodTypeAsDays() //
|
||||
.withExpectedDisbursementDate(disbursementDate) //
|
||||
.withSubmittedOnDate(disbursementDate) //
|
||||
.withwithRepaymentStrategy(repaymentStrategy).withRepaymentFrequencyTypeAsMonths()//
|
||||
.withFirstRepaymentDate(firstRepaymentDate).withCollaterals(collaterals)
|
||||
.build(clientID.toString(), loanProductID.toString(), null);
|
||||
return this.loanTransactionHelper.getLoanId(loanApplicationJSON);
|
||||
}
|
||||
|
||||
private Integer applyForLoanApplicationForInterestRecalculation(final Integer clientID, final Integer loanProductID,
|
||||
final String disbursementDate, final String repaymentStrategy, final List<HashMap> charges) {
|
||||
return applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, disbursementDate, repaymentStrategy, charges, null,
|
||||
@@ -5721,6 +5798,136 @@ public class ClientLoanIntegrationTest {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoanScheduleWithInterestRecalculationMakePrepaymentAfterRepayment() {
|
||||
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.US);
|
||||
dateFormat.setTimeZone(Utils.getTimeZoneOfTenant());
|
||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "42", true);
|
||||
Calendar startDate = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
||||
Calendar currentDate = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
||||
startDate.add(Calendar.MONTH, -8);
|
||||
|
||||
Calendar firstRepaymentDate = (Calendar) startDate.clone();
|
||||
firstRepaymentDate.add(Calendar.MONTH, 1);
|
||||
firstRepaymentDate.add(Calendar.DAY_OF_MONTH, firstRepaymentDate.getActualMaximum(Calendar.DAY_OF_MONTH) - Calendar.DAY_OF_MONTH);
|
||||
String firstRepayment = dateFormat.format(firstRepaymentDate.getTime());
|
||||
|
||||
final String loanDisbursementDate = dateFormat.format(startDate.getTime());
|
||||
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||
final Integer loanProductID = createLoanProductWithInterestRecalculationAndCompoundingDetails(
|
||||
LoanProductTestBuilder.INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY,
|
||||
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE,
|
||||
LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS,
|
||||
LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_SAME_AS_REPAYMENT_PERIOD,
|
||||
LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, null, "12");
|
||||
|
||||
final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, loanDisbursementDate,
|
||||
LoanApplicationTestBuilder.INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY, firstRepayment);
|
||||
|
||||
Assertions.assertNotNull(loanID);
|
||||
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||
|
||||
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------");
|
||||
loanStatusHashMap = this.loanTransactionHelper.approveLoan(loanDisbursementDate, loanID);
|
||||
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||
|
||||
LOG.info("-------------------------------DISBURSE LOAN-------------------------------------------");
|
||||
String loanDetails = this.loanTransactionHelper.getLoanDetails(this.requestSpec, this.responseSpec, loanID);
|
||||
loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(loanDisbursementDate, loanID,
|
||||
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
|
||||
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||
|
||||
ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID);
|
||||
Assertions.assertNotNull(loanSchedule);
|
||||
startDate.add(Calendar.DAY_OF_MONTH, 2);
|
||||
String loanFirstRepaymentDate = dateFormat.format(startDate.getTime());
|
||||
//
|
||||
Float earlyPayment = Float.parseFloat("3000");
|
||||
this.loanTransactionHelper.makeRepayment(loanFirstRepaymentDate, earlyPayment, loanID);
|
||||
|
||||
HashMap prepayDetail = this.loanTransactionHelper.getPrepayAmount(this.requestSpec, this.responseSpec, loanID);
|
||||
String prepayAmount = String.valueOf(prepayDetail.get("amount"));
|
||||
String loanPrepaymentDate = dateFormat.format(currentDate.getTime());
|
||||
this.loanTransactionHelper.makeRepayment(loanPrepaymentDate, Float.parseFloat(prepayAmount), loanID);
|
||||
loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||
LoanStatusChecker.verifyLoanAccountIsClosed(loanStatusHashMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoanScheduleWithInterestRecalculationMakeAdvancePaymentTillSettlement() {
|
||||
this.loanTransactionHelper = new LoanTransactionHelper(this.requestSpec, this.responseSpec);
|
||||
final ResponseSpecification errorResponse = new ResponseSpecBuilder().expectStatusCode(403).build();
|
||||
final LoanTransactionHelper validationErrorHelper = new LoanTransactionHelper(this.requestSpec, errorResponse);
|
||||
DateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy", Locale.US);
|
||||
dateFormat.setTimeZone(Utils.getTimeZoneOfTenant());
|
||||
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(this.requestSpec, this.responseSpec, "42", true);
|
||||
Calendar startDate = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
||||
Calendar currentDate = Calendar.getInstance(Utils.getTimeZoneOfTenant());
|
||||
startDate.add(Calendar.MONTH, -8);
|
||||
|
||||
Calendar firstRepaymentDate = (Calendar) startDate.clone();
|
||||
firstRepaymentDate.add(Calendar.MONTH, 1);
|
||||
firstRepaymentDate.add(Calendar.DAY_OF_MONTH, firstRepaymentDate.getActualMaximum(Calendar.DAY_OF_MONTH) - Calendar.DAY_OF_MONTH);
|
||||
String firstRepayment = dateFormat.format(firstRepaymentDate.getTime());
|
||||
|
||||
final String loanDisbursementDate = dateFormat.format(startDate.getTime());
|
||||
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec);
|
||||
ClientHelper.verifyClientCreatedOnServer(this.requestSpec, this.responseSpec, clientID);
|
||||
final Integer loanProductID = createLoanProductWithInterestRecalculationAndCompoundingDetails(
|
||||
LoanProductTestBuilder.INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY,
|
||||
LoanProductTestBuilder.RECALCULATION_COMPOUNDING_METHOD_NONE,
|
||||
LoanProductTestBuilder.RECALCULATION_STRATEGY_REDUCE_NUMBER_OF_INSTALLMENTS,
|
||||
LoanProductTestBuilder.RECALCULATION_FREQUENCY_TYPE_SAME_AS_REPAYMENT_PERIOD,
|
||||
LoanProductTestBuilder.INTEREST_APPLICABLE_STRATEGY_ON_PRE_CLOSE_DATE, null, "12");
|
||||
|
||||
final Integer loanID = applyForLoanApplicationForInterestRecalculation(clientID, loanProductID, loanDisbursementDate,
|
||||
LoanApplicationTestBuilder.INTEREST_PRINCIPAL_PENALTIES_FEES_ORDER_STRATEGY, firstRepayment);
|
||||
|
||||
Assertions.assertNotNull(loanID);
|
||||
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(this.requestSpec, this.responseSpec, loanID);
|
||||
LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
|
||||
|
||||
LOG.info("-----------------------------------APPROVE LOAN-----------------------------------------");
|
||||
loanStatusHashMap = this.loanTransactionHelper.approveLoan(loanDisbursementDate, loanID);
|
||||
LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
|
||||
LoanStatusChecker.verifyLoanIsWaitingForDisbursal(loanStatusHashMap);
|
||||
|
||||
LOG.info("-------------------------------DISBURSE LOAN-------------------------------------------");
|
||||
String loanDetails = this.loanTransactionHelper.getLoanDetails(this.requestSpec, this.responseSpec, loanID);
|
||||
loanStatusHashMap = this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount(loanDisbursementDate, loanID,
|
||||
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
|
||||
LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
|
||||
|
||||
ArrayList<HashMap> loanSchedule = this.loanTransactionHelper.getLoanRepaymentSchedule(this.requestSpec, this.responseSpec, loanID);
|
||||
Assertions.assertNotNull(loanSchedule);
|
||||
Calendar repaymentDate = (Calendar) firstRepaymentDate.clone();
|
||||
startDate.add(Calendar.DAY_OF_MONTH, 2);
|
||||
String loanFirstRepaymentDate = dateFormat.format(startDate.getTime());
|
||||
//
|
||||
Float earlyPayment = Float.parseFloat("3000");
|
||||
String retrieveDueDate = null;
|
||||
Float amount = null;
|
||||
this.loanTransactionHelper.makeRepayment(loanFirstRepaymentDate, earlyPayment, loanID);
|
||||
for (int i = 1; i < loanSchedule.size(); i++) {
|
||||
|
||||
retrieveDueDate = dateFormat.format(repaymentDate.getTime());
|
||||
amount = (Float) loanSchedule.get(i).get("principalOriginalDue") + (Float) loanSchedule.get(i).get("interestOriginalDue");
|
||||
if (currentDate.after(repaymentDate)) {
|
||||
this.loanTransactionHelper.makeRepayment(retrieveDueDate, amount, loanID);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
repaymentDate.add(Calendar.MONTH, 1);
|
||||
}
|
||||
HashMap savingsAccountErrorData = validationErrorHelper.makeRepayment(retrieveDueDate, amount, loanID);
|
||||
ArrayList<HashMap> error = (ArrayList<HashMap>) savingsAccountErrorData.get("errors");
|
||||
assertEquals("error.msg.loan.transaction.cannot.be.a.future.date", error.get(0).get("userMessageGlobalisationCode"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCollateralDataIsAvailableWhenRequested() {
|
||||
// given
|
||||
|
||||
+9
-1
@@ -131,6 +131,7 @@ public class LoanProductTestBuilder {
|
||||
private boolean syncExpectedWithDisbursementDate = false;
|
||||
private String fixedPrincipalPercentagePerInstallment;
|
||||
private String installmentAmountInMultiplesOf;
|
||||
private boolean canDefineInstallmentAmount;
|
||||
|
||||
public String build(final String chargeId) {
|
||||
final HashMap<String, Object> map = build(chargeId, null);
|
||||
@@ -185,7 +186,9 @@ public class LoanProductTestBuilder {
|
||||
map.put("overAppliedNumber", this.overAppliedNumber);
|
||||
}
|
||||
}
|
||||
|
||||
if (this.canDefineInstallmentAmount) {
|
||||
map.put("canDefineInstallmentAmount", this.canDefineInstallmentAmount);
|
||||
}
|
||||
if (multiDisburseLoan) {
|
||||
map.put("multiDisburseLoan", this.multiDisburseLoan);
|
||||
map.put("maxTrancheCount", this.maxTrancheCount);
|
||||
@@ -473,6 +476,11 @@ public class LoanProductTestBuilder {
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanProductTestBuilder withDefineInstallmentAmount(final boolean canDefineInstallmentAmount) {
|
||||
this.canDefineInstallmentAmount = canDefineInstallmentAmount;
|
||||
return this;
|
||||
}
|
||||
|
||||
public LoanProductTestBuilder currencyDetails(final String digitsAfterDecimal, final String inMultiplesOf) {
|
||||
this.digitsAfterDecimal = digitsAfterDecimal;
|
||||
this.inMultiplesOf = inMultiplesOf;
|
||||
|
||||
Reference in New Issue
Block a user