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 thebankAccount/{id}Firestore doc β set by the user in the Bank Accounts admin form.- Precedence:
nicknameβbankCodeβregistry short nameβ rawbankName. 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 + XLSXBankAccountInfo.nickname, clientResolvedBankAccount.nickname. - Readers:
lib/erlDirectory.server.ts(resolveBankAccountIdentifierServer,listAllBankAccountsServer), clientlib/erlDirectory.ts, andpages/api/bank-accounts/[id].tsall surface the trimmed nickname. - Display sites (nickname wins):
pages/api/accounting/bank-accounts.tsβ both branches;displayNamebecomes"{Subsidiary} - {nickname}";nicknamealso 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 auditbefore/afterlogging 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 --noEmitclean Β· vitest 579/579 (re-run after the drawer fix).- Owner-on-deploy: set nickname "Airwallex" on
ERL-AWX-HKDin 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.nicknameJSDoc (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?: booleanon 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, clienterlDirectory,/api/bank-accounts/[id]) returnnickname: nullwhen 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.
Related¶
- 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
mainby 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 atdoingafter shipping. - Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea
- Merged in PR #838 β
main@c5e1cf75(2026-07-03) and deployed the same day (Verceldpl_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