feat(bank-account): update approved bank account — backend [ENG-509]#444
Merged
islandbitcoin merged 3 commits intoJul 13, 2026
Conversation
Lets an approved user request a change to their Jamaican (ERPNext) bank account details. The request is created as a Bank Account Update Request DocType in ERPNext and reviewed by an admin before it takes effect; cashouts keep settling to the current details until approval. - New GraphQL mutation bankAccountUpdateRequest + BankAccount.pendingUpdate read field (regenerated SDL). - New ERPNext model BankAccountUpdateRequest and three ErpNext service methods (post / query-open / bulk-close). - App layer enforces the invariants: ownership (account must belong to the caller's erpParty), currency lock, non-empty/allowed proposed fields (validated server-side, not trusted from the client), and supersede (created before priors are closed so a failed create never destroys the existing request). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Contributor
Author
|
Part of ENG-509 — this change spans three repos:
Merge order: flash#444 first (it defines the GraphQL contract), then the other two — the mobile change depends on the backend field to work end-to-end. |
- pendingUpdate now surfaces Pending OR Rejected requests (getLatestBankAccountUpdateRequestForAccount), so a rejected change is shown to the user instead of silently vanishing. - Remove the unreachable SetDocTypeValueError branch (+ orphaned imports) in the mutation resolver; close is best-effort and never returns that type. - Dedup the bulk-close helper: parametrize setStatusForRequests(doctype, status) instead of a second near-identical method. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ENG-509] The api-key scope map requires an entry for every authed root field (deny-by-default regression test). Block bankAccountUpdateRequest for API-key access, mirroring businessAccountUpgradeRequest. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
bobodread876
approved these changes
Jul 13, 2026
islandbitcoin
added a commit
that referenced
this pull request
Jul 14, 2026
… [ENG-509] (#447) #444 added `pendingUpdate` to the public subgraph schema but never regenerated the composed Apollo Router supergraph. The router validates against that baked-in supergraph, so it rejects `bankAccounts { pendingUpdate }` with GRAPHQL_VALIDATION_FAILED (400) even on images built from #444 — which blocks the mobile cashout screen ("No bank account found") on TEST. Ran `make codegen` to recompose the supergraph. Only the generated supergraph.graphql changes (58 insertions, 0 deletions). Co-authored-by: Dread <bobodread@bobodread.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 15, 2026
islandbitcoin
added a commit
that referenced
this pull request
Jul 15, 2026
#449) `JMDAmount.dollars(d)` did `new JMDAmount(BigInt(d) * 100n)`, and `BigInt(d)` throws on any non-integer `d`. The cashout offer wraps the live NCB rate (essentially always fractional — 152.7, 155.5, ...) via this method, so `getCashoutExchangeRate` returned `ExchangeRateQueryError` for every real rate and `requestCashout` failed. Rewrote it to mirror `USDAmount.dollars` (Money-lib, HALF_TO_EVEN) so fractional rates convert correctly. Compounding it, `error-map.ts` had no `ExchangeRateQueryError` case, so the failure fell through to the exhaustiveness fallback ("This should never compile ...") and returned a `data: null` top-level GraphQL error — the app therefore showed no message, just a spinner. Added a mapped case. Only surfaces post-#444/#445 (live NCB rate); a deploy would break JMD cashout in every env until this lands. Co-authored-by: Dread <bobodread@bobodread.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What & why
Users submit their Jamaican bank details once, inside the account-upgrade (KYC) flow. After approval there was no way to change them. This adds a self-serve update path that goes through human review before taking effect.
Backend of 3 PRs for ENG-509 (see companion PRs in
frappe-flash-adminandflash-mobile).Changes
bankAccountUpdateRequest(input): BankAccountUpdateRequestPayload!mutationBankAccount.pendingUpdate: BankAccountUpdateRequestread field (drives the app's "Under review" state)BankAccountUpdateRequest+ threeErpNextservice methods (post / query-open / bulk-close)createBankAccountUpdateRequestInvariants enforced
erpPartyaccount_type ∈ {Chequing, Savings}, validated server-side rather than trusted from the clientTesting
yarn build✓ ·yarn test:unit(model round-trip, ErpNext HTTP methods, app-layer invariants incl. create-before-close safety and field validation) ✓ · eslint ✓ · SDL regenerated viamake codegen.Known follow-up
BankAccount.pendingUpdateresolves with one ERPNext call per account (N+1) and returnsnullon an ERPNext error. Fine for the handful of accounts a user has; a batch/dataloader is a deliberate follow-up.🤖 Generated with Claude Code