[e2e-test-engineer]
BUG: Fully-excluding all budget lines on an invoice makes it disappear from the report wizard's step-3 list
Severity: Major
Component: Frontend UI (Bank Report Wizard, ReportInvoiceList.tsx)
Found in: e2e/tests/budget/reportWizardExpansion.spec.ts — Scenario 4 ("Excluding every line on an invoice clamps its amount to zero, renders TriState unchecked, and keeps it counted as included"), authored against Story #1891.
Steps to Reproduce
- Go to
/budget/reports, pick any use case (e.g. "Claim"), pick a source.
- On Step 3, find an invoice that has 2+ budget lines whose
allocatedPortions sum to exactly the invoice's allocatedAmount (the normal case — no deposits, no split-source ibls).
- Expand the invoice row (chevron button).
- Uncheck every line's "Include" checkbox in the items sub-table (excluding every line).
Expected Behavior
Per the story's Frontend Spec ("Design decision" section) and item 5 ("Parent TriStateCheckbox... All-items-excluded renders unchecked but invoice stays counted as included for claiming/PDF — DO NOT two-way sync"), the row should remain visible, showing €0.00 (clamped, never negative), with its TriState checkbox rendering unchecked (not indeterminate). The row's own expand/collapse control should stay available so the user can re-include a line.
Actual Behavior
The invoice row disappears from the Step 3 list entirely. Root cause:
applyLineExclusions() (client/src/lib/reportExclusions.ts) correctly clamps the invoice's allocatedAmount to exactly 0 when all lines are excluded (newAmount = allocatedAmount - excludedPortion, lineKind: newAmount < 0 ? 'refund-adjustment' : 'invoice' — for newAmount === 0, lineKind stays 'invoice').
ReportInvoiceList.tsx's allocatedInvoices filter is:
report.invoices.filter((inv) => inv.allocatedAmount > 0 || inv.lineKind === 'refund-adjustment')
A net-zero, non-refund invoice satisfies neither condition (0 > 0 is false, lineKind !== 'refund-adjustment'), so it is filtered out of allocatedInvoices — which drives both the visible row list and the selectedCount/runningTotal displayed in the SelectionActionBar.
Because the row (and its expand toggle, which lives inside the row) disappears, there is no UI path left to re-include any of the excluded lines short of reloading the page — which resets every exclusion in the session, not just this invoice's.
Note: this is a display-only regression. The actual PDF export (generateReportPdf) and the claim submission (handleMarkClaimed) both operate on excludedInvoiceIds against the original, unfiltered report state — neither is affected by this filter, so the invoice is still correctly included in the generated PDF and still gets claimed in full.
Environment
- Browser: Chromium (desktop project)
- Viewport: desktop (not viewport-specific — pure state/filter logic)
- Docker: N/A (client-only bug, reproducible in dev server too)
Evidence
E2E test e2e/tests/budget/reportWizardExpansion.spec.ts Scenario 4 asserts the spec-conformant behavior (row visible at €0.00, TriState unchecked) and is expected to fail in CI until this is fixed, per the project's test-failure-debugging protocol (correct tests are not weakened to match buggy code).
Relevant code:
client/src/lib/reportExclusions.ts lines 26-35 (the clamp-to-zero logic, correct)
client/src/components/reports/ReportInvoiceList.tsx — allocatedInvoices useMemo (inv.allocatedAmount > 0 || inv.lineKind === 'refund-adjustment')
Notes
Suggested fix direction: extend the allocatedInvoices filter to also retain invoices where the original (pre-line-exclusion) allocatedAmount !== 0 — i.e. filter on the un-adjusted report object rather than the already-adjusted effectiveReport prop, or add an explicit "this invoice has budget lines" escape hatch to the filter so a client-side-only zero-out never removes a row that the server actually returned as allocated.
[e2e-test-engineer]
BUG: Fully-excluding all budget lines on an invoice makes it disappear from the report wizard's step-3 list
Severity: Major
Component: Frontend UI (Bank Report Wizard,
ReportInvoiceList.tsx)Found in:
e2e/tests/budget/reportWizardExpansion.spec.ts— Scenario 4 ("Excluding every line on an invoice clamps its amount to zero, renders TriState unchecked, and keeps it counted as included"), authored against Story #1891.Steps to Reproduce
/budget/reports, pick any use case (e.g. "Claim"), pick a source.allocatedPortions sum to exactly the invoice'sallocatedAmount(the normal case — no deposits, no split-source ibls).Expected Behavior
Per the story's Frontend Spec ("Design decision" section) and item 5 ("Parent TriStateCheckbox... All-items-excluded renders unchecked but invoice stays counted as included for claiming/PDF — DO NOT two-way sync"), the row should remain visible, showing €0.00 (clamped, never negative), with its TriState checkbox rendering unchecked (not indeterminate). The row's own expand/collapse control should stay available so the user can re-include a line.
Actual Behavior
The invoice row disappears from the Step 3 list entirely. Root cause:
applyLineExclusions()(client/src/lib/reportExclusions.ts) correctly clamps the invoice'sallocatedAmountto exactly0when all lines are excluded (newAmount = allocatedAmount - excludedPortion,lineKind: newAmount < 0 ? 'refund-adjustment' : 'invoice'— fornewAmount === 0,lineKindstays'invoice').ReportInvoiceList.tsx'sallocatedInvoicesfilter is:0 > 0is false,lineKind !== 'refund-adjustment'), so it is filtered out ofallocatedInvoices— which drives both the visible row list and theselectedCount/runningTotaldisplayed in theSelectionActionBar.Because the row (and its expand toggle, which lives inside the row) disappears, there is no UI path left to re-include any of the excluded lines short of reloading the page — which resets every exclusion in the session, not just this invoice's.
Note: this is a display-only regression. The actual PDF export (
generateReportPdf) and the claim submission (handleMarkClaimed) both operate onexcludedInvoiceIdsagainst the original, unfilteredreportstate — neither is affected by this filter, so the invoice is still correctly included in the generated PDF and still gets claimed in full.Environment
Evidence
E2E test
e2e/tests/budget/reportWizardExpansion.spec.tsScenario 4 asserts the spec-conformant behavior (row visible at €0.00, TriState unchecked) and is expected to fail in CI until this is fixed, per the project's test-failure-debugging protocol (correct tests are not weakened to match buggy code).Relevant code:
client/src/lib/reportExclusions.tslines 26-35 (the clamp-to-zero logic, correct)client/src/components/reports/ReportInvoiceList.tsx—allocatedInvoicesuseMemo (inv.allocatedAmount > 0 || inv.lineKind === 'refund-adjustment')Notes
Suggested fix direction: extend the
allocatedInvoicesfilter to also retain invoices where the original (pre-line-exclusion)allocatedAmount !== 0— i.e. filter on the un-adjustedreportobject rather than the already-adjustedeffectiveReportprop, or add an explicit "this invoice has budget lines" escape hatch to the filter so a client-side-only zero-out never removes a row that the server actually returned as allocated.