WPP — render every statement as XLSX with shared Bank-Tx letterhead
Why (owner, 2026-06-25)¶
"4) WPP should render xlsx instead of csv, and 5) Put the Bank tx xlsx header to all the WPP xlsx as well"
The Working Paper Pack endpoint (/api/accounting/working-paper-pack)
shipped 11 CSV files inside a zip (one per primary statement +
supporting schedule). The auditor opens the zip in Excel / Numbers —
the moment they do, the CSVs are de-facto XLSX-displayed anyway. The
user wants the WPP to ship native XLSX with the same Bank-Tx-style
letterhead band (logo + subsidiary identity + statement title) every
other owner-facing export now carries (T-124 sibling).
What shipped¶
lib/accounting/wppXlsx.ts (new)¶
One module mirroring wppCsv.ts's 11 *ToCsv builders with parallel
*ToXlsx builders. Each takes the same generator output the matching
CSV builder takes plus a LetterheadSubsidiary | null and returns a
Buffer ready to drop into the zip.
Internally, every *ToXlsx follows the same pattern:
csv ← <existing>ToCsv(arg)
rows ← csvToRows(csv) # CSV-quote-aware split + per-line parse
# (CRLF + escaped "" handled; ready for
# any future multi-line cell content)
ws.addXlsxLetterhead({…})
ws.writeRows(rows, starting at headerRowIdx)
return wb.xlsx.writeBuffer()
Numeric-looking cells are promoted back to real numbers so the
auditor can SUM / pivot them in Excel; everything else stays text.
Account codes (12010-100 etc.) fail the strict numeric regex so
they retain their leading characters.
This keeps wppCsv.ts as the single source of truth for content +
ordering — verification path (re-render → text-diff) is unchanged.
pages/api/accounting/working-paper-pack.ts¶
- Imports swap
wppCsv→wppXlsx. The 11 CSV emit lines undercsv/*.csvbecomexlsx/*.xlsxemit lines, generated in parallel viaPromise.all(each XLSX build is independent). fetchSubsidiaryInfo(subsidiaryId)is called once for the letterhead identity block;.catch(() => null)falls back to a logo-only letterhead if the metadata fetch fails — pack still ships.- README cover sheet + manifest.json entries updated: file names, format strings, and the explanatory text now describe XLSX.
File names¶
| Old | New |
|---|---|
csv/trial-balance.csv |
xlsx/trial-balance.xlsx |
csv/profit-and-loss.csv |
xlsx/profit-and-loss.xlsx |
csv/balance-sheet.csv |
xlsx/balance-sheet.xlsx |
csv/cash-flow.csv |
xlsx/cash-flow.xlsx |
csv/socie.csv |
xlsx/socie.xlsx |
csv/ar-aging.csv |
xlsx/ar-aging.xlsx |
csv/related-party.csv |
xlsx/related-party.xlsx |
csv/ir56m-candidates.csv |
xlsx/ir56m-candidates.xlsx |
csv/notes.csv |
xlsx/notes.xlsx |
csv/fixed-assets.csv |
xlsx/fixed-assets.xlsx |
csv/journals.csv |
xlsx/journals.xlsx |
Why this and not "ship both formats"¶
Two reasons:
- The CSV builders remain importable from
wppCsv.ts— anything needing raw CSV (a downstream pipeline, a future PDF generator, future as-filed snapshot writer) still calls them directly. The only public surface that no longer ships CSV is the zip. - Shipping both formats doubles the zip size and clutters the auditor's working directory. The XLSX IS the deliverable now; no need to also ship the CSV behind it.
Why this and not "build the XLSX from scratch per statement"¶
wppCsv.ts already nails the per-statement layout (section headers,
spacers, totals, prior-period column logic). Re-implementing every
statement's row assembly in XLSX would duplicate that surface — and
it would drift. Reading the CSV back is one extra parse but the
output is byte-identical content (everything below row 8).
Decision log¶
2026-06-25 — T-125 opened + shipped¶
- ✅ Attestation (Records (Infrastructure)): read
AGENTS.md; task is a sibling of T-124 (same shared-letterhead unification) and of T-122 (#7) / T-123 (#6). No board entry covered the WPP CSV→XLSX conversion. - Source: Records (Infrastructure) · https://claude.ai/code/session_018RDB37kCqfouHdygVXTAtD
- Owner direction (verbatim, 2026-06-25): quoted above.
- Tests + tsc:
npx tsc --noEmitclean. Live verification left to the owner — Accounting page → Working Paper Pack → confirm the downloaded zip containsxlsx/(notcsv/), each XLSX opens in Excel / Numbers with the shared letterhead band on rows 1–8 and the same statement rows below, and the manifest + README reflect XLSX. - Blast radius:
wppCsv.tsis unchanged — anything that imports the CSV builders still works. The WPP endpoint's external shape stays a zip with Content-Type application/zip; only the file extensions inside change.
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.
99f9c362026-06-25 — feat(records,wpp): unified Bank-Tx letterhead (T-124) + WPP CSV → XLSX (T-125)a98745b2026-06-25 — Merge main + renumber T-122 → T-126 (renumber-on-merge, parallel-agent T-122/123/124/125 collision) (mentions only)64b1a842026-06-30 — feat(T-136): WPP — accounting number format, single tabbed workbook, journals.csv (mentions only)