What is missing
Fineract accepts chargeback against an individual loan transaction. It reverses a repayment and, where that repayment had closed the loan, reopens it — the correction path for a payment applied in error or later dishonoured. This app offers no way to invoke it.
Confirmed against a running apache/fineract, using the discriminator established in #268 — an unrecognised command answers error.msg.query.parameter.value.unsupported on the command parameter, exactly as an invented name does:
| Probe |
Result |
POST /loans/{id}/transactions?command=totallyNotACommand |
error.msg.query.parameter.value.unsupported — unrecognised (control) |
POST /loans/{id}/transactions?command=writeoff |
validation.msg.loan.transaction.transactionDate.cannot.be.blank — recognised (control, and the app wires this one) |
POST /loans/{id}/transactions?command=chargeback |
error.msg.query.parameter.value.unsupported — unrecognised at this level |
POST /loans/{id}/transactions/{transactionId}?command=chargeback |
validation.msg.loan.transaction.transactionAmount.cannot.be.blank — recognised |
That last pair is the important detail: chargeback is only accepted against a specific transaction, not at loan level. So it does not belong on the loan's Actions menu with the other commands — it belongs as a row-level action on the Transactions tab, next to the repayment being reversed, which is also how a user would think about it.
What the UI offers today
Against a real Active loan seeded into a local Fineract stack, the Transactions tab's only affordances are the shared Repayment / Actions / Back controls — there is no per-row action at all:

grep -rli chargeback src/app returns nothing outside the generated API client. The client method already exists:
loanTransactionsService.postLoansLoanIdTransactionsTransactionId(loanId, transactionId, request, 'chargeback')
The platform permission is CHARGEBACK_LOAN (confirmed present in GET /permissions), so the action can be gated with appRequiresPermission like every other command on this screen.
Suggested scope
A row-level action on the Transactions tab, offered only on rows that can actually be charged back (a repayment-type transaction that has not already been reversed), collecting transactionAmount and a payment type. The request body is:
{ transactionAmount: number, locale: string, paymentTypeId?: number }
Fineract ships "Repayment Adjustment: Chargeback" and "Repayment Adjustment: Refund" payment types for exactly this.
Why it is worth doing
A repayment recorded against the wrong loan, or one whose cheque later bounces, has no correction path in this app once it has closed the loan. The alternative is editing the platform outside the UI, which leaves the back office unable to complete a routine correction on its own.
Environment
main at 7c51d90b. Probes against apache/fineract running locally via deploy/docker-compose-e2e.yml; screenshot from a loan seeded with the repository's own e2e/utils/seed-api.ts helpers.
What is missing
Fineract accepts
chargebackagainst an individual loan transaction. It reverses a repayment and, where that repayment had closed the loan, reopens it — the correction path for a payment applied in error or later dishonoured. This app offers no way to invoke it.Confirmed against a running
apache/fineract, using the discriminator established in #268 — an unrecognised command answerserror.msg.query.parameter.value.unsupportedon thecommandparameter, exactly as an invented name does:POST /loans/{id}/transactions?command=totallyNotACommanderror.msg.query.parameter.value.unsupported— unrecognised (control)POST /loans/{id}/transactions?command=writeoffvalidation.msg.loan.transaction.transactionDate.cannot.be.blank— recognised (control, and the app wires this one)POST /loans/{id}/transactions?command=chargebackerror.msg.query.parameter.value.unsupported— unrecognised at this levelPOST /loans/{id}/transactions/{transactionId}?command=chargebackvalidation.msg.loan.transaction.transactionAmount.cannot.be.blank— recognisedThat last pair is the important detail: chargeback is only accepted against a specific transaction, not at loan level. So it does not belong on the loan's Actions menu with the other commands — it belongs as a row-level action on the Transactions tab, next to the repayment being reversed, which is also how a user would think about it.
What the UI offers today
Against a real Active loan seeded into a local Fineract stack, the Transactions tab's only affordances are the shared Repayment / Actions / Back controls — there is no per-row action at all:
grep -rli chargeback src/appreturns nothing outside the generated API client. The client method already exists:The platform permission is
CHARGEBACK_LOAN(confirmed present inGET /permissions), so the action can be gated withappRequiresPermissionlike every other command on this screen.Suggested scope
A row-level action on the Transactions tab, offered only on rows that can actually be charged back (a repayment-type transaction that has not already been reversed), collecting
transactionAmountand a payment type. The request body is:Fineract ships "Repayment Adjustment: Chargeback" and "Repayment Adjustment: Refund" payment types for exactly this.
Why it is worth doing
A repayment recorded against the wrong loan, or one whose cheque later bounces, has no correction path in this app once it has closed the loan. The alternative is editing the platform outside the UI, which leaves the back office unable to complete a routine correction on its own.
Environment
mainat7c51d90b. Probes againstapache/fineractrunning locally viadeploy/docker-compose-e2e.yml; screenshot from a loan seeded with the repository's owne2e/utils/seed-api.tshelpers.