Skip to content

Commit a68ea39

Browse files
FINERACT-2354: Implemented re-aging for Interest bearing loans - Default Behavior, interestRecalculation = true, without dueDate change
1 parent bef9e24 commit a68ea39

File tree

21 files changed

+670
-182
lines changed

21 files changed

+670
-182
lines changed

fineract-e2e-tests-runner/src/test/resources/features/LoanReAging.feature

Lines changed: 155 additions & 80 deletions
Large diffs are not rendered by default.

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1818,8 +1818,16 @@ public boolean hasAccelerateChargeOffStrategy() {
18181818
return LoanChargeOffBehaviour.ACCELERATE_MATURITY.equals(getLoanProductRelatedDetail().getChargeOffBehaviour());
18191819
}
18201820

1821+
public boolean hasZeroInterestChargeOffStrategy() {
1822+
return LoanChargeOffBehaviour.ZERO_INTEREST.equals(getLoanProductRelatedDetail().getChargeOffBehaviour());
1823+
}
1824+
18211825
public boolean hasContractTerminationTransaction() {
18221826
return getLoanTransactions().stream().anyMatch(t -> t.isContractTermination() && t.isNotReversed());
18231827
}
18241828

1829+
public boolean hasReAgingTransaction() {
1830+
return getLoanTransactions().stream().anyMatch(t -> t.isReAge() && t.isNotReversed());
1831+
}
1832+
18251833
}

fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanDownPaymentHandlerServiceImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,8 @@ public void handleRepaymentOrRecoveryOrWaiverTransaction(final Loan loan, final
157157
if (loan.isCumulativeSchedule() && loan.isInterestBearingAndInterestRecalculationEnabled()) {
158158
loanScheduleService.regenerateRepaymentScheduleWithInterestRecalculation(loan, scheduleGeneratorDTO);
159159
} else if (loan.isProgressiveSchedule() && ((loan.hasChargeOffTransaction() && loan.hasAccelerateChargeOffStrategy())
160-
|| loan.hasContractTerminationTransaction())) {
160+
|| loan.hasContractTerminationTransaction()
161+
|| (loan.isInterestRecalculationEnabled() && loan.hasReAgingTransaction()))) {
161162
loanScheduleService.regenerateRepaymentSchedule(loan, scheduleGeneratorDTO);
162163
}
163164
reprocessLoanTransactionsService.reprocessTransactions(loan);

fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/delinquency/service/ProgressivePossibleNextRepaymentCalculationServiceImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ public BigDecimal calculateInterestRecalculationFutureOutstandingValue(Loan loan
6262
ctx.setChargedOff(loan.isChargedOff());
6363
ctx.setWrittenOff(loan.isClosedWrittenOff());
6464
ctx.setContractTerminated(loan.isContractTermination());
65+
ctx.setReAged(loan.hasReAgingTransaction());
6566
advancedPaymentScheduleTransactionProcessor.recalculateInterestForDate(nextPaymentDueDate, ctx, false);
6667
RepaymentPeriod repaymentPeriod = scheduleModel.findRepaymentPeriodByDueDate(nextPaymentDueDate)
6768
.orElseGet(scheduleModel::getLastRepaymentPeriod);

fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java

Lines changed: 393 additions & 53 deletions
Large diffs are not rendered by default.

fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/ProgressiveTransactionCtx.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ public class ProgressiveTransactionCtx extends TransactionCtx {
4747
@Setter
4848
private boolean isContractTerminated = false;
4949
@Setter
50+
private boolean isReAged = false;
51+
@Setter
5052
private boolean isPrepayAttempt = false;
5153
private final List<LoanRepaymentScheduleInstallment> skipRepaymentScheduleInstallments = new ArrayList<>();
5254

fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/InterestScheduleModelRepositoryWrapperImpl.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ public Optional<ProgressiveLoanInterestScheduleModel> getSavedModel(Loan loan, L
115115
ctx.setChargedOff(loan.isChargedOff());
116116
ctx.setWrittenOff(loan.isClosedWrittenOff());
117117
ctx.setContractTerminated(loan.isContractTermination());
118+
ctx.setReAged(loan.hasReAgingTransaction());
118119
advancedPaymentScheduleTransactionProcessor.recalculateInterestForDate(businessDate, ctx);
119120
}
120121
} else {

fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/EMICalculator.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,4 +140,7 @@ Money getPeriodInterestTillDate(@NotNull ProgressiveLoanInterestScheduleModel sc
140140
* interest paused.
141141
*/
142142
void applyInterestPause(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate fromDate, LocalDate endDate);
143+
144+
void calculateModelForReAgedRepaymentPeriods(ProgressiveLoanInterestScheduleModel scheduleModel, List<RepaymentPeriod> repaymentPeriods,
145+
LocalDate tillDate, Money zeroedCreditedPrincipal);
143146
}

fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/ProgressiveEMICalculator.java

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,23 @@ private void calculateEMIValueAndRateFactorsForDecliningBalanceInterestMethod(fi
559559
}
560560
}
561561

562+
@Override
563+
public void calculateModelForReAgedRepaymentPeriods(final ProgressiveLoanInterestScheduleModel scheduleModel,
564+
final List<RepaymentPeriod> repaymentPeriods, final LocalDate tillDate, final Money zeroedCreditedPrincipal) {
565+
repaymentPeriods.forEach(repaymentPeriod -> {
566+
calculateOutstandingBalance(scheduleModel);
567+
});
568+
569+
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, tillDate, zeroedCreditedPrincipal);
570+
checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(scheduleModel, repaymentPeriods, zeroedCreditedPrincipal);
571+
}
572+
562573
private void calculateLastUnpaidRepaymentPeriodEMI(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate tillDate) {
574+
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, tillDate, null);
575+
}
576+
577+
private void calculateLastUnpaidRepaymentPeriodEMI(ProgressiveLoanInterestScheduleModel scheduleModel, LocalDate tillDate,
578+
Money additionalCreditedPrincipal) {
563579
Optional<RepaymentPeriod> findLastUnpaidRepaymentPeriod = scheduleModel.repaymentPeriods().stream().filter(rp -> !rp.isFullyPaid())
564580
.reduce((first, second) -> second);
565581

@@ -582,14 +598,17 @@ private void calculateLastUnpaidRepaymentPeriodEMI(ProgressiveLoanInterestSchedu
582598
.flatMap(rp -> rp.getInterestPeriods().stream().map(InterestPeriod::getCapitalizedIncomePrincipal))
583599
.reduce(scheduleModel.zero(), (m1, m2) -> m1.plus(m2, mc)); // 100
584600

585-
Money diff = totalDisbursedAmount.plus(totalCapitalizedIncome, mc).plus(scheduleModel.getTotalCreditedPrincipal(), mc)
586-
.plus(totalDueInterest, mc).minus(totalEMI, mc);
601+
Money diff = totalDisbursedAmount.plus(totalCapitalizedIncome, mc).plus(scheduleModel.getTotalCreditedPrincipal(), mc);
602+
if (additionalCreditedPrincipal != null) {
603+
diff = diff.plus(additionalCreditedPrincipal, mc);
604+
}
605+
diff = diff.plus(totalDueInterest, mc).minus(totalEMI, mc);
587606

588607
repaymentPeriod.setEmi(repaymentPeriod.getEmi().add(diff, mc));
589608
if (repaymentPeriod.getEmi()
590609
.isLessThan(repaymentPeriod.getTotalPaidAmount().minus(repaymentPeriod.getTotalCreditedAmount(), mc))) {
591610
repaymentPeriod.setEmi(repaymentPeriod.getTotalPaidAmount().minus(repaymentPeriod.getTotalCreditedAmount(), mc));
592-
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, tillDate);
611+
calculateLastUnpaidRepaymentPeriodEMI(scheduleModel, tillDate, additionalCreditedPrincipal);
593612
}
594613

595614
calculateUnrecognizedInterestTillDateOnScheduleModelCopyAndDefer(scheduleModel, repaymentPeriod, tillDate);
@@ -618,6 +637,11 @@ private void calculateOutstandingBalance(ProgressiveLoanInterestScheduleModel sc
618637

619638
private void checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(final ProgressiveLoanInterestScheduleModel scheduleModel,
620639
final List<RepaymentPeriod> relatedRepaymentPeriods) {
640+
checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(scheduleModel, relatedRepaymentPeriods, null);
641+
}
642+
643+
private void checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(final ProgressiveLoanInterestScheduleModel scheduleModel,
644+
final List<RepaymentPeriod> relatedRepaymentPeriods, final Money additionalCreditedPrincipal) {
621645
MathContext mc = scheduleModel.mc();
622646
ProgressiveLoanInterestScheduleModel newScheduleModel = null;
623647
int adjustCounter = 1;
@@ -644,7 +668,11 @@ private void checkAndAdjustEmiIfNeededOnRelatedRepaymentPeriods(final Progressiv
644668
}
645669
});
646670
calculateOutstandingBalance(newScheduleModel);
647-
calculateLastUnpaidRepaymentPeriodEMI(newScheduleModel, relatedPeriodsFirstDueDate);
671+
if (additionalCreditedPrincipal != null) {
672+
calculateLastUnpaidRepaymentPeriodEMI(newScheduleModel, relatedPeriodsFirstDueDate, additionalCreditedPrincipal);
673+
} else {
674+
calculateLastUnpaidRepaymentPeriodEMI(newScheduleModel, relatedPeriodsFirstDueDate);
675+
}
648676
if (!getEmiAdjustment(newScheduleModel.repaymentPeriods()).hasLessEmiDifference(emiAdjustment)) {
649677
break;
650678
}

fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanproduct/calc/data/InterestPeriod.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ public class InterestPeriod implements Comparable<InterestPeriod> {
6868
@Getter
6969
private final MathContext mc;
7070

71-
private final boolean isPaused;
71+
@Setter
72+
private boolean isPaused;
7273

7374
public static InterestPeriod copy(@NotNull RepaymentPeriod repaymentPeriod, @NotNull InterestPeriod interestPeriod, MathContext mc) {
7475
return new InterestPeriod(repaymentPeriod, interestPeriod.getFromDate(), interestPeriod.getDueDate(),

0 commit comments

Comments
 (0)