Skip to content

T 115


uid: T-115 title: Receipt Register XLSX β€” surface the Substantiating Notes column status: done area: records-export created: 2026-06-25 updated: 2026-06-25 related: T-114, T-046


Why

Every confirmed receipt carries a substantiation note β€” the "who attended + business purpose" text the assessor expects on F&B / entertainment claims per HK IRD DIPN 21. It's required at confirm time for category ∈ {fnb, entertainment} and optional elsewhere; the writer carries it into lines[].memo on the auto-posted journal entry so it survives even if the receipt is later deleted (lib/telegram/receiptStore.ts:178-186).

The Records β†’ Receipts: Expense β†’ Export (Register of Expense Receipts XLSX) was emitting the receipt's date, merchant, category, GL, currency, total, and the file link β€” but not the substantiation note. The export is the audit artifact bookkeepers hand to the assessor, so the note belongs on it. Without it, the assessor has to click through every receipt's PDF or cross-reference journal memos.

Owner, 2026-06-25 (verbatim): "Substantiating Notes need to be included in the Receipt Register"

What changed

  • lib/accounting/expenseRecordsRow.ts β€” extended the unified ExpenseRecordRow with note: string | null. Populated from r.note in mapReceiptToExpenseRecord; set to null in mapServiceInvoiceToExpenseRecord (vendor invoices carry their own substantiation on the invoice/PO, not in a separate note field).
  • components/records/RecordsApp.tsx β€” added a Substantiating Notes column to the Receipts export between Total and File. Empty string when the receipt has no note (the export already shows '' for absent fields elsewhere β€” matches AGENTS.md UI convention that missing data renders as a blank dash, not crashes).

Why it's built this way

  • Column position between Total and File: keeps the "data the assessor reads from left to right" together (date β†’ merchant β†’ category β†’ GL β†’ currency β†’ total β†’ why β†’ evidence).
  • Field name note on ExpenseRecordRow mirrors the underlying TelegramReceipt.note directly β€” no renaming. The XLSX header is the human-facing "Substantiating Notes" so auditors don't need to learn our internal term.
  • No on-screen column added. The drawer already shows the note prominently with a DIPN 21 explainer; the row table stays unchanged so this PR is just the export-artifact fix the owner asked for.

Decision log

2026-06-25 β€” opened + implemented

  • βœ… Attestation (Records (Infrastructure)): read AGENTS.md; scope-scanned the board β€” T-046 is the Bank Transaction Register XLSX overhaul (different register); T-007 dropped the "use the note as a fingerprint" idea (different concern). No covering task for "surface the substantiation note on the Receipt Register."
  • Source: Records (Infrastructure) Β· https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD
  • Proposed by: the owner β€” verbatim above. Approved by: the owner (same line).
  • Branch / PR: claude/receipts-export-substantiating-notes β†’ PR to main (branch policy 2026-06-24: 🟒 main / πŸ”΄ nightly).
  • Verification: could not run tsc in-sandbox (no node_modules); the change is a small data plumb + one column entry β€” symmetric with existing columns + with the note field already on TelegramReceipt. Owner to confirm post-deploy: re-export the Receipts register β†’ the new column appears between Total and File, populated for receipts that carry a note.
  • Blast radius: ExpenseRecordRow.note is additive (no removed/renamed field), so any other consumer of ExpenseRecordRow that doesn't read note is unaffected. Service-invoice rows always set note: null, matching their data model. The on-screen receipts table is unchanged.
  • Cross-link: T-114 is the sibling export fix (durable Invoice Number hyperlink in the Service Invoice register).

Commit index (backfilled 2026-07-01, best-effort Β· Coaching (Diagnostic))

Candidate related commits, auto-backfilled from git on main: commits whose message references this task's UID or a PR number it cites. Not verified β€” this squash-merged history can't yield a precise per-task list, so rows tagged (mentions only) name the task in passing (may be tangential) and untagged work commits may be missing. Treat as a starting point: verify, prune tangential rows, and append any real ones per the AGENTS.md "record every related SHA" policy.

  • 60a75e9 2026-06-25 β€” feat(t-115): vendor template clean-up + live-sync templates β†’ transactions (mentions only)
  • 3f5d795 2026-06-25 β€” Merge #810: feat(records) β€” Substantiating Notes column on the Receipt Register XLSX (mentions only)
  • 44c9107 2026-06-25 β€” fix(records): read receipt.note from metadata (not top-level) β€” T-115 follow-up
  • 12964c1 2026-06-25 β€” Merge: fix(records) β€” read receipt.note from metadata, not top-level (T-115 follow-up)
  • 51f2020 2026-06-25 β€” Merge main + renumber T-114β†’T-117, T-115β†’T-118 (renumber-on-merge)