This is an automated email from the ASF dual-hosted git repository.
arnold pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git
The following commit(s) were added to refs/heads/develop by this push:
new 127898e2f FINERACT-1981: Fix principal due during disbursement on
overpaid loan
127898e2f is described below
commit 127898e2fac4100f7737991eaf1fd35345722df7
Author: Adam Saghy <[email protected]>
AuthorDate: Fri Feb 9 17:09:40 2024 +0100
FINERACT-1981: Fix principal due during disbursement on overpaid loan
---
...dvancedPaymentScheduleTransactionProcessor.java | 45 ++++++++++++++--------
...PaymentAllocationLoanRepaymentScheduleTest.java | 24 ++++++------
2 files changed, 40 insertions(+), 29 deletions(-)
diff --git
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
index a8cc27c0d..f9c223ca5 100644
---
a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
+++
b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java
@@ -493,26 +493,10 @@ public class AdvancedPaymentScheduleTransactionProcessor
extends AbstractLoanRep
downPaymentAmt = Money.roundToMultiplesOf(downPaymentAmt,
installmentAmountInMultiplesOf);
}
downPaymentAmount = Money.of(currency, downPaymentAmt);
- Money autoPayFromOverpayment = overpaymentHolder.getMoneyObject();
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 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()) {
Money increasePrincipalBy =
amortizableAmount.dividedBy(noCandidateRepaymentInstallments,
mc.getRoundingMode());
@@ -530,6 +514,33 @@ public class AdvancedPaymentScheduleTransactionProcessor
extends AbstractLoanRep
candidateRepaymentInstallments.get(noCandidateRepaymentInstallments - 1)
.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,
diff --git
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
index b682c2250..603f10015 100644
---
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
+++
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java
@@ -3131,7 +3131,7 @@ public class
AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
// 1. Create a Loan product with Adv. Pment. Alloc.
// 2. Submit Loan and approve
// 3. Disburse only 100 from 1000
- // 4. Overpay the loan (110)
+ // 4. Overpay the loan (150)
// 5. Disburse again 100
@Test
public void uc122() {
@@ -3166,9 +3166,9 @@ public class
AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
assertTrue(loanDetails.getStatus().getActive());
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());
- 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, 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);
@@ -3179,27 +3179,27 @@ public class
AdvancedPaymentAllocationLoanRepaymentScheduleTest extends BaseLoan
new PostLoansLoanIdRequest().actualDisbursementDate("24
November 2023").dateFormat(DATETIME_PATTERN)
.transactionAmount(BigDecimal.valueOf(100.0)).locale("en"));
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, 2, LocalDate.of(2023, 11,
24), 25.0, 10.0, 15.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, 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, 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, 5, LocalDate.of(2024, 1, 6),
50.0, 25.0, 25.0, 25.0, 0.0);
assertTrue(loanDetails.getStatus().getActive());
verifyTransactions(loanResponse.getLoanId(), //
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(100, "Disbursement", "24 November 2023", 90.0,
0.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", 50.0,
0.0, 0.0, 0.0, 0.0, 0.0, 50.0) //
);
// verify journal entries
verifyJournalEntries(loanResponse.getLoanId(), journalEntry(100.0,
loansReceivableAccount, "DEBIT"), //
journalEntry(100.0, suspenseClearingAccount, "CREDIT"), //
journalEntry(100.0, loansReceivableAccount, "CREDIT"), //
- journalEntry(10.0, overpaymentAccount, "CREDIT"), //
- journalEntry(110.0, suspenseClearingAccount, "DEBIT"), //
- journalEntry(90.0, loansReceivableAccount, "DEBIT"), //
- journalEntry(10.0, overpaymentAccount, "DEBIT"), //
+ journalEntry(50.0, overpaymentAccount, "CREDIT"), //
+ journalEntry(150.0, suspenseClearingAccount, "DEBIT"), //
+ journalEntry(50.0, loansReceivableAccount, "DEBIT"), //
+ journalEntry(50.0, overpaymentAccount, "DEBIT"), //
journalEntry(100.0, suspenseClearingAccount, "CREDIT") //
);
});