Abhishek6122008 opened a new pull request, #6421: URL: https://github.com/apache/fineract/pull/6421
## What this does The `EXECUTE_STANDING_INSTRUCTIONS` job is all-or-nothing today. When the linked savings account cannot cover the amount due, `transferFunds` throws `InsufficientAccountBalanceException`, the run is recorded as `failed`, and nothing at all is collected. On a 40,000 installment with 39,900 available, the lender recovers zero. A standing instruction can now opt into partial collection, so the job transfers what is available instead of nothing. Per Bharath's comment on the ticket, the option sits on the standing instruction itself rather than being a global configuration. ## How - New `allow_partial_transfer` column on `m_account_transfer_standing_instructions`, exposed as `allowPartialTransfer` through the existing create/update endpoints. **Defaults to false**, so existing instructions — and the one Fineract creates automatically on disbursement to a linked savings account — behave exactly as before. No new endpoints. - When the flag is set *and* the from account is a savings account, the job limits the transfer to `SavingsAccount#getWithdrawableBalance()` and skips the instruction when nothing is available. That helper already accounts for the minimum required balance where enforced, overdraft headroom, on-hold funds and hold amounts, so no balance arithmetic is duplicated in the job. - The from account is assembled in the job and passed into the `AccountTransferDTO`. `transferFunds` assembles it itself only when that field is null, so the load moves rather than being added. - The from-account type is guarded because transfer types include `CHARGE_PAYMENT` and `INTEREST_TRANSFER`, where the from account is not necessarily savings. Clamping only ever lowers an amount that would otherwise have been rejected. Anything that still fails lands in the same `failed` history row as today, so this can improve on current behaviour but not regress it. ## Two things worth your view **The `'partial'` history status.** I raised this on the dev list and it did not get a reply, so I made a call and am flagging it. A partly covered run is recorded as `'partial'` instead of `'success'`, with the amount actually transferred. `m_account_transfer_standing_instructions_history.status` is `VARCHAR(20)`, written as a literal and read back as a plain `String` with no enum or switch anywhere in the read path, so a third value needs no migration and breaks no parsing. It only ever appears for instructions that opted in. If you would rather it stayed `'success'`, that is a small change. **Withdrawal fees.** If the account applies a withdrawal fee on transfers, the debit is amount + fee, so a transfer clamped to exactly the withdrawable balance can still fail by the fee. Computing the fee up front would mean duplicating `payWithdrawalFee` — flat vs percentage, free-withdrawal counts, payment-type conditions — so I left it. Such a run falls through to the same `failed` row it gets today. Happy to handle it if you think it is worth the duplication. ## Tests Unit tests on the tasklet cover the flag off with an insufficient balance (full amount still attempted, `failed` recorded — the behaviour this change leaves alone), the clamp to the available balance with a `partial` status, the skip when nothing is available, and a fully covered run still recording `success`. An integration scenario in `SchedulerJobsTestResults` funds a savings account below the instruction amount, runs the job, and asserts the available balance is transferred and the history row reads `partial` with the transferred amount. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
