FINERACT-1958-Overlapping-installment-payment-allocation

This commit is contained in:
Ruchi Dhamankar
2023-08-29 14:08:08 +02:00
committed by Adam Saghy
parent 001ad2fa1f
commit e7013309ca
4 changed files with 757 additions and 15 deletions
@@ -367,6 +367,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
periods.add(installment);
if (loanApplicationTerms.isMultiDisburseLoan() && loanApplicationTerms.isDownPaymentEnabled()) {
createDownPaymentInstallmentForOverlappingInstallments(loanApplicationTerms, scheduleParams, periods, scheduledDueDate);
}
// Updates principal paid map with efective date for reducing
// the amount from outstanding balance(interest calculation)
updateAmountsWithEffectiveDate(loanApplicationTerms, holidayDetailDTO, scheduleParams, scheduledDueDate, currentPeriodParams,
@@ -427,6 +431,29 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
totalOutstanding);
}
private void createDownPaymentInstallmentForOverlappingInstallments(LoanApplicationTerms loanApplicationTerms,
LoanScheduleParams scheduleParams, Collection<LoanScheduleModelPeriod> periods, LocalDate scheduledDueDate) {
for (Map.Entry<LocalDate, Money> disburseDetail : scheduleParams.getDisburseDetailMap().entrySet()) {
if (disburseDetail.getKey().isAfter(scheduleParams.getPeriodStartDate()) && disburseDetail.getKey().isEqual(scheduledDueDate)) {
scheduleParams.incrementPeriodNumber();
scheduleParams.incrementInstalmentNumber();
BigDecimal downPaymentAmount = MathUtil.percentageOf(disburseDetail.getValue().getAmount(),
loanApplicationTerms.getDisbursedAmountPercentageForDownPayment(), 19);
Money downPayment = Money.of(loanApplicationTerms.getCurrency(), downPaymentAmount);
LoanScheduleModelDownPaymentPeriod installment = LoanScheduleModelDownPaymentPeriod.downPayment(
scheduleParams.getInstalmentNumber(), disburseDetail.getKey(), downPayment, scheduleParams.getOutstandingBalance());
addLoanRepaymentScheduleInstallment(scheduleParams.getInstallments(), installment);
periods.add(installment);
scheduleParams.reduceOutstandingBalance(downPayment);
scheduleParams.addTotalCumulativePrincipal(downPayment);
scheduleParams.addTotalRepaymentExpected(downPayment);
}
}
}
private void updateCompoundingDetails(final Collection<LoanScheduleModelPeriod> periods, final LoanScheduleParams params,
final LoanApplicationTerms loanApplicationTerms) {
final Map<LocalDate, Map<LocalDate, Money>> compoundingDetails = params.getCompoundingDateVariations();
@@ -998,8 +1025,10 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
periods.add(disbursementPeriod);
if (loanApplicationTerms.isDownPaymentEnabled()) {
createDownPaymentPeriod(loanApplicationTerms, scheduleParams, periods, disburseDetail.getKey(),
disburseDetail.getValue().getAmount());
if (!disburseDetail.getKey().isEqual(scheduledDueDate)) {
createDownPaymentPeriod(loanApplicationTerms, scheduleParams, periods, disburseDetail.getKey(),
disburseDetail.getValue().getAmount());
}
}
// updates actual outstanding balance with new
@@ -2348,11 +2377,14 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
.disbursement(disburseDetail.getKey(), disburseDetail.getValue(), chargesDueAtTimeOfDisbursement);
periods.add(disbursementPeriod);
if (loanApplicationTerms.isDownPaymentEnabled()) {
loanScheduleParams = LoanScheduleParams.createLoanScheduleParams(currency,
Money.of(currency, chargesDueAtTimeOfDisbursement), loanApplicationTerms.getExpectedDisbursementDate(),
getPrincipalToBeScheduled(loanApplicationTerms));
createDownPaymentPeriod(loanApplicationTerms, loanScheduleParams, periods, disburseDetail.getKey(),
disbursementPeriod.principalDue());
if (!disburseDetail.getKey().isEqual(installment.getDueDate())) {
loanScheduleParams = LoanScheduleParams.createLoanScheduleParams(currency,
Money.of(currency, chargesDueAtTimeOfDisbursement),
loanApplicationTerms.getExpectedDisbursementDate(),
getPrincipalToBeScheduled(loanApplicationTerms));
createDownPaymentPeriod(loanApplicationTerms, loanScheduleParams, periods, disburseDetail.getKey(),
disbursementPeriod.principalDue());
}
}
// updates actual outstanding balance with new
// disbursement detail
@@ -2369,6 +2401,22 @@ public abstract class AbstractLoanScheduleGenerator implements LoanScheduleGener
outstandingBalance = outstandingBalance.minus(installment.getPrincipal(currency));
final LoanScheduleModelPeriod loanScheduleModelPeriod = createLoanScheduleModelPeriod(installment, outstandingBalance);
periods.add(loanScheduleModelPeriod);
// downpayment installment for overlapping case
if (loanApplicationTerms.isMultiDisburseLoan() && loanApplicationTerms.isDownPaymentEnabled()) {
for (Map.Entry<LocalDate, Money> disburseDetail : disburseDetailMap.entrySet()) {
if (disburseDetail.getKey().isAfter(installment.getFromDate())
&& disburseDetail.getKey().isEqual(installment.getDueDate())) {
loanScheduleParams = LoanScheduleParams.createLoanScheduleParams(currency,
Money.of(currency, chargesDueAtTimeOfDisbursement), loanApplicationTerms.getExpectedDisbursementDate(),
getPrincipalToBeScheduled(loanApplicationTerms));
createDownPaymentPeriod(loanApplicationTerms, loanScheduleParams, periods, disburseDetail.getKey(),
disburseDetail.getValue().getAmount());
}
}
}
totalCumulativePrincipal = totalCumulativePrincipal.plus(installment.getPrincipal(currency));
totalCumulativeInterest = totalCumulativeInterest.plus(installment.getInterestCharged(currency));
totalFeeChargesCharged = totalFeeChargesCharged.plus(installment.getFeeChargesCharged(currency));
@@ -447,7 +447,8 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
RepaymentTransactionTemplateMapper mapper = new RepaymentTransactionTemplateMapper(sqlGenerator);
String sql = "select " + mapper.schema();
LoanTransactionData loanTransactionData = this.jdbcTemplate.queryForObject(sql, mapper, // NOSONAR
LoanTransactionType.REPAYMENT.getValue(), LoanTransactionType.REPAYMENT.getValue(), loanId, loanId);
LoanTransactionType.REPAYMENT.getValue(), LoanTransactionType.DOWN_PAYMENT.getValue(),
LoanTransactionType.REPAYMENT.getValue(), LoanTransactionType.DOWN_PAYMENT.getValue(), loanId, loanId);
final Collection<PaymentTypeData> paymentOptions = this.paymentTypeReadPlatformService.retrieveAllPaymentTypes();
return LoanTransactionData.templateOnTop(loanTransactionData, paymentOptions);
}
@@ -2494,9 +2495,9 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
StringBuilder sqlBuilder = new StringBuilder();
sqlBuilder.append("(CASE ");
sqlBuilder.append(
"WHEN (select max(tr.transaction_date) as transaction_date from m_loan_transaction tr where tr.loan_id = l.id AND tr.transaction_type_enum = ? AND tr.is_reversed = false) > ls.dueDate ");
"WHEN (select max(tr.transaction_date) as transaction_date from m_loan_transaction tr where tr.loan_id = l.id AND tr.transaction_type_enum in (?,?) AND tr.is_reversed = false) > ls.dueDate ");
sqlBuilder.append(
"THEN (select max(tr.transaction_date) as transaction_date from m_loan_transaction tr where tr.loan_id = l.id AND tr.transaction_type_enum = ? AND tr.is_reversed = false) ");
"THEN (select max(tr.transaction_date) as transaction_date from m_loan_transaction tr where tr.loan_id = l.id AND tr.transaction_type_enum in (?,?) AND tr.is_reversed = false) ");
sqlBuilder.append("ELSE ls.dueDate END) as transactionDate, ");
sqlBuilder.append(
"ls.principal_amount - coalesce(ls.principal_writtenoff_derived, 0) - coalesce(ls.principal_completed_derived, 0) as principalDue, ");
@@ -2516,7 +2517,7 @@ public class LoanReadPlatformServiceImpl implements LoanReadPlatformService, Loa
sqlBuilder.append(
"JOIN((SELECT ls.loan_id, ls.duedate as datedue FROM m_loan_repayment_schedule ls WHERE ls.loan_id = ? and ls.completed_derived = false ORDER BY ls.duedate LIMIT 1)) asq on asq.loan_id = ls.loan_id ");
sqlBuilder.append("AND asq.datedue = ls.duedate ");
sqlBuilder.append("WHERE l.id = ?");
sqlBuilder.append("WHERE l.id = ? LIMIT 1");
return sqlBuilder.toString();
}