From c7f5743b1252ae70d07af7f0526911ee954b4741 Mon Sep 17 00:00:00 2001 From: mariiaKraievska Date: Fri, 12 Sep 2025 11:43:27 +0300 Subject: [PATCH] FINERACT-2326: Repayment Reversal Failing After Full Repayment of Principal and Fee --- .../resources/features/LoanRepayment.feature | 98 +++++++++++++++++++ ...RepaymentScheduleTransactionProcessor.java | 9 +- .../LoanStatusChangePlatformServiceImpl.java | 2 +- 3 files changed, 106 insertions(+), 3 deletions(-) diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature index 1018170b5..97526a8d0 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanRepayment.feature @@ -5586,3 +5586,101 @@ Feature: LoanRepayment | 22 October 2024 | Repayment | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | true | | 26 October 2024 | Repayment | 100.0 | 100.0 | 0.0 | 0.0 | 0.0 | 0.0 | true | | 26 October 2024 | Repayment | 101.0 | 100.0 | 0.0 | 0.0 | 1.0 | 0.0 | false | + + @TestRailId:C4053 + Scenario: Verify repayment reversal after full repayment of principal and fee + When Admin sets the business date to "16 May 2025" + When Admin creates a client with random data + When Admin creates a fully customized loan with the following data: + | LoanProduct | submitted on date | with Principal | ANNUAL interest rate % | interest type | interest calculation period | amortization type | loanTermFrequency | loanTermFrequencyType | repaymentEvery | repaymentFrequencyType | numberOfRepayments | graceOnPrincipalPayment | graceOnInterestPayment | interest free period | Payment strategy | + | LP2_ADV_CUSTOM_PMT_ALLOC_PROGRESSIVE_LOAN_SCHEDULE_HORIZONTAL | 16 May 2025 | 186.99 | 11.3043 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 3 | MONTHS | 1 | MONTHS | 3 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION | + And Admin successfully approves the loan on "16 May 2025" with "186.99" amount and expected disbursement date on "16 May 2025" + When Admin successfully disburse the loan on "16 May 2025" with "186.99" EUR transaction amount + When Admin sets the business date to "16 August 2025" + And Customer makes "AUTOPAY" repayment on "16 August 2025" with 192.27 EUR transaction amount + Then Loan Repayment schedule has 3 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 16 May 2025 | | 186.99 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 16 June 2025 | 16 August 2025 | 125.24 | 61.75 | 1.76 | 0.0 | 0.0 | 63.51 | 63.51 | 0.0 | 63.51 | 0.0 | + | 2 | 30 | 16 July 2025 | 16 August 2025 | 63.49 | 61.75 | 1.76 | 0.0 | 0.0 | 63.51 | 63.51 | 0.0 | 63.51 | 0.0 | + | 3 | 31 | 16 August 2025 | 16 August 2025 | 0.0 | 63.49 | 1.76 | 0.0 | 0.0 | 65.25 | 65.25 | 0.0 | 0.0 | 0.0 | + Then Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 186.99 | 5.28 | 0.0 | 0.0 | 192.27 | 192.27 | 0.0 | 127.02 | 0.0 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 16 May 2025 | Disbursement | 186.99 | 0.0 | 0.0 | 0.0 | 0.0 | 186.99 | false | false | + | 16 June 2025 | Accrual Activity | 1.76 | 0.0 | 1.76 | 0.0 | 0.0 | 0.0 | false | false | + | 16 July 2025 | Accrual Activity | 1.76 | 0.0 | 1.76 | 0.0 | 0.0 | 0.0 | false | false | + | 16 August 2025 | Repayment | 192.27 | 186.99 | 5.28 | 0.0 | 0.0 | 0.0 | false | false | + | 16 August 2025 | Accrual | 5.28 | 0.0 | 5.28 | 0.0 | 0.0 | 0.0 | false | false | + | 16 August 2025 | Accrual Activity | 1.76 | 0.0 | 1.76 | 0.0 | 0.0 | 0.0 | false | false | + When Admin sets the business date to "21 August 2025" + And Customer makes a repayment undo on "16 August 2025" + And Admin adds "LOAN_NSF_FEE" due date charge with "21 August 2025" due date and 2.8 EUR transaction amount + And Loan Charges tab has the following data: + | Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding | + | NSF fee | true | Specified due date | 21 August 2025 | Flat | 2.8 | 0.0 | 0.0 | 2.8 | + Then Loan Repayment schedule has 4 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 16 May 2025 | | 186.99 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 16 June 2025 | | 125.24 | 61.75 | 1.76 | 0.0 | 0.0 | 63.51 | 0.0 | 0.0 | 0.0 | 63.51 | + | 2 | 30 | 16 July 2025 | | 63.49 | 61.75 | 1.76 | 0.0 | 0.0 | 63.51 | 0.0 | 0.0 | 0.0 | 63.51 | + | 3 | 31 | 16 August 2025 | | 0.0 | 63.49 | 1.76 | 0.0 | 0.0 | 65.25 | 0.0 | 0.0 | 0.0 | 65.25 | + | 4 | 5 | 21 August 2025 | | 0.0 | 0.0 | 0.0 | 0.0 | 2.8 | 2.8 | 0.0 | 0.0 | 0.0 | 2.8 | + Then Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 186.99 | 5.28 | 0.0 | 2.8 | 195.07 | 0.0 | 0.0 | 0.0 | 195.07 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 16 May 2025 | Disbursement | 186.99 | 0.0 | 0.0 | 0.0 | 0.0 | 186.99 | false | false | + | 16 June 2025 | Accrual Activity | 1.76 | 0.0 | 1.76 | 0.0 | 0.0 | 0.0 | false | false | + | 16 July 2025 | Accrual Activity | 1.76 | 0.0 | 1.76 | 0.0 | 0.0 | 0.0 | false | false | + | 16 August 2025 | Repayment | 192.27 | 186.99 | 5.28 | 0.0 | 0.0 | 0.0 | true | false | + | 16 August 2025 | Accrual | 5.28 | 0.0 | 5.28 | 0.0 | 0.0 | 0.0 | false | false | + | 16 August 2025 | Accrual Activity | 1.76 | 0.0 | 1.76 | 0.0 | 0.0 | 0.0 | false | false | + When Admin sets the business date to "22 August 2025" + And Customer makes "AUTOPAY" repayment on "22 August 2025" with 195.07 EUR transaction amount + Then Loan Repayment schedule has 4 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 16 May 2025 | | 186.99 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 16 June 2025 | 22 August 2025 | 125.24 | 61.75 | 1.76 | 0.0 | 0.0 | 63.51 | 63.51 | 0.0 | 63.51 | 0.0 | + | 2 | 30 | 16 July 2025 | 22 August 2025 | 63.49 | 61.75 | 1.76 | 0.0 | 0.0 | 63.51 | 63.51 | 0.0 | 63.51 | 0.0 | + | 3 | 31 | 16 August 2025 | 22 August 2025 | 0.0 | 63.49 | 1.76 | 0.0 | 0.0 | 65.25 | 65.25 | 0.0 | 65.25 | 0.0 | + | 4 | 5 | 21 August 2025 | 22 August 2025 | 0.0 | 0.0 | 0.0 | 0.0 | 2.8 | 2.8 | 2.8 | 0.0 | 2.8 | 0.0 | + Then Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 186.99 | 5.28 | 0.0 | 2.8 | 195.07 | 195.07 | 0.0 | 195.07 | 0.0 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 16 May 2025 | Disbursement | 186.99 | 0.0 | 0.0 | 0.0 | 0.0 | 186.99 | false | false | + | 16 June 2025 | Accrual Activity | 1.76 | 0.0 | 1.76 | 0.0 | 0.0 | 0.0 | false | false | + | 16 July 2025 | Accrual Activity | 1.76 | 0.0 | 1.76 | 0.0 | 0.0 | 0.0 | false | false | + | 16 August 2025 | Repayment | 192.27 | 186.99 | 5.28 | 0.0 | 0.0 | 0.0 | true | false | + | 16 August 2025 | Accrual | 5.28 | 0.0 | 5.28 | 0.0 | 0.0 | 0.0 | false | false | + | 16 August 2025 | Accrual Activity | 1.76 | 0.0 | 1.76 | 0.0 | 0.0 | 0.0 | false | false | + | 21 August 2025 | Accrual | 2.8 | 0.0 | 0.0 | 0.0 | 2.8 | 0.0 | false | false | + | 22 August 2025 | Repayment | 195.07 | 186.99 | 5.28 | 0.0 | 2.8 | 0.0 | false | false | + | 22 August 2025 | Accrual Activity | 2.8 | 0.0 | 0.0 | 0.0 | 2.8 | 0.0 | false | false | + When Admin sets the business date to "29 August 2025" + And Customer makes a repayment undo on "22 August 2025" + Then Loan Repayment schedule has 4 periods, with the following data for periods: + | Nr | Days | Date | Paid date | Balance of loan | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | | | 16 May 2025 | | 186.99 | | | 0.0 | | 0.0 | 0.0 | | | | + | 1 | 31 | 16 June 2025 | | 125.24 | 61.75 | 1.76 | 0.0 | 0.0 | 63.51 | 0.0 | 0.0 | 0.0 | 63.51 | + | 2 | 30 | 16 July 2025 | | 63.49 | 61.75 | 1.76 | 0.0 | 0.0 | 63.51 | 0.0 | 0.0 | 0.0 | 63.51 | + | 3 | 31 | 16 August 2025 | | 0.0 | 63.49 | 1.76 | 0.0 | 0.0 | 65.25 | 0.0 | 0.0 | 0.0 | 65.25 | + | 4 | 5 | 21 August 2025 | | 0.0 | 0.0 | 0.0 | 0.0 | 2.8 | 2.8 | 0.0 | 0.0 | 0.0 | 2.8 | + Then Loan Repayment schedule has the following data in Total row: + | Principal due | Interest | Fees | Penalties | Due | Paid | In advance | Late | Outstanding | + | 186.99 | 5.28 | 0.0 | 2.8 | 195.07 | 0.0 | 0.0 | 0.0 | 195.07 | + Then Loan Transactions tab has the following data: + | Transaction date | Transaction Type | Amount | Principal | Interest | Fees | Penalties | Loan Balance | Reverted | Replayed | + | 16 May 2025 | Disbursement | 186.99 | 0.0 | 0.0 | 0.0 | 0.0 | 186.99 | false | false | + | 16 June 2025 | Accrual Activity | 1.76 | 0.0 | 1.76 | 0.0 | 0.0 | 0.0 | false | false | + | 16 July 2025 | Accrual Activity | 1.76 | 0.0 | 1.76 | 0.0 | 0.0 | 0.0 | false | false | + | 16 August 2025 | Repayment | 192.27 | 186.99 | 5.28 | 0.0 | 0.0 | 0.0 | true | false | + | 16 August 2025 | Accrual | 5.28 | 0.0 | 5.28 | 0.0 | 0.0 | 0.0 | false | false | + | 16 August 2025 | Accrual Activity | 1.76 | 0.0 | 1.76 | 0.0 | 0.0 | 0.0 | false | false | + | 21 August 2025 | Accrual | 2.8 | 0.0 | 0.0 | 0.0 | 2.8 | 0.0 | false | false | + | 22 August 2025 | Repayment | 195.07 | 186.99 | 5.28 | 0.0 | 2.8 | 0.0 | true | false | diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java index 7b2bf677e..7e8adf046 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java @@ -236,11 +236,16 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen final int firstNormalInstallmentNumber = LoanRepaymentScheduleProcessingWrapper.fetchFirstNormalInstallmentNumber(installments); - final LoanRepaymentScheduleInstallment currentInstallment = installments.stream() + final Optional currentInstallmentOpt = installments.stream() .filter(installment -> LoanRepaymentScheduleProcessingWrapper.isInPeriod(loanTransaction.getTransactionDate(), installment, installment.getInstallmentNumber().equals(firstNormalInstallmentNumber))) - .findFirst().orElseThrow(); + .findFirst(); + if (currentInstallmentOpt.isEmpty()) { + return; + } + + final LoanRepaymentScheduleInstallment currentInstallment = currentInstallmentOpt.get(); if (currentInstallment.isNotFullyPaidOff() && (currentInstallment.getDueDate().isAfter(loanTransaction.getTransactionDate()) || (currentInstallment.getDueDate().isEqual(loanTransaction.getTransactionDate()) && loanTransaction.getTransactionDate().equals(DateUtils.getBusinessLocalDate())))) { diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java index 7d4e5a1a7..83b5f03d8 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanStatusChangePlatformServiceImpl.java @@ -62,7 +62,7 @@ public class LoanStatusChangePlatformServiceImpl implements LoanStatusChangePlat if (loan.getLoanProductRelatedDetail().isEnableAccrualActivityPosting()) { LoanStatus oldStatus = event.getOldStatus(); LoanStatus newStatus = loan.getStatus(); - if ((oldStatus.isClosed() || oldStatus.isOverpaid()) && newStatus.isActive()) { + if ((oldStatus.isClosedObligationsMet() || oldStatus.isClosed() || oldStatus.isOverpaid()) && newStatus.isActive()) { loanAccrualActivityProcessingService.processAccrualActivityForLoanReopen(loan); } }