How the three processes work together — SO + PO + WO

Customer side (SO) · Vendor side (PO) · Production side (WO) · all three converge at the Finance handoff

Three transaction lifecycles run the business. Sales Order is the customer side — starts with the customer order and ends with fulfillment + invoice + payment + close. Purchase Order is the vendor side — used when we buy product, ingredients, packaging, components, finished goods, or drop-ship items; ends with Item Receipt + vendor bill + vendor payment + PO closure. Work Order is the production side — used when we assemble or build finished goods; ends when the Assembly Build is completed and finished goods land back in inventory. Two cross-lane bridges keep records linked: SO Path 2 creates a WO from the SO (WO.createdfrom = SO.id), and SO Path 3 creates a PO from the SO (PO.createdfrom = SO.id). All three converge at the ★ Finance handoff post-fulfillment / post-receipt. This is the canonical "system of three" view; the per-lane masters and path-detail docs hold the field-level depth.

D1-mirrored from NS HITL on every write-back Customer side · SO Vendor side · PO Production side · WO ★ Finance handoff (convergence)

4-lane swimlane — customer / vendor / production / finance

idle Driscoll orders 50 cases RS-12: 30 inv + 20 WO + 1 dropship line
layers
WHAT THIS DOES: This is the how the three netsuite transaction lifecycles work together. lane 1 (customer / sales order) runs top. lane 2 (vendor / purchase order) runs middle. lane 3 (production / work order) runs below. cross-lane bridges show so path 2 spawning a wo from the so, and so path 3 spawning a po from the so. lane 4 (finance) spans the bottom as the universal handoff convergence — fulfilled sales orders alert it for customer-side invoicing, received purchase orders alert it for vendor-side billing, completed work orders are reported to it for cost roll-up. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: How the three NetSuite transaction lifecycles work together. Lane 1 (customer / Sales Order) runs top. Lane 2 (vendor / Purchase Order) runs middle. Lane 3 (production / Work Order) runs below. Cross-lane bridges show SO Path 2 spawning a WO from the SO, and SO Path 3 spawning a PO from the SO. Lane 4 (Finance) spans the bottom as the universal handoff convergence — fulfilled Sales Orders alert it for customer-side invoicing, received Purchase Orders alert it for vendor-side billing, completed Work Orders are reported to it for cost roll-up. Lane 1 · Customer side · Sales Order lifecycle · customer order → fulfillment → invoice → payment → close Lane 2 · Vendor side · Purchase Order lifecycle · need to buy → PO → vendor ships → Item Receipt → Vendor Bill → pay Lane 3 · Production side · Work Order lifecycle · need to build → WO → release → consume BOM → Assembly Build → FG in inventory Lane 4 · ★ Finance handoff · universal convergence · receives alerts from all 3 lifecycles · invoices, bills, payments, cost roll-up WHAT THIS DOES: This is the so entry — customer order keyed in netsuite as a sales order. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: SO ENTRY — Customer order keyed in NetSuite as a Sales Order. ACTION intake: email at orders@ / danielle@ enter: NS SalesOrd · capture bodyFields.otherrefnum = customer PO# (e.g. "72622") capture: tranid = NS-assigned SO# (e.g. "1217") TABLES write: NS SalesOrd · D1 transactions · so_lines STATUS: REAL · operator-driven SO entry customer order keyed otherrefnum = "72622" LANE 1 / SO · step 1 i WHAT THIS DOES: This is the classify each line — a single so routinely spans multiple paths. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: CLASSIFY EACH LINE — A single SO routinely spans multiple paths. LOGIC per so_line.item.type: inventory_item → Path 1 (fulfill from inventory) assembly_item → Path 2 (create WO from SO) dropship_item OR special_order → Path 3 (create PO from SO) STATUS: REAL (operator-driven today) · STUB (auto-classify on /api/so/create) classify each line inv · assembly · dropship one SO → multi paths LANE 1 / SO · step 2 i WHAT THIS DOES: This is the path 1 inventory — lines fulfilled directly from stock. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: PATH 1 INVENTORY — Lines fulfilled directly from stock. ACTION reserve · pick · pack · stage TABLES write: itemfulfillment (open) · inventory_balance.reserved_qty DETAIL see ns-sales-order-path-1-inventory.html for field-level depth STATUS: REAL Path 1 · fulfill from inventory reserve · pick · pack no WO · no PO LANE 1 / SO · step 3 i WHAT THIS DOES: This is the item fulfillment (converge) — where all 3 so paths land. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: ITEM FULFILLMENT (CONVERGE) — Where all 3 SO paths land. ACTION status: itemfulfillment → 'Shipped' inventory decrement · BOL/ASN generated LINKAGE itemfulfillment.createdfrom = so.id inherits otherrefnum from SO STATUS: REAL Item Fulfillment all 3 paths land here createdfrom = so.id LANE 1 / SO · step 4 (converge) i WHAT THIS DOES: This is the invoice customer — finance-driven step on the so side. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: INVOICE CUSTOMER — Finance-driven step on the SO side. ACTION post: CustInvc using shipped qty (not ordered qty) AR balance updates TABLES write: CustInvc · invoice_lines · v_customer_ar_aging STATUS: REAL invoice customer CustInvc · AR open otherrefnum threads LANE 1 / SO · step 5 i WHAT THIS DOES: This is the payment + close — customer remits; so closed. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: PAYMENT + CLOSE — Customer remits; SO closed. ACTION receive: CustPymt · apply: payment_applications SO → Closed · fire events.order.closed STATUS: REAL payment · close CustPymt · SO Closed order.closed event LANE 1 / SO · step 6 i SO ↔ WO SO ↔ WO SO ↔ WO SO ↔ WO SO ↔ WO WHAT THIS DOES: This is the need to buy — po trigger has two sources. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: NEED TO BUY — PO trigger has two sources. SOURCES PO Path 1 (SO-connected): drop-ship line from SO Path 3 → PO created FROM SO PO Path 2 (stock replen): inventory low or scheduled buy → standalone PO PO Path 3 (special order): one-off ingredient/packaging/component STATUS: REAL need to buy SO-connected OR replen PO Path 1 / 2 / 3 LANE 2 / PO · step 1 i WHAT THIS DOES: This is the po entry — purchase order keyed in netsuite. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: PO ENTRY — Purchase Order keyed in NetSuite. ACTION enter: NS PurchOrd if SO-connected: createdfrom = so.id · bodyFields.memo = customer PO# if standalone: no createdfrom; vendor + items + terms LINKAGE (Path 1 only) purchaseorder.createdfrom = so.id PO.lineFields.links[].tranid = "<so.tranid> / <customer_po>" STATUS: REAL PO entry PurchOrd posted createdfrom = so.id (P1) LANE 2 / PO · step 2 i WHAT THIS DOES: This is the po transmitted — po sent to vendor (email / edi / portal). step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: PO TRANSMITTED — PO sent to vendor (email / EDI / portal). ACTION email-to-vendor or EDI 850 vendor confirms · ship date negotiated STATUS: REAL · vendor confirmation often manual transmit to vendor email / EDI / portal vendor confirms LANE 2 / PO · step 3 i WHAT THIS DOES: This is the item receipt — vendor ships product; we post receipt against the po. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: ITEM RECEIPT — Vendor ships product; we post receipt against the PO. ACTION status: itemreceipt → posted PO line → 'Fully Received' LINKAGE itemreceipt.createdfrom = po.id inherits memo (customer PO# threads through on Path 1) TABLES write: itemreceipt · inventory_balance (credit) STATUS: REAL Item Receipt vendor ships · IR posted createdfrom = po.id LANE 2 / PO · step 4 i WHAT THIS DOES: This is the vendor bill — finance enters the vendor bill against the po. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: VENDOR BILL — Finance enters the vendor bill against the PO. ACTION enter: NS VendBill · createdfrom = po.id bill matches 3-way: PO + IR + bill qty + price LINKAGE vendorbill.memo · tranid · initialtranid = customer PO# (Path 1) STATUS: REAL vendor bill VendBill posted · AP open 3-way match LANE 2 / PO · step 5 i WHAT THIS DOES: This is the pay + close po — vendor paid per terms; po closed. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: PAY + CLOSE PO — Vendor paid per terms; PO closed. ACTION pay: VendPymt per vendor terms (Net 30 / Net 45) PO → Closed · fire events.po.closed STATUS: REAL pay vendor · close PO VendPymt · AP cleared po.closed event LANE 2 / PO · step 6 i step step step step step WHAT THIS DOES: This is the need to build — wo trigger has two sources. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: NEED TO BUILD — WO trigger has two sources. SOURCES WO Path 1 (SO-connected): assembly line on SO Path 2 → WO created FROM SO WO Path 2 (stock build): low FG inventory or scheduled production → standalone WO ALTERNATE (Path 1) if FG already on hand → fulfill from inventory + close WO marked 'unused' STATUS: REAL need to build SO-connected OR stock WO Path 1 / 2 LANE 3 / WO · step 1 i WHAT THIS DOES: This is the wo entry — work order keyed in netsuite. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: WO ENTRY — Work Order keyed in NetSuite. ACTION enter: NS WorkOrd · assembly item + qty + BOM ref if SO-connected: createdfrom = so.id LINKAGE workorder.createdfrom = so.id (Path 1) WO customer PO# field: TBD — Mike review pending; expected bodyFields.memo STATUS: REAL · WO PO# field TBD WO entry WorkOrd posted createdfrom = so.id (P1) LANE 3 / WO · step 2 i WHAT THIS DOES: This is the wo released — production committed; components reserved. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: WO RELEASED — Production committed; components reserved. ACTION status: WO → 'Released' reserve BOM components against the WO STATUS: REAL WO released components reserved production scheduled LANE 3 / WO · step 3 i WHAT THIS DOES: This is the assembly build — production crew completes the build. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: ASSEMBLY BUILD — Production crew completes the build. ACTION enter: AssemblyBuild · consume BOM components credit: finished-goods qty back into inventory HITL /assembly-build.html → proposed_actions → Mike approves TABLES write: assemblybuild · inventory_balance (credit FG, debit components) STATUS: REAL · HITL on cost variance Assembly Build BOM consume · FG credit HITL via /assembly-build LANE 3 / WO · step 4 (end-state) i WHAT THIS DOES: This is the fg in inventory — finished goods land back in inventory. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: FG IN INVENTORY — Finished goods land back in inventory. ACTION inventory_balance for the assembly item increments by build qty scrap / waste captured as variance DOWNSTREAM if Path 1 WO: triggers Lane 1 SO fulfillment (now stock is available) STATUS: REAL FG in inventory FG available to SO scrap / waste captured LANE 3 / WO · step 5 i WHAT THIS DOES: This is the wo closed — work order completes. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: WO CLOSED — Work Order completes. ACTION WO → 'Closed' · build variance booked EVENTS fire: events.wo.closed · events.assembly.built (with cost variance) DOWNSTREAM Finance: cost roll-up for COGS + variance analysis STATUS: REAL WO closed variance booked wo.closed event LANE 3 / WO · step 6 i step step step step step WHAT THIS DOES: This is the ★ finance alert — so side. fires on every item fulfillment. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: ★ FINANCE ALERT — SO SIDE. Fires on every Item Fulfillment. TRIGGER itemfulfillment.status → 'Shipped' ACTION NS workflow notifies Finance review queue emit: events.so.finance_alert_fired STATUS: STUB — platform-side event fires (REAL); NS workflow build pending (R594) ★ SO finance alert itemfulfillment → Shipped LANE 4 / Finance · from SO i WHAT THIS DOES: This is the ★ finance alert — po side. fires on every item receipt. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: ★ FINANCE ALERT — PO SIDE. Fires on every Item Receipt. TRIGGER itemreceipt posted → 3-way match queued ACTION NS workflow queues bill entry · Finance reviews emit: events.po.finance_alert_fired STATUS: STUB — platform-side event fires (REAL); NS workflow build pending (R594) ★ PO finance alert itemreceipt posted LANE 4 / Finance · from PO i WHAT THIS DOES: This is the ★ finance roll-up — wo side. cost roll-up on every assembly build close. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: ★ FINANCE ROLL-UP — WO SIDE. Cost roll-up on every Assembly Build close. TRIGGER events.wo.closed · events.assembly.built (with variance) ACTION Finance updates COGS · variance reporting · cost-method refresh STATUS: STUB — events fire (REAL); GL posting still manual in Finance review ★ WO cost roll-up wo.closed · variance LANE 4 / Finance · from WO i WHAT THIS DOES: This is the finance reconciles — universal convergence. step. It happens as part of the work-order / order-integration chain and produces the data, status change, or record described below. NS RECORD: see TECHNICAL DETAILS TECHNICAL DETAILS: FINANCE RECONCILES — Universal convergence. ACTION SO side: invoice → payment apply → close PO side: bill → vendor pay → AP clear WO side: variance → COGS adjustment → margin walk monthly close ties all three together STATUS: REAL · Finance-driven Finance reconciles SO + PO + WO · monthly close ties all three LANE 4 / Finance · convergence i SO Path 2: spawns WO from SO WO.createdfrom = SO.id SO Path 3: spawns PO from SO PO.createdfrom = SO.id WO completes → FG available → SO can fulfill Path 3 IR → SO IF (dropship) ★ alert ★ alert ★ alert finance hook finance hook finance hook LEGEND Lane 1 SO (customer) Lane 2 PO (vendor) Lane 3 WO (production) Lane 4 Finance (★ convergence) stepcross-lane bridge (createdfrom) finance hook★ Finance handoff alert CROSS-RECORD LINKAGE · how the 3 lifecycles stay connected SO → WO (Path 2) WO.createdfrom = SO.id preserves SO ↔ WO link SO → PO (Path 3) PO.createdfrom = SO.id preserves SO ↔ PO link SO → Item Fulfillment IF.createdfrom = SO.id inherits otherrefnum PO → Item Receipt IR.createdfrom = PO.id inherits memo PO → Vendor Bill · Customer PO# thread Bill.createdfrom = PO.id · memo / tranid universal trace thread "72622"
Legend · color codes + icons used in this diagram
Color codes:
backend / NS record
work order / SO link
finance / invoice
TBD / escalation
cloud / worker
external / generic
Icon meanings:

The three lifecycles — Mike's framing

SO Sales Order — the customer side customer REAL

Starts with the customer order and ends with customer fulfillment, invoicing, payment, and order closure.
Start
customer email at orders@ / danielle@
End
SO Closed · events.order.closed fires
Master doc
ns-sales-order-master.html + 3 path-detail diagrams
Wiki

PO Purchase Order — the vendor side vendor REAL

Used when the company needs to buy product, ingredients, packaging, components, finished goods, or drop-ship items. Ends with Item Receipt, vendor bill entry, vendor payment, and PO closure.
Start
need-to-buy signal (SO Path 3 dropship, stock replen, or special order)
End
PO Closed · events.po.closed fires
Bridge
PO Path 1 (SO-connected) is the consuming side of SO Path 3
Linkage
PO.createdfrom = SO.id on Path 1

WO Work Order — the production side production REAL

Used when the company needs to assemble or build finished goods. Ends when Assembly Build is completed and finished goods are added back into inventory.
Start
need-to-build signal (SO Path 2 assembly, or stock build)
End
WO Closed · FG inventory credited · events.wo.closed fires
Bridge
WO Path 1 (from-SO) is the consuming side of SO Path 2
Linkage
WO.createdfrom = SO.id on Path 1 (customer PO# field TBD)

FIN Finance handoff — the universal convergence ★ convergence REAL (concept) NS workflow build pending R594

SO and PO each fire a Finance alert directly. WO is asymmetric: WO Path 1 (from-SO) fires the Finance handoff via its parent SO's Item Fulfillment; WO Path 2 (inventory build) has NO direct Finance handoff — Finance enters later when a future SO consumes the FG via SO Path 1. Finance reconciles SO + PO + WO at monthly close. This asymmetry is intentional and reflects accounting reality: standalone WOs produce inventory (a balance-sheet move), not revenue (a P&L move).
SO trigger
itemfulfillment.status → 'Shipped'events.so.finance_alert_fired
PO trigger
itemreceipt posted → events.po.finance_alert_fired
WO Path 1 trigger
via parent SO's IF: SO → IF → events.so.finance_alert_fired (WO itself doesn't fire; its parent SO does)
WO Path 2 trigger
none from the WO itself. Standalone build credits FG inventory; Finance handoff fires when a future SO consumes the FG (via SO Path 1). Variance audit may fire if actual_build != estimated > 5%.
Reconcile
CustInvc / VendBill / COGS variance — monthly close

Cross-record linkage — the bridge table

Cross-record linkageFieldCarries
SO → WO (Path 2)WO.createdfrom = SO.idpreserves the SO ↔ WO link
SO → PO (Path 3)PO.createdfrom = SO.idpreserves the SO ↔ PO link
SO → Item FulfillmentIF.createdfrom = SO.idinherits otherrefnum (customer PO#)
PO → Item ReceiptIR.createdfrom = PO.idinherits memo (customer PO# threads on Path 1)
PO → Vendor BillVendBill.createdfrom = PO.id · VendBill.lineFields.links[].tranid = PO.tranidinherits memo, tranid, initialtranid
WO → Assembly BuildAsmBuild.createdfrom = WO.idfinished-good production posting linked to its work order; carries item, quantity, custbody_waste (TBD), location
WO → Work Order IssueWkOrdIss.createdfrom = WO.idper-BOM-component issue rows; one per component decrement on the build; supports reversal if WO cancelled mid-build
SO → Customer InvoiceCustInvc.createdfrom = SO.idinherits otherrefnum (customer PO# thread to invoice)
SO → Customer PaymentCustPymt applies against CustInvc via NS standard apply tablecloses the SO money loop
Customer PO# thread (universal)otherrefnum (SO, IF, CustInvc) ↔ memo (PO Path 1, VendBill) ↔ memo (WO — TBD pending Mike)universal trace thread "72622" end-to-end (BOL.shipper_ref also carries on freight side)

Open gaps — honest punch list

Related docs