WO created FROM Sales Order
Path 1 covers the case where one or more lines on a customer SO require assembly. A Work Order is created FROM the SO (workorder.createdfrom = so.id) preserving the SO ↔ WO link end-to-end. Components are reviewed at the correct location, production runs, the Assembly Build is entered in NetSuite (Mike approves at /assembly-build.html), finished goods are added to inventory, then the SO is fulfilled. Item Fulfillment fires the ★ automation alert to Finance; Finance reviews, invoices, monitors terms, applies payment, and closes the order.
Shortcut: if the FG is already on hand at the correct location, fulfill the SO from inventory and close the WO marked unused.
Diagram: ns-work-order-path-1-from-sales-order.html. Master wiki: wiki-ns-work-order-master.html. Other path: Path 2 inventory build. Workflow contract: wo_lifecycle_from_sales_order_path (risk 3).
Trigger
- SO master classifies a line as
assembly_item; SO Path 2 (assembly) dispatches WO Path 1 to actually build it. - Direct WO entry in NetSuite linked to an existing SO via
createdfrom = so.id.
Item Fulfillment closes → events.wo.finance_alert_fired emits → Finance picks up review. Same handoff signal as the SO master.
Expected pattern: bodyFields.memo threaded from SO.otherrefnum. Every WO Schema reference flagged TBD until confirmed.
Driscoll Melt Mates kit
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 and memo (TBD, expected "72622"). Component review at location 3 (Heartland): CHED-2LB, PRTZL-BAG, TURKEY-PAK on hand. Production runs; crew completes build with 0 waste. Mike approves at /assembly-build.html; NS posts AB-1881 (createdfrom = 4471, quantity = 24, location = 3). FG credited: 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. Finance posts CINV-9032 for $18,420 Net 30; payment received + applied; SO closed.
From SO assembly-line to WO close (8 stages)
- SO assembly-line detected — intake operator (or auto-classifier) flags a SO line where the item is an Assembly Item. Path 1 dispatched.
- WO created —
WorkOrdrecord:createdfrom = so.id,assemblyitem= SO-line item,quantity= SO-line qty,memo(TBD = SO.otherrefnum). HITL gate: WO creation throughproposed_actionsper ADR-031. - Component review — BOM lookup against
inventoryitemlocationsat WO.location. If shortage: hold + escalate (see Failure modes). If OK: advance. - Production runs — crew completes assembly on the floor. Waste, labor hours, line time captured manually (kiosk/scanner is STUB).
- Assembly Build approval — Mike reviews at
/assembly-build.html; HITL viaproposed_actions. On approve, NS postsAsmBuild:createdfrom = wo.id,item,quantity,custbody_waste(TBD),location,trandate. - Component issue —
WkOrdIssrows created per BOM component (createdfrom = wo.id). Inventory decrements at component level. - FG credit —
inventory_balance.quantityavailable += quantityat WO.location for the finished assembly item. - WO closed + SO fulfilled —
WO.status = 'Built'.ItemFulfillcreated against SO (NOT WO):createdfrom = so.id,otherrefnuminherits from SO. ★ Universal Finance handoff fires (planned R594 automation).
What is true after Path 1 completes
- The customer's SO is fulfilled with a build-to-order assembly that did not exist in inventory before.
- Records linked end-to-end:
SO.id ← WO.createdfrom,WO.id ← AsmBuild.createdfrom,WO.id ← WkOrdIss.createdfrom,SO.id ← IF.createdfrom,SO.id ← CustInvc.createdfrom. - Customer PO# (
SO.otherrefnum) traceable end-to-end through IF + CustInvc; WO carry-through pending thememoTBD resolution. - Inventory math: components decremented per BOM × quantity at WO.location; finished assembly item incremented by the build quantity.
- Variance audit: if
|actual_built − estimated| / estimated > 5%, a HITLproposed_actionsrow is staged for review. - Reflexion writes a quality score: build success, component accuracy, lead time vs. estimate. Used to improve future scheduling.
What can go wrong + recovery
| Failure | Detection | Recovery |
|---|---|---|
| BOM component shortage | Step 3 inventory check fails | Hold WO · escalate to purchasing (stage emergency PO via Path 1 of PO process) · resume when components received |
| Build short (yield variance > 5%) | AsmBuild quantity < WO quantity × 0.95 | Variance audit fires · HITL review · either accept short fulfillment + adjust SO line, OR re-run build for remainder |
| Build over (waste < expected) | AsmBuild quantity > WO quantity | Excess FG goes to inventory (not back to SO) · available for future SOs |
| WO cancel mid-build | SO cancelled before AsmBuild posts | Reverse WkOrdIss issues (components back to inventory) · void WO · do not post AsmBuild |
| AsmBuild void after post | Bad build, defective FG | Void AsmBuild · re-issue components · rebuild · re-link IF to corrected build |
| IF posts before AsmBuild | Order-of-events bug | Invariant violation — alert. IF should only post after AsmBuild credits FG. |
| Customer cancels SO after IF | SO status = 'Cancelled' with IF.status = 'Shipped' | RMA / Return Authorization workflow · receive returned goods · restock or scrap · Credit Memo |
| Finance alert fails to fire | IF.status = 'Shipped' but no CustInvc within 1 hour | Manual Finance handoff · investigate NS workflow trigger · check ns_pending_pushes |
Adjacent flows + diagrams
Code paths + invariants
| Concern | Where |
|---|---|
| Linkage invariant | workorder.createdfrom = so.id |
| Assembly Build linkage | assemblybuild.createdfrom = wo.id |
| Item Fulfillment linkage | itemfulfillment.createdfrom = so.id (NOT wo.id) |
| FG credit | inventory_balance.quantityavailable += quantity at location_id |
| HITL gate | /assembly-build.html → proposed_actions → Mike approves |
| Finance alert (STUB) | events.wo.finance_alert_fired on IF.status='Shipped' |
| WO PO# field (TBD) | bodyFields.memo (expected) = SO.otherrefnum |
Dated trail
| Date | Round | Change | Touched by |
|---|---|---|---|
2026-05-26 | R593 | Path 1 wiki created as part of WO master split (matches R592 SO pattern). Split into master + 2 path-details. Mike + Claude. | Mike + Claude |
Path 1 sub-contract spec
Sub-contract: wo_lifecycle_from_sales_order_path · risk 3 · dispatched by WO master when origin = from_sales_order.
Path 1 NS records (field-level)
| NS record | Key fields | Sample values | Status |
|---|---|---|---|
| SalesOrd (upstream) | tranid, otherrefnum | "1217", "72622" | REAL |
| WorkOrd | tranid, createdfrom, assemblyitem, quantity, location | "WO-4471", 1217, "MELT-MATES-KIT-A", 24, 3 | REAL |
| WorkOrd | bodyFields.memo — customer PO# field | TBD — expected "72622" | TBD — Mike review pending |
| AsmBuild | tranid, createdfrom, quantity, custbody_waste, location | "AB-1881", 4471, 24, 0, 3 | REAL |
| ItemFulfill | tranid, createdfrom, otherrefnum, status | "IF-2210", 1217 (SO id), "72622", "Shipped" | REAL |
| CustInvc | tranid, otherrefnum, amount | "CINV-9032", "72622", 18420.00 | REAL |
| CustPymt | tranid, appliedto | "CPYMT-3120", "CINV-9032" | REAL |
Path 1 verify_checks
| name | window | expected |
|---|---|---|
wo_linked_to_so | 60 min | work_orders.createdfrom = so_id |
assembly_build_posted | 2880 min | assembly_builds.wo_id has ≥1 row |
fg_credited | 2880 min | inventory_balance.quantityavailable increased by build qty at location_id |
if_created_from_so | 2880 min | item_fulfillments.createdfrom = so.id |
finance_alert_fired | 2880 min | events.wo.finance_alert_fired exists for IF (STUB today) |
wo_memo_carries_po · TBD | 2880 min | TBD — Mike review pending; expected WO.memo = SO.otherrefnum once confirmed |
so_closed | 43200 min | transactions.status = 'Closed' |
When Path 1 breaks
Assembly Build done but SO never fulfilled
- Verify the link:
SELECT id, createdfrom, status FROM work_orders WHERE id=? - If createdfrom is NULL: patch with
UPDATE work_orders SET createdfrom=? WHERE id=? - Force resync:
POST /api/sync/run?tier=hot&table=transactions&id=<so_id> - Trigger fulfillment manually in NS: from the SO, hit "Fulfill" with the assembly on hand
WO PO# field threading break (pending Mike)
- Confirm with Mike: which WO body field carries customer PO#? Expected
bodyFields.memo - Until confirmed: trace via
createdfrom = so.idlinkage only - Schema fix post-confirmation: add
WO.memo == SO.otherrefnumverify_check
Open questions for Path 1
-
TBD
WO customer PO# field
Mike to confirm. Expected
bodyFields.memothreaded fromSO.otherrefnum. Every Schema reference flagged TBD until confirmed. -
STUB
NS workflow for Finance alert
Platform event fires; NS workflow trigger on
itemfulfillment.status='Shipped'still pending build. -
OPEN
FG on-hand shortcut auto-detection
Operator decides "fulfill from inventory + close WO unused" today. Could auto-detect at WO creation.