Wiki · Purchase order · PATH 1 · R593

Path 1 · PO connected to a Sales Order

For drop ship vendor orders or customer-specific fulfillment. PO is created FROM the SOpurchaseorder.createdfrom = so.id — preserving the SO ↔ PO link and threading the customer PO# via memo and VendBill.memo / tranid / initialtranid.

PATH 1 REAL ★ Finance alert pending NS automation
What this is

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.

When to use it

Trigger conditions

★ The handoff

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.

Worked example

Driscoll SO 1217 (PO# 72622) → Cardinal Foods PO-7833

Scenario

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.

Step-by-step what happens

PO create → ship → receipt → Finance · vendor bill cycle

  1. 01

    Classify SO line as dropship

    items.dropship = true OR items.special_order = true; trigger PO creation FROM the SO.

  2. 02

    Create PO FROM SO

    NS PurchOrd: createdfrom = so.id, memo = customer_po_number, lineFields.links[].tranid = "<so.tranid> / <customer_po>", entity = vendor_id.

    PO# threads SO.otherrefnum → PO.memo
  3. 03

    Transmit PO to vendor

    Manual today (email PDF). EDI 850 outbound is STUB.

  4. 04

    Vendor confirms

    Order accepted, pricing locked, expected ship/delivery date captured. purchase_orders.vendor_confirmed = 1.

  5. 05

    Vendor ships

    Direct to customer (true drop ship) OR to GFS crossdock. Tracking + ETA captured.

  6. 06

    Item Receipt (when applicable)

    NS ItemRcpt: createdfrom = po.id, status = "Received". PO line closes. Direct dropship may skip warehouse entirely (no Item Receipt).

  7. 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.

  8. 08

    ★ Finance alert (STUB)

    Fires events.po.finance_alert_fired.

  9. 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.

    PO# threads memo + tranid + initialtranid all carry "72622"
  10. 10

    Vendor Bill paid per vendor terms

    NS VendPymt: appliedto = vendor_bill.id.

  11. 11

    Resolve outstanding issues · close PO + SO

    Shortages, damages, subs, pricing reconciled. PO → 'Closed'. SO closes once both sides done.

Outcomes

What's different after the cycle

PO
Closed
received + billed + paid
SO
Closed
both sides done
PO# trace
5 fields
on the vendor side
Inventory
Untouched
(crossdock transient)
Failure modes

What can go wrong

PO sent but vendor never shipped

PO transmitted, no Item Receipt after expected lead time. Customer-side SO blocked. Vendor outreach required with "<so.tranid> / <customer_po>" combo as reference.

PO.memo missing customer PO# thread

Customer PO# threading broken. Vendor can't reference the customer order. Patch PO.memo to SO.otherrefnum; resync to NS.

VendBill tranid/initialtranid don't match expected combo

Expected: "<so.tranid> / <customer_po>". If different, reconcile vs originating PO.

Vendor bill prematurely entered

Sign of receipt confusion. Pause and reconcile against PO state.

Related

Adjacent flows + diagrams

For developers

Code paths + invariants

ConcernWhere
Workflow contractpo_lifecycle_sales_order_connected_path (risk 3)
PO ↔ SO linkpurchase_orders.createdfrom = transactions.id
VendBill ↔ PO linkvendor_bills.createdfrom = purchase_orders.id
PO# thread on POPO.memo = SO.otherrefnum
PO# thread on PO linesPO.lineFields.links[].tranid = "<so.tranid> / <customer_po>"
PO# thread on VendBillmemo = tranid (parsed) = initialtranid (parsed) = customer PO#
EDI 850 outboundSTUB — manual email today
// Path 1 threading invariant — verify across customer + vendor side SELECT po.memo, so.otherrefnum, vb.memo, vb.tranid, vb.initialtranid FROM purchase_orders po JOIN transactions so ON po.createdfrom = so.id AND so.type = 'SalesOrd' LEFT JOIN vendor_bills vb ON vb.createdfrom = po.id WHERE so.id = ? // expected: po.memo = so.otherrefnum AND vb.memo = so.otherrefnum // AND vb.tranid LIKE '%' || so.otherrefnum AND vb.initialtranid LIKE '%' || so.otherrefnum
Changelog

Dated trail

DateRoundChangeTouched by
2026-05-26R593Built PO master + 2 path-details matching SO pattern.Mike + Claude
Schema · data contract

The machine-readable spec

workflow_type · po_lifecycle_sales_order_connected_path · risk_level · 3.

Customer PO# threading (Path 1)

NS recordFieldSample value
Sales Order (origin)bodyFields.otherrefnum"72622"
Purchase OrderbodyFields.memo"72622"
Purchase OrderlineFields.links[].tranid"1217 / 72622"
Purchase OrderbodyFields.createdfrom1217 (SO id)
Item Receiptinherits via createdfrom = po.idtraces memo via PO
Item FulfillmentbodyFields.otherrefnum (inherits from SO)"72622"
Vendor BillbodyFields.memo"72622"
Vendor BillbodyFields.tranid"1217 / 72622"
Vendor BillbodyFields.initialtranid"1217 / 72622"
Vendor BillbodyFields.createdfrompo.id (e.g. "PO-7833")

D1 tables touched

TableOperation
purchase_ordersINSERT (createdfrom, memo, entity)
purchase_order_linesINSERT (lineFields.links[].tranid)
item_receiptsINSERT (createdfrom = po.id) · may skip on direct dropship
item_fulfillmentsINSERT (on SO chain)
customer_invoicesINSERT on SO sidecar
customer_paymentsINSERT on SO sidecar
payment_applicationsINSERT on SO sidecar
vendor_billsINSERT (memo, tranid, initialtranid carry PO#)
vendor_paymentsINSERT
transactionsUPDATE (SO + PO + Invoice status changes)
eventsINSERT (po.created, po.transmitted, po.finance_alert_fired, vendor_bill.paid, po.closed)
Runbook · when it breaks

It broke at 2am

Scenario · PO sent but vendor didn't ship

Path 1 stalled.

  1. Check PO status: SELECT id, status, trandate, expected_delivery FROM purchase_orders WHERE createdfrom=?
  2. Vendor outreach: contact vendor with PO + customer PO# combo (e.g. "1217 / 72622").
  3. Check premature vendor bill: SELECT * FROM vendor_bills WHERE createdfrom=? — if present, pause + reconcile.
  4. 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.

  1. Verify: SELECT po.memo, so.otherrefnum FROM purchase_orders po JOIN transactions so ON po.createdfrom=so.id WHERE po.id=?
  2. Patch: UPDATE purchase_orders SET memo = ? WHERE id = ? with SO.otherrefnum value.
  3. NS push back: stage proposed_action to update NS PurchOrd.memo.

Scenario · VendBill tranid/initialtranid don't match expected combo

Vendor-side threading broken.

  1. Verify: SELECT memo, tranid, initialtranid FROM vendor_bills WHERE createdfrom=?
  2. Expected pattern: memo = customer_po_number, tranid = initialtranid = "<so.tranid> / <customer_po>".
  3. Patch all three fields: threading depends on all three carrying the same combo.

Logs to check

Backlog · open questions

What's not done