Skip to content

uid: T-156 title: Bank-account nickname β€” brand label wins over the clearing-bank registry name (Airwallex vs "DBS") status: done area: accounting created: 2026-07-02 updated: 2026-07-09 related: I-023


T-156 β€” Bank-account nickname (display label)

Why (owner, 2026-07-02)

The company's Airwallex account clears through DBS (Hong Kong) β€” Airwallex's Global Account is provided by DBS, so the account record carries clearing code 016. The Bank Accounts admin tab showed the account's own name ("Airwallex"), but the Bank Transaction Register XLSX and the accounting page resolved the display name from bankCode β†’ HKMA registry, which returns "DBS" and overrode the account's own name. Two surfaces, two different names for the same account.

Owner (verbatim): "The web app … has been referring the bank account as DBS rather than Airwallex"; after weighing an auto-combined label ("Airwallex Β· DBS (Hong Kong)" gets ugly and uncontrollable): "let's give it a nickname that allows user to use it universally then." Explicit constraint from the first framing: no hardcoded special case for Airwallex.

Design

One optional field, one precedence rule, applied at every display site:

  • nickname (string, trimmed, empty β†’ null) on the bankAccount/{id} Firestore doc β€” set by the user in the Bank Accounts admin form.
  • Precedence: nickname β†’ bankCodeβ†’registry short name β†’ raw bankName. Accounts without a nickname behave exactly as before.
  • Generic: any brand-on-a-bank account (a future "Wise (HSBC)") works the same way. Zero provider-specific code.
  • Cross-subsidiary dropdowns keep the "{Subsidiary} - {name}" prefix (owner-approved default); the nickname replaces only the name part.

What shipped (this session)

  • Types: BankAccountRecord.nickname (bankAccountsDirectory), BankAccountEntry.nickname (useBankAccounts), workspace + XLSX BankAccountInfo.nickname, client ResolvedBankAccount.nickname.
  • Readers: lib/erlDirectory.server.ts (resolveBankAccountIdentifierServer, listAllBankAccountsServer), client lib/erlDirectory.ts, and pages/api/bank-accounts/[id].ts all surface the trimmed nickname.
  • Display sites (nickname wins):
  • pages/api/accounting/bank-accounts.ts β€” both branches; displayName becomes "{Subsidiary} - {nickname}"; nickname also emitted so client-side registry lookups can defer to it.
  • components/accounting/BankTransactionsTab.tsx β€” Bank column cell render
    • the bank filter labels.
  • lib/export/transactionsXlsx.ts β€” the register XLSX Bank column.
  • components/projects/ProjectShowApp.tsx β€” invoice Pay-To bank labels (existing rows + pending-draft row).
  • Admin surface: Nickname input in the Bank Accounts form (tooltip explains precedence), nickname shown in the table (raw bank name kept as secondary text so the clearing rail stays auditable), search matches nicknames. Write paths: POST + PATCH accept/normalize/persist nickname (PATCH via the allowed-fields diff, so audit before/after logging covers it automatically; POST logs it in the create entry).

Propagation audit (owner-requested, 2026-07-02) β€” every surface accounted for

The owner asked for proof the change reflects at all affected places (past structural changes left stale readers behind). Full repo sweep of every bank-account-name resolution/render path:

Surface How it resolves Status
Bank Transactions table — Bank column + tooltip client bankCode→registry FIXED (nickname wins)
Bank Transactions — bank filter labels client bankCode→registry FIXED
Bank Transaction Register XLSX — Bank column client bankCode→registry FIXED
/api/accounting/bank-accounts (bankName + displayName) server bankCode→registry FIXED (both branches; nickname also emitted)
↳ Standalone transaction page (/transaction/[id]) API displayName AUTO (inherits the API fix)
↳ AccountingApp COA linked-account chip API map (renders accountType only) AUTO / unaffected
↳ every other useBankAccounts() consumer API displayName/bankName AUTO
Projects page — invoice Pay-To labels (existing + pending rows) client bankCode→registry FIXED
Invoice details drawer β€” Pay-To line raw via /api/bank-accounts/[id] FIXED (nickname preferred)
Admin Bank Accounts tab β€” table + search + form raw bankName FIXED (nickname primary, raw kept as secondary; searchable)
Customer-facing payment instructions β€” invoice preview/PDF/XLSX "Bank Name", invoice email (invoices/send.ts), coaching payment-request payeeBank raw stored bankName UNCHANGED β€” DELIBERATE. These tell the payer where to wire money; the receiving rail ("DBS Bank (Hong Kong)", code 016) is the correct name there, not the internal brand label. Do not "fix" these to nickname.
Contacts / profile bank fields (BankAccountsField, T-048) payee/individual accounts β€” different collection out of scope
Records β†’ Bank Statements provider labels; Bank Access dashboards hardcoded provider names (already "Airwallex"/"OCBC") unaffected
import-mt940 / sync internals resolver used for matching, not display unaffected

No data migration needed β€” verified: nothing persists a company-account display name snapshot. Invoices store payTo = the account id and resolve at render; transactions store bankAccountId; coaching payment requests snapshot payeeBank at creation by design (issued documents are frozen). Live doc check (read-only, SA): ERL-AWX-HKD has bankName: "DBS Bank (Hong Kong)", bankCode: "016", nickname: unset β€” so today's "Airwallex" on the admin tab comes from the group header (BANK_ABBREVIATIONS[AWX], a bank-level label), while the registry override showed "DBS" elsewhere. After setting nickname: "Airwallex", internal surfaces read "Airwallex" and payment instructions keep naming the DBS rail β€” both correct.

Verification

  • tsc --noEmit clean Β· vitest 579/579 (re-run after the drawer fix).
  • Owner-on-deploy: set nickname "Airwallex" on ERL-AWX-HKD in the Bank Accounts tab β†’ Bank Transactions table, its filter, the register XLSX, Pay-To labels, and the invoice drawer all read "Airwallex"; invoice payment-instruction blocks still name "DBS Bank (Hong Kong)" (deliberate, see the audit table).

Decision log

2026-07-02 β€” opened + implemented (nickname design, owner-approved)

  • βœ… Attestation (Accounting (Diagnostics)): read AGENTS.md; checked the board by scope β€” no existing task covers bank-account display naming (T-024 is Airwallex payouts; T-048 is the Contacts refactor). Tracking T-156.
  • Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea
  • UID note: drafted as T-150 per the README pointer, but T-150 (Badges editor) + T-151 (two-layer RBAC) were already taken by User Management with the pointer left stale β€” allocated past all taken UIDs β†’ T-156 and fixed the pointer (allocating is not reserving; renumber-on-collision per AGENTS.md).
  • Proposed by: the owner (nickname idea, refining an earlier checkbox concept once auto-combined labels proved uncontrollable). Approved by: the owner β€” "let's give it a nickname that allows user to use it universally then" + "Proceed" (2026-07-02). Prefix default (keep "{Subsidiary} - " in cross-subsidiary dropdowns) offered and not objected to.
  • Why not auto-combine brand+rail: registry names vary ("DBS (Hong Kong)", "DBS Bank (Hong Kong) Limited") so a generated compound label is ugly and uncontrollable; a user-set label gives exact control and stays provider-agnostic.
  • Blast radius (accounting/display): bank-account name resolution at the sites listed above + the admin form/APIs. No transaction data touched; no behavior change for accounts without a nickname.

2026-07-02 β€” DEFINITION (owner ruling): nickname = internal Β· bankName = customer-facing/official

  • βœ… Attestation (Accounting (Diagnostics)): read AGENTS.md; recording the owner's semantic ruling so the internal/official split can't be blurred by a future change.
  • Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea
  • Ruling (owner, verbatim): "Okay, good call for the deliberately unchanged. So just for the record, we should define the 'nickName' as internally used vs. customer facing/ officially used."
  • Definition, for the record:
  • nickname β€” INTERNAL display label. What the team sees inside the app: transactions table + filters, register XLSX, Pay-To labels, drawers, dropdowns. Wins over the bankCodeβ†’registry short name wherever set.
  • bankName β€” CUSTOMER-FACING / OFFICIAL bank name. What payers see on payment instructions (invoice preview/PDF/XLSX, invoice email, coaching payment requests). Always the real receiving bank (e.g. "DBS Bank (Hong Kong)", code 016) β€” never replaced by a nickname, because the payer's bank routes by it.
  • Codified in: the BankAccountRecord.nickname JSDoc (lib/bankAccountsDirectory.ts, the canonical type) and the admin form's field label ("Nickname (internal display)") + tooltip, so both developers and users hit the definition where they'd act on it.

2026-07-02 β€” enable-checkbox added (owner follow-up)

  • βœ… Attestation (Accounting (Diagnostics)): read AGENTS.md.
  • Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea
  • Owner (verbatim): "there should be a check box allowing the enabling of usage of the nickName, is it implemented in the code but not reflected to the build as the web app hasn't been deployed?" β€” Answer given honestly: it was not implemented; the earlier design used presence-of-nickname as the switch. Added now per the owner's original intent.
  • What changed: new nicknameEnabled?: boolean on the doc (false = suppressed; unset/true = enabled, so pre-checkbox docs keep behaving as built). Gated at the source β€” the four nickname-emitting readers (erlDirectory.server Γ—2, client erlDirectory, /api/bank-accounts/[id]) return nickname: null when disabled, so every display site obeys the checkbox with zero per-site changes (same no-stale-reader principle as the propagation audit). Admin APIs expose the raw nickname + flag (form can edit a disabled nickname); POST/PATCH persist it audited; the form gains a "Use nickname" checkbox beside the input (default ticked); the admin table shows the nickname only when enabled.
  • Deploy note: nothing about T-156 is deployed yet (PR #838 draft) β€” the owner is holding deploy to ship together with another agent's in-flight work.
  • Issue 2 from the same owner message β€” migrating the Bank Accounts tab into the Contacts β†’ Subsidiary tab (full entity consolidation incl. Firestore re-homing) β€” is a separate structural design routed to Accounting (Infrastructure); Diagnostics will supply the current-structure write-up. This task deliberately does NOT move any data or UI home.

2026-07-03 β€” renumbered T-152 β†’ T-156 (second collision on this task)

  • βœ… Attestation (Accounting (Diagnostics)): read AGENTS.md; renumber-on-merge, scope unchanged.
  • Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea
  • Why: T-152 was taken on main by a parallel agent (Project quotations delete affordance) before this branch merged. This task (drafted T-150 β†’ T-152 β†’ now T-156) moved whole-identity: file, frontmatter, cross-links, and in-code [T-156] markers. Historical commit messages keep the old numbers (immutable).

2026-07-09 β€” CLOSED (shipped, deployed, live) β€” status β†’ done

  • βœ… Attestation (Accounting (Diagnostics)): read AGENTS.md. Owner asked for a board checkup; this task was stale at doing after shipping.
  • Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea
  • Merged in PR #838 β†’ main @ c5e1cf75 (2026-07-03) and deployed the same day (Vercel dpl_4mjR4jpEGL1v3JmftEnWKh8dn6MC, then superseded by later deploys that carry the same code β€” prod verified on a descendant sha). Nickname + "Use nickname" checkbox, source-gated precedence, and every internal display site are live.
  • Open ops item (not a code gap): ERL-AWX-HKD still has no nickname value set β€” set "Airwallex" in Contacts β†’ Subsidiary β†’ ERL β†’ Bank accounts (or Diagnostics writes it via SA on request). The feature itself is complete; recording here so the original complaint ("Airwallex shows as DBS") is traceably closed once the value is entered.

2026-07-09 (later) β€” ops item closed: nickname entered by the owner

  • The owner set nickname "Airwallex" (enabled) on ERL-AWX-HKD through the live UI; verified in both Firestore homes with matching updateLog entries. The original complaint (Airwallex account displaying as "DBS") is now fully resolved end-to-end. Nothing remains on this task.
  • Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea