Skip to content

feat(products): correct a deposit or savings transaction after it is recorded - #341

Merged
Aman-Mittal merged 1 commit into
apache:mainfrom
Aman-Mittal:feat/deposit-transaction-servicing
Aug 14, 2026
Merged

feat(products): correct a deposit or savings transaction after it is recorded#341
Aman-Mittal merged 1 commit into
apache:mainfrom
Aman-Mittal:feat/deposit-transaction-servicing

Conversation

@Aman-Mittal

Copy link
Copy Markdown
Member

What and why

The transactions tables on the savings and term deposit screens could be read but never acted on. A teller who mis-keyed an amount had no reversal, and a hold placed against a claim could never be released from this application — money could be earmarked and then only freed outside the product.

Closes #298. Also closes the last two rows of the table in #197 (holdAmount / releaseAmount, which was half-built: funds could be held but not freed).

Verified against a running Fineract

Probing the command discriminator on each endpoint:

Endpoint Commands
POST /savingsaccounts/{id}/transactions/{txId} undo, modify, releaseAmount, reverse
POST /recurringdepositaccounts/{id}/transactions deposit, withdrawal
POST /recurringdepositaccounts/{id}/transactions/{txId} undo, modify
POST /fixeddepositaccounts/{id}/transactions deposit, withdrawal
POST /fixeddepositaccounts/{id}/transactions/{txId} undo, modify

What the discriminator does not say, and the platform does:

  • A fixed deposit refuses a withdrawal. The command is enumerated but answers 503 error.msg.fixeddepositaccount.account.trasaction.withdraw.notallowed. The screen offered Withdraw on every term deposit and navigated to a route that does not exist. It is now offered for recurring deposits only, where it works — money leaves a fixed deposit through maturity or premature closure, both already on the menu.
  • undo is lenient. It answers 200 against an already-reversed transaction, and against a hold or a release, doing nothing useful. releaseAmount is strict, refusing a second release with validation.msg.amount.is.not.on.hold. So the screens gate both themselves rather than offering a button that reports success and changes nothing.
  • A hold records releaseTransactionId as 0 while it stands, not as absent, so the release gate tests for a truthy id rather than for the key.

The term deposit transactions tab was empty by construction

It read account['transactions'], and neither GET /fixeddepositaccounts/{id} nor its recurring twin returns that key unless asked. So the tab showed nothing on every account regardless of activity.

  • Fixed deposit has a list endpoint and a generated method for it — that is the typed path and it is used.
  • Recurring deposit has neither. GET /recurringdepositaccounts/{id}/transactions answers 405, and the upstream OpenAPI document describes no associations parameter for either deposit type — although it does for /savingsaccounts/{accountId}. That one read therefore goes through the same HttpClient escape hatch group-view already uses, with the condition for deleting it recorded in the comment.

Worth raising upstream: associations is honoured by the platform on both deposit account GETs but missing from the spec.

Reversed rows stay visible

Struck through, marked, never removed — on both screens. Fineract flags reversed rather than deleting, and dropping the row would take the audit trail with it. Same failure class as #280.

The fixed-deposit transaction form

Did not exist. One trap worth flagging for reviewers: its generated operation is (accountId, command, body) where the recurring twin is (accountId, request, command), and the body is typed string. A call copied from the twin binds the request object to command and still compiles. The spec asserts the argument positions explicitly.

Two more spec-versus-platform gaps handled locally, both commented: paymentTypeOptions is typed Array<number> but arrives as objects (binding the raw element would render [object Object]), and paymentTypeId is absent from the request model although the platform accepts it and echoes it back under changes.

Not touched

#279/#278 overlap with open PR #335, which fixes the recurring-deposit route and command. Nothing here changes that file or the recurring-deposit navigation.

Verification

  • Unit: 909 pass, including the gating rules and the argument-order assertion.
  • Mocked e2e: 247 pass.
  • Real-backend e2e, both new:
    • a fixed deposit is approved, activated, a deposit recorded, listed on the tab, and reversed while staying on screen;
    • a savings hold is released and a savings deposit reversed.
  • npm run lint, format:check, check:icons, i18n:check, build all clean.

Checklist

  • I did not hand-edit generated files under src/app/api/.
  • New component uses the adapter boundary (| appTranslate), per ADR-0003.
  • New user-facing strings added to en.json and verified by hand — scripts/check-translations.mjs does not match | appTranslate.
  • Commits are signed.

…recorded

The transactions tables on the savings and term deposit screens could be read
but never acted on. A teller who mis-keyed an amount had no reversal, and a
hold placed against a claim could never be released from this application —
money could be earmarked and then only freed outside the product.

Verified against a running Fineract rather than assumed. Probing the command
discriminator on each endpoint gives:

  savings   POST /savingsaccounts/{id}/transactions/{txId}
            undo, modify, releaseAmount, reverse
  RD        POST /recurringdepositaccounts/{id}/transactions      deposit, withdrawal
            POST /recurringdepositaccounts/{id}/transactions/{txId}  undo, modify
  FD        POST /fixeddepositaccounts/{id}/transactions          deposit, withdrawal
            POST /fixeddepositaccounts/{id}/transactions/{txId}   undo, modify

What the discriminator does not say, and the platform does:

* A fixed deposit **refuses** a withdrawal. The command is enumerated but
  answers 503 error.msg.fixeddepositaccount.account.trasaction.withdraw.notallowed.
  The screen offered Withdraw on every term deposit and navigated to a route
  that does not exist; it is now offered for recurring deposits only, where it
  works. Money leaves a fixed deposit through maturity or premature closure.

* undo is lenient — it answers 200 against an already-reversed transaction and
  against a hold or a release, doing nothing useful. releaseAmount is strict,
  refusing a second release with validation.msg.amount.is.not.on.hold. So the
  screens gate both themselves rather than offering a button that reports
  success and changes nothing.

* A hold records releaseTransactionId as 0 while it stands, not as absent, so
  the release gate tests for a truthy id rather than for the key.

The term deposit transactions tab was empty by construction. It read
account['transactions'], and neither GET /fixeddepositaccounts/{id} nor its
recurring twin returns that key unless asked. Fixed deposits have a list
endpoint and a generated method for it. Recurring deposits have neither — the
list path answers 405, and the upstream document describes no associations
parameter for either deposit type although it does for /savingsaccounts/{id} —
so that one read goes through the HttpClient escape hatch group-view already
uses, with the condition for deleting it recorded.

Reversed rows stay on screen, struck through, on both screens. Removing them
would take the audit trail with them.

Also adds the fixed-deposit transaction form, which did not exist. Its
generated operation is (accountId, command, body) where the recurring twin is
(accountId, request, command), and the body is typed string; a call copied from
the twin binds the request to command and still compiles, so the spec asserts
the positions.

Closes apache#298.

Covered by unit specs for the gating and the argument order, and by two
real-backend suites: a fixed deposit recorded and reversed, and a savings
deposit reversed with a hold released.
@Aman-Mittal
Aman-Mittal merged commit 164ade1 into apache:main Aug 14, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Term deposits: transactions tab with undo, and a fixed-deposit transaction form

1 participant