PO triggered by an SO · vendor side of drop ship
Path 1 is the PO process triggered by a Sales Order. For each dropship_line on the SO, a PO is created FROM the SO (purchaseorder.createdfrom = so.id), preserving the SO ↔ PO link end-to-end. The customer PO# threads via PO.bodyFields.memo = SO.otherrefnum (e.g. "72622") and PO.lineFields.links[].tranid = "<so.tranid> / <customer_po>" (e.g. "1217 / 72622").
Vendor ships product directly to the customer (true drop ship) or to a GFS facility for crossdock. Item Receipt closes the PO line when applicable; on direct drop ship that bypasses GFS entirely, Item Receipt may be skipped. After SO Item Fulfillment, the universal ★ automation alert fires to Finance. Vendor Bill carries the customer PO# on three fields: memo, tranid, and initialtranid. Paid per vendor terms. The customer-side cycle (invoice + dunning + payment apply) runs in parallel on the SO chain.
Diagram: ns-purchase-order-path-1-sales-order-connected.html.
Trigger conditions
- SO entered with one or more lines where
items.dropship = trueoritems.special_order = true. - Vendor is active (
vendors.status = 'active') and can fulfill the item. - PO master dispatches into
po_lifecycle_sales_order_connected_path.
After Item Receipt (or after SO Item Fulfillment on direct dropship), the automation alert fires to Finance. The customer-side invoice cycle runs on the SO chain in parallel to the vendor-side bill cycle on the PO chain.
Driscoll SO 1217 (PO# 72622) → Cardinal Foods PO-7833
Driscoll's SO 1217 (otherrefnum = "72622") includes 3 dropship lines totaling $4,800 via Cardinal Foods vendor #891.
PO-7833 created FROM SO 1217: createdfrom = 1217, bodyFields.memo = "72622", lineFields.links[].tranid = "1217 / 72622", entity = 891. Manually transmitted to Cardinal Foods (email PDF; EDI 850 outbound is STUB).
Cardinal confirms order, locks pricing, commits to ship day 3. Cardinal ships direct to Driscoll. Item Receipt posted day 5: createdfrom = "PO-7833", status = "Received". PO line closes.
SO Item Fulfillment posted (on the SO chain): createdfrom = 1217, otherrefnum = "72622". ★ po.finance_alert_fired event fires.
Customer side (parallel, SO chain): CustInvc with otherrefnum = "72622", $4,800, Net 30. Driscoll pays day 24.
Vendor side (PO chain): Cardinal sends invoice; Finance enters VendBill memo = "72622", tranid = "1217 / 72622", initialtranid = "1217 / 72622", createdfrom = "PO-7833". Paid per vendor terms.
PO create → ship → receipt → Finance · vendor bill cycle
- 01
Classify SO line as dropship
items.dropship = trueORitems.special_order = true; trigger PO creation FROM the SO. - 02
Create PO FROM SO
NS
PurchOrd:createdfrom = so.id,memo = customer_po_number,lineFields.links[].tranid = "<so.tranid> / <customer_po>",entity = vendor_id. - 03
Transmit PO to vendor
Manual today (email PDF). EDI 850 outbound is STUB.
- 04
Vendor confirms
Order accepted, pricing locked, expected ship/delivery date captured.
purchase_orders.vendor_confirmed = 1. - 05
Vendor ships
Direct to customer (true drop ship) OR to GFS crossdock. Tracking + ETA captured.
- 06
Item Receipt (when applicable)
NS
ItemRcpt:createdfrom = po.id,status = "Received". PO line closes. Direct dropship may skip warehouse entirely (no Item Receipt). - 07
SO Item Fulfillment to customer
Runs on the SO chain. NS
ItemFulfill:createdfrom = so.id(the SO, not the PO),otherrefnum = "72622"inherits from SO. - 08
★ Finance alert (STUB)
Fires
events.po.finance_alert_fired. - 09
Vendor Bill entered (parallel to customer-side cycle)
NS
VendBill:memo = "72622",tranid = "1217 / 72622",initialtranid = "1217 / 72622",createdfrom = po.id,entity = vendor_id. - 10
Vendor Bill paid per vendor terms
NS
VendPymt:appliedto = vendor_bill.id. - 11
Resolve outstanding issues · close PO + SO
Shortages, damages, subs, pricing reconciled. PO →
'Closed'. SO closes once both sides done.
What's different after the cycle
What can go wrong
PO transmitted, no Item Receipt after expected lead time. Customer-side SO blocked. Vendor outreach required with "<so.tranid> / <customer_po>" combo as reference.
Customer PO# threading broken. Vendor can't reference the customer order. Patch PO.memo to SO.otherrefnum; resync to NS.
Expected: "<so.tranid> / <customer_po>". If different, reconcile vs originating PO.
Sign of receipt confusion. Pause and reconcile against PO state.
Adjacent flows + diagrams
Code paths + invariants
| Concern | Where |
|---|---|
| Workflow contract | po_lifecycle_sales_order_connected_path (risk 3) |
| PO ↔ SO link | purchase_orders.createdfrom = transactions.id |
| VendBill ↔ PO link | vendor_bills.createdfrom = purchase_orders.id |
| PO# thread on PO | PO.memo = SO.otherrefnum |
| PO# thread on PO lines | PO.lineFields.links[].tranid = "<so.tranid> / <customer_po>" |
| PO# thread on VendBill | memo = tranid (parsed) = initialtranid (parsed) = customer PO# |
| EDI 850 outbound | STUB — manual email today |
Dated trail
| Date | Round | Change | Touched by |
|---|---|---|---|
2026-05-26 | R593 | Built PO master + 2 path-details matching SO pattern. | Mike + Claude |
The machine-readable spec
workflow_type · po_lifecycle_sales_order_connected_path · risk_level · 3.
Customer PO# threading (Path 1)
| NS record | Field | Sample value |
|---|---|---|
| Sales Order (origin) | bodyFields.otherrefnum | "72622" |
| Purchase Order | bodyFields.memo | "72622" |
| Purchase Order | lineFields.links[].tranid | "1217 / 72622" |
| Purchase Order | bodyFields.createdfrom | 1217 (SO id) |
| Item Receipt | inherits via createdfrom = po.id | traces memo via PO |
| Item Fulfillment | bodyFields.otherrefnum (inherits from SO) | "72622" |
| Vendor Bill | bodyFields.memo | "72622" |
| Vendor Bill | bodyFields.tranid | "1217 / 72622" |
| Vendor Bill | bodyFields.initialtranid | "1217 / 72622" |
| Vendor Bill | bodyFields.createdfrom | po.id (e.g. "PO-7833") |
D1 tables touched
| Table | Operation |
|---|---|
purchase_orders | INSERT (createdfrom, memo, entity) |
purchase_order_lines | INSERT (lineFields.links[].tranid) |
item_receipts | INSERT (createdfrom = po.id) · may skip on direct dropship |
item_fulfillments | INSERT (on SO chain) |
customer_invoices | INSERT on SO sidecar |
customer_payments | INSERT on SO sidecar |
payment_applications | INSERT on SO sidecar |
vendor_bills | INSERT (memo, tranid, initialtranid carry PO#) |
vendor_payments | INSERT |
transactions | UPDATE (SO + PO + Invoice status changes) |
events | INSERT (po.created, po.transmitted, po.finance_alert_fired, vendor_bill.paid, po.closed) |
It broke at 2am
Scenario · PO sent but vendor didn't ship
Path 1 stalled.
- Check PO status:
SELECT id, status, trandate, expected_delivery FROM purchase_orders WHERE createdfrom=? - Vendor outreach: contact vendor with PO + customer PO# combo (e.g. "1217 / 72622").
- Check premature vendor bill:
SELECT * FROM vendor_bills WHERE createdfrom=?— if present, pause + reconcile. - Alternate source: cancel original PO, create new PO under alternate vendor, post Item Receipt against the new source.
Scenario · PO.memo missing customer PO# thread
Threading invariant broken on customer side.
- Verify:
SELECT po.memo, so.otherrefnum FROM purchase_orders po JOIN transactions so ON po.createdfrom=so.id WHERE po.id=? - Patch:
UPDATE purchase_orders SET memo = ? WHERE id = ?with SO.otherrefnum value. - NS push back: stage proposed_action to update NS PurchOrd.memo.
Scenario · VendBill tranid/initialtranid don't match expected combo
Vendor-side threading broken.
- Verify:
SELECT memo, tranid, initialtranid FROM vendor_bills WHERE createdfrom=? - Expected pattern:
memo = customer_po_number,tranid = initialtranid = "<so.tranid> / <customer_po>". - Patch all three fields: threading depends on all three carrying the same combo.
Logs to check
workflow_run_logevents· po.created + po.transmitted + po.finance_alert_fired + vendor_bill.paid + po.closedproposed_actions· HITL for PO/VendBill creation + issue resolves
What's not done
-
STUB
Finance automation alert — NS workflow build
Platform fires
events.po.finance_alert_fired. NS workflow build pending Mike's deploy. -
STUB
EDI 850 outbound PO transmission
Today: manual email of PO PDF. EDI 850 outbound would auto-transmit the PO + threading to EDI-enabled vendors.
-
OPEN
Auto-match VendBill on receipt by tranid combo
When vendor invoice arrives referencing the "
<so.tranid> / <customer_po>" combo, auto-match against the originating PO without manual keying. -
DECISION
Direct dropship vs crossdock policy
Today operator decides per vendor / customer. Confirm with Mike whether to surface the decision earlier in the PO creation flow.