feat(reports): add bank report wizard with client-side PDF pipeline - #1887
Conversation
Adds the bank report wizard: a 4-step flow (use case, source selection, preview, options) that assembles a claim/status report entirely client-side using a pdfmake + pdf-lib pipeline (cover letter generation, budget/invoice overview table, and linked document embedding/merging), plus a mark-claimed action to flag included invoices. Adds a "Generate report" entry to the budget source row overflow menu and wires up the shared Reports tab across the budget pages. Fixes #1879 Fixes #1886 Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com> Co-Authored-By: Claude translator (Sonnet 4.5) <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com>
|
[security-engineer] Security review of PR #1887 (feat/1879-report-wizard → beta, Story #1879). Scope covered: new production dependencies ( Dependency supply-chain review — clean
Client-side PDF pipeline — no injection surface found
Blob/object-URL lifecycle — correctly managed
Paperless upload path — no credential exposure
Logging — no sensitive data exposure
Forward-looking note from #1877 — resolved, no new finding
No critical, high, medium, or low findings. No informational findings beyond what's noted above. Verdict: APPROVED |
steilerDev
left a comment
There was a problem hiding this comment.
[security-engineer] Approved — see detailed findings in PR comment above. No critical/high/medium/low findings; dependency bulk-advisory check clean on all 27 newly added transitive packages. Verdict: APPROVED.
|
[ux-designer] Design review of PR #1887 against the visual spec posted on issue #1879 (comment #5122593052 — note: that comment itself rendered as the literal string Reviewed: Critical finding (blocking)Refund amount text is unreadable in both light and dark mode. .amountNegative {
color: var(--color-danger-text);
}
The spec explicitly said to reuse the #1876 refund-row recipe verbatim, and the actual #1876 implementation ( .amountNegative {
color: var(--color-danger-text-on-light);
}One-line fix: swap Medium findings (non-blocking; fix before merge or in refinement)
Low / informational
Deviations from the literal spec text — validated on their merits
What matched the spec cleanly
Verdict: CHANGES_REQUIREDBlocking on the single Critical finding (refund amount contrast/visibility failure — a one-token fix). The three Medium findings should be fixed in the same pass or in refinement; they don't need to block on their own per the severity matrix, but since a fix is already required for the Critical item, bundling all four into one follow-up commit is the efficient path. |
|
[product-owner] Product Owner review of PR #1887 against Story #1879 (final story of the Bank Report Wizard mini-epic). Scope reviewed: full diff (9,573 additions), story ACs group by group, unit/integration/E2E test content, CI run 30503657153, and the German copy for the two terminology decisions escalated to me. Credit where due: the wizard's structure, the shared-component reuse ( Blockers — functional acceptance criteria not metB1. The PDF pipeline does not work at runtime. No report is ever produced.
Root cause, from the failing trace's browser console:
This fails the PDF-pipeline AC group and the "preview regenerates / whatever is previewed is what ships" AC. B2. The
|
| AC group | Result |
|---|---|
| Route, tabs & entry points | Partial — route + lazy load + BUDGET_TABS hoist + Reports tab all correct; ?sourceId= deep link dead-ends (B2) |
| Wizard steps 1–2 | Met — three radio cards with helper text; single-select sources with colour badges, use-case-appropriate amount labels, discretionary last and de-emphasised |
| Wizard step 3 | Partial — TriStateCheckbox select-all, per-row checkboxes, SelectionActionBar, status badges, collapsible non-selectable unallocated warning group and EmptyState all present; refund sign and running total wrong (B4); split footnote / attachment indicator / stage matching incomplete (B6); raw i18n keys (B5) |
| Preview & actions | Not met — preview never renders (B1). Claim confirmation with pending count, skippable marking, 409 refetch, filename pattern, Paperless gating and success toast, and the preview-blob-is-the-download-blob guarantee are all implemented correctly and will pass once B1 is fixed |
| PDF pipeline | Not met — pipeline throws (B1); grand total ignores exclusions (B3); refund double negation (B4); split footnotes missing (B6). Lazy dynamic import() with type-only static imports is correct, deps pinned exactly (pdfmake@0.3.11, pdf-lib@1.17.1, @types/pdfmake@0.3.3), npm audit clean per the security review. Cover letter correctly sources sender/signature from household settings and recipient/reference from the source, and gracefully omits missing fields |
| i18n & PDFs | Partial — full sourceReports.* tree in both locales with German reviewed and approved below; PDFs correctly use hardcoded light-theme hexes while the UI uses tokens; but unresolvable keys (B5), locale/currency bypass (M1–M3), and one hardcoded aria-label (M7) |
Priority order for the fix loop: B1 first (nothing else is verifiable until a PDF renders), then B4 and B5 (both are contract/fixture corrections in the same files), then B3, B2, B6, then the M-items. Re-run the full reportWizard.spec.ts suite after B1 — the remaining scenarios have never actually executed past step 3 on desktop, so expect further findings once they do.
VERDICT: CHANGES_REQUIRED
|
[product-architect] Architecture review of #1879 (bank report wizard frontend). I verified the dependency policy, the What I verified as correctDependency policy — fully compliant. Lazy-loading is correctly airtight. Every static reference to either package is
No API contract drift. Also clean: Blocking findingsB1 (Critical) — Unbounded PDF regeneration loop in production
I measured it. Taking Six full report generations in 2.5 seconds, growing without bound for as long as the user stays on step 4. In production each iteration re-fetches every linked Paperless document over HTTP and re-renders + re-merges the whole PDF, and the iframe Why the suite is green: Note the comment at Fix direction: remove B2 (High) — German overview table overflows A4; rightmost column is clipped
Measured with the real translated labels, real pdfmake layout, and the 7-column ( German header labels are the driver — English's 2% headroom is not a safe margin either — it is one label revision away from the same failure. Fix direction: give the vendor column B3 (High) — PDF amounts and dates ignore the user's locale and
|
… refund signs, locale formatting, footnote model Resolves PR #1887 review findings: pdfmake vfs/font wiring via the real addVirtualFileSystem()/addFonts() API (fixes runtime crash on any bold run), kills the unbounded preview-regeneration loop, fixes refund double-negation and running-total sign errors, threads locale-aware formatters into the pure PDF content builders, and reworks the footnote/marker model so split rows are footnoted without colliding with the appendix numbering namespace. Also fixes dead ?sourceId= deep-linking and gates PDF actions correctly during regeneration. Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com> Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) <noreply@anthropic.com> Co-Authored-By: Claude translator (Sonnet 4.5) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com>
…enarios Moves setMaxReachedStep(4) from the PDF-generation-ready effect to the Step 3 Next button's onClick, so Step 4 stays forward-locked until the user has actually reached it. Also stabilizes 4 E2E scenarios that had stale assumptions: preview-regeneration is now proven via the iframe's blob: src changing instead of racing the transient loading spinner, the empty-state scenario branches on the household-wide unallocated list (which has no per-source filter) instead of assuming EmptyState is always reachable, the mobile-stepper assertion checks visibility instead of DOM presence (both stepper trees are always mounted, toggled by CSS), and the refund scenario seeds a genuinely out-of-scope invoice so its refund produces its own negative row instead of merging into a positive one. Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com>
The claim-walk scenario's Source A lacked a contact address and reference number, so the cover-letter checkbox was correctly disabled by the app (Story #1877 rule) and the toggle click timed out. Seeds both fields on the source and widens createBudgetSourceViaApi's data type to accept them. Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) <noreply@anthropic.com>
setShowClaimConfirm(false) was only called on the 409 conflict path. The success path and the two generic error paths left the confirmation modal's backdrop mounted, intercepting pointer events on the success banner's "View invoices" link. Co-Authored-By: Claude dev-team-lead (Sonnet 4.6) <noreply@anthropic.com> Co-Authored-By: Claude frontend-developer (Haiku 4.5) <noreply@anthropic.com>
|
[ux-designer] Round 2 re-review of PR #1887, verified against current HEAD ( Round-1 findings — verified against actual code (not just the changelog)
Re-ran Bonus fixes noticed while re-reading (beyond my original findings, both good)
Verdict: APPROVEDAll blocking and non-blocking findings from round 1 are resolved and verified directly in the current file contents (not just inferred from the commit message). The one item still open ( |
|
[product-owner] Round-2 review of PR #1887 against Story #1879. I re-verified every item from my round-1 review against the diff at Blockers — all verified fixedB1 — PDF pipeline (font failure). Fixed at the root, not worked around. The part I want to call out: B2 — B3 — grand total vs. exclusions. B4 — refund sign contract. All client-side negation removed: One deliberate change I'm accepting: the refund row's Invoice Amount column now shows the invoice's gross positive amount in the refund colour, rather than a negated gross. That is the more defensible reading — the invoice's amount genuinely is positive; the refund is the contribution — and the row stays unambiguous via the negative allocated amount, the refund colour, and the B5 — unresolvable i18n keys. Re-pathed to B6 — split footnotes and attachment indicators. Properly re-modelled rather than patched:
MUST FIX items — all verified fixed
Also picked up along the way and not asked for: an One AC consciously deferred → #1888The step-3 AC also asked for a stage-matched attachment indicator. That part is not implemented — the indicator still lights on Reasoning: Housekeeping — please fix before merge, non-blocking
Scope and processNo scope creep. All four fix commits are tightly scoped to review findings, with correct co-author trailers. German terminology (Verwendungsnachweis, Einreichung) stands as approved in round 1; I re-checked the new strings ( AC coverage
Good, disciplined round. The fixes went after root causes rather than symptoms, and the unmocked render test is the kind of change that stops this class of defect recurring — please carry that pattern into any future third-party rendering work. VERDICT: APPROVED |
|
[product-architect] Round 2 re-review of #1879. I re-ran every probe from round 1 against B1 — Regeneration loop: FIXED (verified)
I re-ran my round-1 probe and added the case the QA test does not cover — settling after a debounce-path regeneration: Both entry paths settle. B2 — German A4 overflow: FIXED (verified by measurement)
Both fit inside the 515.28pt printable width with the right margin at x=555.28. The One correction, and it is mine to make. B3 — Locale and CURRENCY: FIXED (verified end-to-end)
Correct German grouping/decimal separators, symbol placement, long-form date, and correctly-signed refund lines. The grand total is included-only and correctly signed (4000 + (-500) = 3500). You also fixed two currency defects I had not flagged, both real: B4 — Marker namespaces: FIXED (verified)Two distinct glyphs, both referenced from the owning row, both attributed: No collision (appendix numbers stay in their own column), no orphans, and vendor + invoice number on every footnote. B5 — Stale export on failure: FIXEDThe catch nulls B6 — ADR: FIXEDADR-034 exists (wiki Mediums from round 1M1 ( Not blocking, but please pick up1. All 16 reportWizard-bearing shards pass, and this PR's only touch to 2. ADR-034 corrections (my error, two lines). I own this page; please land these with a pointer bump. I did not push them myself because the base-repo
3. 4. Step-4 merge failures reach the UI but not the PDF ( VERDICT: APPROVEDEvery blocker is fixed at the property level, not just the code level, and the two fixes that were hardest to see are now pinned by tests that would actually fail if regressed: unique-value mocks plus an idle-stability assertion for B1, and unmocked real renders across both locales for B2/B3/M4. The extra currency defects you found in Please treat the shard-5 triage as a gate for the |
|
🎉 This PR is included in version 2.13.0-beta.29 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
/budget/reportsBudgetOverviewPage,InvoicesPage,BudgetSourcesPage,SubsidyProgramsPagenow share aBUDGET_TABSconstant)pdfmakeandpdf-libinclient/package.jsonFixes #1879
Fixes #1886
Test plan
WizardStepper,ReportInvoiceList,ReportPdfPreview,reportPdf/*pipeline modules,sourceReportsApi,ReportWizardPage+ step components,budgetTabs,paperlessApi.uploadPaperlessDocumentsourceReportServiceM2 (batched document lookup) andpaperlessroute M3 (malformedtaskId→ 502) extensionse2e/tests/budget/reportWizard.spec.tswith newReportWizardPagePOMCo-Authored-By: Claude dev-team-lead (Sonnet 4.6) noreply@anthropic.com
Co-Authored-By: Claude frontend-developer (Haiku 4.5) noreply@anthropic.com
Co-Authored-By: Claude translator (Sonnet 4.5) noreply@anthropic.com
Co-Authored-By: Claude qa-integration-tester (Sonnet 4.5) noreply@anthropic.com
Co-Authored-By: Claude e2e-test-engineer (Sonnet 4.5) noreply@anthropic.com