- Added a new `quotes.submit` tool that transitions a quote from `new` to `approval_pending` once all line items are priced. This tool ensures that only fully priced quotes can be submitted for approval, enhancing the integrity of the approval process.
- Updated the API documentation in `docs/api-mcp-layer.md` to include the new `quotes.submit` tool and its usage details.
- Enhanced the `quotes` status enum in `docs/agents/hermes-skill.md` to reflect the new transition and its implications.
- Regenerated documentation to ensure accuracy, increasing the total tool count from 126 to 127.
- Added comprehensive tests for the `submitQuote` function to cover various scenarios, including idempotency and error handling for unpriced items.
- Introduced a new `quotes.create` tool for creating quotes from scratch without an inbound email, allowing operators to generate quotes directly from the dashboard.
- Updated `customers.list` to support filtering by representative using either a 15-char PB user ID or a case-insensitive name/email match.
- Enhanced API documentation in `docs/api-mcp-layer.md` to reflect the addition of the new quote creation tool and updated customer filtering capabilities.
- Regenerated documentation to ensure accuracy, increasing the total tool count from 125 to 126.
- Updated `docs/api-mcp-layer.md` to reflect the current state of the API, including new endpoints and tools.
- Added `category` field to `QUOTE_ITEM_PATCH_FIELDS` and `ORDER_ITEM_PATCH_FIELDS` to support item categorization.
- Introduced 22 new MCP tools to cover previously missing REST functionalities, increasing the total tool count from 103 to 125.
- Improved descriptions for several existing tools to clarify their usage and parameters.
- Regenerated the API documentation to ensure accuracy and completeness.
Add full purchase order agent tooling and supporting changes:
- Add server-side purchase-order-agent-service for scanning both stock and customer POs, with complete test coverage
- Add inline SKU and supplier SKU editing flows to stock order detail pages
- Update MCP API route to support new PO agent tools (purchase_orders.scan, diff, record_diff_report) and stock_purchase_items.list
- Fix flaky CollectionPoolPage tests by wrapping state-dependent assertions in waitFor
- Refactor CollectionPoolPage auto-collapse logic to avoid react-hooks lint trap and run exactly once on initial load
- Delete temporary .tmp-inspect-pool.mjs development script
This commit adds several key features, improvements, and cleanups across the codebase:
- Add `order_items.confirm_collected` MCP/REST endpoint to mark order items as collected without requiring driver assignment
- Add standalone order creation and order split APIs for flexible order management
- Update order list sort to oldest-first to align with user workflow of working through oldest orders first
- Improve order search to show cross-tab matches and display source tab indicators on search results
- Add customer name similarity check to warn about mismatched QuickBooks customers during invoice creation
- Remove supplier name column from delivery note PDFs and clean up related data passing across delivery note routes
- Add validation and debug logging to ItemIntelPopover for missing item ID on apply actions
- Remove unused OrderItemContextPopoverTrigger component from order detail page
- Add delivery note regeneration script and update test suites for new and existing functionality
- Update documentation and add test coverage for the new collection confirm tool
This commit replaces the legacy item history and AI insights system with a standardized, fuzzy-matched item intel workflow:
- Update AiItemHistoryTrigger component to support both quote and order items via the `collection` prop, replacing the old `insights` prop with `intel`
- Add full `ItemIntel` type definitions to `src/lib/types.ts` for the new standardized insight payload
- Create new REST API endpoints for fetching and computing item intel for `quote_items` and `order_items`
- Rewrite the MCP insight endpoint to use the new shared `item-intel-service` instead of legacy individual insight functions
- Reorder quotes page tabs to place Delivery Note quotes alongside New quotes for better daily operator workflow
- Add item intel triggers to quote process and order process pages
- Remove deprecated document-level `AiInsightsPanel` components from quote and order detail pages
- Add bulk backfill endpoint for populating intel across existing active items
Add a new MCP tool that lets the agent read a supplier PDF, quote PDF,
or pasted supplier email with its native vision, extract the per-line
fields, and commit them to the price_list catalogue in one round-trip.
Why: the only pre-existing path was operator upload via /pricelist ->
n8n ingest -> raw_price_list -> AI extraction -> price_list. That flow
is async, human-in-the-loop, and gates on n8n being healthy. This
tool gives the agent a synchronous shortcut for ad-hoc catalogue
updates -- paste a PDF, get the catalogue updated, no async hops.
The agent does the OCR; the tool does the commit. No LLM work moves
into the MCP server.
Behaviour:
- Dedupe by (sku, supplier) -- exact match. Different suppliers with
the same SKU get separate rows.
- Upsert policy: cost_price overwrites (latest price wins).
Description, selling_price, unit, category, subcategory, brand,
markup, stock_qty, search_text, sku_normalised also patched in
place on update.
- sku_normalised auto-computed (uppercase, strip non-alphanumeric --
same canonical form price-list-match.ts uses).
- search_text auto-computed (lowercased, space-separated concat of
sku + description + brand + supplier + category + subcategory +
synonyms).
- Skipped items returned with per-item reason (missing_sku,
missing_supplier, invalid_cost_price, pb_write_failed). Never
silent.
- Bulk cap 500 items per call; larger lists rejected with
too_many_items reason.
- source_file_id + source_label supported so new rows can be
traced back to a raw_price_list upload (optional).
- Supplier stored verbatim on price_list.supplier (free-text field,
no FK resolution -- same as the live schema).
Files:
- src/lib/server/price-list-service.ts (new) -- addPriceListItems +
buildSearchText + computeSellingPrice helpers
- src/lib/server/price-list-service.test.ts (new) -- 17 tests
covering validation, upsert, dedupe filter shape, normalisation
- src/app/api/mcp/route.ts -- new tool definition + dispatch block
- src/app/api/mcp/route.test.ts -- 2 dispatch tests + tool-list
assertion update
- .agents/skills/wfdash-giellie/SKILL.md -- new section "8. Price-
list catalogue" documenting input/output shapes + failure modes
Tests:
- 134/134 pass across the affected files
- 1775/1778 in the full suite (3 pre-existing failures unrelated
to this change: pocketbase-admin.test.ts PB mock mismatch,
quotes/items/[id]/attachments/upload/route.test.ts timeout,
supplier-form.test.tsx race -- all reproducible on main)
- npm run build clean
Comprehensive changes including:
- Refactor picking slip line handling for multiple document types across print endpoints
- Add new `/api/v1/stock-purchases/picking-slip` API for stock booking picking slips
- Implement reusable Splitter component and useSplitRatio hook for resizable panels
- Add floating/fullscreen invoice viewer to collection confirm page
- Add real-time stock booking update event for invoicing tab
- Add full test coverage for all new components and APIs
- Include temporary chat attachment images for testing purposes
When the supplier ships a partial invoice (BREATHER on PO26080502STK was
not invoiced this round, but the other 7 lines were), the buyer wants
to:
- mark and flip what we can confirm came in
- leave the rest for the buyer to reconcile manually
Adding a new 'partial: true' flag to stock_purchases.match_and_receive_from_invoice
that:
- widens the verdict gate to allow 'unmatched_po_lines' and 'cost_mismatch'
to proceed when partial=true (the buyer is explicitly accepting these)
- passes per-line quantity_received:0 overrides for unmatched PO lines
into the existing receiveStockPurchase(items) override path, so only
matched lines get booked into stock_ledger
- surfaces 'skipped_po_line_ids' in the response so the buyer sees
exactly what was held back
- leaves the customer-PO path unchanged (every matched line is stamped,
partial semantics only apply to stock purchases for now)
Hard stops regardless of partial: qty_mismatch on matched lines,
unmatched_invoice_lines, supplier_mismatch, wrong_status.
Test coverage: +1 dispatch test (partial flag forwarding) plus the
existing 'partial: false' assertion on the dry-run dispatch test.
Live-validated on PO26080502STK + invoice WR248939 (G-Man #1, 7 lines):
- 6 matched CIRCLIP/KM/MB lines booked, qty_received bumped
- BREATHER 3/4 line skipped via qty_received=0, surfaced in
skipped_po_line_ids for manual reconciliation
- PO status flipped sent -> received
Build out the buyer-side PO reconciliation workflow that landed in
60d3204 ('stock purchase invoice reconciliation tool') into a full
3-document × 2-PO-kind matching service. The tool now covers every
real-life supplier document the buyer can receive.
Renamed the service entry point from matchAndReceiveFromInvoice to
matchAndReceiveFromPoDocument (old name kept as a back-compat alias).
The MCP tool name stays stock_purchases.match_and_receive_from_invoice
so existing callers don't break; the input arg shape changed from
'invoice:' to 'document:' (old 'invoice:' arg still accepted for
back-compat).
Phase 1 — supplier_sku backfill (prep work, not in service code)
Phase 2 — auto-detect stock vs customer POs (single tool, both kinds)
Phase 3 — GRN (delivery-note) mode via new mode: 'grn' flag
Phase 4 — pack-size inference (Orapi case: per-unit vs per-pack)
Phase 5 — PO reference normalisation (PO202608017 vs PO-2026-08-017)
Also adds 3 new MCP tools for the collection-confirm buyer workflow:
collection_confirm.list, collection_confirm.upload_invoice,
collection_confirm.confirm_order_items.
Docs: updated .agents/skills/wfdash-giellie/SKILL.md and
src/app/api/mcp/FEATURE-REQUESTS.md to describe the new modes and
shape. 1,661/1,664 vitest tests pass; the 3 failing tests are
pre-existing (PocketBase SDK mock mismatch in pocketbase-admin.test,
5s timeout in quotes/items/[id]/attachments/upload/route.test —
both reproducible on main without this change).
Add the full buyer-side supplier invoice reconciliation workflow:
- Include the new `stock_purchases.match_and_receive_from_invoice` MCP tool for deterministic PO-invoice matching
- Create core matching service with strict, rule-based line matching (sku → supplier_sku → description overlap)
- Support dry-run default mode to validate matches before committing
- Add automatic attachment handling with standard `supplier_invoice_*` naming convention
- Add full test suite for the matching logic
- Update documentation and agent skill profile to document the new tool and workflow
Pre-existing test failures (3 total) are unrelated to this change.
Ship full split-invoice stock purchase order workflow:
- Add 5 new MCP tools for per-line stock purchase item management and catalogue updates
- Add idempotent database migration adding sku, unit, and supplier_sku fields to stock_purchase_items
- Implement service layers for all new operations, including auto-closing the parent PO when all lines are fully collected
- Add complete test coverage for new services and API routes
- Update all relevant documentation including MCP API specs, agent instructions, and feature request logs
Add `sku` to the order_item.patch MCP tool whitelist and update its documentation
Fix stuck stock POs in buyer tasks po_sent tab by filtering out downstream statuses
Rewrite margin approval webhook to use fire-and-forget dispatch to avoid UI blocking, add server-side error logging
Add MCP feature requests and local setup documentation
- Add server-side `/api/v1/dashboard` endpoint to reduce client-side payload and round trips
- Implement invoice upload and order-item SKU update APIs with complete test suites
- Refactor order and order-items list endpoints (including MCP tools) to support proper PocketBase filter and sort params
- Fix pagination validation for order-items route to clamp page ≥1 and perPage 1-500
- Add redirects in next.config.ts to map underscore collection paths to hyphenated API routes
- Improve buyer tasks page to avoid redundant tab count refetches when switching tabs
- Refactor collection confirm components with editable SKUs and updated accessibility labels
- Add cached admin PocketBase auth to minimize repeated authentication calls
- Add performance testing scripts for dashboard endpoint latency
- Update quotes list route to default to excluding archived counts
- Fix photo strip component text and labels for non-invoice documents
- Refactor sprint page to lazy load supplier/customer data for pricing modal
- Clean up unused imports in test files
- Add full test coverage for all new APIs, services, and components
- order_items.update happy path: patches cost_price + qty_invoiced
+ supplier_invoice_number, verifies the whitelist strips nothing
and the row is written
- order_items.update forbidden: system-controlled fields (order,
purchase_order, quote_item, id) are rejected
- quotes.update regenerate_pdf:true: patch is saved first, then
the PDF endpoint is called with the freshly-persisted letterhead,
and the response includes both the updated quote and the new PDF
attachment metadata
- tools/list test count: 77 (was 76)
All 1385 tests pass.
Co-authored-by: Orca <help@stably.ai>
Two new capabilities, both requested by the agent for the
collected-vs-invoice reconciliation workflow:
1. order_items.update — patch fields on an order_item for invoice
reconciliation. Lets the agent compare what the supplier invoiced
(qty_invoiced + supplier_invoice_number + cost_price) against what
was ordered (quantity) and what was collected (qty_collected),
then write the differences. Whitelist mirrors the quote_items.update
pattern:
cost_price, selling_price, quantity, markup, markup_percent,
notes, qty_processed/assigned/collected/verified/stocked/picked/
loaded/delivered/invoiced/cancelled, supplier_invoice_number,
customer_invoice_number, collection_date, collected_date,
verified_date, delivered_date, invoiced_date
System-controlled fields (id, order, purchase_order, quote_item,
product, created, updated) are rejected with forbidden_field so
the agent can never accidentally reassign an item to a different
order or PO.
2. quotes.update with regenerate_pdf:true — the agent can now
switch letterhead (or any other header field) AND re-render the
PDF in a single call. The patch is saved first, then the existing
POST /api/v1/quotes/{id}/pdf route is called using the freshly-
persisted letterhead, so the response always matches the record.
Useful for: 'switch this quote to ISHERWOOD and send me the new
PDF'. Without the flag, the existing 2-step workflow still works
(quotes.update, then quotes.get_pdf).
All 1385 tests pass (was 1382; +3: regenerate_pdf happy path,
order_items.update happy path, order_items.update forbidden field).
Co-authored-by: Orca <help@stably.ai>
- quotes.list_attachments: proxies to GET /api/v1/quotes/{id}/attachments
- quotes.get_pdf: proxies to /api/v1/quotes/{id}/pdf/base64 (with optional
letterhead + regenerate query params)
- orders.add_attachment: posts multipart to /api/v1/orders/{id}/attachments/upload
- orders.list_attachments: proxies to /api/v1/orders/{id}/matched-attachments
All four tests use a stubbed global fetch (restored in finally) so
the MCP route's internal HTTP calls are deterministic and don't hit
the real server. Updated the tools/list test to include the four new
tools (76 total, was 72).
All 1382 tests pass.
Co-authored-by: Orca <help@stably.ai>
Closes the four gaps the agent flagged for the email→quote→PDF→send
workflow:
NEW REST ROUTES
- GET /api/v1/quotes/[id]/pdf/base64 — auto-renders the quote PDF
(idempotent against an existing pdf_generated_at) and returns it as
{ pdf: <base64>, attachment_id, filename, size, content_type,
letterhead }. 422 on placeholder customer / unpriced items. Used by
the new quotes.get_pdf MCP tool to give MCP clients a way to
download the rendered PDF — the streaming /api/v1/quotes/[id]/pdf
endpoint is still available for the WebUI's iframe.
- POST /api/v1/orders/[id]/attachments — uploads a file to the
order's MATCHED QUOTE. PB has no order_attachments collection;
orders share the quote_attachments table via their matched quote.
201 returns the new attachment + matched_quote_id so the agent can
confirm where the file landed.
NEW MCP TOOLS (catalog now 76, was 72)
- quotes.list_attachments — list all attachments on a quote
(generated PDF + original RFQ email attachments + operator uploads)
- quotes.get_pdf — auto-render + return the PDF binary as
base64 so the agent can save/forward/email it
- orders.add_attachment — upload to an order (proxy to the new
order upload route; multipart form)
- orders.list_attachments — list attachments on an order (proxies
the existing /api/v1/orders/[id]/matched-attachments route, which
returns the matched quote's attachments)
The 'upload to a PDF' workflow is now: attachments.upload_for_quote
(or orders.add_attachment) → quotes.list_attachments to see what's
there (including the generated PDF) → quotes.get_pdf to download
the PDF → quotes.send_email with selectedAttachmentIds to ship it all.
WHY the existing 'attachments.upload_for_quote' was not enough:
the agent previously had no way to retrieve the rendered PDF binary
or to attach files to orders. Now it has both. The order upload goes
via the matched quote because that's the single attachment table in PB.
All 1382 tests pass (was 1378; +4 for the new tools).
Co-authored-by: Orca <help@stably.ai>
- quotes.update: happy path with whitelisted fields, forbidden_field
rejection for system-controlled fields (status/customer/company/
quote_number/id), empty-patch rejection, missing-id rejection
- create_from_email: unit field now preserved end-to-end, result.items
returns the actual QuoteItem array (not just a count)
Also updates the existing tools/list test (now 72 tools, was 71) and
the create_from_email mock in route.test.ts to use the new
items/items_count shape so it matches the type.
All 1379 tests pass.
Co-authored-by: Orca <help@stably.ai>
Closes the two real gaps from the agent's gap analysis (2026-08-05):
1. **quotes.update MCP tool** — was missing entirely. The quote header
has fields (company_letterhead, quote_date, quote_reference, etc.)
that couldn't be patched via MCP, so the agent had to pass
letterhead at PDF-render time and couldn't persist it on the record.
Now exposed with a strict field whitelist mirroring the
quote_items whitelist pattern: any field outside the allowed set
(status, customer, company, quote_number, id, created, updated, …)
errors with forbidden_field rather than silently writing to PB.
New catalog entry + dispatch handler in src/app/api/mcp/route.ts.
Whitelist constant QUOTE_PATCH_FIELDS colocated with the existing
QUOTE_ITEM_PATCH_FIELDS for reviewer discoverability.
2. **create_from_email now preserves the unit field** — extracted_items
from the AI's RFQ parsing carries {description, quantity, unit,
notes} but the create loop was silently dropping unit. Also the
service returned items: <count> which made the agent believe no
items were created when in fact stub rows existed. Now returns
the actual QuoteItem array (with a back-compat items_count field)
and the unit value lands on each row.
The agent's third claim (PB admin auth unavailable) is a misdiagnosis
— WFDASH uses its own AGENT_API_KEY and all writes go through the
Next.js admin client. No code change needed for that.
All 1379 tests pass. quote_items.update path unaffected. The
quotes.update path mirrors the same pattern: whitelist + reject empty
patch + call pb.collection.update.
Co-authored-by: Orca <help@stably.ai>
Closes architectural gap #1 from the Giellie skill (the operator's AI
agent could ingest quotes via /api/mcp but had no way to enumerate,
add, or patch the resulting line items - every auto-price flow had to
stop and ask a human for the item IDs).
Adds:
- GET /api/v1/quote_items (list w/ filter, sort, expand)
- POST /api/v1/quote_items (create on existing quote)
- GET /api/v1/quote_items/[id] (fetch one)
- PATCH /api/v1/quote_items/[id] (whitelisted patch)
- quote_items.list MCP tool
- quote_items.get MCP tool
- quote_items.create MCP tool
- quote_items.update MCP tool
Patch whitelist on update is identical to bulk_update_quote_items:
cost_price, selling_price, description, sku, unit, supplier, markup,
pricing_source, pricing_notes, matched_price_list_id, user_edited
Everything else (id, quote, created, updated, quantity, is_processed)
returns 400 / MCP error with the allow-list echoed back so the agent
can self-correct.
quote_items.update does NOT enforce the already_priced skip - that's
the safety-net for bulk_update_quote_items. Single-row patches are
explicit writes.
Newly created rows have is_processed=false forced server-side; Giellie
still cannot flip the flag (architectural gap #2 unchanged - that's a
UI gate by design).
Also: removed "JEI" from quotes.company_letterhead select values on
the live PB - valid values are now ["CNL", "ISHERWOOD"]. Use the
uppercase form when setting this field.
Giellie skill doc refreshed: tool count 69 to 73, new "quote_items
CRUD" subsection, three new REST rows, architectural gap #1 marked
RESOLVED, hard rule #1 updated to cover the new endpoint, new worked
example for "add a missing line item + price it", change-log entry.
Tests: 82 passing across the three touched files (was 63 before).
Type-check: zero new errors (pre-existing 29 in
orders-service-review.test.ts unchanged).
Deployment: pending operator rebuild + redeploy. Until then
POST /api/v1/quote_items returns 404 and the new tools don't appear
in tools/list.
Co-authored-by: Orca <help@stably.ai>
WFDASH /api/mcp previously only handled tools/list and tools/call,
which is fine for our internal curl-based testing but broke every
spec-compliant MCP client (Hermes, Claude Desktop, Cursor, mcp-cli).
The MCP protocol requires a three-step handshake before tools/list:
1. client → server: initialize
2. server → client: result with protocolVersion, capabilities, serverInfo
3. client → server: notifications/initialized (notification, no response)
Before this fix, step 1 got HTTP 404 with error.code -32601, which
MCP clients interpret as 'this isn't an MCP server' and terminate
the session. Symptom: configured servers show 'CONFIGURED' in the
Hermes WebUI but '0 tools', with the runtime logging
'unhandled errors in a TaskGroup ... Session terminated'.
Three new handlers, each with the right response shape:
initialize → 200 + plain result {protocolVersion, capabilities, serverInfo}
notifications/initialized → 204 + empty body (notification, no envelope)
ping → 200 + result {}
Critical: the initialize response uses a PLAIN object for result,
not the content/text envelope that our jsonRpcOk helper wraps
tools/list and tools/call results in. Using jsonRpcOk here would
have left the handshake broken in a subtler way.
Four new Vitest cases:
- initialize returns server info (regression: 2026-08-04)
- initialize echoes client-provided protocolVersion (forward compat)
- notifications/initialized returns 204
- ping returns {}
Bug report with full timeline + lessons: see
docs/bugs/2026-08-04-mcp-initialize-handshake-missing.md
(same commit series; pushed separately as the second commit).
All 46 tests pass.
Co-authored-by: Orca <help@stably.ai>
The tools.discovery tool was declared in the catalog (line 1300) but no
tools/call dispatch branch existed for it — every call fell through to
'Unknown tool: tools.discovery'. Functionality was reachable only via
the parallel GET /api/mcp endpoint, which the JSON-RPC-only MCP clients
in n8n couldn't use.
Add a real handler that returns the same `tools` array as tools/list,
wrapped in the standard JSON-RPC content envelope. Mirrors GET /api/mcp
for JSON-RPC-only clients.
Also add a Vitest case that asserts the catalog shape (array of tools,
each with name+description+inputSchema, and tools.discovery in the list).
Deployment note: this fix is in source; the live apphub.co.za server
runs a built bundle and will keep returning 'Unknown tool' until rebuilt.
Co-authored-by: Orca <help@stably.ai>
Restores the /collection-pool feature in this codebase.
- src/lib/server/orders-service.ts
- Re-adds `listCollectionPool({ status, page, perPage })` which
queries the `collection_pool` PB collection via the admin
client (filter on pool_status, sort -created, expand
order_item/order/supplier/drv_id). MCP route.ts already
imported this — without it, the MCP route failed to
compile.
- src/app/(app)/collection-pool/{page,components,view-model}*.tsx
- Re-creates the page directory (5 files, 2 315 lines) carrying
the supplier-group + per-stop checkbox UI, the
`CollectionPoolRowSource = "customer" | "stock"` union, the
assignment-state badge helpers (buildProximityMarkerMap,
buildSupplierGroups, buildVisibleRows), and the
MarkReadyDialog / AssignDriverDialog wiring.
- src/app/api/mcp/route.test.ts
- Adds `const listCollectionPool = vi.fn()` and exposes it in
the vi.mock factory so the existing collection_pool.list test
cases (and the bulk_update_quote_items ones that call
`.mockReset()` on every fixture) stop crashing with
'ReferenceError: listCollectionPool is not defined'.
Not in this commit (kept narrow per request):
- The /api/v1/collection-pool route handler and its test
- The 'Collection Pool' entry in the sidebar nav
- The migration script that creates the live `collection_pool`
PB collection (it doesn't exist on the cloud PB instance
yet; the page render relies on it being created separately
or via `npm run pb:migrate:orders-v2`)
src/app/api/v1/sprint/attachment-counts/route.ts
- Chunk itemIds into batches of 80 before building the PocketBase filter;
the live cap was hit at ~115 IDs × 15 chars and surfaced as a
generic Next 500 ("Something went wrong while processing your
request.") that hid the real cause. 7 chunks for the sprint
page's max of 500 items.
src/app/layout.tsx
- Add metadataBase derived from NEXT_PUBLIC_SITE_URL /
NEXT_PUBLIC_POCKETBASE_URL. Silences the per-page
"metadataBase not set" warning during dev renders and gives
OG / Twitter cards a stable canonical base.
src/app/api/v1/{quotes,order-items,purchase-orders,stock,
stock-purchases,users,sprint,driver,ai-insights}/route.ts (NEW)
- Add the missing top-level collection routes the agent API
needs. seven are straight passthroughs to their PB collection;
/sprint proxies to quote_items with optional claimStatus and
category query params; /driver lists PB users (no role field
in PB so callers narrow with ?filter=); /ai-insights returns
a roll-up count + most-recent regenerate timestamp across the
four collections that carry ai_* JSON fields.
src/app/api/v1/collection-pool/{route.ts,route.test.ts},
src/app/(app)/collection-pool/{page.tsx,page.test.tsx,
view-model.ts,view-model.test.ts,components.tsx} (DELETED)
- Drop the collection_pool feature entirely: PB has no
collection_pool collection on the live instance
(pb:migrate:orders-v2 was never run), the codebase comment
in purchase-orders-service.ts says "now-removed", and the
sidebar entrypoint has been migrated to /sprint + /deliveries.
The replacement operational status is the `item_status =
"collection_pool"` enum value on order_items, which stays.
src/lib/server/orders-service.ts (+ test), src/lib/pb-helpers.ts,
src/components/sidebar.tsx, src/components/ui/assign-driver-dialog.tsx,
src/app/(app)/collection-map/sidebar.tsx,
src/app/api/mcp/route.ts (+ test) (MODIFIED)
- Remove listCollectionPool + getCollectionPool +
updateCollectionPoolStatus, the /collection-pool sidebar entry,
the deep-link in assign-driver-dialog and collection-map
sidebar, and the collection_pool.list MCP tool (the tool,
handler, and test mock).
scripts/migrations/2026-07-26-fix-quote-counter-and-reassign-numbers.ts (NEW)
- One-shot migration script that:
1. Reads every quote, sorts by created ASC.
2. Reassigns clean Q202607001 … Q202607{N} over the whole
table (including the e+22 garbage and the legacy
Qnnnnnn rows).
3. Sets quote_counters.(year=2026, month=7).last_seq = N
so the next /api/v1/quotes/next-number returns a clean,
atomic, unique number.
Default mode = dry-run; pass --apply to write.
Already executed against the live cloud PB during this session
(verified: 200/200 rows updated, counter at 200, next-number
returns Q202607201, Q202607202, Q202607203 on consecutive calls).
AGENTS.md (MODIFIED)
- Add data-layer-discipline block matching the style of the
three existing blocks. Documents which layer is legacy (pb-helpers)
vs current (lib/server/*-service.ts via /api/v1/*), gives the
migration order (customers → orders → quotes → suppliers →
companies → rest), and includes the grep one-liner future AI
agents should run before extending pb-helpers.
Not in this commit (your WIP, untouched):
src/app/(app)/{dashboard,delivery-notes,layout,orders/[id],
quotes/[id],quotes/approvals,quotes,sprint}/page.tsx,
src/app/(auth)/login/page.tsx, src/components/charts/*,
src/components/{company-form,customer-form,quote-email-card,
sprint-item-pricing-modal,supplier-form,theme-bootstrap,
theme-picker}*.{tsx,test.tsx}, src/app/api/invoicing/delivery-note/*
src/app/api/v1/quotes/[id]/pdf/route.ts, src/app/globals.css,
src/lib/{item-status-ui,pdf/quote-document,quote-status*,themes,
types}.ts, src/lib/server/{order-item-status*,quotes-service}.ts,
src/styles/themes.css, APP_CAPABILITIES.md, README.md,
docs/SHIPPED.md, scripts/smoke/screenshot-pages.mjs,
and the new untracked files under docs/, scripts/migrations/
2026-07-19…25, src/app/{(app)/deliveries,(app)/delivery-notes,
api/v1/ai-insights/rerun,api/v1/deliveries,api/v1/delivery-notes,
api/v1/quotes/[id]/partial-approve,api/v1/quotes/[id]/split},
src/components/{ai-insights-panel,ai-insights-renderer,
ai-item-history-trigger,brand-stripe,delivery-notes,ui/stock-item-search,
ui/duplicate-warning,ui/duplicate-warning.test}.tsx, supplier-form.test.tsx,
src/lib/{pb-helpers-duplicate.test,server/deliveries-service*,
server/delivery-notes-service*}, public/wfdash-logo*.png,
opencode.jsonc.
Add full sprint pricing workflow and supporting infrastructure:
- New sidebar navigation entry for the sprint dashboard
- PocketBase category collection schema and migration script
- Server API routes for sprint item claims, attachments, counts, and file proxying
- Dev identity utilities and UI switcher for multi-user testing
- Reusable FilterTabs component replacing ItemStatusTabs
- Updated TypeScript types for sprint-related entities
Also include:
- Fix PocketBase file URL method casing (getUrl → getURL) across codebase
- Add development utility, smoke test, and helper scripts
- Update package.json with new sprint migration command
This commit overhauls the order management system to use item-level status tracking instead of a single order-level status:
- Remove deprecated `orders.status` field and add `invoiced` and quote match audit fields
- Add `isOrderComplete` utility with tests to validate fully delivered orders
- Add purchase order PDF rendering support and tooling
- Add geo coordinate fields for companies/customers with map link UI
- Add REST management APIs for customers, companies, and suppliers
- Update customer/company services to use relation fields instead of free text
- Add status pill and tab filter components for item status visualization
- Add quote-to-order matching logic and unit tests
- Update all pages, components, and tests to match the new schema
- Include all required PocketBase migrations and schema updates
Fix inconsistent line endings across all project files for cross-environment consistency.
Add complete unit test suite covering core utilities, API routes, and server services.
Add Playwright E2E test setup with smoke, auth, and agent API tests.
Update all project configuration files to match standardized formatting.
Fix MCP API error handling to properly propagate HTTP status codes and use correct JSON-RPC error codes.
Add VSCode workspace configuration, extensions, and example MCP config files.
Update .gitignore to exclude new test and build artifacts.
Introduce PocketBase workflow support, migration tooling, server APIs and documentation. Adds a set of PocketBase collection JSONs (order_items, order_items_v2, collection_pool, delivery_notes, delivery_note_items, repairs) and a large import manifest for workflow collections; includes docs describing MCP/API layer, migration audit, order-item schema and rollout plans. Adds server-side agent/MCP endpoints and services (api/mcp, api/v1/orders, collection-pool, order-items status, health) plus helper modules (pocketbase-admin, orders-service, order-item-status, agent-auth) and a migration script to create order_items_v2/collection_pool. UI: new pages for collection-pool, delivery-notes, repairs and several updates to orders/quotes pages, sidebar, tooltip and pb helpers/types. Also updates package.json (migration script and related changes) and global styles. Required env vars and suggested migration command are documented (e.g. NEXT_PUBLIC_POCKETBASE_URL, PB_ADMIN_EMAIL, PB_ADMIN_PASSWORD, ORDER_ITEMS_COLLECTION; run npm run pb:migrate:orders-v2).