You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
As a homeowner, I want to attach invoices to household item budget lines (using the same logic as work item budget lines) so that I can track actual costs, payment status, and outstanding balances for household item purchases just as I do for construction work items.
Household items have budget lines (household_item_budgets table) that mirror work item budget lines (work_item_budgets table), but invoices can only be linked to work item budget lines. The invoices table has a work_item_budget_id FK column but no corresponding household_item_budget_id FK. As a result:
The household item budget summary always returns totalActual: 0 (hardcoded in householdItemService.ts:229)
Users cannot attach invoices to household item purchases
The budget overview page does not include household item invoice data in project-wide actual cost totals
Invoice create/edit forms only offer work item budget lines in the "Link to Budget Line" dropdown
Expected Behavior
Invoices should be linkable to either a work item budget line OR a household item budget line (mutually exclusive -- an invoice links to one or the other, not both). The full invoice lifecycle (create, view, edit, link/unlink, payment status tracking) should work identically for household item budget lines as it does for work item budget lines.
Acceptance Criteria
1. The invoices table gains a household_item_budget_id column (TEXT, FK -> household_item_budgets(id) ON DELETE SET NULL, nullable) via a new migration. An invoice may have work_item_budget_id set OR household_item_budget_id set OR neither, but never both simultaneously.
2. The Invoice API response type includes a householdItemBudgetId field (string | null) and a householdItemBudget enrichment object (parallel to workItemBudget) containing: id, householdItemId, householdItemName, description, plannedAmount, confidence.
3. The CreateInvoiceRequest and UpdateInvoiceRequest types accept an optional householdItemBudgetId field. The backend validates mutual exclusivity: if both workItemBudgetId and householdItemBudgetId are provided and non-null, the API returns 400 with error code MUTUALLY_EXCLUSIVE_BUDGET_LINK.
4. The invoice create/edit form UI includes a "Link to Household Item" dropdown alongside the existing "Link to Work Item" dropdown. Selecting one clears the other (mutual exclusivity enforced in the UI). The dropdown lists household item budget lines grouped by household item name, showing: household item name, budget line description, planned amount.
5. The GET /api/household-items/:id response budgetSummary.totalActual field returns the sum of all invoice amounts linked to that household item's budget lines (replacing the hardcoded 0), matching the work item pattern where totalActual = SUM(invoices.amount) WHERE invoices.household_item_budget_id IN (budget lines for this item).
6. The household item detail page Budget section displays linked invoices per budget line (invoice number, amount, status badge, date) with a link to the invoice detail page -- mirroring the work item detail page budget section behavior.
7. The invoice detail page shows the linked household item budget line (household item name + budget line description) when householdItemBudgetId is set, with a navigable link to the household item detail page -- mirroring the existing work item budget line display.
8. The budget overview page (/budget) includes household item invoice amounts in the project-wide actual cost totals, so that the overall budget accurately reflects all actual expenditure across both work items and household items.
9. The invoices list page (/invoices) and vendor detail page invoice section correctly display household item budget links (showing household item name instead of work item title) when an invoice is linked to a household item budget line.
10. Deleting a household item cascades to its budget lines, which causes linked invoices to have their household_item_budget_id set to NULL (ON DELETE SET NULL) -- matching the work item cascade behavior.
This story extends the invoice system from EPIC-05 to cover household items, completing the budget integration gap identified in Story 4.6 AC EPIC-10: UX Polish & Accessibility #10 where totalActual was left as a placeholder
The architect should decide whether to use a CHECK constraint in the migration (CHECK(NOT(work_item_budget_id IS NOT NULL AND household_item_budget_id IS NOT NULL))) or rely solely on application-layer validation
As a homeowner, I want to attach invoices to household item budget lines (using the same logic as work item budget lines) so that I can track actual costs, payment status, and outstanding balances for household item purchases just as I do for construction work items.
Parent Epic: #4
Priority: Must Have
Problem
Household items have budget lines (
household_item_budgetstable) that mirror work item budget lines (work_item_budgetstable), but invoices can only be linked to work item budget lines. Theinvoicestable has awork_item_budget_idFK column but no correspondinghousehold_item_budget_idFK. As a result:totalActual: 0(hardcoded inhouseholdItemService.ts:229)Expected Behavior
Invoices should be linkable to either a work item budget line OR a household item budget line (mutually exclusive -- an invoice links to one or the other, not both). The full invoice lifecycle (create, view, edit, link/unlink, payment status tracking) should work identically for household item budget lines as it does for work item budget lines.
Acceptance Criteria
invoicestable gains ahousehold_item_budget_idcolumn (TEXT, FK ->household_item_budgets(id)ON DELETE SET NULL, nullable) via a new migration. An invoice may havework_item_budget_idset ORhousehold_item_budget_idset OR neither, but never both simultaneously.InvoiceAPI response type includes ahouseholdItemBudgetIdfield (string | null) and ahouseholdItemBudgetenrichment object (parallel toworkItemBudget) containing:id,householdItemId,householdItemName,description,plannedAmount,confidence.CreateInvoiceRequestandUpdateInvoiceRequesttypes accept an optionalhouseholdItemBudgetIdfield. The backend validates mutual exclusivity: if bothworkItemBudgetIdandhouseholdItemBudgetIdare provided and non-null, the API returns 400 with error codeMUTUALLY_EXCLUSIVE_BUDGET_LINK.GET /api/household-items/:idresponsebudgetSummary.totalActualfield returns the sum of all invoice amounts linked to that household item's budget lines (replacing the hardcoded0), matching the work item pattern wheretotalActual = SUM(invoices.amount) WHERE invoices.household_item_budget_id IN (budget lines for this item).householdItemBudgetIdis set, with a navigable link to the household item detail page -- mirroring the existing work item budget line display./budget) includes household item invoice amounts in the project-wide actual cost totals, so that the overall budget accurately reflects all actual expenditure across both work items and household items./invoices) and vendor detail page invoice section correctly display household item budget links (showing household item name instead of work item title) when an invoice is linked to a household item budget line.household_item_budget_idset to NULL (ON DELETE SET NULL) -- matching the work item cascade behavior.Notes
totalActualwas left as a placeholderCHECK(NOT(work_item_budget_id IS NOT NULL AND household_item_budget_id IS NOT NULL))) or rely solely on application-layer validationHouseholdItemBudgetSummarytype (AC EPIC-02: Application Shell & Infrastructure #2) parallelsWorkItemBudgetSummaryfromshared/src/types/invoice.ts