Wiki · NetSuite expansion · R593 master · 2 path subdocs

NS work order — master

The high-altitude master view of the production side. WO is used when GFS assembles or builds finished goods. Two categories: from a Sales Order (preserves SO ↔ WO link, Finance handoff at the end) or inventory build (standalone, no customer side, FG for future SOs). The customer PO# field on the WO body is TBD pending Mike's review.

Master · dispatcher + 2 path subdocs WO PO# field TBD
What this is

The production side, two categories

The NS work order lifecycle is the production side of the transaction at GFS — the way the company assembles or builds finished goods. Per Mike's actual process, every WO falls into one of two categories.

Path 1 — WO created FROM a Sales Order. One or more lines on a customer SO require assembly. A Work Order is created FROM the SO so records stay linked end-to-end (workorder.createdfrom = so.id). Components are reviewed, production runs, Assembly Build is entered in NetSuite (Mike approves at /assembly-build.html), finished goods are added to inventory at the correct location, then the SO is fulfilled. Item Fulfillment fires the ★ automation alert to Finance, who reviews, invoices, monitors terms, applies payment, and closes the order. If the FG is already on hand, the shortcut is to fulfill from inventory and close the WO marked unused.

Path 2 — WO created for inventory build (for stock). When the company builds product into inventory for future sales / sell-through. WO is entered standalone in NetSuite (no parent SO), item / qty / location / components confirmed, production runs, Assembly Build completed, FG added to inventory, components consumed, WO closes naturally. No customer side. No Finance handoff on the WO itself — Finance enters later when a future SO sells the FG.

Diagram: ns-work-order-master.html. Path detail docs: Path 1 from sales order · Path 2 inventory build. Open TBD: Work Order customer PO# field — pending Mike's review (expected pattern bodyFields.memo).

When to use it

Trigger conditions

Origin decision

If the WO is created from a customer SO assembly_line, it goes Path 1. Everything else (stock build for future demand) goes Path 2. Operator-driven today; auto-classifier on /api/wo/create is a STUB.

★ The handoff (Path 1 only)

On Path 1, after Item Fulfillment fires, an automation alert routes to Finance. Same shape as the SO master's Finance handoff — the single named bridge between Operations and Finance for assembly-driven orders.

WO PO# field — TBD pending Mike review

Mike's notes confirmed customer PO# threading on SO (otherrefnum), PO (memo + lineFields.links[].tranid), Invoice (otherrefnum), Vendor Bill (memo / tranid / initialtranid). The WO body field is the only TBD. Expected pattern: bodyFields.memo. Every WO Schema reference flagged TBD until confirmed.

Worked example

Two builds in one day

Scenario A · Path 1 from SO (Driscoll Melt Mates)

Friday morning. Driscoll SO #1217 (otherrefnum "72622") includes 1 assembly line for Melt Mates kit qty 24. Operator dispatches Path 1; WO-4471 created with createdfrom = 1217. Component review shows BOM components on hand at Heartland (location 3). Production runs; crew completes the build with 0 waste. Mike approves at /assembly-build.html; NS posts AB-1881 (createdfrom = 4471, quantity_built = 24). FG credit: inventory_balance.quantityavailable += 24 for MELT-MATES-KIT-A at location 3. WO close: status='Built'. SO fulfilled: IF-2210 (createdfrom = 1217, otherrefnum "72622"). ★ Automation alert fires to Finance. Finance posts CINV-9032 for $18,420 Net 30. Payment received + applied; SO closed.

Scenario B · Path 2 inventory build (RSF burrito kit for B5875)

Same day. RSF-BURR-KIT on-hand = 0, reorder_point = 240, B5875 QBL approaching. Operator dispatches Path 2; WO-4499 created standalone (createdfrom = NULL), assemblyitem "RSF-BURR-KIT", quantity 480, location 3, custbody_build_reason = "bid_pre_build", memo = "bid B5875 pre-build for QBL" (operator note — no customer PO# because no parent SO). Item / qty / location / components confirmed. BOM lookup; components on hand. Production runs; crew completes build for 472 (8 waste). Mike approves Assembly Build (AB-1902). FG credit: inventory_balance.quantityavailable += 472 for RSF-BURR-KIT at location 3. Components consumed (WkOrdIss per line). WO closes naturally (status='Built'). FG remains available for future SOs. No Item Fulfillment, no Finance handoff on the WO. Finance enters later when the B5875 release order actually sells this FG.

Step-by-step what happens

Intake → classify → build → close

Common intake steps run first. Then the WO branches into one of two paths based on origin. Both paths share the common production lane (components → build → Assembly Build → FG to inventory). Path 1 continues into SO fulfillment + Finance; Path 2 closes at FG availability.

Common steps (every WO)

  1. 01

    WO intake (production side)

    Production request enters NetSuite. Sources: upstream SO with assembly_line, or inventory build need (reorder, forecast, bid pre-build, USDA, R&D pilot).

    Writes NS workorder, work_orders
  2. 02

    Classify WO origin

    Operator (today) classifies by origin. SO-driven assembly → Path 1. Stock build for future demand → Path 2. Auto-classifier is a STUB.

    Decision from_sales_order · inventory_build

Path 1 — WO created FROM Sales Order

  1. 1.1

    WO created FROM SO

    For each assembly line, a Work Order is created with workorder.createdfrom = so.id — preserves SO ↔ WO link end-to-end. Alternate: if FG already on hand at location, fulfill SO from inventory + close WO marked unused.

    WO PO# field TBD — expected bodyFields.memo = SO.otherrefnum
  2. 1.2

    Component availability reviewed

    BOM lookup, on-hand check at correct location, warn if short components.

  3. 1.3

    Production / assembly completed

    Crew executes build on production floor. Components debited per BOM.

  4. 1.4

    Assembly Build completed in NetSuite

    NS AsmBuild record posted (createdfrom = wo.id, quantity_built, waste). Mike approves at /assembly-build.html.

  5. 1.5

    FG added to inventory at correct location

    inventory_balance.quantityavailable += quantity_built at the WO's location_id.

  6. 1.6

    SO fulfilled to customer · Item Fulfillment

    NS ItemShip posted with createdfrom = so.id (NOT wo.id), otherrefnum inherits from SO. ★ Automation alert fires to Finance.

  7. 1.7

    Finance review · invoice · payment · close

    Finance reviews fulfillment, posts CustInvc, monitors AR terms, payment received + applied, order closed.

Path 2 — Inventory build for stock

  1. 2.1

    WO entered in NetSuite (standalone)

    WO created with createdfrom = NULL — no parent SO. Build reason captured (reorder_point / forecast / bid_pre_build / usda_drawdown / rd_pilot).

  2. 2.2

    Item, qty, location, component requirements confirmed

    Operator confirms item, quantity, location, component requirements derived from assembly_bom.

  3. 2.3

    Component availability reviewed

    BOM lookup, on-hand check. If short, stage a purchase order (Path 2 has no customer pressure since no SO).

  4. 2.4

    Production / assembly completed

    Crew executes build for target qty. Components debited per BOM.

  5. 2.5

    Assembly Build completed in NetSuite

    NS AsmBuild record posted (createdfrom = wo.id). Mike approves at /assembly-build.html.

  6. 2.6

    FG added to inventory at correct location

    FG credited at correct location_id; allocatable to future SOs.

  7. 2.7

    Components consumed · WO closes naturally

    Formal component consumption posted (WkOrdIss). WO closes naturally (status='Built'). No customer side. No Finance handoff.

  8. 2.8

    FG remains available for future SOs

    Finished goods sit on the shelf at the correct location, allocatable to any future Sales Order. Finance enters when that future SO sells the FG.

Outcomes

What's different after the cycle

WO
Built
Assembly Build completed
FG inventory
+qty
at correct location
Components
-qty
consumed through build
Path 1 SO
Closed
after Finance cycle
Failure modes

What can go wrong

Path 1 · Assembly Build done but SO never fulfilled (WO/SO link broken)

Production completed the assembly build, finished goods are credited, but the SO still shows "Pending Fulfillment". The createdfrom link likely got dropped. Resync the WO; rebuild link manually if needed. See runbook scenario.

Path 1 · Finance never got the alert (handoff failed)

Item Fulfillment closed but Finance's review queue is empty. NS workflow trigger fired but delivery failed. Check NS workflow execution log, sync_log, ns_pending_pushes.

Path 2 · WO closed but FG not credited to correct location

Assembly Build posted, but FG landed at wrong location_id (or no location captured). FG won't be allocatable to the expected location's SOs. Patch inventory_balance; investigate why the location field was missing.

Components short — can build proceed?

Path 1 with short components can proceed and backorder downstream lines. Path 2 should stage a PO first (no customer pressure). Today operator decides; we don't have an auto-PO trigger.

Waste exceeds expected (yield variance)

Mike's "production issues, shortages, variances reviewed" gate. If waste > threshold, flag for review before closing the WO. Today qualitative; should be quantitative against BOM yield estimate.

WO PO# field undefined — threading break (Path 1)

Until Mike confirms the WO body field for customer PO#, the threading from SO.otherrefnum into the WO is unverifiable. Grep finds the PO# on SO, PO, Invoice, VendBill, IF — but the WO row is the missing link in the chain audit.

Related

Adjacent flows + diagrams

For developers

Code paths + invariants

ConcernWhere
WO mirror tablework_orders, transactions WHERE type='WorkOrd'
Assembly Build tableassembly_builds (createdfrom = wo.id)
Path 1 linkworkorder.createdfrom = transactions.id (SO id)
Path 2 standaloneworkorder.createdfrom IS NULL
FG creditinventory_balance.quantityavailable += quantity_built WHERE item_code=? AND location_id=?
HITL surface/assembly-build.html → proposed_actions
Finance alert (Path 1, STUB)events.wo.finance_alert_fired on itemfulfillment.status='Shipped'
Sync tierhot (2 min) for WO + Assembly Build
STUB — WO PO# fieldTBD — Mike review pending; expected bodyFields.memo
STUB — auto-classify originoperator decides; no auto-classifier on /api/wo/create
// Origin classification (intent — today operator-driven) function classifyWO(wo) { if (wo.createdfrom_so_id) return 'from_sales_order'; return 'inventory_build'; } // Path 1: WO is linked end-to-end to the parent SO // workorder.createdfrom = so.id // assemblybuild.createdfrom = wo.id // itemfulfillment.createdfrom = so.id (NOT wo.id) // Path 2: WO is standalone // workorder.createdfrom = NULL // assemblybuild.createdfrom = wo.id // no itemfulfillment, no Finance alert on the WO // WO PO# threading — TBD pending Mike review // Expected pattern (Path 1 only): wo.memo = so.otherrefnum // Path 2: wo.memo = freeform operator note (no customer PO#) // ★ THE handoff (Path 1 only) — fires on Item Fulfillment if (path === 'from_sales_order' && itemfulfillment.status === 'Shipped') { fireFinanceAlert({ fulfillment_id, so_id, wo_id, customer_id, total }); }
Changelog

Dated trail · spot stale claims

Dated trail of when this doc was last touched, what changed, and what to look at if it feels stale.

DateRoundChangeTouched by
2026-05-26R593Split into master + 2 path-detail docs matching SO pattern. Master is now the dispatcher; wo_lifecycle_from_sales_order_path and wo_lifecycle_inventory_build_path sub-contracts each have their own deep diagram + wiki. Renamed ns-work-order-lifecycle.htmlns-work-order-master.html (and the wiki). WO customer PO# field flagged TBD pending Mike's review (expected pattern bodyFields.memo).Mike + Claude
2026-05-25R586Added CHANGELOG · SCHEMA · RUNBOOK · BACKLOG sections — wiki became best-in-class operating documentation.Mike + Claude
2026-05-24R585Wiki originally shipped — 9-phase pipeline (intake / pull / start / labor / issue / build / close / variance / FG). Pre-split monolith.Mike + Claude
If today is more than 60 days past the latest changelog row, treat live system behavior as the source of truth. Verify against workflow_definitions WHERE workflow_type='work_order_lifecycle' before acting on these claims.
Schema · data contract

The machine-readable spec

Canonical fields, table names, endpoint signatures. Master workflow_type · work_order_lifecycle · risk_level · 2. Sub-contracts: wo_lifecycle_from_sales_order_path (risk 3), wo_lifecycle_inventory_build_path (risk 2).

Customer PO# threading on WO — TBD pending Mike review

Mike's notes confirmed PO# threading on every other record in the SO chain. The WO body field is the only TBD. Expected pattern: bodyFields.memo (mirrors PO threading). Path 1 only; Path 2 has no customer PO# (no parent SO).

NS recordField carrying customer PO#Sample valueStatus
Sales Order (parent for Path 1)bodyFields.otherrefnum (thread origin)"72622"REAL
Work Order (Path 1)TBD — Mike review pending; expected pattern bodyFields.memo"72622" (TBD)TBD — Mike review pending. Expected: bodyFields.memo
Work Order (Path 2)N/A — no parent SO, no customer PO#. memo = freeform operator note (e.g. "bid B5875 pre-build")n/aREAL (no threading)
Assembly Build (both paths)createdfrom = wo.id — ties to WO; WO ↔ SO link (Path 1 only)WO idREAL
Item Fulfillment (Path 1)otherrefnum inherits from SO via NS standard linkage"72622"REAL
Customer Invoice (Path 1)bodyFields.otherrefnum"72622"REAL

Trace recipe (Path 1): grep "72622" across transactions.otherrefnum + item_fulfillments.otherrefnum + customer_invoices.otherrefnum. The WO row is the missing link until Mike confirms which body field carries the threading. Action: Mike to confirm. Default placeholder: WO.memo = SO.otherrefnum.

WO origin classification (drives sub-contract dispatch)

originSub-contractLinkageCustomer side?
from_sales_orderwo_lifecycle_from_sales_order_path (risk 3)workorder.createdfrom = so.idYes — SO fulfilled, Finance handoff, invoice, payment, close
inventory_buildwo_lifecycle_inventory_build_path (risk 2)workorder.createdfrom = NULLNo — FG to inventory, future SO may consume

Inputs (required + optional)

FieldTypeDescription
originenumfrom_sales_order · inventory_build. Drives sub-contract dispatch. Required.
assembly_itemstringNS assembly item_code. Required.
quantityintegerTarget build qty. Required.
location_idintegerNS location id where FG will land. Required.
so_idintegerParent SO id. Required for Path 1; NULL for Path 2.
customer_po_numberstringTBD — Mike review pending. Expected: WO.bodyFields.memo on Path 1 (threaded from SO.otherrefnum).
build_reasonenumPath 2 only: reorder_point · forecast · bid_pre_build · usda_drawdown · rd_pilot · other

D1 tables touched (across both paths)

TablePathOperation
work_orders / transactions (WorkOrd)commonINSERT (Path 1 with createdfrom = so.id; Path 2 NULL)
assembly_bomcommonREAD (BOM lookup)
inventory_balancecommonUPDATE: components -=, FG +=
assembly_builds / transactions (Build)commonINSERT (createdfrom = wo.id)
transactions (WkOrdIss)commonINSERT per component consume
item_fulfillmentsPath 1 onlyINSERT (createdfrom = so.id)
customer_invoicesPath 1 onlyINSERT (CustInvc; otherrefnum from SO)
customer_payments · payment_applicationsPath 1 onlyINSERT on remit
proposed_actionscommonINSERT for Assembly Build HITL gate
eventscommon (Path 1 extra)workorder.created, workorder.built, wo.finance_alert_fired (Path 1 STUB), order.closed (Path 1)

Endpoints called

MethodPathPurpose
POST/api/wo/createWO orchestration (today manual; auto-classify STUB)
POST/api/ns/workorder/releaseflips status In Progress via NS_PUSH_QUEUE
POST/api/ns/workorder/closefinalizes WO; triggers variance audit
GET/api/assemblies/{id}/bomresolves current BOM
POST/api/proposed-actions/bulk-decideHITL approve Assembly Build

Events fired

event_typeWhenSubscribers
workorder.createdWO posted in NS, mirrored to D1production_admin, inventory
workorder.builtWO closes (status=Built) after Assembly Buildinventory, customer_health (Path 1)
assembly_build.postedAsmBuild record postedanalytics, cost_rollup
inventory.fg_creditedFG added to inventory_balance at locationSO_allocation_watcher, item_entity_page
wo.finance_alert_firedPath 1 only — Item Fulfillment closes (STUB pending NS workflow)Finance
order.closedPath 1 only — SO closes after payment applycustomer_health, analytics
Runbook · when it breaks

It broke at 2am — what now

Different from "how do I use this." This is the page Mike pulls up when something is wrong: logs to check, recovery steps, who to escalate to.

Scenario · Path 1 Assembly Build completed but SO never fulfilled (WO/SO link broken)

Production finished the build, finished goods are credited, but the SO still shows "Pending Fulfillment". Path 1 stalled at step 1.6.

  1. Verify the link: SELECT id, createdfrom, status FROM work_orders WHERE id=? — createdfrom should equal SO id
  2. If createdfrom is NULL: patch with UPDATE work_orders SET createdfrom=? WHERE id=? and force resync
  3. Force SO resync: POST /api/sync/run?tier=hot&table=transactions&id=<so_id>
  4. Trigger fulfillment manually in NS: from the SO, hit "Fulfill" with the assembly already on hand

Scenario · Path 1 Finance never got the alert

Item Fulfillment closed but Finance's review queue is empty. The automation alert didn't deliver.

  1. Check NS workflow execution: NS workflow studio → wo_fulfillment_alert → execution log for this fulfillment
  2. Check event: SELECT * FROM events WHERE event_type='wo.finance_alert_fired' AND entity_id=? ORDER BY created_at DESC
  3. Check ns_pending_pushes: SELECT * FROM ns_pending_pushes WHERE record_type='itemfulfillment_alert' ORDER BY created_at DESC LIMIT 10
  4. Fall back: manually notify Finance with the fulfillment ID; investigate NS workflow config afterwards

Scenario · Path 2 FG landed at wrong location

Assembly Build posted, but inventory_balance shows FG at a location the operator didn't intend. Future SO allocation will fail.

  1. Find the FG row: SELECT * FROM inventory_balance WHERE item_code=? ORDER BY updated_at DESC LIMIT 5
  2. Check WO.location vs AsmBuild.location: SELECT id, location FROM work_orders WHERE id=? and SELECT id, location FROM assembly_builds WHERE wo_id=?
  3. If WO has correct location but AsmBuild doesn't: investigate NS AsmBuild record's location field; manually move inventory via location transfer
  4. NS location transfer: issue a transfer order from wrong-location to correct-location for the FG qty

Scenario · WO PO# field threading break (Path 1) — pending Mike

Auditing the SO chain for customer PO# "72622": SO shows it on otherrefnum, IF inherits, Invoice carries it — but the WO row is unverifiable because the body field is TBD.

  1. Confirm with Mike: which WO body field is intended to carry the customer PO#? Expected: bodyFields.memo
  2. Until confirmed: trace via createdfrom = so.id linkage only; this gets you from SO to WO without needing the PO# in a body field
  3. Schema fix (post-confirmation): add verify_check to wo_lifecycle_from_sales_order_pathWO.memo == SO.otherrefnum

Logs to check

Kill switch · emergency stop

Escalation

Primary: Mike Levine (single-admin) · mikelevine@globalfoodsolutions.co. For production-floor outage during business hours, notify warehouse lead so dependent SOs can be re-planned.

Backlog · open questions

What's not done · what's uncertain

What's not done, what's uncertain, what we punted. Captured so it survives context switches and doesn't die in someone's head.