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.
Trigger conditions
- SO entered; one or more lines have
items.itemtype = "Assembly". - BOM resolves: every assembly_item has rows in
assembly_bom. - Components available: BOM component qty available (warn if not — backorder handled separately).
- Master dispatches into
so_lifecycle_assembly_path.
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.
After Item Fulfillment, the automation alert fires to Finance. STUB at NS layer (event fires now; NS workflow deploy pending).
Driscoll Melt Mates Kit qty 24
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.
WO create → build → ship → Finance
- 01
Create Work Order FROM SO
NS
WorkOrd:createdfrom = so.id,assemblyitem,quantity. Customer PO# field: TBD pending Mike's review. - 02
BOM lookup
SELECT * FROM assembly_bom WHERE assembly_item = ?— returns components + per-kit qty. - 03
Shortcut decision: FG already on hand?
If yes → close WO marked "unused", fulfill from inventory (defers to Path 1 mechanics).
- 04
BOM consume
UPDATE inventory_balance SET quantityavailable -= bom.qty * WO.quantityper component. - 05
Assembly build on production floor
NS
AsmBuild:createdfrom = WO.id,quantity,custbody_waste. HITL: Mike approves at/assembly-build.html. - 06
FG credit
UPDATE inventory_balance SET quantityavailable += WO.quantityfor the assembly item. - 07
Close WO
UPDATE work_orders SET status='Built'. - 08
Item Fulfillment created
NS
ItemFulfill:createdfrom = so.id(the SO, not the WO),otherrefnum = "72622"from SO. - 09
★ Finance alert (STUB)
Fires
events.so.finance_alert_fired. - 10
Invoice · payment · close
Same Finance steps as Path 1.
What's different after the cycle
What can go wrong
Production runs, FG credited, but the SO still shows "Pending Fulfillment." The createdfrom link likely got dropped. Resync the WO; rebuild the link.
Verify_check warns when components fall below BOM qty. Resolve via backorder workflow or substitution.
Until Mike confirms the expected pattern, this isn't enforced. Trace via createdfrom → SO → otherrefnum instead.
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.
Adjacent flows + diagrams
Code paths + invariants
| Concern | Where |
|---|---|
| Workflow contract | so_lifecycle_assembly_path |
| WO ↔ SO link | workorder.createdfrom = transactions.id |
| AsmBuild ↔ WO link | assemblybuild.createdfrom = workorder.id |
| PO# threading on WO | workorder.memo (TBD — Mike confirm; expected pattern) |
| PO# threading on IF | item_fulfillments.otherrefnum (inherits SO.otherrefnum via createdfrom) |
| BOM | assembly_bom WHERE assembly_item = ? |
| HITL | /assembly-build.html → proposed_actions |
Dated trail
| Date | Round | Change | Touched by |
|---|---|---|---|
2026-05-26 | R592 | Split into master + 3 path-detail docs; added Customer PO# threading schema. WO field marked TBD pending Mike's review. | Mike + Claude |
The machine-readable spec
workflow_type · so_lifecycle_assembly_path · risk_level · 3.
Customer PO# threading (Path 2 · WO chain)
| NS record | Field | Sample value |
|---|---|---|
| Sales Order | bodyFields.otherrefnum (thread origin) | "72622" |
| Work Order | bodyFields.memo — TBD — Mike review pending; expected pattern | "72622" (TBD) |
| Work Order | bodyFields.createdfrom (link to SO regardless of memo) | 1217 (SO id) |
| Assembly Build | bodyFields.createdfrom (link to WO) | "WO-4471" |
| Item Fulfillment | inherits via NS std linkage — createdfrom = so.id | carries otherrefnum = "72622" |
| Customer Invoice | bodyFields.otherrefnum | "72622" |
D1 tables touched
| Table | Operation |
|---|---|
work_orders | INSERT (createdfrom, assemblyitem, quantity, memo TBD) |
assembly_bom | SELECT (component lookup) |
assembly_builds | INSERT (createdfrom = WO id, quantity) |
inventory_balance | UPDATE (debit components; credit FG) |
item_fulfillments | INSERT (createdfrom = SO id; otherrefnum from SO) |
customer_invoices | INSERT (otherrefnum threaded) |
customer_payments | INSERT |
payment_applications | INSERT |
transactions | UPDATE (SO + WO status changes) |
proposed_actions | INSERT (HITL for assembly build) |
events | INSERT (workorder.created, so.finance_alert_fired, order.closed) |
It broke at 2am
Scenario · Assembly build done but SO never fulfilled
WO/SO link broken.
- Verify link:
SELECT id, createdfrom, status FROM work_orders WHERE id=? - If createdfrom NULL: patch with the SO id; force resync.
- 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.
- Trace via createdfrom:
SELECT so.otherrefnum FROM work_orders wo JOIN transactions so ON wo.createdfrom=so.id WHERE wo.id=? - Once Mike confirms: add
verify_checkonwo.memo == so.otherrefnum.
Logs to check
workflow_run_logevents· workorder.created + so.finance_alert_firedproposed_actions· HITL queue for assembly build
What's not done
-
TBD
Confirm Work Order customer PO# field with Mike
Expected:
bodyFields.memocarries the customer PO# threading fromSO.otherrefnum. Pending Mike's review. Once confirmed, add verify_check and update Schema sections. -
STUB
Finance automation alert NS workflow build
Platform fires the event; NS workflow build pending next round.
-
OPEN
Auto-detect shortcut: FG on hand → skip build
Today operator decides. Could automate by querying
inventory_balanceat WO creation time.