From 6259ed8407ee009bcb6750cd4a9b8f18c4447435 Mon Sep 17 00:00:00 2001 From: SJCoetz Date: Wed, 29 Jul 2026 22:02:33 +0200 Subject: [PATCH] =?UTF-8?q?fix(orders):=20transition=20pending=20=E2=86=92?= =?UTF-8?q?=20processed=20on=20save=20when=20priced?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/app/(app)/orders/[id]/process/page.tsx | 31 ++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/app/(app)/orders/[id]/process/page.tsx b/src/app/(app)/orders/[id]/process/page.tsx index 8234358..58e09f2 100644 --- a/src/app/(app)/orders/[id]/process/page.tsx +++ b/src/app/(app)/orders/[id]/process/page.tsx @@ -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