FINERACT-2326: Fix payment allocation when current period is closed
This commit is contained in:
+70
-27
@@ -1570,10 +1570,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
} else {
|
||||
lastOverdueBalanceChange = currentDate;
|
||||
}
|
||||
if (DateUtils.isBefore(ctx.getModel().lastOverdueBalanceChange(), toDate)) {
|
||||
emiCalculator.addBalanceCorrection(ctx.getModel(), lastOverdueBalanceChange, aggregatedOverDuePrincipal.negated());
|
||||
ctx.getModel().lastOverdueBalanceChange(lastOverdueBalanceChange);
|
||||
}
|
||||
emiCalculator.addBalanceCorrection(ctx.getModel(), lastOverdueBalanceChange, aggregatedOverDuePrincipal.negated());
|
||||
ctx.getModel().lastOverdueBalanceChange(lastOverdueBalanceChange);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1599,10 +1597,32 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
if (loanTransaction.isRepaymentLikeType() || loanTransaction.isInterestWaiver() || loanTransaction.isRecoveryRepayment()) {
|
||||
loanTransaction.resetDerivedComponents();
|
||||
}
|
||||
calculateUnrecognizedInterestForClosedPeriodByInterestRecalculationStrategy(loanTransaction, transactionCtx);
|
||||
|
||||
Money transactionAmountUnprocessed = loanTransaction.getAmount(transactionCtx.getCurrency());
|
||||
processTransaction(loanTransaction, transactionCtx, transactionAmountUnprocessed);
|
||||
}
|
||||
|
||||
private void calculateUnrecognizedInterestForClosedPeriodByInterestRecalculationStrategy(LoanTransaction loanTransaction,
|
||||
TransactionCtx transactionCtx) {
|
||||
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx && progressiveTransactionCtx.isPrepayAttempt()
|
||||
&& loanTransaction.isRepaymentLikeType() && loanTransaction.getLoan().getLoanInterestRecalculationDetails()
|
||||
.getPreCloseInterestCalculationStrategy().calculateTillRestFrequencyEnabled()) {
|
||||
Optional<RepaymentPeriod> oCurrentRepaymentPeriod = progressiveTransactionCtx.getModel().repaymentPeriods().stream()
|
||||
.filter(rm -> DateUtils.isDateInRangeFromInclusiveToExclusive(rm.getFromDate(), rm.getDueDate(),
|
||||
loanTransaction.getTransactionDate()))
|
||||
.findFirst();
|
||||
if (oCurrentRepaymentPeriod.isPresent() && oCurrentRepaymentPeriod.get().isFullyPaid()) {
|
||||
RepaymentPeriod currentRepaymentPeriod = oCurrentRepaymentPeriod.get();
|
||||
OutstandingDetails outstandingAmountsTillDate = emiCalculator
|
||||
.getOutstandingAmountsTillDate(progressiveTransactionCtx.getModel(), currentRepaymentPeriod.getDueDate());
|
||||
if (outstandingAmountsTillDate.getOutstandingInterest().isGreaterThanZero()) {
|
||||
currentRepaymentPeriod.setFutureUnrecognizedInterest(outstandingAmountsTillDate.getOutstandingInterest());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private LoanTransactionToRepaymentScheduleMapping getTransactionMapping(
|
||||
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings, LoanTransaction loanTransaction,
|
||||
LoanRepaymentScheduleInstallment currentInstallment, MonetaryCurrency currency) {
|
||||
@@ -1751,26 +1771,28 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
final LoanRepaymentScheduleInstallment currentInstallment = loan.getRelatedRepaymentScheduleInstallment(transactionDate);
|
||||
|
||||
if (!installments.isEmpty() && transactionDate.isBefore(loan.getMaturityDate())) {
|
||||
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
|
||||
&& loanTransaction.getLoan().isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
final BigDecimal interestOutstanding = currentInstallment.getInterestOutstanding(loan.getCurrency()).getAmount();
|
||||
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(),
|
||||
currentInstallment.getDueDate(), transactionDate, true).getAmount();
|
||||
if (interestOutstanding.compareTo(BigDecimal.ZERO) > 0 || newInterest.compareTo(BigDecimal.ZERO) > 0) {
|
||||
currentInstallment.updateInterestCharged(newInterest);
|
||||
}
|
||||
} else {
|
||||
final BigDecimal totalInterest = currentInstallment.getInterestOutstanding(transactionCtx.getCurrency()).getAmount();
|
||||
if (totalInterest.compareTo(BigDecimal.ZERO) > 0) {
|
||||
final long totalDaysInPeriod = ChronoUnit.DAYS.between(currentInstallment.getFromDate(),
|
||||
currentInstallment.getDueDate());
|
||||
final long daysTillChargeOff = ChronoUnit.DAYS.between(currentInstallment.getFromDate(), transactionDate);
|
||||
if (currentInstallment.isNotFullyPaidOff()) {
|
||||
if (transactionCtx instanceof ProgressiveTransactionCtx progressiveTransactionCtx
|
||||
&& loanTransaction.getLoan().isInterestBearingAndInterestRecalculationEnabled()) {
|
||||
final BigDecimal interestOutstanding = currentInstallment.getInterestOutstanding(loan.getCurrency()).getAmount();
|
||||
final BigDecimal newInterest = emiCalculator.getPeriodInterestTillDate(progressiveTransactionCtx.getModel(),
|
||||
currentInstallment.getDueDate(), transactionDate, true).getAmount();
|
||||
if (interestOutstanding.compareTo(BigDecimal.ZERO) > 0 || newInterest.compareTo(BigDecimal.ZERO) > 0) {
|
||||
currentInstallment.updateInterestCharged(newInterest);
|
||||
}
|
||||
} else {
|
||||
final BigDecimal totalInterest = currentInstallment.getInterestOutstanding(transactionCtx.getCurrency()).getAmount();
|
||||
if (totalInterest.compareTo(BigDecimal.ZERO) > 0) {
|
||||
final long totalDaysInPeriod = ChronoUnit.DAYS.between(currentInstallment.getFromDate(),
|
||||
currentInstallment.getDueDate());
|
||||
final long daysTillChargeOff = ChronoUnit.DAYS.between(currentInstallment.getFromDate(), transactionDate);
|
||||
|
||||
final MathContext mc = MoneyHelper.getMathContext();
|
||||
final Money interestTillChargeOff = Money.of(transactionCtx.getCurrency(), totalInterest
|
||||
.divide(BigDecimal.valueOf(totalDaysInPeriod), mc).multiply(BigDecimal.valueOf(daysTillChargeOff), mc), mc);
|
||||
final MathContext mc = MoneyHelper.getMathContext();
|
||||
final Money interestTillChargeOff = Money.of(transactionCtx.getCurrency(), totalInterest
|
||||
.divide(BigDecimal.valueOf(totalDaysInPeriod), mc).multiply(BigDecimal.valueOf(daysTillChargeOff), mc), mc);
|
||||
|
||||
currentInstallment.updateInterestCharged(interestTillChargeOff.getAmount());
|
||||
currentInstallment.updateInterestCharged(interestTillChargeOff.getAmount());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1790,6 +1812,21 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
|
||||
currentInstallment.updatePrincipal(MathUtil.nullToZero(currentInstallment.getPrincipal()).add(futurePrincipal));
|
||||
|
||||
if (currentInstallment.isObligationsMet()) {
|
||||
final BigDecimal futureOutstandingPrincipal = futureInstallments.stream()
|
||||
.map(installment -> installment.getPrincipalOutstanding(transactionCtx.getCurrency()).getAmount())
|
||||
.filter(Objects::nonNull).reduce(BigDecimal.ZERO, BigDecimal::add);
|
||||
|
||||
final BigDecimal futureTotalPaidInAdvance = futureInstallments.stream()
|
||||
.map(LoanRepaymentScheduleInstallment::getTotalPaidInAdvance).filter(Objects::nonNull)
|
||||
.reduce(ZERO, BigDecimal::add);
|
||||
|
||||
currentInstallment
|
||||
.setPrincipalCompleted(MathUtil.nullToZero(currentInstallment.getPrincipal()).subtract(futureOutstandingPrincipal));
|
||||
currentInstallment.setTotalPaidInAdvance(
|
||||
MathUtil.nullToZero(currentInstallment.getTotalPaidInAdvance()).add(futureTotalPaidInAdvance));
|
||||
}
|
||||
|
||||
final List<LoanRepaymentScheduleInstallment> installmentsUpToTransactionDate = installments.stream()
|
||||
.filter(installment -> transactionDate.isAfter(installment.getFromDate()))
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
@@ -1817,7 +1854,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
}
|
||||
}
|
||||
|
||||
transactionCtx.getInstallments().stream().filter(installment -> installment.getFromDate().isBefore(transactionDate))
|
||||
transactionCtx.getInstallments().stream()
|
||||
.filter(installment -> installment.getFromDate().isBefore(transactionDate) && installment.isNotFullyPaidOff())
|
||||
.filter(installment -> transactionsToBeReprocessed.stream()
|
||||
.anyMatch(transaction -> transaction.getLoanTransactionToRepaymentScheduleMappings().stream().anyMatch(
|
||||
mapping -> mapping.getInstallment().getInstallmentNumber().equals(installment.getInstallmentNumber()))))
|
||||
@@ -1835,8 +1873,8 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
newTransaction.updateLoan(loan);
|
||||
loan.getLoanTransactions().add(newTransaction);
|
||||
}
|
||||
loanBalanceService.updateLoanSummaryDerivedFields(loan);
|
||||
}
|
||||
loanBalanceService.updateLoanSummaryDerivedFields(loan);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2870,9 +2908,14 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
||||
.filter(repaymentPeriod -> previousInstallmentsMapping.stream()
|
||||
.anyMatch(installment -> installment.getFromDate().equals(repaymentPeriod.getFromDate())))
|
||||
.forEach(RepaymentPeriod::resetDerivedComponents);
|
||||
emiCalculator.addBalanceCorrection(transactionCtx.getModel(), processTransaction.getTransactionDate(),
|
||||
processTransaction.getPrincipalPortion(transactionCtx.getCurrency()));
|
||||
processSingleTransaction(processTransaction, transactionCtx);
|
||||
|
||||
LoanRepaymentScheduleInstallment installment = processTransaction.getLoan()
|
||||
.getRelatedRepaymentScheduleInstallment(processTransaction.getTransactionDate());
|
||||
if (installment == null || installment.isNotFullyPaidOff()) {
|
||||
emiCalculator.addBalanceCorrection(transactionCtx.getModel(), processTransaction.getTransactionDate(),
|
||||
processTransaction.getPrincipalPortion(transactionCtx.getCurrency()));
|
||||
processSingleTransaction(processTransaction, transactionCtx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user