fix(orders): transition pending → processed on save when priced

The buyer-tasks Needs PO tab filters by
  item_status = 'processed' && purchase_order = ''
but the order process page's saveItem() never updated item_status,
so priced items stayed pending and never surfaced on the buyer
board.

Save now promotes pending → processed when the operator has filled
supplier + cost_price + po_description. Items already past processed
(stock_booked, po_generated, ready_delivery, …) are never downgraded.
The stock-fulfil branch writes processed too as a no-op for the
case where the subsequent stock/fulfil call fails — it will be
overwritten with stock_booked on success.

Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
SJCoetz 2026-07-29 22:02:33 +02:00
parent 76f1d79082
commit 6259ed8407

View file

@ -473,6 +473,25 @@ export default function ProcessOrderPage({ params }: { params: Promise<{ id: str
try {
const pb = getPocketBase();
// Determine whether this save should transition the item
// from `pending` to `processed`. The buyer-tasks page
// (`/orders/tasks`) filters Needs PO by
// `item_status = "processed" && purchase_order = ""`,
// so a priced item with no supplier/cost/PO description
// shouldn't surface there yet. Once all three fields are
// filled we promote it. Items already past `processed`
// (stock_booked, po_generated, ready_delivery, etc.) are
// never downgraded by this save.
const currentItem = items.find((it) => it.id === itemId);
const isReadyForBuyer =
committed.supplier.trim() !== "" &&
committed.cost_price > 0 &&
committed.po_description.trim() !== "";
const nextStatus =
currentItem?.item_status === "pending" && isReadyForBuyer
? "processed"
: currentItem?.item_status;
// v3 stock-out flow (Stream E). When the operator has
// toggled the line into "fulfill from stock" mode, the
// standard order_item update path is wrong — it would
@ -506,6 +525,12 @@ export default function ProcessOrderPage({ params }: { params: Promise<{ id: str
pricing_source: committed.pricing_source || null,
matched_price_list_id: committed.matched_price_list_id || null,
pricing_notes: committed.pricing_notes || null,
// Stock-fulfil path: the subsequent /api/v1/stock/fulfill
// call will overwrite this with stock_booked before the
// buyer ever sees the row on the buyer-tasks page, so
// promoting to processed here is harmless and keeps the
// row coherent if the stock call fails.
item_status: nextStatus,
});
// Step 2: stock-fulfil call (mode: "update"). The
// route PATCHes the order_item with stock-fulfil
@ -587,6 +612,12 @@ export default function ProcessOrderPage({ params }: { params: Promise<{ id: str
supplier: committed.supplier || null,
in_stock: committed.in_stock,
notes: committed.notes,
// Promote pending → processed when the operator has
// filled supplier + cost + PO description. This is the
// gate that makes the item visible on the buyer-tasks
// Needs PO tab. Items already past `processed` keep
// their current status (no downgrade).
item_status: nextStatus,
// Pricing audit trail — round-trip from quote_item when copied,
// or stay blank when the operator typed manually. The price
// sheet exposes these fields; the Quote Source panel displays