[ 
https://issues.apache.org/jira/browse/FINERACT-2797?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
 ]

Josue Araujo updated FINERACT-2797:
-----------------------------------
    Priority: Critical  (was: Blocker)

> Force withdrawal balance validation fails on subsequent regular transactions
> ----------------------------------------------------------------------------
>
>                 Key: FINERACT-2797
>                 URL: https://issues.apache.org/jira/browse/FINERACT-2797
>             Project: Apache Fineract
>          Issue Type: Bug
>          Components: Savings, System
>    Affects Versions: 1.15.0
>            Reporter: Josue Araujo
>            Priority: Critical
>         Attachments: Screenshot 2026-08-31 at 7.36.40 p.m..png, Screenshot 
> 2026-08-31 at 7.37.15 p.m..png, Screenshot 2026-08-31 at 7.40.09 p.m..png
>
>
> _*Summary*_
> When "Force Withdrawal" (allow-force-withdrawal-on-savings-account global
> config, FINERACT-2471) is used to push a savings account balance below its
> minimum/zero threshold, any *subsequent* regular (non-forced) withdrawal on
> that same account throws InsufficientAccountBalanceException — even when the
> account's current balance is fully sufficient to cover the new withdrawal.
>  
> _*Root cause*_
> validateAccountBalanceConstraints() replays the ENTIRE transaction history on
> every balance-affecting operation to recompute the running balance at each
> historical point (needed to correctly validate backdated transactions).
> isForceWithdrawalAllowed(isForceWithdrawal, runningBalance) is passed a
> single boolean scoped to the CURRENT call, not to the specific historical
> transaction being evaluated in the loop:
>     private boolean isForceWithdrawalAllowed(final boolean isForceWithdrawal, 
> final Money runningBalance) {
>         if (!isForceWithdrawal || this.configurationDomainService == null)
> {             return false;         }
>         ...
>     }
> So when a later *regular* withdrawal is validated (isForceWithdrawal=false
> for that call), the loop re-encounters the earlier forced-withdrawal dip in
> the account's history, and — since the flag for the CURRENT call is false —
> throws, even though that historical dip was legitimately authorized at the
> time it happened. The "force" authorization is never persisted on the
> transaction itself, only used transiently at write time.
>  * Steps to reproduce
> 1. Create a savings account/product: no interest, overdraft NOT allowed.
> 2. Global config: allow-force-withdrawal-on-savings-account=true,
>    force-withdrawal-on-savings-account-limit=1000.
> 3. Deposit 10.00 (balance: 10.00)
> 4. Force withdrawal 15.00 (balance: -5.00) — succeeds as expected.
> 5. Deposit 20.00 (balance: 15.00)
> 6. Deposit 5.00 (balance: 20.00)
> 7. Regular withdrawal 3.00 (expected balance: 17.00)
>    -> ACTUAL: throws InsufficientAccountBalanceException.
> _*Expected behavior*_
> Step 7 should succeed: the account's current balance (20.00) is more than
> sufficient for a 3.00 withdrawal. A previously authorized forced-withdrawal
> dip should not retroactively block unrelated future regular transactions.
>  
> _*Environment*_
>  - Branch: develop
>  - Commit introducing the feature: 8c187f9d17fb839f26fc888f34a64f7c1f57a802
>   (FINERACT-2471)
>  - Verified still present as of commit 
> c6328ab3062d8e1cf8395c00c40918a1741134ad
>   (2026-07-27)
> _*Suggested fix*_
> Persist an is_force_transaction flag on m_savings_account_transaction at
> creation time, and check that per-transaction flag (or the current
> transaction's flag) inside the validateAccountBalanceConstraints loop
> instead of relying solely on the call-scoped isForceWithdrawal boolean.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to