FINERACT-2060: re-amortization

This commit is contained in:
taskain7
2024-03-13 14:15:14 +01:00
parent f05be57c50
commit dce264c169
3 changed files with 585 additions and 2 deletions
@@ -18,9 +18,11 @@
*/
package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.impl;
import static java.math.BigDecimal.ZERO;
import static java.util.stream.Collectors.mapping;
import static java.util.stream.Collectors.toList;
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionRelationTypeEnum.CHARGEBACK;
import static org.apache.fineract.portfolio.loanaccount.domain.LoanTransactionType.REAMORTIZE;
import static org.apache.fineract.portfolio.loanproduct.domain.AllocationType.FEE;
import static org.apache.fineract.portfolio.loanproduct.domain.AllocationType.INTEREST;
import static org.apache.fineract.portfolio.loanproduct.domain.AllocationType.PENALTY;
@@ -182,6 +184,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
case CHARGE_PAYMENT -> handleChargePayment(loanTransaction, ctx.getCurrency(), ctx.getInstallments(), ctx.getCharges(),
ctx.getOverpaymentHolder());
case WAIVE_CHARGES -> log.debug("WAIVE_CHARGES transaction will not be processed.");
case REAMORTIZE -> handleReAmortization(loanTransaction, ctx.getCurrency(), ctx.getInstallments());
// TODO: Cover rest of the transaction types
default -> {
log.warn("Unhandled transaction processing for transaction type: {}", loanTransaction.getTypeOf());
@@ -189,6 +192,52 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
}
}
private void handleReAmortization(LoanTransaction loanTransaction, MonetaryCurrency currency,
List<LoanRepaymentScheduleInstallment> installments) {
BigDecimal remainingAmount = loanTransaction.getAmount();
LocalDate transactionDate = loanTransaction.getTransactionDate();
List<LoanRepaymentScheduleInstallment> previousInstallments = installments.stream() //
.filter(installment -> !installment.getDueDate().isAfter(transactionDate)) //
.toList();
List<LoanRepaymentScheduleInstallment> futureInstallments = installments.stream() //
.filter(installment -> installment.getDueDate().isAfter(transactionDate)) //
.filter(installment -> !installment.isAdditional() && !installment.isDownPayment()) //
.toList();
BigDecimal overallOverDuePrincipal = ZERO;
for (LoanRepaymentScheduleInstallment installment : previousInstallments) {
Money principalCompleted = installment.getPrincipalCompleted(currency);
overallOverDuePrincipal = overallOverDuePrincipal.add(installment.getPrincipal(currency).minus(principalCompleted).getAmount());
installment.updatePrincipal(installment.getPrincipalCompleted(currency).getAmount());
installment.updateDerivedFields(currency, transactionDate);
}
if (overallOverDuePrincipal.compareTo(remainingAmount) != 0) {
remainingAmount = overallOverDuePrincipal;
loanTransaction.updateComponentsAndTotal(Money.of(currency, remainingAmount), Money.zero(currency), Money.zero(currency),
Money.zero(currency));
}
LoanRepaymentScheduleInstallment lastFutureInstallment = futureInstallments.stream()
.max(Comparator.comparing(LoanRepaymentScheduleInstallment::getDueDate)).get();
BigDecimal reAmortizationAmountPerInstallment = remainingAmount.divide(BigDecimal.valueOf(futureInstallments.size()),
MoneyHelper.getRoundingMode());
Integer installmentAmountInMultiplesOf = loanTransaction.getLoan().getLoanProduct().getInstallmentAmountInMultiplesOf();
for (LoanRepaymentScheduleInstallment installment : futureInstallments) {
if (lastFutureInstallment.equals(installment)) {
installment.addToPrincipal(transactionDate, Money.of(currency, remainingAmount));
} else {
if (installmentAmountInMultiplesOf != null) {
reAmortizationAmountPerInstallment = Money.roundToMultiplesOf(reAmortizationAmountPerInstallment,
installmentAmountInMultiplesOf);
}
installment.addToPrincipal(transactionDate, Money.of(currency, reAmortizationAmountPerInstallment));
remainingAmount = remainingAmount.subtract(reAmortizationAmountPerInstallment);
}
}
}
@Override
protected void handleChargeback(LoanTransaction loanTransaction, TransactionCtx ctx) {
processCreditTransaction(loanTransaction, ctx);