[product-owner]
Type: Bug
Severity: MEDIUM
Priority: Should Have
Related: #1891 (found during product-architect audit of the dual-rail deposit aggregation on feat/1891-wizard-followup / PR #1894)
Summary
GET /api/budget-sources/:id/budget-lines is deposit-blind. getWorkItemLineInvoiceData and getHouseholdItemLineInvoiceData (server/src/services/budgetSourceService.ts:812-848 and :850-888) compute per-line paid amounts as:
COALESCE(SUM(CASE WHEN i.status IN ('paid', 'claimed') THEN ibl.itemized_amount ELSE 0 END), 0) AS actualCostPaid
This attributes the whole itemized amount based on the parent invoice's status and ignores deposits entirely. The same budget line rendered on the work-item page goes through the deposit-aware budgetServiceFactory.getInvoiceAggregates, so the two views disagree.
Reproduction
- Invoice €1000, status
pending.
- One budget line €1000, attributed to source A.
- One deposit €400 on that invoice, status
paid.
- Open the budget-source drill-down for A, then the work-item page for the same line.
Observed: drill-down shows actualCostPaid = 0; work-item page shows 400.
Expected: both views show 400.
This is a display inconsistency, not a conservation break — budget totals are unaffected. Pre-existing since deposits were introduced; not a #1891 regression.
Acceptance Criteria
Rider (minor, same file)
Notes
- Fix should reuse the existing deposit-aware aggregate helper rather than duplicating deposit math in
budgetSourceService. If the shapes don't line up, flag it to the product-architect rather than forking the logic.
[product-owner]
Type: Bug
Severity: MEDIUM
Priority: Should Have
Related: #1891 (found during product-architect audit of the dual-rail deposit aggregation on
feat/1891-wizard-followup/ PR #1894)Summary
GET /api/budget-sources/:id/budget-linesis deposit-blind.getWorkItemLineInvoiceDataandgetHouseholdItemLineInvoiceData(server/src/services/budgetSourceService.ts:812-848and:850-888) compute per-line paid amounts as:This attributes the whole itemized amount based on the parent invoice's status and ignores deposits entirely. The same budget line rendered on the work-item page goes through the deposit-aware
budgetServiceFactory.getInvoiceAggregates, so the two views disagree.Reproduction
pending.paid.Observed: drill-down shows
actualCostPaid = 0; work-item page shows400.Expected: both views show
400.This is a display inconsistency, not a conservation break — budget totals are unaffected. Pre-existing since deposits were introduced; not a #1891 regression.
Acceptance Criteria
GET /api/budget-sources/:id/budget-linescomputes per-line paid amounts through the same deposit-aware path used by the work-item page (budgetServiceFactory.getInvoiceAggregates) rather than the parent-invoice-status SQL above.getWorkItemLineInvoiceDataandgetHouseholdItemLineInvoiceData).actualCostPaid = 400in the budget-source drill-down, matching the work-item page.paidorclaimedwith no deposits continues to report the full itemized amount as paid (no regression to the existing common case).Rider (minor, same file)
budgetSourceService.ts:387-390coerces any non-'claimed'status argument to'paid'via a ternary:This is latent today (only
'paid'and'claimed'are ever passed) but silently mislabels any future status. Fix isnew Set([status]).The rider fix does not change behaviour for the currently-passed values — verified by test.
Notes
budgetSourceService. If the shapes don't line up, flag it to the product-architect rather than forking the logic.