Skip to content

uid: T-189 title: Postage expense category β†’ GL 6300 (category-only, no dedicated account) status: done area: accounting created: 2026-07-27 updated: 2026-07-27 owner: girafeev1 related: T-178, T-181


T-189 β€” Postal expenses get a category

Why

Owner, 2026-07-27: "do we have any expense GL account for postal expenses (mailing of company's documents)? Cos I'm starting to have that expense now, and I'd like it to be properly categorized (for the Telegram AI categorization and on the web app)."

Verified before answering β€” both the code-side maps and the live chart of accounts (43 accounts in tebs-erl, read 2026-07-27): no postage, courier or mailing account existed anywhere. A stamps or SF Express receipt would have fallen to other β†’ 6300, invisible as a class.

Decision (owner, 2026-07-27)

Two options were put to the owner: A β€” a dedicated account 6310 Postage & Courier plus the category; B β€” category only, mapped to existing 6300 Office & Administrative. The owner chose B, verbatim: "6300 it is." So the chart of accounts is untouched; the category gives the Telegram AI and the web pickers a proper postage classification, and the GL rolls it into admin. If postal spend ever deserves its own reporting line, adding 6310 later is a data change plus a two-line remap β€” nothing about this choice blocks it.

What shipped

  • lib/telegram/categories.ts β€” postage joins RECEIPT_CATEGORIES with label "Postage" (the Drive folder under 14a. Receipts/{YYYY}/ lazy-creates on first hit, so no manual Drive work), a Gemini hint (post office, stamps, registered mail, PO box, courier/express of documents and parcels β€” Hongkong Post, SF Express, DHL, FedEx, UPS; explicitly NOT passenger transport), and coercion synonyms (mail, postal, post office, stamps, courier, registered mail, express delivery).
  • lib/accounting/receiptCategoryToGl.ts β€” postage: '6300', rationale recorded in the file's own mapping table.
  • lib/accounting/expenseCategories.ts β€” postage in the unified enum (receipt-origin block), label, GL 6300, hint. The web pickers derive from these lists, so the receipt upload modal, the Records filters, and the Service-Invoice modal's category dropdown all gain it with no per-component edits; the Telegram bot's inline keyboard likewise renders from RECEIPT_CATEGORIES.

Firestore: no structure change, no data change. The category is a new value in an existing string field; the chart of accounts was deliberately not touched (that was the decision). Compile-time Record<Category, …> types forced every map to be updated together β€” a missing one would not build.

Verified: unfiltered tsc --noEmit --incremental false clean Β· vitest run 738 passed (the same 4 known pre-existing billing-ingest failures) Β· eslint on changed files: error count identical to a stashed clean HEAD (all pre-existing).

Needs a deploy to take effect (code-only change; none fired β€” on-command per policy). After deploy: a Telegram receipt from a post office should classify as Postage β†’ GL 6300, and "Postage" appears in the web category pickers.

Blast radius: additive enum member. Anything asserting a fixed category count would need a bump β€” none found (the category test iterates the list).

Renumber log

  • 2026-07-28 β€” Renumbered T-188 β†’ T-189 on merge. Two agents allocated T-188 from the same next-free pointer on parallel branches: User Management (one profile per person, merged first via their PRs) and Records (Infrastructure) (this task). Per the AGENTS.md collision rule β€” allocating is not reserving; whoever merges second renumbers β€” this task moved to T-189. Cross-links in decisions.md and the README pointer moved with it; commit messages citing T-188 for postage are immutable history and stay as written. Source: Records (Infrastructure) Β· https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD

Source: Records (Infrastructure) Β· https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD

Log addendum

  • 2026-07-28 β€” Codex review (P2 on #933) confirmed and fixed: the Document AI fallback classifier (documentAiReceiptProcessor.ts CATEGORY_KEYWORDS) had no postage expression, so when Gemini is unavailable a postal/courier receipt fell through to other. Keywords added (post office, Hongkong Post, Speedpost, stamps, registered mail, PO box, SF Express, DHL, FedEx, UPS, courier) β€” the category now works on both classification paths. Source: Records (Infrastructure) Β· https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD

  • 2026-07-28 β€” Merged to main in PR #933 (f2d310a), not deployed. SHA list gains cbe41f5 (Codex round) and the merge SHA. Deploy verification pending per the task's own notes. Source: Records (Infrastructure) Β· https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD