FINERACT-1981: Reschedule loan with new repayment date (Progressive loan schedule)

This commit is contained in:
Adam Saghy
2023-11-07 16:48:10 +01:00
parent 436877d94b
commit 983f3d2da0
5 changed files with 191 additions and 23 deletions
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
import java.math.BigDecimal;
import java.math.MathContext;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
@@ -117,16 +118,13 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
isFirstRepayment = false;
LocalDate scheduledDueDate = adjustedDateDetailsDTO.getChangedScheduleDate();
// this block is to generate the schedule till the specified
// date(used for calculating preclosure)
boolean isCompletePeriod = true;
if (scheduleParams.getScheduleTillDate() != null && !scheduledDueDate.isBefore(scheduleParams.getScheduleTillDate())) {
if (!scheduledDueDate.isEqual(scheduleParams.getScheduleTillDate())) {
isCompletePeriod = false;
}
scheduledDueDate = scheduleParams.getScheduleTillDate();
isNextRepaymentAvailable = false;
}
// Loan Schedule Exceptions that need to be applied for Loan Account
LoanTermVariationParams termVariationParams = applyLoanTermVariations(loanApplicationTerms, scheduleParams, scheduledDueDate);
scheduledDueDate = termVariationParams.scheduledDueDate();
// Updates total days in term
scheduleParams
.addLoanTermInDays(Math.toIntExact(ChronoUnit.DAYS.between(scheduleParams.getPeriodStartDate(), scheduledDueDate)));
ScheduleCurrentPeriodParams currentPeriodParams = new ScheduleCurrentPeriodParams(currency, BigDecimal.ZERO);
@@ -134,10 +132,6 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
processDisbursements(loanApplicationTerms, chargesDueAtTimeOfDisbursement, scheduleParams, periods, scheduledDueDate);
}
if (currentPeriodParams.isSkipCurrentLoop()) {
continue;
}
// 5 determine principal,interest of repayment period
PrincipalInterest principalInterestForThisPeriod = calculatePrincipalInterestComponentsForPeriod(
getPaymentPeriodsInOneYearCalculator(), currentPeriodParams.getInterestCalculationGraceOnRepaymentPeriodFraction(),
@@ -178,7 +172,7 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
scheduleParams.getPeriodStartDate(), scheduledDueDate, currentPeriodParams.getPrincipalForThisPeriod(),
scheduleParams.getOutstandingBalance(), currentPeriodParams.getInterestForThisPeriod(),
currentPeriodParams.getFeeChargesForInstallment(), currentPeriodParams.getPenaltyChargesForInstallment(),
totalInstallmentDue, !isCompletePeriod);
totalInstallmentDue, false);
if (principalInterestForThisPeriod.getRescheduleInterestPortion() != null) {
installment.setRescheduleInterestPortion(principalInterestForThisPeriod.getRescheduleInterestPortion().getAmount());
}
@@ -190,7 +184,7 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
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)
updateAmountsWithEffectiveDate(loanApplicationTerms, holidayDetailDTO, scheduleParams, scheduledDueDate, currentPeriodParams,
installment);
@@ -229,7 +223,9 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
public LoanScheduleDTO rescheduleNextInstallments(MathContext mc, LoanApplicationTerms loanApplicationTerms, Loan loan,
HolidayDetailDTO holidayDetailDTO, LoanRepaymentScheduleTransactionProcessor loanRepaymentScheduleTransactionProcessor,
LocalDate rescheduleFrom) {
return null;
LoanScheduleModel model = generate(mc, loanApplicationTerms, loan.getActiveCharges(), holidayDetailDTO);
return LoanScheduleDTO.from(null, model);
}
@Override
@@ -626,4 +622,33 @@ public abstract class AbstractProgressiveLoanScheduleGenerator implements LoanSc
scheduleParams
.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);
}
}
@@ -443,7 +443,12 @@ public class LoanRescheduleRequestWritePlatformServiceImpl implements LoanResche
final LoanScheduleDTO loanSchedule = loanScheduleGenerator.rescheduleNextInstallments(mathContext, loanApplicationTerms, loan,
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();
ChangedTransactionDetail changedTransactionDetail = loan.processTransactions();