This is an automated email from the ASF dual-hosted git repository. adamsaghy pushed a commit to branch develop in repository https://gitbox.apache.org/repos/asf/fineract.git
commit 33c74abeb73f72bdafb7569cf35d2358f6f49a44 Author: Oleksii Novikov <[email protected]> AuthorDate: Tue Aug 26 16:56:00 2025 +0300 FINERACT-2355: Fix charge off and loan charge for zero installments --- .../src/test/resources/features/LoanCharge.feature | 49 +++++++++++++++++++++- .../loanaccount/domain/LoanTransaction.java | 4 ++ .../LoanTransactionToRepaymentScheduleMapping.java | 4 ++ ...dvancedPaymentScheduleTransactionProcessor.java | 7 +++- 4 files changed, 61 insertions(+), 3 deletions(-) diff --git a/fineract-e2e-tests-runner/src/test/resources/features/LoanCharge.feature b/fineract-e2e-tests-runner/src/test/resources/features/LoanCharge.feature index 6bbb3de045..b4ddda4c2c 100644 --- a/fineract-e2e-tests-runner/src/test/resources/features/LoanCharge.feature +++ b/fineract-e2e-tests-runner/src/test/resources/features/LoanCharge.feature @@ -7776,4 +7776,51 @@ Feature: LoanCharge | 1125.0 | 0.0 | 30.0 | 0.0 | 1155.0 | 250.0 | 0.0 | 0.0 | 905.0 | Then Loan Charges tab has the following data: | Name | isPenalty | Payment due at | Due as of | Calculation type | Due | Paid | Waived | Outstanding | - | Installment flat fee | false | Installment Fee | | Flat | 30.0 | 0.0 | 0.0 | 30.0 | \ No newline at end of file + | Installment flat fee | false | Installment Fee | | Flat | 30.0 | 0.0 | 0.0 | 30.0 | + + Scenario: Verify Loan Charge together with paid installments with amounts zero + When Admin sets the business date to "11 April 2025" + And Admin creates a client with random data + And 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 | 11 April 2025 | 1001 | 12.25 | DECLINING_BALANCE | DAILY | EQUAL_INSTALLMENTS | 24 | MONTHS | 1 | MONTHS | 24 | 0 | 0 | 0 | ADVANCED_PAYMENT_ALLOCATION | + And Admin successfully approves the loan on "11 April 2025" with "209.72" amount and expected disbursement date on "11 April 2025" + And Admin successfully disburse the loan on "11 April 2025" with "209.72" EUR transaction amount + And Admin sets the business date to "11 May 2025" + And Customer makes "AUTOPAY" repayment on "11 May 2025" with 9.9 EUR transaction amount + And Admin sets the business date to "11 June 2025" + And Customer makes "AUTOPAY" repayment on "11 June 2025" with 9.9 EUR transaction amount + And Admin sets the business date to "11 July 2025" + And Customer makes "AUTOPAY" repayment on "11 July 2025" with 9.9 EUR transaction amount + And Admin sets the business date to "11 August 2025" + And Customer makes "AUTOPAY" repayment on "11 August 2025" with 9.9 EUR transaction amount + And Admin sets the business date to "13 August 2025" + And Customer makes "MERCHANT_ISSUED_REFUND" transaction with "AUTOPAY" payment type on "13 August 2025" with 188.8 EUR transaction amount and system-generated Idempotency key + Then Loan Repayment schedule has 24 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 | + | 1 | 30 | 11 May 2025 | 11 May 2025 | 201.96 | 7.76 | 2.14 | 0.0 | 0.0 | 9.9 | 9.9 | 0.0 | 0.0 | 0.0 | + | 2 | 31 | 11 June 2025 | 11 June 2025 | 194.12 | 7.84 | 2.06 | 0.0 | 0.0 | 9.9 | 9.9 | 0.0 | 0.0 | 0.0 | + | 3 | 30 | 11 July 2025 | 11 July 2025 | 186.2 | 7.92 | 1.98 | 0.0 | 0.0 | 9.9 | 9.9 | 0.0 | 0.0 | 0.0 | + | 4 | 31 | 11 August 2025 | 11 August 2025 | 178.2 | 8.0 | 1.9 | 0.0 | 0.0 | 9.9 | 9.9 | 0.0 | 0.0 | 0.0 | + | 5 | 31 | 11 September 2025 | 13 August 2025 | 178.2 | 0.0 | 0.12 | 0.0 | 0.0 | 0.12 | 0.12 | 0.12 | 0.0 | 0.0 | + | 6 | 30 | 11 October 2025 | 13 August 2025 | 178.2 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | + | 7 | 31 | 11 November 2025 | 13 August 2025 | 168.3 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 8 | 30 | 11 December 2025 | 13 August 2025 | 158.4 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 9 | 31 | 11 January 2026 | 13 August 2025 | 148.5 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 10 | 31 | 11 February 2026 | 13 August 2025 | 138.6 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 11 | 28 | 11 March 2026 | 13 August 2025 | 128.7 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 12 | 31 | 11 April 2026 | 13 August 2025 | 118.8 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 13 | 30 | 11 May 2026 | 13 August 2025 | 108.9 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 14 | 31 | 11 June 2026 | 13 August 2025 | 99.0 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 15 | 30 | 11 July 2026 | 13 August 2025 | 89.1 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 16 | 31 | 11 August 2026 | 13 August 2025 | 79.2 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 17 | 31 | 11 September 2026 | 13 August 2025 | 69.3 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 18 | 30 | 11 October 2026 | 13 August 2025 | 59.4 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 19 | 31 | 11 November 2026 | 13 August 2025 | 49.5 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 20 | 30 | 11 December 2026 | 13 August 2025 | 39.6 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 21 | 31 | 11 January 2027 | 13 August 2025 | 29.7 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 22 | 31 | 11 February 2027 | 13 August 2025 | 19.8 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 23 | 28 | 11 March 2027 | 13 August 2025 | 9.9 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + | 24 | 31 | 11 April 2027 | 13 August 2025 | 0.0 | 9.9 | 0.0 | 0.0 | 0.0 | 9.9 | 9.9 | 9.9 | 0.0 | 0.0 | + When Admin sets the business date to "15 August 2025" + When Admin adds "LOAN_NSF_FEE" due date charge with "15 August 2025" due date and 35 EUR transaction amount \ No newline at end of file diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java index 230d3f3dde..f77a490ae1 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransaction.java @@ -914,6 +914,10 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom<Long retainMappings.add(newMapping); } } + + if (retainMappings != null) { + retainMappings.removeIf(LoanTransactionToRepaymentScheduleMapping::isZeroAmount); + } } public void updateLoanChargePaidMappings(final Collection<LoanChargePaidBy> updatedMappings) { diff --git a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionToRepaymentScheduleMapping.java b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionToRepaymentScheduleMapping.java index f07d0bfda7..13680708b0 100644 --- a/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionToRepaymentScheduleMapping.java +++ b/fineract-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanTransactionToRepaymentScheduleMapping.java @@ -141,4 +141,8 @@ public class LoanTransactionToRepaymentScheduleMapping extends AbstractPersistab return Money.of(currency, this.penaltyChargesPortion); } + public boolean isZeroAmount() { + return this.amount == null || this.amount.compareTo(BigDecimal.ZERO) == 0; + } + } diff --git a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java index 94896dc8ae..974597f897 100644 --- a/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java +++ b/fineract-progressive-loan/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/impl/AdvancedPaymentScheduleTransactionProcessor.java @@ -1973,8 +1973,11 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep } }); - final Money amountToEditLastInstallment = loanTransaction.getLoan().getPrincipal().minus(installments.stream() - .filter(i -> !i.isAdditional()).map(LoanRepaymentScheduleInstallment::getPrincipal).reduce(ZERO, BigDecimal::add)); + final Money amountToEditLastInstallment = loanTransaction.getLoan().getPrincipal().minus(installments.stream() // + .filter(i -> i.getPrincipal() != null) // + .filter(i -> !i.isAdditional()) // + .map(LoanRepaymentScheduleInstallment::getPrincipal) // + .reduce(ZERO, BigDecimal::add)); BigDecimal principalBalance = amountToEditLastInstallment.getAmount(); for (int i = installments.size() - 1; i > 0 && BigDecimal.ZERO.compareTo(principalBalance) != 0; i--) {
