Skip to content

Add Excel format option to the invoicing report - #861

Merged
rzueger merged 1 commit into
developfrom
claude/excel-invoicing-report-ubxr5w
Aug 24, 2026
Merged

Add Excel format option to the invoicing report#861
rzueger merged 1 commit into
developfrom
claude/excel-invoicing-report-ubxr5w

Conversation

@rzueger

@rzueger rzueger commented Aug 24, 2026

Copy link
Copy Markdown
Member

Summary

  • The invoice recipient report was PDF only, so figures couldn't be filtered, corrected or re-summed without retyping them. Adds an Excel option alongside the existing PDF, chosen via a new format dropdown in the export form (Admin → Export → invoice reports).
  • The workbook mirrors the PDF's one-worksheet-per-recipient layout, but writes amounts as real numbers and totals as SUM formulas, each carrying its computed result as a cached value (tools that don't evaluate formulas, e.g. pandas via openpyxl, would otherwise read those cells as empty).
  • Recipient names are user-controlled, so worksheet names are sanitized against exceljs's actual rules: forbidden characters, the 31-char cap, case-insensitive collisions, the reserved name History, and edge quotes — verified against installed exceljs 4.4.0 rather than assumed.
  • Data loading and recipient grouping moved to a shared InvoicesReportData base class so the PDF and Excel reports always show the same rows in the same order; InvoicesReport.spec.ts is unchanged and covers the refactor.
  • Generation failures now propagate instead of being silently swallowed (fixes the same latent gap in the pre-existing PDF path too), so a failed report can't leave the form stuck disabled with nothing shown.
  • exceljs (~1MB) is loaded via a dynamic import kept in its own module, so it lands in a separate chunk fetched only when a report is generated, and is excluded from the service worker's precache manifest.

Test plan

  • npm test — 2495 tests passing across 215 suites
  • npm run typecheck — clean
  • npm run build --project=lszt — confirmed exceljs lands in its own on-demand chunk (not the main bundle) using the prebuilt browser build, and is excluded from the workbox precache manifest
  • InvoicesExcelReport.ts at 100% statement/branch/function/line coverage; the InvoicesReportData.collect() wiring, the saga's format branching, and the format parameter's round trip through the container and form are each covered directly
  • Manual: generate an Excel report for a real month and compare totals against the PDF for the same month before this touches actual invoicing

Generated by Claude Code

The invoice recipient report was PDF only, so the figures could not
be filtered, corrected or re-summed without retyping them. A format
dropdown in the export form now offers Excel alongside the PDF.

The workbook mirrors the PDF layout with one worksheet per recipient,
but writes amounts as real numbers and every totals row as a SUM
formula. Each formula also carries its computed result as a cached
value: tools that do not evaluate formulas, such as pandas reading
via openpyxl, otherwise see those cells as empty. Recipient names are
user-controlled, so worksheet names are sanitised against exceljs's
rules: forbidden characters, the 31-char cap, case-insensitive
collisions, the reserved name "History", and edge quotes exceljs
rejects. Non-finite amounts are written as empty cells rather than
NaN, which Excel would otherwise report as a corrupt workbook.

Data loading and recipient grouping move to InvoicesReportData, which
both report formats extend, so the two always show the same rows in
the same order. InvoicesReport.spec.ts is unchanged and covers the
refactor.

Generation failures now propagate instead of being swallowed: the
saga clears the in-progress flag in a finally block (fixing the same
gap in the pre-existing PDF path), so a failed report cannot leave
the form stuck disabled with nothing shown.

exceljs is reached through a dynamic import kept in its own module,
so its ~1MB bundle lands in a separate chunk that is only fetched
when a report is generated, and is excluded from the service worker's
precache manifest so it isn't downloaded on install either.

InvoicesExcelReport.ts is at 100% statement/branch/function/line
coverage; the wiring in InvoicesReportData.collect(), the saga's
format branching, and the format parameter's round trip through the
container and form are each covered directly rather than simulated.
@rzueger
rzueger merged commit f409e8e into develop Aug 24, 2026
2 checks passed
@rzueger
rzueger deleted the claude/excel-invoicing-report-ubxr5w branch August 24, 2026 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants