uid: T-168 title: Legacy auto-match lanes repaired β prefix-aware invoice lookup Β· WOPC bucketed shape + amount guard Β· sync-time WOPC linking Β· partial-payment visibility status: done area: accounting-matching created: 2026-07-17 updated: 2026-07-17 related: I-034, I-035, T-038, T-089, T-168
T-168 β Legacy auto-match lanes repaired¶
Why (owner, 2026-07-17, verbatim)¶
"tx with id zSkSbMmoY37xvLAmQrpY has the title of the invoice number, which that tx should match to 4000 or 4001 in accordance to that. While tx with id xTrsN4SGiPPuWLwA7oZg has the WOPC ref number, which should automatically match to the WOPC either at Pay-with-WOPC-time or using the WOPC ref number in the tx description, but that didn't happen. β¦ the tx amount should match with the amount on WOPC β¦ while I understand that matching invoices to tx could be tricky as there's a possibility for clients to pay the partial amount of the invoice, but we should also have detection in place to react accordingly."
Diagnosis (live-data verified) in I-034 and I-035. Owner go (verbatim): "Sure, proceed with the 3."
What landed¶
- Prefix-aware invoice lookup, deduplicated (
lib/accounting/projectInvoiceLookup.server.ts) β one sharedfindProjectInvoiceByNumber(exact doc id fast-path for the 2025-era unprefixed convention, then a subcollection listing matched byid === n || id.endsWith('-'+n)for the 2026 prefixed convention). Replaced THREE duplicated copies (sync-timetransactionAutoLinker+ both ininvoice-auto-match). Allocation keys use the doc's own storedinvoiceNumber(prefixed-era canonical) for consistency. - WOPC auto-match core, extracted + repaired (
lib/accounting/wopcAutoMatch.server.ts) β reads through the canonicalwopcDocShape.toView(the endpoint was the last flat-shape reader after T-089 P5); already-linked guard rewritten for the real status domain (active/pending_transaction+ transaction linkage + void/deleted); amount guard (owner rule): tx amount must equal WOPC total Β±0.01, mismatch βmatch_suggestionnotification, never a silent link; per-match notification moved into the core so every caller reports identically. The page-load endpoint now delegates to the core. - Sync-time WOPC linking β both sync routes (
airwallex/sync,ocbc/velocity/sync-accounting) run the WOPC core on freshly created, still-unmatched transactions, so a paid WOPC links the moment its transaction lands instead of waiting for an accounting-page visit. - Partial-payment visibility β
notifyPaymentMatchedgainsinvoiceTotal; when the applied amount is short, the title/message flag "Partial payment β¦ (invoice total X, Y outstanding)". The sync-time auto-linker (previously entirely silent on project links) now fires this notification per link β the auto lane finally matches the manual PATCH lane's behaviour.
Verification¶
12 new tests (legacyLaneFixes.test.ts β pure id matcher both conventions, prefixed-era lookup
hit incl. the reported miss, partial-message wording; wopcAutoMatchCore.test.ts β migrated-shape
happy path mirroring the live ERL-WOPC|2026-009 doc, amount-mismatch β suggestion + zero writes,
already-linked / active / void skips, direction guard, missing-WOPC skip). Suite 675/675;
unfiltered tsc clean.
Aftermath for the two reported transactions¶
No data surgery needed: after the next production deploy, either the next sync or one
accounting-page rescan links both β zSkSβ¦ to invoice ERL-2026-005-0601 (with a
payment-matched notification; amounts equal so not partial), xTrs⦠to WOPC ERL-WOPC/2026-009
(amounts agree at HK$2,500 β links; status β active).
- β Read AGENTS.md Β· checked the board by scope (no dup β T-038/040/041 covered the NEW vendor engine's notifications; these are the OLD lanes' correctness defects) Β· tracking T-168.
- Source: Accounting (Diagnostics) Β· https://claude.ai/code/session_01G58Y71noihrYCDEDMexmea
- Commit SHAs (append-only): this ship commit (T-168 in subject; merged via PR).
- Deploy: pending (main π‘π‘ manual; done = merged).
- Blast radius: matching lanes only β invoice lookup now also matches prefixed doc ids
(strictly widens hits); WOPC lane writes the same two-step link as the manual flow but now with
correct data; new notifications: partial-payment variant of
payment_status, andmatch_suggestionfor WOPC amount mismatches. Sync routes gained a WOPC pass (per-tx, failure-tolerant). No schema changes.