FINERACT-1981: Fix principal due during disbursement on overpaid loan
This commit is contained in:
committed by
Arnold Gálovics
parent
fcb824378e
commit
127898e2fa
+28
-17
@@ -493,26 +493,10 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
downPaymentAmt = Money.roundToMultiplesOf(downPaymentAmt, installmentAmountInMultiplesOf);
|
downPaymentAmt = Money.roundToMultiplesOf(downPaymentAmt, installmentAmountInMultiplesOf);
|
||||||
}
|
}
|
||||||
downPaymentAmount = Money.of(currency, downPaymentAmt);
|
downPaymentAmount = Money.of(currency, downPaymentAmt);
|
||||||
Money autoPayFromOverpayment = overpaymentHolder.getMoneyObject();
|
|
||||||
downPaymentInstallment.addToPrincipal(disbursementTransaction.getTransactionDate(), downPaymentAmount);
|
downPaymentInstallment.addToPrincipal(disbursementTransaction.getTransactionDate(), downPaymentAmount);
|
||||||
Money paidAmount = downPaymentInstallment.payPrincipalComponent(disbursementTransaction.getTransactionDate(),
|
|
||||||
autoPayFromOverpayment);
|
|
||||||
disbursementTransaction.setOverPayments(paidAmount);
|
|
||||||
overpaymentHolder.setMoneyObject(overpaymentHolder.getMoneyObject().minus(paidAmount));
|
|
||||||
}
|
}
|
||||||
|
disbursementTransaction.setOverPayments(overpaymentHolder.getMoneyObject());
|
||||||
Money amortizableAmount = disbursementTransaction.getAmount(currency).minus(downPaymentAmount);
|
Money amortizableAmount = disbursementTransaction.getAmount(currency).minus(downPaymentAmount);
|
||||||
Money overpaidAmount = overpaymentHolder.getMoneyObject();
|
|
||||||
if (overpaidAmount.isGreaterThanZero()) {
|
|
||||||
if (amortizableAmount.isGreaterThan(overpaidAmount)) {
|
|
||||||
overpaymentHolder.setMoneyObject(Money.zero(currency));
|
|
||||||
amortizableAmount = amortizableAmount.minus(overpaidAmount);
|
|
||||||
disbursementTransaction.setOverPayments(disbursementTransaction.getOverPaymentPortion(currency).add(overpaidAmount));
|
|
||||||
} else {
|
|
||||||
overpaymentHolder.setMoneyObject(overpaymentHolder.getMoneyObject().minus(amortizableAmount));
|
|
||||||
amortizableAmount = Money.zero(currency);
|
|
||||||
disbursementTransaction.setOverPayments(disbursementTransaction.getOverPaymentPortion(currency).add(amortizableAmount));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (amortizableAmount.isGreaterThanZero()) {
|
if (amortizableAmount.isGreaterThanZero()) {
|
||||||
Money increasePrincipalBy = amortizableAmount.dividedBy(noCandidateRepaymentInstallments, mc.getRoundingMode());
|
Money increasePrincipalBy = amortizableAmount.dividedBy(noCandidateRepaymentInstallments, mc.getRoundingMode());
|
||||||
@@ -530,6 +514,33 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
|
|||||||
candidateRepaymentInstallments.get(noCandidateRepaymentInstallments - 1)
|
candidateRepaymentInstallments.get(noCandidateRepaymentInstallments - 1)
|
||||||
.addToPrincipal(disbursementTransaction.getTransactionDate(), remainingAmount);
|
.addToPrincipal(disbursementTransaction.getTransactionDate(), remainingAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allocateOverpayment(disbursementTransaction, currency, installments, overpaymentHolder);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void allocateOverpayment(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
|
List<LoanRepaymentScheduleInstallment> installments, MoneyHolder overpaymentHolder) {
|
||||||
|
List<LoanTransactionToRepaymentScheduleMapping> transactionMappings = new ArrayList<>();
|
||||||
|
List<LoanPaymentAllocationRule> paymentAllocationRules = loanTransaction.getLoan().getPaymentAllocationRules();
|
||||||
|
LoanPaymentAllocationRule defaultPaymentAllocationRule = paymentAllocationRules.stream()
|
||||||
|
.filter(e -> PaymentAllocationTransactionType.DEFAULT.equals(e.getTransactionType())).findFirst().orElseThrow();
|
||||||
|
|
||||||
|
Money transactionAmountUnprocessed = null;
|
||||||
|
Money zero = Money.zero(currency);
|
||||||
|
Balances balances = new Balances(zero, zero, zero, zero);
|
||||||
|
if (LoanScheduleProcessingType.HORIZONTAL
|
||||||
|
.equals(loanTransaction.getLoan().getLoanProductRelatedDetail().getLoanScheduleProcessingType())) {
|
||||||
|
transactionAmountUnprocessed = processPeriodsHorizontally(loanTransaction, currency, installments,
|
||||||
|
overpaymentHolder.getMoneyObject(), defaultPaymentAllocationRule, transactionMappings, Set.of(), balances);
|
||||||
|
} else if (LoanScheduleProcessingType.VERTICAL
|
||||||
|
.equals(loanTransaction.getLoan().getLoanProductRelatedDetail().getLoanScheduleProcessingType())) {
|
||||||
|
transactionAmountUnprocessed = processPeriodsVertically(loanTransaction, currency, installments,
|
||||||
|
overpaymentHolder.getMoneyObject(), defaultPaymentAllocationRule, transactionMappings, Set.of(), balances);
|
||||||
|
}
|
||||||
|
if (transactionAmountUnprocessed != null && transactionAmountUnprocessed.isGreaterThanZero()) {
|
||||||
|
overpaymentHolder.setMoneyObject(transactionAmountUnprocessed);
|
||||||
|
}
|
||||||
|
loanTransaction.updateLoanTransactionToRepaymentScheduleMappings(transactionMappings);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleRepayment(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
private void handleRepayment(LoanTransaction loanTransaction, MonetaryCurrency currency,
|
||||||
|
|||||||
+12
-12
@@ -3131,7 +3131,7 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
// 1. Create a Loan product with Adv. Pment. Alloc.
|
// 1. Create a Loan product with Adv. Pment. Alloc.
|
||||||
// 2. Submit Loan and approve
|
// 2. Submit Loan and approve
|
||||||
// 3. Disburse only 100 from 1000
|
// 3. Disburse only 100 from 1000
|
||||||
// 4. Overpay the loan (110)
|
// 4. Overpay the loan (150)
|
||||||
// 5. Disburse again 100
|
// 5. Disburse again 100
|
||||||
@Test
|
@Test
|
||||||
public void uc122() {
|
public void uc122() {
|
||||||
@@ -3166,9 +3166,9 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
assertTrue(loanDetails.getStatus().getActive());
|
assertTrue(loanDetails.getStatus().getActive());
|
||||||
|
|
||||||
loanTransactionHelper.makeLoanRepayment(loanResponse.getLoanId(), new PostLoansLoanIdTransactionsRequest()
|
loanTransactionHelper.makeLoanRepayment(loanResponse.getLoanId(), new PostLoansLoanIdTransactionsRequest()
|
||||||
.dateFormat(DATETIME_PATTERN).transactionDate("23 November 2023").locale("en").transactionAmount(110.0));
|
.dateFormat(DATETIME_PATTERN).transactionDate("23 November 2023").locale("en").transactionAmount(150.0));
|
||||||
loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
||||||
validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 10.0);
|
validateLoanSummaryBalances(loanDetails, 0.0, 100.0, 0.0, 100.0, 50.0);
|
||||||
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 11, 22), 25.0, 25.0, 0.0, 0.0, 25.0);
|
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 11, 22), 25.0, 25.0, 0.0, 0.0, 25.0);
|
||||||
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 12, 7), 25.0, 25.0, 0.0, 25.0, 0.0);
|
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 12, 7), 25.0, 25.0, 0.0, 25.0, 0.0);
|
||||||
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 12, 22), 25.0, 25.0, 0.0, 25.0, 0.0);
|
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 12, 22), 25.0, 25.0, 0.0, 25.0, 0.0);
|
||||||
@@ -3179,27 +3179,27 @@ public class AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
|
|||||||
new PostLoansLoanIdRequest().actualDisbursementDate("24 November 2023").dateFormat(DATETIME_PATTERN)
|
new PostLoansLoanIdRequest().actualDisbursementDate("24 November 2023").dateFormat(DATETIME_PATTERN)
|
||||||
.transactionAmount(BigDecimal.valueOf(100.0)).locale("en"));
|
.transactionAmount(BigDecimal.valueOf(100.0)).locale("en"));
|
||||||
loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
loanDetails = loanTransactionHelper.getLoanDetails(loanResponse.getLoanId());
|
||||||
validateLoanSummaryBalances(loanDetails, 90.0, 110.0, 90.0, 110.0, null);
|
validateLoanSummaryBalances(loanDetails, 50.0, 150.0, 50.0, 150.0, null);
|
||||||
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 11, 22), 25.0, 25.0, 0.0, 0.0, 25.0);
|
validateRepaymentPeriod(loanDetails, 1, LocalDate.of(2023, 11, 22), 25.0, 25.0, 0.0, 0.0, 25.0);
|
||||||
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 11, 24), 25.0, 10.0, 15.0, 0.0, 0.0);
|
validateRepaymentPeriod(loanDetails, 2, LocalDate.of(2023, 11, 24), 25.0, 25.0, 0.0, 0.0, 0.0);
|
||||||
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 12, 7), 50.0, 25.0, 25.0, 25.0, 0.0);
|
validateRepaymentPeriod(loanDetails, 3, LocalDate.of(2023, 12, 7), 50.0, 50.0, 0.0, 50.0, 0.0);
|
||||||
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 12, 22), 50.0, 25.0, 25.0, 25.0, 0.0);
|
validateRepaymentPeriod(loanDetails, 4, LocalDate.of(2023, 12, 22), 50.0, 25.0, 25.0, 25.0, 0.0);
|
||||||
validateRepaymentPeriod(loanDetails, 5, LocalDate.of(2024, 1, 6), 50.0, 25.0, 25.0, 25.0, 0.0);
|
validateRepaymentPeriod(loanDetails, 5, LocalDate.of(2024, 1, 6), 50.0, 25.0, 25.0, 25.0, 0.0);
|
||||||
assertTrue(loanDetails.getStatus().getActive());
|
assertTrue(loanDetails.getStatus().getActive());
|
||||||
|
|
||||||
verifyTransactions(loanResponse.getLoanId(), //
|
verifyTransactions(loanResponse.getLoanId(), //
|
||||||
transaction(100, "Disbursement", "22 November 2023", 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
|
transaction(100, "Disbursement", "22 November 2023", 100.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0), //
|
||||||
transaction(110, "Repayment", "23 November 2023", 0.0, 100.0, 0.0, 0.0, 0.0, 0.0, 10.0), //
|
transaction(150, "Repayment", "23 November 2023", 0.0, 100.0, 0.0, 0.0, 0.0, 0.0, 50.0), //
|
||||||
transaction(100, "Disbursement", "24 November 2023", 90.0, 0.0, 0.0, 0.0, 0.0, 0.0, 10.0) //
|
transaction(100, "Disbursement", "24 November 2023", 50.0, 0.0, 0.0, 0.0, 0.0, 0.0, 50.0) //
|
||||||
);
|
);
|
||||||
// verify journal entries
|
// verify journal entries
|
||||||
verifyJournalEntries(loanResponse.getLoanId(), journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
|
verifyJournalEntries(loanResponse.getLoanId(), journalEntry(100.0, loansReceivableAccount, "DEBIT"), //
|
||||||
journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
|
journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
|
||||||
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
|
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
|
||||||
journalEntry(10.0, overpaymentAccount, "CREDIT"), //
|
journalEntry(50.0, overpaymentAccount, "CREDIT"), //
|
||||||
journalEntry(110.0, suspenseClearingAccount, "DEBIT"), //
|
journalEntry(150.0, suspenseClearingAccount, "DEBIT"), //
|
||||||
journalEntry(90.0, loansReceivableAccount, "DEBIT"), //
|
journalEntry(50.0, loansReceivableAccount, "DEBIT"), //
|
||||||
journalEntry(10.0, overpaymentAccount, "DEBIT"), //
|
journalEntry(50.0, overpaymentAccount, "DEBIT"), //
|
||||||
journalEntry(100.0, suspenseClearingAccount, "CREDIT") //
|
journalEntry(100.0, suspenseClearingAccount, "CREDIT") //
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user