2 paths · universal Finance handoff · PO# threading on Path 1
The Purchase Order is the vendor side of every GFS transaction. POs are used whenever the company buys product, ingredients, packaging, components, finished goods, or drop-ships from a partner vendor. Mike's actual process resolves to two categories:
- Path 1 — PO connected to a Sales Order: for drop ship vendor orders or customer-specific fulfillment. PO is created from / connected to the SO (preserves SO ↔ PO link), customer PO# threads via
PO.memoandVendBill.memo / tranid / initialtranid. - Path 2 — PO for inventory or assembly build needs: for replenishment, production support, packaging supply, or general purchasing. No SO link, no customer PO# threading —
memocarries internal req# or vendor invoice#.
The universal step is the ★ automation alert → Finance after Item Receipt. Receiving happens at the dock but billing is Finance-owned. Finance reviews PO + Item Receipt + supporting docs (packing slip, vendor invoice) before processing the Vendor Bill and paying per vendor terms.
Diagram: ns-purchase-order-master.html.
Trigger conditions
- A Sales Order is entered with a line marked
dropshiporspecial_order→ Path 1. - Inventory at a location drops below reorder point → Path 2 (replenishment).
- Upcoming assembly build run needs BOM components not on hand → Path 2 (build).
- Packaging supply restock, MRO, freight, or general purchasing → Path 2.
Every PO — on either path — fires the Finance alert after Item Receipt. Same event (events.po.finance_alert_fired). Same review/bill/pay/close pattern. Only the threading on the memo + sidecar customer cycle differ.
Two POs in one week
Driscoll's SO includes 3 dropship lines via Cardinal Foods (vendor #891). PO-7833 created FROM SO 1217: createdfrom = 1217, memo = "72622", lineFields.links[].tranid = "1217 / 72622". Cardinal ships direct. Item Receipt closes PO line. ★ Finance alert fires. VendBill carries memo = "72622", tranid = "1217 / 72622", initialtranid = "1217 / 72622". Paid per Net 30. Customer-side cycle runs on the SO chain in parallel.
NJ Heartland inventory of Bongards barrel cheddar drops below 500 cases. PO-7910 entered: entity = 412 (Bongards Creameries), memo = "REQ-04472" (internal req#), createdfrom = NULL, location = NJ Heartland. Truck arrives day 10. Dock unloads, counts, condition-checks. Item Receipt completed. inventory_balance.quantityavailable += received qty at NJ Heartland. ★ Finance alert fires. VendBill entered with memo = "REQ-04472" (no customer PO# thread). Paid per Net 30.
Trigger → PO entry → transmit → classify → dispatch
- 01
Trigger origin identified
Either an SO needs drop ship (Path 1) OR an internal purchasing need is identified (Path 2).
- 02
Enter PO in NetSuite
NS
PurchOrdwithtranid,entity,memo, lines. Path 1 setsmemo = customer_po_numberandcreatedfrom = so.id; Path 2 leavescreatedfrom = NULLandmemo = internal req# / vendor invoice#. - 03
Transmit PO to vendor
Manual email PDF today; STUB EDI 850 outbound.
- 04
Vendor confirms order, pricing, delivery date
Captured: order accepted, vendor cost locked, expected ship / delivery date, any subs / shortages flagged.
- 05
Classify origin · dispatch sub-contract
If
createdfrompoints at an SO → dispatchpo_lifecycle_sales_order_connected_path. Else dispatchpo_lifecycle_inventory_or_build_path. - 06
Sub-contract executes path-specific steps
See path detail docs for field-level depth.
- 07
Item Receipt (universal when applicable)
Both paths converge here. Path 1 may skip on direct dropship; Path 2 always credits inventory at
location_id. - 08
★ Automation alert → Finance
Universal handoff. Fires
events.po.finance_alert_fired. STUB until NS workflow build. - 09
Finance review · Vendor Bill · pay per terms
Finance reviews PO + Item Receipt + supporting docs. VendBill entered. Paid per vendor terms.
- 10
Resolve issues · close PO
Shortages, damages, substitutions, pricing reconciled. PO →
'Closed'.events.po.closedfires.
What's different after the cycle
What can go wrong
Vendor outreach required; if no reply after window, cancel + alt-source.
PO stalled. Vendor outreach with PO# (Path 1: include "<so.tranid> / <customer_po>" combo); investigate freight delay.
Short ship or over-ship. Flag for Finance reconcile before VendBill processing.
Vendor cannot reference the customer order; downstream invoice reconciliation breaks. Patch purchase_orders.memo ← SO.otherrefnum; resync to NS.
Misclassified — should be Path 1. Re-dispatch under the correct sub-contract.
Adjacent flows + diagrams
Code paths + invariants
| Concern | Where |
|---|---|
| Master contract | purchase_order_lifecycle (risk 2) |
| Path 1 contract | po_lifecycle_sales_order_connected_path (risk 3) |
| Path 2 contract | po_lifecycle_inventory_or_build_path (risk 2) |
| SO ↔ PO link (Path 1) | purchase_orders.createdfrom = transactions.id (type=SalesOrd) |
| VendBill ↔ PO link | vendor_bills.createdfrom = purchase_orders.id |
| Path 1 threading invariant | PO.memo = SO.otherrefnum AND VendBill.memo = PO.memo |
| Path 2 no-SO-link invariant | purchase_orders.createdfrom IS NULL or parent ≠ SalesOrd |
| Finance alert event | events.po.finance_alert_fired (STUB) |
| 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 · purchase_order_lifecycle · risk_level · 2.
Customer PO# threading (Path 1 only)
| NS record | Field | Sample value |
|---|---|---|
| Sales Order (origin) | bodyFields.otherrefnum | "72622" |
| Purchase Order | bodyFields.memo | "72622" |
| Purchase Order | lineFields.links[].tranid | "1217 / 72622" |
| Item Receipt | inherits via NS std linkage from PO | traces via createdfrom = po.id |
| Vendor Bill | bodyFields.memo | "72622" |
| Vendor Bill | bodyFields.tranid | "1217 / 72622" |
| Vendor Bill | bodyFields.initialtranid | "1217 / 72622" |
memo on PurchOrd + VendBill carries an internal req# or vendor invoice# instead. The threading invariant only applies on Path 1.D1 tables touched (master + both paths)
| Table | Operation |
|---|---|
purchase_orders | INSERT (header) |
purchase_order_lines | INSERT (per line) |
item_receipts | INSERT (createdfrom = po.id) |
inventory_balance | UPDATE (Path 2 credits qty at location) |
vendor_bills | INSERT (Path 1 carries thread on memo/tranid/initialtranid) |
vendor_payments | INSERT |
transactions | UPDATE (status changes) |
events | INSERT (po.created · po.transmitted · po.finance_alert_fired · po.closed) |
proposed_actions | INSERT (shortages, damages, subs HITL) |
It broke at 2am
Scenario · Vendor never confirmed PO
Transmitted but no acknowledgement.
- Check PO state:
SELECT id, tranid, status, vendor_confirmed, expected_delivery FROM purchase_orders WHERE id=? - Reach out to vendor: use PO tranid; Path 1 include "
<so.tranid> / <customer_po>" combo. - If no reply window exceeded: cancel PO; identify alt source; create replacement PO.
Scenario · Item Receipt missing after expected delivery
PO stalled.
- Check item receipts:
SELECT COUNT(*) FROM item_receipts WHERE po_id=? - Confirm physical arrival: dock team to verify nothing arrived without paperwork.
- Vendor outreach with tracking + ETA.
- Update purchase_orders.expected_delivery if vendor confirms new ETA; re-fire Finance alert event if needed.
Scenario · Misclassified Path 2 PO has SO link
Path 2 invariant violation.
- Verify:
SELECT p.id, p.createdfrom, t.type FROM purchase_orders p LEFT JOIN transactions t ON p.createdfrom=t.id WHERE p.id=? - If
t.type = 'SalesOrd': this should be Path 1; re-dispatchpo_lifecycle_sales_order_connected_path; backfill threading onmemofromso.otherrefnum.
Logs to check
workflow_run_logevents· po.created + po.transmitted + po.finance_alert_fired + po.closedproposed_actions· HITL for VendBill / shortages / claims
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 POs (+ threading on Path 1) to EDI-enabled vendors.
-
OPEN
EDI 855 inbound (PO acknowledgement) + EDI 856 ASN
Vendor confirmations and ASN inbound are manual today. EDI 855/856 would auto-set
vendor_confirmedandexpected_delivery. -
OPEN
Auto-match Path 1 VendBill by tranid combo
When vendor invoice arrives referencing "
<so.tranid> / <customer_po>", auto-match against originating PO without manual keying. -
DECISION
Auto-classify on intake vs operator-driven
Classification today is system-driven on
createdfrom. Confirm with Mike whether to surface the classification on PO creation UI as a sanity check.