FINERACT-1981: Reschedule loan with new repayment date (Progressive loan schedule)
This commit is contained in:
+6
-1
@@ -5516,7 +5516,12 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
|
|||||||
if (loanSchedule == null) {
|
if (loanSchedule == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateLoanSchedule(loanSchedule.getInstallments());
|
// Either the installments got recalculated or the model
|
||||||
|
if (loanSchedule.getInstallments() != null) {
|
||||||
|
updateLoanSchedule(loanSchedule.getInstallments());
|
||||||
|
} else {
|
||||||
|
updateLoanSchedule(loanSchedule.getLoanScheduleModel());
|
||||||
|
}
|
||||||
this.interestRecalculatedOn = DateUtils.getBusinessLocalDate();
|
this.interestRecalculatedOn = DateUtils.getBusinessLocalDate();
|
||||||
LocalDate lastRepaymentDate = this.getLastRepaymentPeriodDueDate(true);
|
LocalDate lastRepaymentDate = this.getLastRepaymentPeriodDueDate(true);
|
||||||
Set<LoanCharge> charges = this.getActiveCharges();
|
Set<LoanCharge> charges = this.getActiveCharges();
|
||||||
|
|||||||
+42
-17
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.MathContext;
|
import java.math.MathContext;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
|
import java.time.temporal.ChronoUnit;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
@@ -117,16 +118,13 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
|
|||||||
isFirstRepayment = false;
|
isFirstRepayment = false;
|
||||||
LocalDate scheduledDueDate = adjustedDateDetailsDTO.getChangedScheduleDate();
|
LocalDate scheduledDueDate = adjustedDateDetailsDTO.getChangedScheduleDate();
|
||||||
|
|
||||||
// this block is to generate the schedule till the specified
|
// Loan Schedule Exceptions that need to be applied for Loan Account
|
||||||
// date(used for calculating preclosure)
|
LoanTermVariationParams termVariationParams = applyLoanTermVariations(loanApplicationTerms, scheduleParams, scheduledDueDate);
|
||||||
boolean isCompletePeriod = true;
|
scheduledDueDate = termVariationParams.scheduledDueDate();
|
||||||
if (scheduleParams.getScheduleTillDate() != null && !scheduledDueDate.isBefore(scheduleParams.getScheduleTillDate())) {
|
|
||||||
if (!scheduledDueDate.isEqual(scheduleParams.getScheduleTillDate())) {
|
// Updates total days in term
|
||||||
isCompletePeriod = false;
|
scheduleParams
|
||||||
}
|
.addLoanTermInDays(Math.toIntExact(ChronoUnit.DAYS.between(scheduleParams.getPeriodStartDate(), scheduledDueDate)));
|
||||||
scheduledDueDate = scheduleParams.getScheduleTillDate();
|
|
||||||
isNextRepaymentAvailable = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
ScheduleCurrentPeriodParams currentPeriodParams = new ScheduleCurrentPeriodParams(currency, BigDecimal.ZERO);
|
ScheduleCurrentPeriodParams currentPeriodParams = new ScheduleCurrentPeriodParams(currency, BigDecimal.ZERO);
|
||||||
|
|
||||||
@@ -134,10 +132,6 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
|
|||||||
processDisbursements(loanApplicationTerms, chargesDueAtTimeOfDisbursement, scheduleParams, periods, scheduledDueDate);
|
processDisbursements(loanApplicationTerms, chargesDueAtTimeOfDisbursement, scheduleParams, periods, scheduledDueDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentPeriodParams.isSkipCurrentLoop()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5 determine principal,interest of repayment period
|
// 5 determine principal,interest of repayment period
|
||||||
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
|
||||||
getPaymentPeriodsInOneYearCalculator(), currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(),
|
getPaymentPeriodsInOneYearCalculator(), currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(),
|
||||||
@@ -178,7 +172,7 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
|
|||||||
scheduleParams.getPeriodStartDate(), scheduledDueDate, currentPeriodParams.getPrincipalForThisPeriod(),
|
scheduleParams.getPeriodStartDate(), scheduledDueDate, currentPeriodParams.getPrincipalForThisPeriod(),
|
||||||
scheduleParams.getOutstandingBalance(), currentPeriodParams.getInterestForThisPeriod(),
|
scheduleParams.getOutstandingBalance(), currentPeriodParams.getInterestForThisPeriod(),
|
||||||
currentPeriodParams.getFeeChargesForInstallment(), currentPeriodParams.getPenaltyChargesForInstallment(),
|
currentPeriodParams.getFeeChargesForInstallment(), currentPeriodParams.getPenaltyChargesForInstallment(),
|
||||||
totalInstallmentDue, !isCompletePeriod);
|
totalInstallmentDue, false);
|
||||||
if (principalInterestForThisPeriod.getRescheduleInterestPortion() != null) {
|
if (principalInterestForThisPeriod.getRescheduleInterestPortion() != null) {
|
||||||
installment.setRescheduleInterestPortion(principalInterestForThisPeriod.getRescheduleInterestPortion().getAmount());
|
installment.setRescheduleInterestPortion(principalInterestForThisPeriod.getRescheduleInterestPortion().getAmount());
|
||||||
}
|
}
|
||||||
@@ -190,7 +184,7 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
|
|||||||
|
|
||||||
periods.add(installment);
|
periods.add(installment);
|
||||||
|
|
||||||
// Updates principal paid map with efective date for reducing
|
// Updates principal paid map with effective date for reducing
|
||||||
// the amount from outstanding balance(interest calculation)
|
// the amount from outstanding balance(interest calculation)
|
||||||
updateAmountsWithEffectiveDate(loanApplicationTerms, holidayDetailDTO, scheduleParams, scheduledDueDate, currentPeriodParams,
|
updateAmountsWithEffectiveDate(loanApplicationTerms, holidayDetailDTO, scheduleParams, scheduledDueDate, currentPeriodParams,
|
||||||
installment);
|
installment);
|
||||||
@@ -229,7 +223,9 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
|
|||||||
public LoanScheduleDTO rescheduleNextInstallments(MathContext mc, LoanApplicationTerms loanApplicationTerms, Loan loan,
|
public LoanScheduleDTO rescheduleNextInstallments(MathContext mc, LoanApplicationTerms loanApplicationTerms, Loan loan,
|
||||||
HolidayDetailDTO holidayDetailDTO, LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
HolidayDetailDTO holidayDetailDTO, LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
|
||||||
LocalDate rescheduleFrom) {
|
LocalDate rescheduleFrom) {
|
||||||
return null;
|
|
||||||
|
LoanScheduleModel model = generate(mc, loanApplicationTerms, loan.getActiveCharges(), holidayDetailDTO);
|
||||||
|
return LoanScheduleDTO.from(null, model);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -626,4 +622,33 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
|
|||||||
scheduleParams
|
scheduleParams
|
||||||
.reduceOutstandingBalance(currentPeriodParams.getPrincipalForThisPeriod().minus(currentPeriodParams.getReducedBalance()));
|
.reduceOutstandingBalance(currentPeriodParams.getPrincipalForThisPeriod().minus(currentPeriodParams.getReducedBalance()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param loanApplicationTerms
|
||||||
|
* @param scheduleParams
|
||||||
|
* @param scheduledDueDate
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private LoanTermVariationParams applyLoanTermVariations(final LoanApplicationTerms loanApplicationTerms,
|
||||||
|
final LoanScheduleParams scheduleParams, final LocalDate scheduledDueDate) {
|
||||||
|
boolean skipPeriod = false;
|
||||||
|
boolean recalculateAmounts = false;
|
||||||
|
LocalDate modifiedScheduledDueDate = scheduledDueDate;
|
||||||
|
ArrayList<LoanTermVariationsData> variationsData = null;
|
||||||
|
|
||||||
|
// due date changes should be applied only for that dueDate
|
||||||
|
if (loanApplicationTerms.getLoanTermVariations().hasDueDateVariation(scheduledDueDate)) {
|
||||||
|
LoanTermVariationsData loanTermVariationsData = loanApplicationTerms.getLoanTermVariations().nextDueDateVariation();
|
||||||
|
if (DateUtils.isEqual(modifiedScheduledDueDate, loanTermVariationsData.getTermVariationApplicableFrom())) {
|
||||||
|
modifiedScheduledDueDate = loanTermVariationsData.getDateValue();
|
||||||
|
if (!loanTermVariationsData.isSpecificToInstallment()) {
|
||||||
|
scheduleParams.setActualRepaymentDate(modifiedScheduledDueDate);
|
||||||
|
loanApplicationTerms.setNewScheduledDueDateStart(modifiedScheduledDueDate);
|
||||||
|
}
|
||||||
|
loanTermVariationsData.setProcessed(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LoanTermVariationParams(skipPeriod, recalculateAmounts, modifiedScheduledDueDate, variationsData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -443,7 +443,12 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
|
|||||||
final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms, loan,
|
final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms, loan,
|
||||||
loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate);
|
loanApplicationTerms.getHolidayDetailDTO(), loanRepaymentScheduleTransactionProcessor, rescheduleFromDate);
|
||||||
|
|
||||||
loan.updateLoanSchedule(loanSchedule.getInstallments());
|
// Either the installments got recalculated or the model
|
||||||
|
if (loanSchedule.getInstallments() != null) {
|
||||||
|
loan.updateLoanSchedule(loanSchedule.getInstallments());
|
||||||
|
} else {
|
||||||
|
loan.updateLoanSchedule(loanSchedule.getLoanScheduleModel());
|
||||||
|
}
|
||||||
loan.recalculateAllCharges();
|
loan.recalculateAllCharges();
|
||||||
ChangedTransactionDetail changedTransactionDetail = loan.processTransactions();
|
ChangedTransactionDetail changedTransactionDetail = loan.processTransactions();
|
||||||
|
|
||||||
|
|||||||
+123
-3
@@ -28,6 +28,7 @@ import io.restassured.http.ContentType;
|
|||||||
import io.restassured.specification.RequestSpecification;
|
import io.restassured.specification.RequestSpecification;
|
||||||
import io.restassured.specification.ResponseSpecification;
|
import io.restassured.specification.ResponseSpecification;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
@@ -38,15 +39,19 @@ import org.apache.fineract.client.models.GetLoansLoanIdRepaymentPeriod;
|
|||||||
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
import org.apache.fineract.client.models.GetLoansLoanIdResponse;
|
||||||
import org.apache.fineract.client.models.PaymentAllocationOrder;
|
import org.apache.fineract.client.models.PaymentAllocationOrder;
|
||||||
import org.apache.fineract.client.models.PostClientsResponse;
|
import org.apache.fineract.client.models.PostClientsResponse;
|
||||||
|
import org.apache.fineract.client.models.PostCreateRescheduleLoansRequest;
|
||||||
|
import org.apache.fineract.client.models.PostCreateRescheduleLoansResponse;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdRequest;
|
import org.apache.fineract.client.models.PostLoansLoanIdRequest;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
|
||||||
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
|
import org.apache.fineract.client.models.PostLoansLoanIdTransactionsTransactionIdRequest;
|
||||||
import org.apache.fineract.client.models.PostLoansRequest;
|
import org.apache.fineract.client.models.PostLoansRequest;
|
||||||
import org.apache.fineract.client.models.PostLoansResponse;
|
import org.apache.fineract.client.models.PostLoansResponse;
|
||||||
|
import org.apache.fineract.client.models.PostUpdateRescheduleLoansRequest;
|
||||||
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
|
||||||
import org.apache.fineract.integrationtests.common.BusinessDateHelper;
|
import org.apache.fineract.integrationtests.common.BusinessDateHelper;
|
||||||
import org.apache.fineract.integrationtests.common.ClientHelper;
|
import org.apache.fineract.integrationtests.common.ClientHelper;
|
||||||
import org.apache.fineract.integrationtests.common.GlobalConfigurationHelper;
|
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.Utils;
|
||||||
import org.apache.fineract.integrationtests.common.accounting.Account;
|
import org.apache.fineract.integrationtests.common.accounting.Account;
|
||||||
import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
|
import org.apache.fineract.integrationtests.common.accounting.AccountHelper;
|
||||||
@@ -76,6 +81,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest {
|
|||||||
private static AccountHelper accountHelper;
|
private static AccountHelper accountHelper;
|
||||||
private static Integer commonLoanProductId;
|
private static Integer commonLoanProductId;
|
||||||
private static PostClientsResponse client;
|
private static PostClientsResponse client;
|
||||||
|
private static LoanRescheduleRequestHelper loanRescheduleRequestHelper;
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void setup() {
|
public static void setup() {
|
||||||
@@ -88,6 +94,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest {
|
|||||||
businessDateHelper = new BusinessDateHelper();
|
businessDateHelper = new BusinessDateHelper();
|
||||||
accountHelper = new AccountHelper(requestSpec, responseSpec);
|
accountHelper = new AccountHelper(requestSpec, responseSpec);
|
||||||
ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
|
ClientHelper clientHelper = new ClientHelper(requestSpec, responseSpec);
|
||||||
|
loanRescheduleRequestHelper = new LoanRescheduleRequestHelper(requestSpec, responseSpec);
|
||||||
|
|
||||||
final Account assetAccount = accountHelper.createAssetAccount();
|
final Account assetAccount = accountHelper.createAssetAccount();
|
||||||
final Account incomeAccount = accountHelper.createIncomeAccount();
|
final Account incomeAccount = accountHelper.createIncomeAccount();
|
||||||
@@ -1718,7 +1725,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest {
|
|||||||
// UC101: Multiple disbursement test
|
// UC101: Multiple disbursement test
|
||||||
// ADVANCED_PAYMENT_ALLOCATION_STRATEGY
|
// ADVANCED_PAYMENT_ALLOCATION_STRATEGY
|
||||||
// 1. Disburse the loan
|
// 1. Disburse the loan
|
||||||
// 3. Pay over the down payment
|
// 2. Pay over the down payment
|
||||||
// 3. Disburse again
|
// 3. Disburse again
|
||||||
@Test
|
@Test
|
||||||
public void uc101() {
|
public void uc101() {
|
||||||
@@ -1790,7 +1797,107 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest {
|
|||||||
validateRepaymentPeriod(loanDetails, 3, 250.0, 50.0, 200.0, 50.0, 0.0);
|
validateRepaymentPeriod(loanDetails, 3, 250.0, 50.0, 200.0, 50.0, 0.0);
|
||||||
validateRepaymentPeriod(loanDetails, 4, 250.0, 0.0, 250.0, 0.0, 0.0);
|
validateRepaymentPeriod(loanDetails, 4, 250.0, 0.0, 250.0, 0.0, 0.0);
|
||||||
validateRepaymentPeriod(loanDetails, 5, 250.0, 0.0, 250.0, 0.0, 0.0);
|
validateRepaymentPeriod(loanDetails, 5, 250.0, 0.0, 250.0, 0.0, 0.0);
|
||||||
validateRepaymentPeriod(loanDetails, 6, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0, 0.0, 100.0, 0.0, 0.0, 0.0, 0.0, 0.0);
|
validateRepaymentPeriod(loanDetails, 6, LocalDate.of(2023, 2, 22), 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 100.0, 0.0, 100.0, 0.0, 0.0,
|
||||||
|
0.0, 0.0, 0.0);
|
||||||
|
assertTrue(loanDetails.getStatus().getActive());
|
||||||
|
} finally {
|
||||||
|
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UC102: Multiple disbursement & reschedule test
|
||||||
|
// ADVANCED_PAYMENT_ALLOCATION_STRATEGY
|
||||||
|
// 1. Disburse the loan (500)
|
||||||
|
// 2. Reschedule the 2nd period
|
||||||
|
// 3. Repayment (450)
|
||||||
|
// 3. Disburse again (250)
|
||||||
|
// 4. Reschedule the 3rd period to be later than the 2nd disbursement
|
||||||
|
@Test
|
||||||
|
public void uc102() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.TRUE);
|
||||||
|
businessDateHelper.updateBusinessDate(new BusinessDateRequest().type(BusinessDateType.BUSINESS_DATE.getName())
|
||||||
|
.date("2023.02.20").dateFormat("yyyy.MM.dd").locale("en"));
|
||||||
|
|
||||||
|
final Account assetAccount = accountHelper.createAssetAccount();
|
||||||
|
final Account incomeAccount = accountHelper.createIncomeAccount();
|
||||||
|
final Account expenseAccount = accountHelper.createExpenseAccount();
|
||||||
|
final Account overpaymentAccount = accountHelper.createLiabilityAccount();
|
||||||
|
Integer localLoanProductId = createLoanProduct("500", "15", "4", false, assetAccount, incomeAccount, expenseAccount,
|
||||||
|
overpaymentAccount);
|
||||||
|
final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), localLoanProductId, 500L, 45, 15, 3, 0,
|
||||||
|
"01 January 2023", "01 January 2023");
|
||||||
|
|
||||||
|
loanTransactionHelper.approveLoan(loanResponse.getLoanId(),
|
||||||
|
new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN)
|
||||||
|
.approvedOnDate("01 January 2023").locale("en"));
|
||||||
|
|
||||||
|
loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
|
||||||
|
new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN)
|
||||||
|
.transactionAmount(BigDecimal.valueOf(500.00)).locale("en"));
|
||||||
|
|
||||||
|
GetLoansLoanIdResponse loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
||||||
|
validateLoanSummaryBalances(loanDetails, 500.0, 0.0, 500.0, 0.0, null);
|
||||||
|
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), 125.0, 0.0, 125.0, 0.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 1, 16), 125.0, 0.0, 125.0, 0.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 1, 31), 125.0, 0.0, 125.0, 0.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 15), 125.0, 0.0, 125.0, 0.0, 0.0);
|
||||||
|
assertTrue(loanDetails.getStatus().getActive());
|
||||||
|
|
||||||
|
PostCreateRescheduleLoansResponse rescheduleLoansResponse = loanRescheduleRequestHelper
|
||||||
|
.createLoanRescheduleRequest(new PostCreateRescheduleLoansRequest().loanId(loanDetails.getId()).locale("en")
|
||||||
|
.dateFormat(DATETIME_PATTERN).rescheduleReasonId(1L).rescheduleFromDate("16 January 2023")
|
||||||
|
.adjustedDueDate("31 January 2023").submittedOnDate("16 January 2023"));
|
||||||
|
|
||||||
|
loanRescheduleRequestHelper.approveLoanRescheduleRequest(rescheduleLoansResponse.getResourceId(),
|
||||||
|
new PostUpdateRescheduleLoansRequest().approvedOnDate("16 January 2023").locale("en").dateFormat(DATETIME_PATTERN));
|
||||||
|
|
||||||
|
loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
||||||
|
validateLoanSummaryBalances(loanDetails, 500.0, 0.0, 500.0, 0.0, null);
|
||||||
|
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), 125.0, 0.0, 125.0, 0.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 1, 31), 125.0, 0.0, 125.0, 0.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 2, 15), 125.0, 0.0, 125.0, 0.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 3, 2), 125.0, 0.0, 125.0, 0.0, 0.0);
|
||||||
|
assertTrue(loanDetails.getStatus().getActive());
|
||||||
|
|
||||||
|
loanTransactionHelper.makeLoanRepayment(loanResponse.getLoanId(), new PostLoansLoanIdTransactionsRequest()
|
||||||
|
.dateFormat(DATETIME_PATTERN).transactionDate("16 January 2023").locale("en").transactionAmount(350.0));
|
||||||
|
loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
||||||
|
validateLoanSummaryBalances(loanDetails, 150.0, 350.0, 150.0, 350.0, null);
|
||||||
|
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), 125.0, 125.0, 0.0, 0.0, 125.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 1, 31), 125.0, 125.0, 0.0, 125.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 2, 15), 125.0, 100.0, 25.0, 100.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 3, 2), 125.0, 0.0, 125.0, 0.0, 0.0);
|
||||||
|
assertTrue(loanDetails.getStatus().getActive());
|
||||||
|
|
||||||
|
loanTransactionHelper.disburseLoan(loanResponse.getLoanId(),
|
||||||
|
new PostLoansLoanIdRequest().actualDisbursementDate("20 February 2023").dateFormat(DATETIME_PATTERN)
|
||||||
|
.transactionAmount(BigDecimal.valueOf(250.0)).locale("en"));
|
||||||
|
|
||||||
|
loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
||||||
|
validateLoanSummaryBalances(loanDetails, 400.0, 350.0, 400.0, 350.0, null);
|
||||||
|
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), 125.0, 125.0, 0.0, 0.0, 125.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 1, 31), 125.0, 125.0, 0.0, 125.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 2, 15), 125.0, 100.0, 25.0, 100.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 20), 62.5, 0.0, 62.5, 0.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 5, LocalDate.of(2023, 3, 2), 312.5, 0.0, 312.5, 0.0, 0.0);
|
||||||
|
assertTrue(loanDetails.getStatus().getActive());
|
||||||
|
|
||||||
|
rescheduleLoansResponse = loanRescheduleRequestHelper.createLoanRescheduleRequest(new PostCreateRescheduleLoansRequest()
|
||||||
|
.loanId(loanDetails.getId()).locale("en").dateFormat(DATETIME_PATTERN).rescheduleReasonId(1L)
|
||||||
|
.rescheduleFromDate("15 February 2023").adjustedDueDate("25 February 2023").submittedOnDate("15 February 2023"));
|
||||||
|
|
||||||
|
loanRescheduleRequestHelper.approveLoanRescheduleRequest(rescheduleLoansResponse.getResourceId(),
|
||||||
|
new PostUpdateRescheduleLoansRequest().approvedOnDate("15 February 2023").locale("en").dateFormat(DATETIME_PATTERN));
|
||||||
|
|
||||||
|
loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
||||||
|
validateLoanSummaryBalances(loanDetails, 400.0, 350.0, 400.0, 350.0, null);
|
||||||
|
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 1, 1), 125.0, 125.0, 0.0, 0.0, 125.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 1, 31), 125.0, 125.0, 0.0, 125.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 2, 20), 62.5, 0.0, 62.5, 0.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 2, 25), 218.75, 100.0, 118.75, 100.0, 0.0);
|
||||||
|
validateRepaymentPeriod(loanDetails, 5, LocalDate.of(2023, 3, 12), 218.75, 0.0, 218.75, 0.0, 0.0);
|
||||||
assertTrue(loanDetails.getStatus().getActive());
|
assertTrue(loanDetails.getStatus().getActive());
|
||||||
} finally {
|
} finally {
|
||||||
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
|
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, Boolean.FALSE);
|
||||||
@@ -1867,6 +1974,18 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest {
|
|||||||
return loanTransactionHelper.getLoanProductId(loanProductJSON);
|
return loanTransactionHelper.getLoanProductId(loanProductJSON);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void validateRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, LocalDate dueDate, double principalDue,
|
||||||
|
double principalPaid, double principalOutstanding, double paidInAdvance, double paidLate) {
|
||||||
|
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
|
||||||
|
.filter(p -> Objects.equals(p.getPeriod(), index)).findFirst().orElseThrow();
|
||||||
|
assertEquals(dueDate, period.getDueDate());
|
||||||
|
assertEquals(principalDue, period.getPrincipalDue());
|
||||||
|
assertEquals(principalPaid, period.getPrincipalPaid());
|
||||||
|
assertEquals(principalOutstanding, period.getPrincipalOutstanding());
|
||||||
|
assertEquals(paidInAdvance, period.getTotalPaidInAdvanceForPeriod());
|
||||||
|
assertEquals(paidLate, period.getTotalPaidLateForPeriod());
|
||||||
|
}
|
||||||
|
|
||||||
private static void validateRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, double principalDue,
|
private static void validateRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, double principalDue,
|
||||||
double principalPaid, double principalOutstanding, double paidInAdvance, double paidLate) {
|
double principalPaid, double principalOutstanding, double paidInAdvance, double paidLate) {
|
||||||
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
|
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
|
||||||
@@ -1878,12 +1997,13 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest {
|
|||||||
assertEquals(paidLate, period.getTotalPaidLateForPeriod());
|
assertEquals(paidLate, period.getTotalPaidLateForPeriod());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void validateRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, double principalDue,
|
private static void validateRepaymentPeriod(GetLoansLoanIdResponse loanDetails, Integer index, LocalDate dueDate, double principalDue,
|
||||||
double principalPaid, double principalOutstanding, double feeDue, double feePaid, double feeOutstanding, double penaltyDue,
|
double principalPaid, double principalOutstanding, double feeDue, double feePaid, double feeOutstanding, double penaltyDue,
|
||||||
double penaltyPaid, double penaltyOutstanding, double interestDue, double interestPaid, double interestOutstanding,
|
double penaltyPaid, double penaltyOutstanding, double interestDue, double interestPaid, double interestOutstanding,
|
||||||
double paidInAdvance, double paidLate) {
|
double paidInAdvance, double paidLate) {
|
||||||
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
|
GetLoansLoanIdRepaymentPeriod period = loanDetails.getRepaymentSchedule().getPeriods().stream()
|
||||||
.filter(p -> Objects.equals(p.getPeriod(), index)).findFirst().orElseThrow();
|
.filter(p -> Objects.equals(p.getPeriod(), index)).findFirst().orElseThrow();
|
||||||
|
assertEquals(dueDate, period.getDueDate());
|
||||||
assertEquals(principalDue, period.getPrincipalDue());
|
assertEquals(principalDue, period.getPrincipalDue());
|
||||||
assertEquals(principalPaid, period.getPrincipalPaid());
|
assertEquals(principalPaid, period.getPrincipalPaid());
|
||||||
assertEquals(principalOutstanding, period.getPrincipalOutstanding());
|
assertEquals(principalOutstanding, period.getPrincipalOutstanding());
|
||||||
|
|||||||
+14
-1
@@ -23,8 +23,13 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||||||
import io.restassured.specification.RequestSpecification;
|
import io.restassured.specification.RequestSpecification;
|
||||||
import io.restassured.specification.ResponseSpecification;
|
import io.restassured.specification.ResponseSpecification;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import org.apache.fineract.client.models.PostCreateRescheduleLoansRequest;
|
||||||
|
import org.apache.fineract.client.models.PostCreateRescheduleLoansResponse;
|
||||||
|
import org.apache.fineract.client.models.PostUpdateRescheduleLoansRequest;
|
||||||
|
import org.apache.fineract.client.models.PostUpdateRescheduleLoansResponse;
|
||||||
|
import org.apache.fineract.integrationtests.client.IntegrationTest;
|
||||||
|
|
||||||
public class LoanRescheduleRequestHelper {
|
public class LoanRescheduleRequestHelper extends IntegrationTest {
|
||||||
|
|
||||||
private final RequestSpecification requestSpec;
|
private final RequestSpecification requestSpec;
|
||||||
private final ResponseSpecification responseSpec;
|
private final ResponseSpecification responseSpec;
|
||||||
@@ -70,4 +75,12 @@ public class LoanRescheduleRequestHelper {
|
|||||||
final Integer id = Utils.performServerGet(requestSpec, responseSpec, URL, "id");
|
final Integer id = Utils.performServerGet(requestSpec, responseSpec, URL, "id");
|
||||||
assertEquals(requestId, id, "ERROR IN CREATING LOAN RESCHEDULE REQUES");
|
assertEquals(requestId, id, "ERROR IN CREATING LOAN RESCHEDULE REQUES");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PostCreateRescheduleLoansResponse createLoanRescheduleRequest(PostCreateRescheduleLoansRequest request) {
|
||||||
|
return ok(fineract().rescheduleLoans.createLoanRescheduleRequest(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
public PostUpdateRescheduleLoansResponse approveLoanRescheduleRequest(Long scheduleId, PostUpdateRescheduleLoansRequest request) {
|
||||||
|
return ok(fineract().rescheduleLoans.updateLoanRescheduleRequest(scheduleId, request, "approve"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user