Skip to content

fix(budget): fix invoice edit modal pre-population and add invoice popover#202

Merged
steilerDev merged 1 commit into
betafrom
fix/invoice-linking-and-popover
Feb 23, 2026
Merged

fix(budget): fix invoice edit modal pre-population and add invoice popover#202
steilerDev merged 1 commit into
betafrom
fix/invoice-linking-and-popover

Conversation

@steilerDev
Copy link
Copy Markdown
Owner

Summary

  • Fix invoice edit modal work item dropdown (VendorDetailPage): When editing an invoice linked to a budget line, the "Link to Work Item" dropdown always showed "None" because the API only returned workItemBudgetId. Now the Invoice response includes a workItemBudget summary with workItemId and workItemTitle, which is used to pre-populate the work item dropdown and immediately load the matching budget lines.

  • Replace invoice count link with popover (WorkItemDetailPage): The invoice count badge was a <Link> that navigated away to the vendor page. Replaced with a <button> that toggles an absolutely-positioned dropdown listing each linked invoice (number, amount, vendor, date, status badge). Each entry links to the vendor detail page. Includes a click-outside handler and mobile bottom-sheet layout.

Files Changed

File Change
shared/src/types/invoice.ts Add WorkItemBudgetSummary interface; add workItemBudget to Invoice
shared/src/types/workItemBudget.ts Add InvoiceSummary interface; add invoices[] to WorkItemBudgetLine
shared/src/index.ts Export both new types
server/src/services/invoiceService.ts toWorkItemBudgetSummary() helper; enrich toInvoice() with joined budget+workItem data
server/src/services/workItemBudgetService.ts getLinkedInvoices() via raw SQL LEFT JOIN; include in toWorkItemBudgetLine()
client/src/pages/VendorDetailPage/VendorDetailPage.tsx Pre-populate work item in openEditInvoiceModal(); fetch budget lines immediately; update info note
client/src/pages/WorkItemDetailPage/WorkItemDetailPage.tsx Popover state + ref; click-outside effect; replace <Link> with button+popover
client/src/pages/WorkItemDetailPage/WorkItemDetailPage.module.css Popover styles + button reset
client/src/lib/invoicesApi.test.ts Add workItemBudget: null to fixture
client/src/pages/VendorDetailPage/VendorDetailPage.test.tsx Add workItemBudget: null and invoices: [] to fixtures

Test Plan

  • TypeScript typechecks pass (shared, server, client)
  • All existing tests pass (CI quality gates)
  • Manual: Edit an invoice linked to a budget line on VendorDetailPage — work item dropdown should be pre-populated
  • Manual: Click invoice count badge on WorkItemDetailPage — popover shows individual invoices
  • Manual: Click outside popover — popover closes
  • Manual: Mobile view — popover renders as bottom sheet

🤖 Generated with Claude Code

…pover

Issue 1: When editing an invoice linked to a budget line, the "Link to
Work Item" dropdown always showed "None". Fixed by enriching the Invoice
API response with a WorkItemBudgetSummary (workItemId, workItemTitle,
description, plannedAmount, confidence) and using it to pre-populate
the work item dropdown in the edit modal, along with an immediate fetch
of the matching budget lines.

Issue 2: The invoice count badge on WorkItemDetailPage navigated away to
the vendor page. Replaced the <Link> with a button that toggles an
absolutely-positioned popover listing all linked invoices with number,
amount, vendor, date, and status. Each item links to the vendor detail
page. Includes a click-outside handler and mobile bottom-sheet layout.

Co-Authored-By: Claude <frontend-developer> (Sonnet 4.6) <noreply@anthropic.com>
Co-Authored-By: Claude <backend-developer> (Sonnet 4.6) <noreply@anthropic.com>
@steilerDev steilerDev merged commit 30127a8 into beta Feb 23, 2026
5 checks passed
@github-actions
Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 1.9.0-beta.44 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

steilerDev pushed a commit that referenced this pull request Feb 23, 2026
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>
steilerDev pushed a commit that referenced this pull request Feb 23, 2026
…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>
steilerDev added a commit that referenced this pull request Feb 23, 2026
* feat(invoices): add standalone cross-vendor invoice API endpoints

- 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>

* feat(invoices): add standalone invoices list and detail pages

- 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>

* style(invoices): fix Prettier formatting in new invoice pages

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

* fix(invoices): add vendorName to Invoice mock fixtures in client tests

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>

* fix(invoices): use InvoiceDetailResponse type in fetchInvoiceById

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>

* fix(invoices): eliminate N+1 vendor query in vendor-scoped invoice service 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>

* docs(invoices): update API contract with standalone invoice endpoints

Co-Authored-By: Claude product-architect (Opus 4.6) <noreply@anthropic.com>

* test(invoices): add unit tests for listAllInvoices, getInvoiceById, fetchAllInvoices, 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>

* fix(invoices): rename InvoiceSummary to InvoiceStatusBreakdown to avoid 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>

* fix(invoices): fix type errors after merging beta changes into feature 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>

---------

Co-authored-by: Claude frontend-developer (Opus 4.6) <noreply@anthropic.com>
steilerDev pushed a commit that referenced this pull request Feb 23, 2026
Fixes claimed invoice handling, universal subsidy application, uncategorized
line display, vendor invoice cross-linking, budget line deletion error
surfacing, pageSize exceeding server max, and API client response wrapper
unwrapping. (#189, #201, #202, #208, #213)
@steilerDev steilerDev deleted the fix/invoice-linking-and-popover 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