Closes the pre-Fix-#1 stock pricing gap so the till and the POS see
prices on day one.
What's new:
- price-list-service: addPriceListItems now mirrors the upserted
selling_price + markup onto the matching stock_items row when
(and only when) the stock row has no price yet. Sync failures
are returned as warnings — never as a failed price_list write,
so a single broken stock row can't poison a 500-item batch.
- stock-quick-draft-service: addQuickDraftLine seeds cost_price +
bootstraps a one-entry cost_history on inline-create when the
buyer supplies newStockItem.costPrice. Existing-item path
backfills cost_price when zero and always appends a
cost_history entry so the line addition shows up in the audit
log without waiting on bookStockFromInvoice.
- stock/items/create (POST): accepts optional costPrice,
sellingPrice, and markup on the standalone create endpoint so
the operator can stamp a price and bootstrap cost_history in
one round-trip. Empty bodies still produce an empty
cost_history (legacy callers unchanged).
- scripts/backfill-stock-prices.ts: one-shot operator tool that
mirrors price_list rows onto existing stock_items rows whose
selling_price is still zero. Pure decision function exported
for unit tests; --commit / --limit / --sku CLI; idempotent
(never overwrites a non-zero shelf price or cost).
- vitest.config.ts: pull scripts/**/*.test.ts into the vitest
include list so backfill-stock-prices.test.ts runs in
`npx vitest run` without a per-script config.
Tests: 65 new test cases across price-list-service,
stock-quick-draft-service, stock/items create, and
backfill-stock-prices.
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