Skip to content

Commit fa5be3f

Browse files
somasorosdpcadamsaghy
authored andcommitted
FINERACT-2354: [BE] chargeback handling with backdated re-age for last adjustment strategy with equal amortization re-aging behaviour
1 parent 1ad4575 commit fa5be3f

File tree

6 files changed

+293
-94
lines changed

6 files changed

+293
-94
lines changed

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import jakarta.persistence.Table;
2929
import java.math.BigDecimal;
3030
import java.time.LocalDate;
31+
import java.util.Comparator;
3132
import java.util.HashSet;
3233
import java.util.List;
3334
import java.util.Objects;
@@ -518,8 +519,9 @@ public int compareTo(LoanRepaymentScheduleInstallment o) {
518519
return this.installmentNumber.compareTo(o.installmentNumber);
519520
}
520521

521-
public int compareToByDueDate(LoanRepaymentScheduleInstallment o) {
522-
return this.dueDate.compareTo(o.dueDate);
522+
public int compareToByFromDueDate(LoanRepaymentScheduleInstallment o) {
523+
return Comparator.comparing(LoanRepaymentScheduleInstallment::getDueDate)
524+
.thenComparing(LoanRepaymentScheduleInstallment::getFromDate).compare(this, o);
523525
}
524526

525527
public boolean isPrincipalNotCompleted(final MonetaryCurrency currency) {

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

Lines changed: 34 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3171,14 +3171,15 @@ private LoanRepaymentScheduleInstallment createInstallmentWithMovedPaidAmounts(f
31713171
private void reprocessInstallments(final List<LoanRepaymentScheduleInstallment> installments) {
31723172
final AtomicInteger counter = new AtomicInteger(1);
31733173
final AtomicReference<LocalDate> previousDueDate = new AtomicReference<>(null);
3174-
installments.stream().sorted(LoanRepaymentScheduleInstallment::compareToByDueDate).forEachOrdered(i -> {
3174+
installments.stream().sorted(LoanRepaymentScheduleInstallment::compareToByFromDueDate).forEachOrdered(i -> {
31753175
i.updateInstallmentNumber(counter.getAndIncrement());
31763176
final LocalDate prev = previousDueDate.get();
31773177
if (prev != null && (i.isAdditional() || i.isReAged())) {
31783178
i.updateFromDate(prev);
31793179
}
31803180
previousDueDate.set(i.getDueDate());
31813181
});
3182+
installments.sort(LoanRepaymentScheduleInstallment::compareToByFromDueDate);
31823183
}
31833184

31843185
private LocalDate calculateReAgedInstallmentDueDate(final LoanReAgeParameter reAgeParameter, final LocalDate dueDate) {
@@ -3479,12 +3480,8 @@ private void handleReAgeEqualAmortizationEMICalculator(LoanTransaction loanTrans
34793480
BalancesWithPaidInAdvance paidInAdvanceBalances = liftEarlyRepaidBalances(installments, transactionDate, currency,
34803481
ctx.getAlreadyProcessedTransactions());
34813482

3482-
// TODO add as Parameter here: paidInAdvanceBalances.getAggregatedFeeChargesPortion().isGreaterThanZero() ||
3483-
// paidInAdvanceBalances.getAggregatedPenaltyChargesPortion().isGreaterThanZero()
34843483
emiCalculator.reAgeEqualAmortization(model, transactionDate, loanReAgeParameter,
3485-
outstandingBalances.fees.add(outstandingBalances.penalties),
3486-
new EqualAmortizationValues(calculatedFees.value().add(calculatedPenalties.value()),
3487-
calculatedFees.adjustment().add(calculatedPenalties.adjustment())));
3484+
outstandingBalances.fees.add(outstandingBalances.penalties), calculatedFees.add(calculatedPenalties));
34883485

34893486
installments.removeIf(i -> (i.getInstallmentNumber() != null && !i.isDownPayment() && !i.getDueDate().isBefore(transactionDate)
34903487
&& !i.isAdditional()) || (!i.getDueDate().isAfter(model.getMaturityDate()) && i.isAdditional()));
@@ -3494,6 +3491,7 @@ private void handleReAgeEqualAmortizationEMICalculator(LoanTransaction loanTrans
34943491
i.setInstallmentNumber(model.repaymentPeriods().size());
34953492
});
34963493

3494+
int reAgedInstallmentIndex = 0;
34973495
for (int index = 0; index < model.repaymentPeriods().size(); index++) {
34983496
RepaymentPeriod rp = model.repaymentPeriods().get(index);
34993497
if (rp.getDueDate().isBefore(transactionDate)) {
@@ -3504,9 +3502,9 @@ private void handleReAgeEqualAmortizationEMICalculator(LoanTransaction loanTrans
35043502
installment.setInterestCharged(installment.getInterestPaid());
35053503
installment.setPrincipal(installment.getPrincipalCompleted(currency).getAmount());
35063504
installment.setInstallmentNumber(index + 1);
3505+
installment.setCreditedPrincipal(rp.getCreditedPrincipal().getAmount());
35073506

35083507
installment.updateObligationsMet(currency, transactionDate);
3509-
// TODO add remaining components
35103508
} else {
35113509
LoanRepaymentScheduleInstallment created = LoanRepaymentScheduleInstallment.newReAgedInstallment(loanTransaction.getLoan(),
35123510
index + 1, rp.getFromDate(), rp.getDueDate(), rp.getDuePrincipal().getAmount(), rp.getDueInterest().getAmount(),
@@ -3525,16 +3523,17 @@ private void handleReAgeEqualAmortizationEMICalculator(LoanTransaction loanTrans
35253523

35263524
paidInAdvanceBalances.loanTransactionToRepaymentScheduleMappings.forEach(m -> m.setInstallment(created));
35273525
} else {
3528-
boolean isLastRepaymentPeriod = model.isLastRepaymentPeriod(rp);
3529-
created.setFeeChargesCharged(calculatedFees.calculateValueBigDecimal(isLastRepaymentPeriod));
3530-
created.setPenaltyCharges(calculatedPenalties.calculateValueBigDecimal(isLastRepaymentPeriod));
3526+
created.setFeeChargesCharged(calculatedFees.calculateValueBigDecimal(reAgedInstallmentIndex));
3527+
created.setPenaltyCharges(calculatedPenalties.calculateValueBigDecimal(reAgedInstallmentIndex));
35313528

3532-
created.setInterestAccrued(calculatedInterestAccrued.calculateValueBigDecimal(isLastRepaymentPeriod));
3533-
created.setFeeAccrued(calculatedFeeAccrued.calculateValueBigDecimal(isLastRepaymentPeriod));
3534-
created.setPenaltyAccrued(calculatedPenaltyAccrued.calculateValueBigDecimal(isLastRepaymentPeriod));
3529+
created.setInterestAccrued(calculatedInterestAccrued.calculateValueBigDecimal(reAgedInstallmentIndex));
3530+
created.setFeeAccrued(calculatedFeeAccrued.calculateValueBigDecimal(reAgedInstallmentIndex));
3531+
created.setPenaltyAccrued(calculatedPenaltyAccrued.calculateValueBigDecimal(reAgedInstallmentIndex));
35353532

3536-
createChargeMappingsForInstallment(created, calculatedCharges, isLastRepaymentPeriod);
3533+
createChargeMappingsForInstallment(created, calculatedCharges, reAgedInstallmentIndex);
3534+
reAgedInstallmentIndex++;
35373535
}
3536+
created.setCreditedPrincipal(rp.getCreditedPrincipal().getAmount());
35383537
created.updateObligationsMet(currency, transactionDate);
35393538
installments.add(created);
35403539
}
@@ -3549,6 +3548,9 @@ private void handleReAgeWithCommonStrategy(LoanTransaction loanTransaction, Comm
35493548
List<LoanRepaymentScheduleInstallment> installments = ctx.getInstallments();
35503549
LoanReAgeParameter loanReAgeParameter = loanTransaction.getLoanReAgeParameter();
35513550
LocalDate transactionDate = loanTransaction.getTransactionDate();
3551+
LocalDate originalMaturityDate = installments.stream()
3552+
.filter(i -> !i.isDownPayment() && !i.isAdditional() && i.getDueDate() != null)
3553+
.map(LoanRepaymentScheduleInstallment::getDueDate).max(LocalDate::compareTo).orElseThrow();
35523554

35533555
Integer numberOfReAgeInstallments = loanReAgeParameter.getNumberOfInstallments();
35543556
Integer installmentAmountInMultiplesOf = loanTransaction.getLoan().getLoanProductRelatedDetail()
@@ -3613,8 +3615,7 @@ private void handleReAgeWithCommonStrategy(LoanTransaction loanTransaction, Comm
36133615
return res;
36143616
}).reduce(new BalancesWithPaidInAdvance(currency), BalancesWithPaidInAdvance::summarizerAccumulator);
36153617

3616-
if (!balances.getPrincipal().isZero() || !balances.getInterest().isZero() || !balances.getFee().isZero()
3617-
|| !balances.getPenalty().isZero()) {
3618+
if (!transactionDate.isAfter(originalMaturityDate)) {
36183619

36193620
final LoanRepaymentScheduleInstallment earlyRepaidInstallment = LoanRepaymentScheduleInstallment.newReAgedInstallment(loan,
36203621
firstReAgeInstallmentProps.reAgedInstallmentNumber(), firstReAgeInstallmentProps.fromDate(), transactionDate,
@@ -3634,37 +3635,39 @@ private void handleReAgeWithCommonStrategy(LoanTransaction loanTransaction, Comm
36343635

36353636
InstallmentProcessingHelper.addOneToInstallmentNumberFromInstallment(installments,
36363637
earlyRepaidInstallment.getInstallmentNumber());
3637-
loan.getRepaymentScheduleInstallments().add(earlyRepaidInstallment);
3638+
installments.add(earlyRepaidInstallment);
36383639
}
36393640

3641+
// installment index which excludes earlyRepaidInstallment intallment index.
3642+
Integer reAgedInstallmentIndex = 0;
36403643
LoanRepaymentScheduleInstallment reAgedInstallment = LoanRepaymentScheduleInstallment.newReAgedInstallment(loan,
36413644
firstReAgeInstallmentProps.reAgedInstallmentNumber, firstReAgeInstallmentProps.fromDate, loanReAgeParameter.getStartDate(),
36423645
calculatedPrincipal.value().getAmount(), calculatedInterest.value().getAmount(), calculatedFees.value().getAmount(),
36433646
calculatedPenalties.value().getAmount(), calculatedInterestAccrued.value().getAmount(),
36443647
calculatedFeeAccrued.value().getAmount(), calculatedPenaltyAccrued.value().getAmount());
36453648

36463649
reAgedInstallment = insertOrReplaceRelatedInstallment(installments, reAgedInstallment, currency, transactionDate);
3647-
createChargeMappingsForInstallment(reAgedInstallment, calculatedCharges, false);
3648-
3650+
createChargeMappingsForInstallment(reAgedInstallment, calculatedCharges, reAgedInstallmentIndex);
3651+
reAgedInstallmentIndex++;
36493652
for (int i = 1; i < numberOfReAgeInstallments; i++) {
36503653
LocalDate calculatedDueDate = scheduledDateGenerator.getRepaymentPeriodDate(loanReAgeParameter.getFrequencyType(),
36513654
loanReAgeParameter.getFrequencyNumber(), reAgedInstallment.getDueDate());
36523655
calculateReAgedInstallmentDueDate(loanReAgeParameter, reAgedInstallment.getDueDate());
36533656
int nextReAgedInstallmentNumber = firstReAgeInstallmentProps.reAgedInstallmentNumber + i;
3654-
boolean isLastInstallment = i + 1 == numberOfReAgeInstallments;
36553657

36563658
reAgedInstallment = LoanRepaymentScheduleInstallment.newReAgedInstallment(reAgedInstallment.getLoan(),
36573659
nextReAgedInstallmentNumber, reAgedInstallment.getDueDate(), calculatedDueDate,
3658-
calculatedPrincipal.calculateValueBigDecimal(isLastInstallment),
3659-
calculatedInterest.calculateValueBigDecimal(isLastInstallment),
3660-
calculatedFees.calculateValueBigDecimal(isLastInstallment),
3661-
calculatedPenalties.calculateValueBigDecimal(isLastInstallment),
3662-
calculatedInterestAccrued.calculateValueBigDecimal(isLastInstallment),
3663-
calculatedFeeAccrued.calculateValueBigDecimal(isLastInstallment),
3664-
calculatedPenaltyAccrued.calculateValueBigDecimal(isLastInstallment));
3660+
calculatedPrincipal.calculateValueBigDecimal(reAgedInstallmentIndex),
3661+
calculatedInterest.calculateValueBigDecimal(reAgedInstallmentIndex),
3662+
calculatedFees.calculateValueBigDecimal(reAgedInstallmentIndex),
3663+
calculatedPenalties.calculateValueBigDecimal(reAgedInstallmentIndex),
3664+
calculatedInterestAccrued.calculateValueBigDecimal(reAgedInstallmentIndex),
3665+
calculatedFeeAccrued.calculateValueBigDecimal(reAgedInstallmentIndex),
3666+
calculatedPenaltyAccrued.calculateValueBigDecimal(reAgedInstallmentIndex));
36653667

36663668
reAgedInstallment = insertOrReplaceRelatedInstallment(installments, reAgedInstallment, currency, transactionDate);
3667-
createChargeMappingsForInstallment(reAgedInstallment, calculatedCharges, isLastInstallment);
3669+
createChargeMappingsForInstallment(reAgedInstallment, calculatedCharges, reAgedInstallmentIndex);
3670+
reAgedInstallmentIndex++;
36683671
}
36693672
int lastReAgedInstallmentNumber = reAgedInstallment.getInstallmentNumber();
36703673
List<LoanRepaymentScheduleInstallment> toRemove = installments.stream()
@@ -3676,11 +3679,10 @@ private void handleReAgeWithCommonStrategy(LoanTransaction loanTransaction, Comm
36763679
}
36773680

36783681
private void createChargeMappingsForInstallment(final LoanRepaymentScheduleInstallment installment,
3679-
List<ReAgedChargeEqualAmortizationValues> reAgedChargeEqualAmortizationValues, boolean isLastInstallment) {
3682+
List<ReAgedChargeEqualAmortizationValues> reAgedChargeEqualAmortizationValues, Integer index) {
36803683
reAgedChargeEqualAmortizationValues.forEach(amortizationValue -> {
3681-
installment.getInstallmentCharges()
3682-
.add(new LoanInstallmentCharge(amortizationValue.equalAmortizationValues.calculateValueBigDecimal(isLastInstallment),
3683-
amortizationValue.charge, installment));
3684+
installment.getInstallmentCharges().add(new LoanInstallmentCharge(
3685+
amortizationValue.equalAmortizationValues.calculateValueBigDecimal(index), amortizationValue.charge, installment));
36843686
});
36853687
}
36863688

0 commit comments

Comments
 (0)