Skip to content

I-030 β€” Hardcoded payee blocks have drifted from the bank-account registry

Symptom (verified against Firestore + the I-023 bank statements)

The full ERL payee block (bank name, account number, FPS ID) is hardcoded in 7+ files instead of resolved from the bankAccount/{id} registry β€” and it has drifted, in two ways that are live, customer-facing wrong:

  1. Wrong account number. Hardcoded: 802-756699-831. The registry β€” and the actual OCBC statements (I-023) β€” say 802-757699-831 (756 vs 757, one digit off). Any payer who typed the account number from a payment request got a wrong account. (FPS payers were unaffected β€” the request's FPS ID 115278533 is correct for ERL-OCBC-S β€” which is likely why this went unnoticed.) Sites: app/coaching/invoice/[sessionId]/client.tsx:48 (DEFAULT_PAYEE), components/tools/ToolsApp.tsx:418, lib/share/fetchSharedDoc.server.ts:300 (shared payment-request docs), app/tools/payment-request-preview/client.tsx:55,91.
  2. Closed account's FPS ID on receipts. Payment receipts hardcode fpsReceiverId: 114418007 β€” that is ERL-DSB-S's FPS ID, a closed Dah Sing account (registry-verified). Current receipts should carry the active receiving account. Sites: components/tools/ToolsApp.tsx:404, app/tools/payment-receipt-preview/client.tsx:51,88, app/coaching/[studentId]/payment/[paymentId]/receipt/client.tsx:410.
  3. Related seed drift: scripts/migrate-coa.ts:61-66 links ERL-DBS-S/C where the canonical seed (lib/accounting/types.ts:2553) says ERL-DSB-S/C (Dah Sing). Bank name string also inconsistent ("Overseas Chinese…" vs the registry's "Oversea-Chinese…").

Root cause

The bank-account registry (bankAccount/{id} + resolveBankAccountIdentifier) is the intended single source and works on the invoice-PDF path β€” but the coaching payment-request / receipt / Tools / shared-doc surfaces predate or bypassed it, each baking its own copy of the payee block. Copies drifted; the registry stayed right. (Exactly the owner's suspicion: the bank-account identifier is under-utilized.)

Escalation

Structural (7+ call sites need one shared resolver) β†’ T-158 carries the fix: a getCompanyPayeeBlock(bankAccountId) helper resolving name / clearing code / account number / FPS ID from the registry, replacing every hardcoded block; historical issued documents keep their frozen values by design.

Decision log

2026-07-02 β€” βœ… Read AGENTS.md. Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea

Owner confirmation + hotfix (same day): owner (verbatim): "I confirm that 757699-831 is the correct one." The five wrong 802-756699-831 literals (plus one type-comment example) were hot-fixed to 802-757699-831 immediately β€” repo-wide grep now clean. The structural fix (registry resolver replacing all literals) and the receipts' closed-account FPS ID question (nuance: a re-rendered receipt of a DSB-era payment may legitimately reference the old FPS ID, so it must not be blind-swapped) remain with T-158.

Found by the owner-requested hardcode sweep ("investigate whether that bank information are hardcoded or written anywhere"). Verified against the live registry docs (SA read-only) and the I-023 statement reconciliation before recording: registry ERL-OCBC-S accountNumber=802-757699-831 fpsId=115278533; ERL-DSB-S fpsId=114418007 status=closed.

2026-07-03 β€” renumbered I-029 β†’ I-030

  • βœ… Read AGENTS.md; renumber-on-merge (I-029 taken on main by the coaching-invoice-links recovery ticket). Scope unchanged; task cross-link now T-158.
  • Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea

2026-07-09 β€” FIXED in production

  • βœ… Read AGENTS.md. T-158 merged (PR #838 @ c5e1cf75) and deployed 2026-07-03; verified still live on the current prod sha. Customer-facing payee blocks resolve from the registry; the wrong account number and the closed-account FPS ID can no longer render. Status β†’ fixed.
  • Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea