FINERACT-1981: Fix down-payment handling on overpaid loan (Advanced payment allocation loan)

This commit is contained in:
Adam Saghy
2024-02-27 13:50:42 +01:00
parent 4a11f0ca29
commit 5be1001fd5
10 changed files with 772 additions and 505 deletions
@@ -2955,7 +2955,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
}
public LoanTransaction handleDownPayment(final BigDecimal disbursedAmount, final JsonCommand command,
public LoanTransaction handleDownPayment(final LoanTransaction disbursementTransaction, final JsonCommand command,
final ScheduleGeneratorDTO scheduleGeneratorDTO) {
LocalDate disbursedOn = command.localDateValueOfParameterNamed(ACTUAL_DISBURSEMENT_DATE);
BigDecimal disbursedAmountPercentageForDownPayment = this.loanRepaymentScheduleDetail.getDisbursedAmountPercentageForDownPayment();
@@ -2964,19 +2964,26 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
externalId = ExternalId.generate();
}
Money downPaymentMoney = Money.of(getCurrency(),
MathUtil.percentageOf(disbursedAmount, disbursedAmountPercentageForDownPayment, 19));
LoanTransaction downPaymentTransaction = LoanTransaction.downPayment(getOffice(), downPaymentMoney, null, disbursedOn, externalId);
MathUtil.percentageOf(disbursementTransaction.getAmount(), disbursedAmountPercentageForDownPayment, 19));
LoanEvent event = LoanEvent.LOAN_REPAYMENT_OR_WAIVER;
validateRepaymentTypeAccountStatus(downPaymentTransaction, event);
HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
validateRepaymentDateIsOnHoliday(downPaymentTransaction.getTransactionDate(), holidayDetailDTO.isAllowTransactionsOnHoliday(),
holidayDetailDTO.getHolidays());
validateRepaymentDateIsOnNonWorkingDay(downPaymentTransaction.getTransactionDate(), holidayDetailDTO.getWorkingDays(),
holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
Money adjustedDownPaymentMoney = MathUtil
.negativeToZero(downPaymentMoney.minus(disbursementTransaction.getOverPaymentPortion(getCurrency())));
if (adjustedDownPaymentMoney.isGreaterThanZero()) {
LoanTransaction downPaymentTransaction = LoanTransaction.downPayment(getOffice(), adjustedDownPaymentMoney, null, disbursedOn,
externalId);
LoanEvent event = LoanEvent.LOAN_REPAYMENT_OR_WAIVER;
validateRepaymentTypeAccountStatus(downPaymentTransaction, event);
HolidayDetailDTO holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO();
validateRepaymentDateIsOnHoliday(downPaymentTransaction.getTransactionDate(), holidayDetailDTO.isAllowTransactionsOnHoliday(),
holidayDetailDTO.getHolidays());
validateRepaymentDateIsOnNonWorkingDay(downPaymentTransaction.getTransactionDate(), holidayDetailDTO.getWorkingDays(),
holidayDetailDTO.isAllowTransactionsOnNonWorkingDay());
handleRepaymentOrRecoveryOrWaiverTransaction(downPaymentTransaction, loanLifecycleStateMachine, null, scheduleGeneratorDTO);
return downPaymentTransaction;
handleRepaymentOrRecoveryOrWaiverTransaction(downPaymentTransaction, loanLifecycleStateMachine, null, scheduleGeneratorDTO);
return downPaymentTransaction;
} else {
return null;
}
}
public boolean isAutoRepaymentForDownPaymentEnabled() {
@@ -465,10 +465,13 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
newLoanTransaction.adjustInterestComponent(currency);
}
/*
* Check if the transaction amounts have changed. If so, reverse the original transaction and update
* changedTransactionDetail accordingly
* Check if the transaction amounts have changed or was there any transaction for the same date which was
* reverse-replayed. If so, reverse the original transaction and update changedTransactionDetail accordingly
*/
if (LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)) {
boolean aTransactionWasAlreadyReplayedForTheSameDate = changedTransactionDetail.getNewTransactionMappings().values().stream()
.anyMatch(lt -> lt.getTransactionDate().equals(loanTransaction.getTransactionDate()));
if (LoanTransaction.transactionAmountsMatch(currency, loanTransaction, newLoanTransaction)
&& !aTransactionWasAlreadyReplayedForTheSameDate) {
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(
newLoanTransaction.getLoanTransactionToRepaymentScheduleMappings());
} else {
@@ -503,6 +506,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
private void updateLoanSchedule(LoanTransaction disbursementTransaction, MonetaryCurrency currency,
List<LoanRepaymentScheduleInstallment> installments, MoneyHolder overpaymentHolder) {
disbursementTransaction.resetDerivedComponents();
final MathContext mc = MoneyHelper.getMathContext();
List<LoanRepaymentScheduleInstallment> candidateRepaymentInstallments = installments.stream().filter(
i -> i.getDueDate().isAfter(disbursementTransaction.getTransactionDate()) && !i.isDownPayment() && !i.isAdditional())
@@ -570,9 +574,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
transactionAmountUnprocessed = processPeriodsVertically(loanTransaction, currency, installments,
overpaymentHolder.getMoneyObject(), defaultPaymentAllocationRule, transactionMappings, Set.of(), balances);
}
if (transactionAmountUnprocessed != null && transactionAmountUnprocessed.isGreaterThanZero()) {
overpaymentHolder.setMoneyObject(transactionAmountUnprocessed);
}
overpaymentHolder.setMoneyObject(transactionAmountUnprocessed);
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings);
}
}