Wiki · Sales order · PATH 2 · R592

Path 2 · assembly (Work Orders FROM SO)

For assembly build items needing production. A Work Order is created FROM the SO (workorder.createdfrom = so.id), preserving the SO ↔ WO link end-to-end. BOM components consumed, build runs on the floor (HITL Mike approval), FG credited, then Item Fulfillment fires. Shortcut: if FG already on hand, fulfill from inventory + close WO.

PATH 2 REAL WO PO# field TBD — Mike review ★ Finance alert pending NS automation
What this is

Production via Work Orders · SO ↔ WO link preserved

Path 2 covers SO lines where the item is an assembly that must be produced. The sub-contract so_lifecycle_assembly_path handles: WO create FROM SO → BOM consume → assembly build → FG credit → Item Fulfillment → ★ Finance alert → CustInvc → payment → close. A shortcut branch fulfills from inventory if FG is already on hand (closes the WO marked "unused").

The customer PO# field on the Work Order is TBD — pending Mike's review. Expected pattern: bodyFields.memo = customer PO#. The rest of the chain threads cleanly via SO.otherrefnum → ItemFulfill.otherrefnum → CustInvc.otherrefnum. The WO is linked to the SO via createdfrom = so.id regardless of the memo field.

Diagram: ns-sales-order-path-2-assembly.html.

When to use it

Trigger conditions

TBD · Work Order customer PO# field

The expected pattern is that WorkOrd.bodyFields.memo carries the customer PO# (threading from SO.otherrefnum). Mike to confirm in his review. Until confirmed, this is the only TBD field in the entire SO chain. WorkOrd.createdfrom = so.id guarantees the records are linked regardless.

★ The handoff

After Item Fulfillment, the automation alert fires to Finance. STUB at NS layer (event fires now; NS workflow deploy pending).

Worked example

Driscoll Melt Mates Kit qty 24

Scenario

Driscoll's SO 1217 (PO# 72622) includes 1 assembly line: 24 units of MELT-MATES-KIT-A.

WO-4471 created FROM SO 1217: createdfrom = 1217, assemblyitem = MELT-MATES-KIT-A, quantity = 24, memo = (TBD — Mike review).

BOM lookup pulls 5 components per kit (120 total component consumes). Inventory debited. Production crew completes the build; Mike approves at /assembly-build.html. AsmBuild record posts: createdfrom = "WO-4471", quantity = 24. FG credited.

Item Fulfillment posted (createdfrom = 1217, otherrefnum = "72622"). ★ Finance alert fires. Invoice posted; same customer PO# threading.

Step-by-step what happens

WO create → build → ship → Finance

  1. 01

    Create Work Order FROM SO

    NS WorkOrd: createdfrom = so.id, assemblyitem, quantity. Customer PO# field: TBD pending Mike's review.

    Link WO.createdfrom = SO.id
  2. 02

    BOM lookup

    SELECT * FROM assembly_bom WHERE assembly_item = ? — returns components + per-kit qty.

  3. 03

    Shortcut decision: FG already on hand?

    If yes → close WO marked "unused", fulfill from inventory (defers to Path 1 mechanics).

  4. 04

    BOM consume

    UPDATE inventory_balance SET quantityavailable -= bom.qty * WO.quantity per component.

  5. 05

    Assembly build on production floor

    NS AsmBuild: createdfrom = WO.id, quantity, custbody_waste. HITL: Mike approves at /assembly-build.html.

  6. 06

    FG credit

    UPDATE inventory_balance SET quantityavailable += WO.quantity for the assembly item.

  7. 07

    Close WO

    UPDATE work_orders SET status='Built'.

  8. 08

    Item Fulfillment created

    NS ItemFulfill: createdfrom = so.id (the SO, not the WO), otherrefnum = "72622" from SO.

  9. 09

    ★ Finance alert (STUB)

    Fires events.so.finance_alert_fired.

  10. 10

    Invoice · payment · close

    Same Finance steps as Path 1.

Outcomes

What's different after the cycle

SO
Closed
paid + applied
WO
Built
SO ↔ WO linked
FG
Credited
+ raw debited
PO# trace
3+ records
WO field TBD
Failure modes

What can go wrong

WO created but createdfrom NULL (SO link broken)

Production runs, FG credited, but the SO still shows "Pending Fulfillment." The createdfrom link likely got dropped. Resync the WO; rebuild the link.

BOM components short — build can't run

Verify_check warns when components fall below BOM qty. Resolve via backorder workflow or substitution.

WO.memo (PO# field) blank or mismatched

Until Mike confirms the expected pattern, this isn't enforced. Trace via createdfrom → SO → otherrefnum instead.

Shortcut taken when shouldn't have been

Operator fulfilled from inventory because FG appeared available, but the inventory reflected a reservation not yet decremented. Reconcile quantityavailable vs reserved_qty for the assembly item.

Related

Adjacent flows + diagrams

For developers

Code paths + invariants

ConcernWhere
Workflow contractso_lifecycle_assembly_path
WO ↔ SO linkworkorder.createdfrom = transactions.id
AsmBuild ↔ WO linkassemblybuild.createdfrom = workorder.id
PO# threading on WOworkorder.memo (TBD — Mike confirm; expected pattern)
PO# threading on IFitem_fulfillments.otherrefnum (inherits SO.otherrefnum via createdfrom)
BOMassembly_bom WHERE assembly_item = ?
HITL/assembly-build.html → proposed_actions
// Path 2 — TBD invariant; awaiting Mike's confirmation // Expected: WO.memo = SO.otherrefnum (customer PO# threads from SO to WO) // Today: only WO.createdfrom = SO.id is confirmed SELECT wo.id, wo.createdfrom, wo.memo, so.otherrefnum FROM work_orders wo JOIN transactions so ON wo.createdfrom = so.id WHERE wo.id = ? // pending Mike: confirm WO.memo == SO.otherrefnum
Changelog

Dated trail

DateRoundChangeTouched by
2026-05-26R592Split into master + 3 path-detail docs; added Customer PO# threading schema. WO field marked TBD pending Mike's review.Mike + Claude
Schema · data contract

The machine-readable spec

workflow_type · so_lifecycle_assembly_path · risk_level · 3.

Customer PO# threading (Path 2 · WO chain)

NS recordFieldSample value
Sales OrderbodyFields.otherrefnum (thread origin)"72622"
Work OrderbodyFields.memoTBD — Mike review pending; expected pattern"72622" (TBD)
Work OrderbodyFields.createdfrom (link to SO regardless of memo)1217 (SO id)
Assembly BuildbodyFields.createdfrom (link to WO)"WO-4471"
Item Fulfillmentinherits via NS std linkage — createdfrom = so.idcarries otherrefnum = "72622"
Customer InvoicebodyFields.otherrefnum"72622"

D1 tables touched

TableOperation
work_ordersINSERT (createdfrom, assemblyitem, quantity, memo TBD)
assembly_bomSELECT (component lookup)
assembly_buildsINSERT (createdfrom = WO id, quantity)
inventory_balanceUPDATE (debit components; credit FG)
item_fulfillmentsINSERT (createdfrom = SO id; otherrefnum from SO)
customer_invoicesINSERT (otherrefnum threaded)
customer_paymentsINSERT
payment_applicationsINSERT
transactionsUPDATE (SO + WO status changes)
proposed_actionsINSERT (HITL for assembly build)
eventsINSERT (workorder.created, so.finance_alert_fired, order.closed)
Runbook · when it breaks

It broke at 2am

Scenario · Assembly build done but SO never fulfilled

WO/SO link broken.

  1. Verify link: SELECT id, createdfrom, status FROM work_orders WHERE id=?
  2. If createdfrom NULL: patch with the SO id; force resync.
  3. Trigger fulfillment manually in NS: from the SO, hit "Fulfill" with assembly on hand.

Scenario · WO.memo not carrying customer PO#

TBD — awaiting Mike's review on the expected pattern.

  1. Trace via createdfrom: SELECT so.otherrefnum FROM work_orders wo JOIN transactions so ON wo.createdfrom=so.id WHERE wo.id=?
  2. Once Mike confirms: add verify_check on wo.memo == so.otherrefnum.

Logs to check

Backlog · open questions

What's not done