Doubles down on the /stock list to make catalogue and inventory
edits one-click for the operator.
What's new:
- /stock: clickable column headers on Cost / Markup / Selling
cycle none → asc → desc → none and feed through to the PB sort
param. The apply bar now commits pricing AND qty in parallel
(Promise.all against /set-pricing and /set-quantities) with
per-row failure tracking and focused error toasts. In edit
mode the row is no longer a button — inputs render in-place
for current_qty, min_qty, and target_qty alongside the existing
pricing inputs, and the row click is gated off so clicks on an
input never navigate to the detail page.
- stock-quantities-service + /api/v1/stock/items/set-quantities
(POST): bulk-update current_qty / min_qty / target_qty on a
set of stock_items rows. Whitelist-validated patches, per-row
results with stable failure reasons, and a stock_ledger row
written FIRST whenever current_qty actually moves so the audit
trail stays ahead of the cached total (same pattern as
stock-surplus and stock-booking-service).
- scripts/apply-default-markup.ts: one-shot operator tool that
seeds selling_price = round(cost_price * 1.80, 2) and markup = 80
on stock_items rows whose cost_price is set but selling_price
is zero. Bypasses STOCK_ITEM_PATCHABLE_FIELDS (operator-initiated
bulk action); idempotent (never overwrites a non-zero shelf
price or markup).
- scripts/list-irda-stock.ts: read-only inspector for the
IRIDA spray-caint line with summary counts (without
selling_price, without cost_price, zero stock) so the
operator can see exactly which rows the backfill tools above
will touch.
Tests: 81 new test cases across /stock page, stock-quantities
service + route, and apply-default-markup.