Skip to content

fix(budget): fix invoice/work-item cross-linking#206

Closed
steilerDev wants to merge 13 commits into
betafrom
fix/invoice-work-item-linking
Closed

fix(budget): fix invoice/work-item cross-linking#206
steilerDev wants to merge 13 commits into
betafrom
fix/invoice-work-item-linking

Conversation

@steilerDev
Copy link
Copy Markdown
Owner

Summary

  • InvoiceDetailPage: Replace the "Budget Line" row (which showed a raw UUID) with a "Work Item" row displaying the linked work item's title as a link to /work-items/:id. Uses invoice.workItemBudget already returned by the API — no additional fetch needed.
  • WorkItemDetailPage: Invoice popover items now link to /budget/invoices/:id (invoice detail page) instead of /budget/vendors/:vendorId.

Test plan

  • Open an invoice detail page for an invoice linked to a budget line — confirm "Work Item" row appears with the work item title as a clickable link, and clicking it navigates to the correct work item page
  • Open an invoice detail page for an invoice with no budget line link — confirm the row is absent
  • Open a work item detail page with budget lines that have invoices — open the invoice popover and click an invoice — confirm it navigates to /budget/invoices/:id

🤖 Generated with Claude Code

- Add vendorName field to Invoice type in shared/src/types/invoice.ts
- Export InvoiceStatusSummary, InvoiceSummary, InvoiceListPaginatedResponse,
  and InvoiceDetailResponse from @cornerstone/shared
- Update toInvoice() in invoiceService.ts to resolve and include vendorName
- Add listAllInvoices() with pagination, filtering (status, vendorId, q),
  sorting (date, amount, status, vendor_name, due_date), and global status summary
- Add getInvoiceById() for cross-vendor single invoice lookup
- Create server/src/routes/standaloneInvoices.ts with GET /api/invoices
  and GET /api/invoices/:invoiceId endpoints
- Register standaloneInvoiceRoutes at /api/invoices prefix in app.ts

Co-Authored-By: Claude backend-developer (Sonnet 4.5) <noreply@anthropic.com>
- Add fetchAllInvoices and fetchInvoiceById to invoicesApi client
- Add Invoices tab to BudgetSubNav between Vendors and Sources
- Implement InvoicesPage with summary cards (Pending/Paid/Claimed),
  filterable/sortable paginated table, mobile card view, and create modal
- Implement InvoiceDetailPage with edit and delete modals, budget line
  linking via work item selector, breadcrumb navigation
- Register /budget/invoices and /budget/invoices/:id routes in App.tsx

Fixes #200

Co-Authored-By: Claude frontend-developer (Sonnet 4.6) <noreply@anthropic.com>
Co-Authored-By: Claude orchestrator (Sonnet 4.6) <noreply@anthropic.com>
The vendorName field added to the Invoice type requires updating all
existing mock objects in test files.

Co-Authored-By: Claude orchestrator (Sonnet 4.6) <noreply@anthropic.com>
Replace inline anonymous type `{ invoice: Invoice }` with the explicit
`InvoiceDetailResponse` shared type in fetchInvoiceById, consistent with
how other shared response types are used across the API client.

Co-Authored-By: Claude <frontend-developer> (Sonnet 4.6) <noreply@anthropic.com>
…rvice functions

- `assertVendorExists()` now returns the vendor name (was void)
- `toInvoice()` accepts an optional `knownVendorName` 3rd parameter; when
  provided it skips the per-call vendor DB lookup
- `listInvoices`, `createInvoice`, and `updateInvoice` capture the vendor
  name returned by `assertVendorExists` and forward it to `toInvoice`,
  avoiding one extra SELECT per invoice row
- `deleteInvoice` ignores the returned name (no `toInvoice` call needed)
- `getInvoiceById` and `listAllInvoices` are unchanged — they either rely
  on the fallback lookup or already use a JOIN

Co-Authored-By: Claude <backend-developer> (Sonnet 4.6) <noreply@anthropic.com>
Co-Authored-By: Claude product-architect (Opus 4.6) <noreply@anthropic.com>
…etchAllInvoices, fetchInvoiceById

- Add 14 tests for listAllInvoices(): empty results, multi-vendor vendorName via JOIN,
  pagination (page/pageSize/metadata), status filter, vendorId filter, q search
  (case-insensitive), sort by amount (asc/desc), sort by vendor_name, global summary
  (unaffected by current page filter), zero values for unused statuses, combined filters
- Add 4 tests for getInvoiceById(): all fields with vendorName, NotFoundError,
  cross-vendor lookup, null createdBy
- Add 10 tests for fetchAllInvoices(): GET /api/invoices with no params, query strings
  (page/pageSize/q/status/vendorId/sortBy/sortOrder), full InvoiceListPaginatedResponse
  unwrapping, undefined param omission, clean path without query string, 401/500 errors
- Add 6 tests for fetchInvoiceById(): GET /api/invoices/:id URL, correct invoiceId,
  Invoice unwrapped from { invoice } envelope, vendorName populated, 404/401 errors

Co-Authored-By: Claude <qa-integration-tester> (Sonnet 4.6) <noreply@anthropic.com>
Merges the following beta commits that landed during development:
- fix(budget): surface budget line deletion errors to the user (#201)
- fix(budget): fix invoice edit modal pre-population and add invoice popover (#202)

Co-Authored-By: Claude <orchestrator> (Sonnet 4.6) <noreply@anthropic.com>
…id collision

The beta branch introduced a new InvoiceSummary type in workItemBudget.ts
representing a per-invoice line summary. Our feature branch added a different
InvoiceSummary type in invoice.ts representing a status breakdown (pending/
paid/claimed counts + totals). Rename ours to InvoiceStatusBreakdown to
eliminate the duplicate identifier.

Co-Authored-By: Claude <orchestrator> (Sonnet 4.6) <noreply@anthropic.com>
…e branch

Add missing `workItemBudget` field to the manual Invoice object construction
in `listAllInvoices()`. The `Invoice` type now requires `workItemBudget:
WorkItemBudgetSummary | null` (added by PR #202), and `listAllInvoices()`
was building Invoice objects inline rather than using `toInvoice()`, so
the field was omitted.

Co-Authored-By: Claude <backend-developer> (Sonnet 4.6) <noreply@anthropic.com>
- InvoiceDetailPage: replace "Budget Line" raw ID display with "Work Item"
  linked to the work item detail page (/work-items/:id), using the
  workItemBudget.workItemTitle and workItemBudget.workItemId already
  returned by the API
- WorkItemDetailPage: invoice popover links now navigate to the invoice
  detail page (/budget/invoices/:id) instead of the vendor page

Co-Authored-By: Claude frontend-developer (Sonnet 4.6) <noreply@anthropic.com>
@steilerDev steilerDev changed the base branch from feat/standalone-invoice-api to beta February 23, 2026 11:10
@steilerDev
Copy link
Copy Markdown
Owner Author

Closing in favour of a clean branch rebased directly onto beta (PR #207)

@steilerDev steilerDev closed this Feb 23, 2026
@steilerDev steilerDev deleted the fix/invoice-work-item-linking branch March 7, 2026 07:45
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