Skip to content

Commit

Permalink
FINERACT-1958-Overlapping-installment-payment-allocation
Browse files Browse the repository at this point in the history
  • Loading branch information
ruchiD authored and adamsaghy committed Aug 29, 2023
1 parent 001ad2f commit e701330
Show file tree
Hide file tree
Showing 4 changed files with 757 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2972,10 +2972,6 @@ public void handleDownPayment(final BigDecimal disbursedAmount, final JsonComman
MathUtil.percentageOf(disbursedAmount, disbursedAmountPercentageForDownPayment, 19));
LoanTransaction downPaymentTransaction = LoanTransaction.downPayment(getOffice(), downPaymentMoney, null, disbursedOn,
externalId);
/*
* Currently mapping down payment transaction as repayment transaction to loan repayment schedule for
* consistency. Need to replace below logic for creating new installment for down payment and mapping.
*/

LoanEvent event = LoanEvent.LOAN_REPAYMENT_OR_WAIVER;
validateRepaymentTypeAccountStatus(downPaymentTransaction, event);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,6 +367,10 @@ private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTe

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,
Expand Down Expand Up @@ -427,6 +431,29 @@ private LoanScheduleModel generate(final MathContext mc, final LoanApplicationTe
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();
Expand Down Expand Up @@ -998,8 +1025,10 @@ private void updateBalanceBasedOnDisbursement(final LoanApplicationTerms loanApp
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
Expand Down Expand Up @@ -2348,11 +2377,14 @@ private LoanScheduleDTO rescheduleNextInstallments(final MathContext mc, final L
.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
Expand All @@ -2369,6 +2401,22 @@ private LoanScheduleDTO rescheduleNextInstallments(final MathContext mc, final L
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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,8 @@ public LoanTransactionData retrieveLoanTransactionTemplate(final Long loanId) {
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);
}
Expand Down Expand Up @@ -2494,9 +2495,9 @@ public String schema() {
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, ");
Expand All @@ -2516,7 +2517,7 @@ public String schema() {
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();
}

Expand Down
Loading

0 comments on commit e701330

Please sign in to comment.