FINERACT-2354: Re-aging - Accrual and Accrual Activity handling - Equal Amortization
This commit is contained in:
+5
-4
@@ -2034,7 +2034,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
&& loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
final BigDecimal interestOutstanding = currentInstallment.getInterestOutstanding(loan.getCurrency()).getAmount();
|
||||
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(),
|
||||
currentInstallment.getFromDate(), currentInstallment.getDueDate(), transactionDate, true).getAmount();
|
||||
currentInstallment.getFromDate(), currentInstallment.getDueDate(), transactionDate, true, false).getAmount();
|
||||
if (interestOutstanding.compareTo(BigDecimal.ZERO) > 0 || newInterest.compareTo(BigDecimal.ZERO) > 0) {
|
||||
currentInstallment.updateInterestCharged(newInterest);
|
||||
}
|
||||
@@ -2146,8 +2146,9 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
installments.stream().filter(installment -> !installment.getFromDate().isAfter(transactionDate)
|
||||
&& installment.getDueDate().isAfter(transactionDate)).forEach(installment -> {
|
||||
final BigDecimal interestOutstanding = installment.getInterestOutstanding(currency).getAmount();
|
||||
|
||||
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(),
|
||||
installment.getFromDate(), installment.getDueDate(), transactionDate, true).getAmount();
|
||||
installment.getFromDate(), installment.getDueDate(), transactionDate, true, false).getAmount();
|
||||
if (MathUtil.isGreaterThanZero(interestOutstanding) || MathUtil.isGreaterThanZero(newInterest)) {
|
||||
final BigDecimal interestRemoved = MathUtil.subtract(MathUtil.nullToZero(installment.getInterestCharged()),
|
||||
newInterest);
|
||||
@@ -3390,7 +3391,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
BigDecimal::add);
|
||||
|
||||
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(transactionCtx.getModel(), lastPeriod.getFromDate(),
|
||||
lastPeriod.getDueDate(), transactionDate, false).getAmount();
|
||||
lastPeriod.getDueDate(), transactionDate, false, false).getAmount();
|
||||
|
||||
lastPeriod.setEmi(lastPeriod.getDuePrincipal().add(totalPrincipal).add(newInterest));
|
||||
|
||||
@@ -3434,7 +3435,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
|
||||
&& loan.isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
interest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(), installment.getFromDate(),
|
||||
installment.getDueDate(), chargeOffDate, true).getAmount();
|
||||
installment.getDueDate(), chargeOffDate, true, false).getAmount();
|
||||
} else {
|
||||
final BigDecimal totalInterest = installment.getInterestOutstanding(currency).getAmount();
|
||||
if (LoanChargeOffBehaviour.ZERO_INTEREST.equals(loan.getLoanProductRelatedDetail().getChargeOffBehaviour())
|
||||
|
||||
+3
-5
@@ -42,7 +42,6 @@ import org.apache.fineract.portfolio.loanaccount.domain.Loan;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanRepaymentScheduleInstallment;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.LoanTransaction;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeInterestHandlingType;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.reaging.LoanReAgeParameter;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.LoanRepaymentScheduleTransactionProcessor;
|
||||
import org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl.AdvancedPaymentScheduleTransactionProcessor;
|
||||
@@ -249,16 +248,15 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
|
||||
final LoanTransaction reAgeTransaction = loan.findReAgeTransaction();
|
||||
final LoanReAgeParameter loanReAgeParameter = reAgeTransaction != null ? reAgeTransaction.getLoanReAgeParameter() : null;
|
||||
|
||||
if (!(transactionProcessor instanceof AdvancedPaymentScheduleTransactionProcessor processor)) {
|
||||
if (!(transactionProcessor instanceof AdvancedPaymentScheduleTransactionProcessor)) {
|
||||
throw new IllegalStateException("Expected an AdvancedPaymentScheduleTransactionProcessor");
|
||||
}
|
||||
if (installment.isAdditional() || installment.isDownPayment() || (installment.isReAged() && loanReAgeParameter != null
|
||||
&& !LoanReAgeInterestHandlingType.DEFAULT.equals(loanReAgeParameter.getInterestHandlingType()))) {
|
||||
if (installment.isAdditional() || installment.isDownPayment()) {
|
||||
return Money.zero(loan.getCurrency());
|
||||
}
|
||||
Optional<ProgressiveLoanInterestScheduleModel> savedModel = interestScheduleModelRepositoryWrapper.getSavedModel(loan, targetDate);
|
||||
ProgressiveLoanInterestScheduleModel model = savedModel.orElseThrow();
|
||||
return emiCalculator.getPeriodInterestTillDate(model, installment.getFromDate(), installment.getDueDate(), targetDate, false);
|
||||
return emiCalculator.getPeriodInterestTillDate(model, installment.getFromDate(), installment.getDueDate(), targetDate, false, true);
|
||||
}
|
||||
|
||||
// Private, internal methods
|
||||
|
||||
+8
-2
@@ -124,11 +124,17 @@ public interface EMICalculator {
|
||||
@NotNull LocalDate periodDueDate, @NotNull LocalDate targetDate);
|
||||
|
||||
/**
|
||||
* Gives back the sum of the interest from the whole model on the given date.
|
||||
* Gives back the sum of the interest from the whole model on the given date. Fixed interest till date calculation
|
||||
* flag indicates that the fixed interest should be calculated only till date or not. This flag should be true for
|
||||
* accrual calculation. It should be false for repayment or repayment schedule related calculation.
|
||||
*
|
||||
* @param fixedInterestTillDate
|
||||
* indicates that fixed interest should be calculated till the date.
|
||||
*/
|
||||
@NotNull
|
||||
Money getPeriodInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodFromDate,
|
||||
@NotNull LocalDate periodDueDate, @NotNull LocalDate targetDate, boolean includeChargebackInterest);
|
||||
@NotNull LocalDate periodDueDate, @NotNull LocalDate targetDate, boolean includeChargebackInterest,
|
||||
boolean fixedInterestTillDate);
|
||||
|
||||
Money getOutstandingLoanBalanceOfPeriod(ProgressiveLoanInterestScheduleModel interestScheduleModel, LocalDate targetDate);
|
||||
|
||||
|
||||
+16
-9
@@ -519,15 +519,21 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
|
||||
@Override
|
||||
@NotNull
|
||||
public Money getPeriodInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel scheduleModel, @NotNull LocalDate periodFromDate,
|
||||
@NotNull LocalDate periodDueDate, @NotNull LocalDate targetDate, boolean includeCreditedInterest) {
|
||||
ProgressiveLoanInterestScheduleModel recalculatedScheduleModelTillDate = recalculateScheduleModelTillDate(scheduleModel,
|
||||
public Money getPeriodInterestTillDate(@NotNull final ProgressiveLoanInterestScheduleModel scheduleModel,
|
||||
@NotNull final LocalDate periodFromDate, @NotNull final LocalDate periodDueDate, @NotNull final LocalDate targetDate,
|
||||
final boolean includeCreditedInterest, final boolean fixedInterestTillDate) {
|
||||
final ProgressiveLoanInterestScheduleModel recalculatedScheduleModelTillDate = recalculateScheduleModelTillDate(scheduleModel,
|
||||
targetDate);
|
||||
RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate
|
||||
final MathContext mc = recalculatedScheduleModelTillDate.mc();
|
||||
final RepaymentPeriod repaymentPeriod = recalculatedScheduleModelTillDate
|
||||
.findRepaymentPeriodByFromAndDueDate(periodFromDate, periodDueDate).orElseThrow();
|
||||
return includeCreditedInterest ? repaymentPeriod.getCalculatedDueInterest()
|
||||
: repaymentPeriod.getCalculatedDueInterest().minus(repaymentPeriod.getCreditedInterest(),
|
||||
recalculatedScheduleModelTillDate.mc());
|
||||
Money calculatedDueInterest = repaymentPeriod.getCalculatedDueInterest();
|
||||
if (fixedInterestTillDate) {
|
||||
calculatedDueInterest = MathUtil.negativeToZero(
|
||||
calculatedDueInterest.minus(repaymentPeriod.getFixedInterest()).add(repaymentPeriod.calculateFixedInterestTillDate()),
|
||||
mc);
|
||||
}
|
||||
return includeCreditedInterest ? calculatedDueInterest : calculatedDueInterest.minus(repaymentPeriod.getCreditedInterest(), mc);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -853,7 +859,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
calculateRateFactorForPeriods(futurePeriods, model);
|
||||
|
||||
for (final RepaymentPeriod futurePeriod : futurePeriods) {
|
||||
futurePeriod.setReAgedInterest(MathUtil.plus(futurePeriod.getReAgedInterest(), interestPortionPerPeriod, mc));
|
||||
futurePeriod.setFixedInterest(MathUtil.plus(futurePeriod.getFixedInterest(), interestPortionPerPeriod, mc));
|
||||
}
|
||||
|
||||
calculateEMIOnActualModel(futurePeriods, model);
|
||||
@@ -1908,7 +1914,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
EqualAmortizationValues emiAEV = principalEAV.add(interestEAV);
|
||||
for (int i = 0; i < repaymentPeriods.size(); i++) {
|
||||
RepaymentPeriod rp = repaymentPeriods.get(i);
|
||||
rp.setReAgedInterest(interestEAV.calculateValue(i));
|
||||
rp.setFixedInterest(interestEAV.calculateValue(i));
|
||||
Money emi = emiAEV.calculateValue(i);
|
||||
rp.setEmi(emi);
|
||||
rp.setOriginalEmi(emi);
|
||||
@@ -1953,6 +1959,7 @@ public final class ProgressiveEMICalculator implements EMICalculator {
|
||||
.addCreditedInterestAmount(MathUtil.min(rp.getOutstandingInterest(), rp.getCreditedInterest(), false).negated());
|
||||
rp.setEmi(rp.getTotalPaidAmount());
|
||||
rp.moveOutstandingDueToReAging();
|
||||
rp.setNoUnrecognisedInterest(true);
|
||||
});
|
||||
|
||||
// stop calculate unrecognised interest at this point because all
|
||||
|
||||
+30
-9
@@ -32,6 +32,7 @@ import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.apache.fineract.infrastructure.core.serialization.gson.JsonExclude;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.infrastructure.core.service.MathUtil;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
import org.apache.fineract.organisation.monetary.domain.Money;
|
||||
@@ -105,14 +106,13 @@ public class RepaymentPeriod {
|
||||
@Setter
|
||||
@Getter
|
||||
private boolean reAgedEarlyRepaymentHolder;
|
||||
@Getter
|
||||
@Setter
|
||||
private Money reAgedInterest;
|
||||
private Money fixedInterest;
|
||||
|
||||
protected RepaymentPeriod(RepaymentPeriod previous, LocalDate fromDate, LocalDate dueDate, List<InterestPeriod> interestPeriods,
|
||||
Money emi, Money originalEmi, Money paidPrincipal, Money paidInterest, Money futureUnrecognizedInterest, MathContext mc,
|
||||
ILoanConfigurationDetails loanProductRelatedDetail, boolean noUnrecognisedInterest, boolean reAged,
|
||||
boolean reAgedEarlyRepaymentHolder, Money reAgedInterest) {
|
||||
boolean reAgedEarlyRepaymentHolder, Money fixedInterest) {
|
||||
this.previous = previous;
|
||||
this.fromDate = fromDate;
|
||||
this.dueDate = dueDate;
|
||||
@@ -127,7 +127,7 @@ public class RepaymentPeriod {
|
||||
this.noUnrecognisedInterest = noUnrecognisedInterest;
|
||||
this.reAged = reAged;
|
||||
this.reAgedEarlyRepaymentHolder = reAgedEarlyRepaymentHolder;
|
||||
this.reAgedInterest = reAgedInterest;
|
||||
this.fixedInterest = fixedInterest;
|
||||
this.creditedInterestMovedDueReAge = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
|
||||
this.creditedInterestMovedDueReAge = Money.zero(loanProductRelatedDetail.getCurrencyData(), mc);
|
||||
}
|
||||
@@ -152,7 +152,7 @@ public class RepaymentPeriod {
|
||||
repaymentPeriod.getDueDate(), new ArrayList<>(), repaymentPeriod.getEmi(), repaymentPeriod.getOriginalEmi(),
|
||||
repaymentPeriod.getPaidPrincipal(), repaymentPeriod.getPaidInterest(), repaymentPeriod.getFutureUnrecognizedInterest(), mc,
|
||||
repaymentPeriod.getLoanProductRelatedDetail(), repaymentPeriod.isNoUnrecognisedInterest(), repaymentPeriod.isReAged(),
|
||||
repaymentPeriod.isReAgedEarlyRepaymentHolder(), repaymentPeriod.getReAgedInterest());
|
||||
repaymentPeriod.isReAgedEarlyRepaymentHolder(), repaymentPeriod.getFixedInterest());
|
||||
newRepaymentPeriod.setCreditedPrincipalMovedDueReAge(repaymentPeriod.getCreditedPrincipalMovedDueReAge());
|
||||
newRepaymentPeriod.setCreditedInterestMovedDueReAge(repaymentPeriod.getCreditedInterestMovedDueReAge());
|
||||
newRepaymentPeriod.setTotalDisbursedAmount(repaymentPeriod.getTotalDisbursedAmount());
|
||||
@@ -171,7 +171,7 @@ public class RepaymentPeriod {
|
||||
final RepaymentPeriod newRepaymentPeriod = new RepaymentPeriod(previous, repaymentPeriod.getFromDate(),
|
||||
repaymentPeriod.getDueDate(), new ArrayList<>(), repaymentPeriod.getEmi(), repaymentPeriod.getOriginalEmi(), zero, zero,
|
||||
zero, mc, repaymentPeriod.getLoanProductRelatedDetail(), repaymentPeriod.isNoUnrecognisedInterest(),
|
||||
repaymentPeriod.isReAged(), repaymentPeriod.isReAgedEarlyRepaymentHolder(), repaymentPeriod.getReAgedInterest());
|
||||
repaymentPeriod.isReAged(), repaymentPeriod.isReAgedEarlyRepaymentHolder(), repaymentPeriod.getFixedInterest());
|
||||
newRepaymentPeriod.setCreditedPrincipalMovedDueReAge(repaymentPeriod.getCreditedPrincipalMovedDueReAge());
|
||||
newRepaymentPeriod.setCreditedInterestMovedDueReAge(repaymentPeriod.getCreditedInterestMovedDueReAge());
|
||||
newRepaymentPeriod.setTotalDisbursedAmount(repaymentPeriod.getTotalDisbursedAmount());
|
||||
@@ -218,11 +218,28 @@ public class RepaymentPeriod {
|
||||
public Money getCalculatedDueInterest() {
|
||||
if (calculatedDueInterestCalculation == null) {
|
||||
calculatedDueInterestCalculation = Memo.of(this::calculateCalculatedDueInterest, () -> new Object[] { previous, interestPeriods,
|
||||
futureUnrecognizedInterest, isInterestMoved, totalDisbursedAmount, reAgedInterest, reAged });
|
||||
futureUnrecognizedInterest, isInterestMoved, totalDisbursedAmount, fixedInterest, reAged });
|
||||
}
|
||||
return calculatedDueInterestCalculation.get();
|
||||
}
|
||||
|
||||
public Money calculateFixedInterestTillDate() {
|
||||
Money calculatedFixedInterest = getZero();
|
||||
if (!getFixedInterest().isZero()) {
|
||||
long length = DateUtils.getDifferenceInDays(getFromDate(), getDueDate());
|
||||
if (length == 0 || getInterestPeriods() == null || getInterestPeriods().isEmpty()) {
|
||||
// if the repayment period length is zero. return reAgedInterest.
|
||||
calculatedFixedInterest = getFixedInterest();
|
||||
} else {
|
||||
long interestCalculationLength = DateUtils.getDifferenceInDays(getInterestPeriods().getFirst().getFromDate(),
|
||||
getInterestPeriods().getLast().getDueDate());
|
||||
calculatedFixedInterest = Money.of(getZero().getCurrencyData(), BigDecimal.valueOf(interestCalculationLength)
|
||||
.divide(BigDecimal.valueOf(length), getMc()).multiply(getFixedInterest().getAmount(), getMc()));
|
||||
}
|
||||
}
|
||||
return calculatedFixedInterest;
|
||||
}
|
||||
|
||||
public Money calculateCalculatedDueInterest() {
|
||||
Money calculatedDueInterest = getZero();
|
||||
if (!isInterestMoved()) {
|
||||
@@ -230,7 +247,7 @@ public class RepaymentPeriod {
|
||||
getInterestPeriods().stream().map(InterestPeriod::getCalculatedDueInterest).reduce(BigDecimal.ZERO, BigDecimal::add),
|
||||
mc);
|
||||
}
|
||||
calculatedDueInterest = calculatedDueInterest.add(reAgedInterest);
|
||||
calculatedDueInterest = calculatedDueInterest.add(getFixedInterest());
|
||||
calculatedDueInterest = calculatedDueInterest.add(getFutureUnrecognizedInterest(), getMc());
|
||||
if (getPrevious().isPresent()) {
|
||||
calculatedDueInterest = calculatedDueInterest.add(getPrevious().get().getUnrecognizedInterest(), getMc());
|
||||
@@ -251,7 +268,7 @@ public class RepaymentPeriod {
|
||||
: MathUtil.min(getCalculatedDueInterest(), getEmiPlusCreditedAmountsPlusFutureUnrecognizedInterest(), false),
|
||||
getPaidInterest(), false),
|
||||
() -> new Object[] { paidPrincipal, paidInterest, interestPeriods, futureUnrecognizedInterest, totalDisbursedAmount,
|
||||
reAgedInterest, reAged, emi });
|
||||
fixedInterest, reAged, emi });
|
||||
}
|
||||
return dueInterestCalculation.get();
|
||||
}
|
||||
@@ -497,6 +514,10 @@ public class RepaymentPeriod {
|
||||
return MathUtil.nullToZero(totalCapitalizedIncomeAmount, getCurrency(), getMc());
|
||||
}
|
||||
|
||||
public Money getFixedInterest() {
|
||||
return MathUtil.nullToZero(fixedInterest, getCurrency(), getMc());
|
||||
}
|
||||
|
||||
public void moveOutstandingDueToReAging() {
|
||||
setCreditedPrincipalMovedDueReAge(getCreditedPrincipal());
|
||||
setCreditedInterestMovedDueReAge(getCreditedInterest());
|
||||
|
||||
+2
-2
@@ -5090,9 +5090,9 @@ class ProgressiveEMICalculatorTest {
|
||||
private static void checkDailyInterest(final ProgressiveLoanInterestScheduleModel interestModel, final LocalDate repaymentPeriodDueDate,
|
||||
final LocalDate interestStartDay, final int dayOffset, final double dailyInterest, final double interest) {
|
||||
Money previousInterest = emiCalculator.getPeriodInterestTillDate(interestModel, interestStartDay, repaymentPeriodDueDate,
|
||||
interestStartDay.plusDays(dayOffset - 1), true);
|
||||
interestStartDay.plusDays(dayOffset - 1), true, true);
|
||||
Money currentInterest = emiCalculator.getPeriodInterestTillDate(interestModel, interestStartDay, repaymentPeriodDueDate,
|
||||
interestStartDay.plusDays(dayOffset), true);
|
||||
interestStartDay.plusDays(dayOffset), true, true);
|
||||
Assertions.assertEquals(dailyInterest, toDouble(currentInterest.minus(previousInterest)));
|
||||
Assertions.assertEquals(interest, toDouble(currentInterest));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user