NetSuite purchase order — master

SO-trigger or purchasing need → PO entry → transmit → vendor confirm → branch into 2 path sub-contracts → receive → ★ Finance handoff → Vendor Bill → pay → close

High-altitude master view of the PO process — the vendor side of the transaction. POs originate in one of two ways: Path 1 connected to a Sales Order (drop ship vendor orders or customer-specific fulfillment), or Path 2 for inventory replenishment, assembly build components, packaging, or general purchasing. Master dispatches into one of two sub-contracts — Path 1 SO-connected (drop ship; carries customer PO# threading from the SO chain via PO.memo and VendBill.memo/tranid/initialtranid) or Path 2 inventory/build (no customer PO# threading; memo carries internal req# or vendor invoice#). After Item Receipt, the universal ★ automation alert fires to Finance — receiving happens at the dock but billing/payment is Finance-owned. Finance reviews PO + Item Receipt + supporting docs before processing the Vendor Bill. PO closes once items received + billed + paid + any shortages/damages/substitutions/pricing resolved. See path detail docs for field-level depth.

D1-mirrored from NS HITL outside guardrails Path 1 · SO-connected Path 2 · inventory / build EDI 850 outbound STUB

Pipeline — PO trigger → 2 fulfillment paths → finance convergence

idle
NS purchase order lifecycle — trigger (SO-connected OR purchasing need) → PO entry → transmit → vendor confirm → branch by origin into 2 paths (SO-connected dropship / inventory or build) → Item Receipt → Finance alert (key handoff) → vendor bill → pay → close 01 / Trigger origin · SO-connected OR purchasing need 02 / PO entry in NetSuite (capture memo / entity / lineFields) 03 / Transmit · vendor confirm (order / pricing / delivery date) 04 / Branch by origin · dispatch to sub-contract PATH 1 / SO-connected · see ns-purchase-order-path-1-sales-order-connected.html PATH 2 / Inventory or build · see ns-purchase-order-path-2-inventory-or-build.html 05 / Item Receipt (when applicable) · universal step 06 / ★ Automation alert · Finance handoff · STUB (pending NS automation) 07 / Finance review · Vendor Bill · pay per terms · close PO PATH 1 ORIGIN — SO triggers PO creation. Vendor ships to customer (drop ship) or to GFS crossdock. TRIGGER customer SO entered in NetSuite one or more lines: items.dropship = true OR items.special_order = true LINKAGE purchaseorder.createdfrom will be set to so.id THREADING SO.bodyFields.otherrefnum (customer PO#) carries to PO.bodyFields.memo STATUS: REAL PATH 1 origin · SO triggers PO SO has dropship_item / special_order line PO will carry SO.otherrefnum on memo DATABASE · SO-connected origin i PATH 2 ORIGIN — Purchasing need identified internally. TRIGGER inventory replenishment (reorder point hit) assembly build components needed (BOM consume upcoming) packaging supply general purchasing (services, supplies, freight) LINKAGE purchaseorder.createdfrom is NULL (no SO link) THREADING No customer PO# threading. memo holds internal req# or vendor invoice#. STATUS: REAL PATH 2 origin · purchasing need inventory · assembly components · packaging no SO link · no customer PO# thread CLOUD · internal-need origin i VENDOR SELECTION (universal). CHECKS vendor active (vendors.status = 'active') vendor terms known (Net 30 / Net 45 / Prepay / 2/10 Net 30) item is purchasable from this vendor SAMPLE Cardinal Foods #891 (drop ship) Bongards Creameries (USDA barrel cheddar) Echo Lake Foods (frozen breakfast) STATUS: REAL · operator-driven vendor selection (universal) vendor active · terms known · purchasable e.g. Cardinal #891 / Bongards / Echo Lake EXTERNAL · vendor record i ENTER PO IN NETSUITE — PO posted as system-of-record. ACTION create: NS Purchase Order (PurchOrd) capture: bodyFields.tranid (NS-assigned PO#, e.g. "PO-7833") capture: bodyFields.entity = vendor_id Path 1: bodyFields.memo = customer_po_number (e.g. "72622") Path 1: bodyFields.createdfrom = so.id Path 2: bodyFields.memo = internal req# / vendor invoice# (no customer PO#) mirror: D1 purchase_orders + purchase_order_lines on next hot-tier sync (2min) TABLES write: NS PurchOrd · D1 purchase_orders · purchase_order_lines STATUS: REAL enter PO in NetSuite tranid = "PO-7833" · entity = vendor_id Path 1: memo=customer_po · createdfrom=so.id Path 2: memo=internal req# / vendor invoice# BACKEND · NS + D1 mirror i TRANSMIT PO TO VENDOR. TODAY: manual (email PDF to vendor) FUTURE STUB: auto-transmit (EDI 850 outbound) TABLES write: events (po.transmitted) STATUS: REAL (manual) · STUB (auto) transmit PO to vendor manual: email PDF today STUB: EDI 850 outbound CLOUD · vendor channel i VENDOR CONFIRMS ORDER / PRICING / DELIVERY DATE. CAPTURED order accepted pricing locked (vendor cost per line) expected ship date · expected delivery date any substitutions / shortages flagged TABLES write: purchase_orders (vendor_confirmed=1, expected_delivery) STATUS: REAL · operator records vendor reply vendor confirms order accepted · pricing locked expected ship / delivery date subs / shortages flagged EXTERNAL · vendor side i CLASSIFY PO ORIGIN — dispatch into sub-contract. LOGIC if purchaseorder.createdfrom IS NOT NULL AND that record is type=SalesOrd → Path 1 else → Path 2 TABLES read: purchase_orders.createdfrom STATUS: REAL (operator/system-driven) classify PO origin createdfrom = SO → Path 1 no SO link → Path 2 SECURITY · dispatch · sub-contract i PATH 1 · SO-CONNECTED — Summary. See ns-purchase-order-path-1-sales-order-connected.html for field-level depth. WORKFLOW po_lifecycle_sales_order_connected_path (sub-contract dispatched from master) SUMMARY PO has createdfrom=so.id → vendor ships (direct or crossdock) → Item Receipt (when applicable) → SO fulfilled → Finance alert → invoice customer → VendBill carries thread → pay → close THREADING PO.bodyFields.memo = "72622" (customer PO#, from SO.otherrefnum) PO.lineFields.links[].tranid = "1217 / 72622" (SO + customer PO# combo) VendBill.memo = tranid = initialtranid carry customer PO# STATUS: REAL PATH 1 · PO connected to a Sales Order createdfrom = so.id · memo = customer PO# = "72622" drop ship to customer OR crossdock at GFS VendBill memo + tranid + initialtranid all carry "72622" closes when both customer side + vendor side complete PATH 1 · po_lifecycle_sales_order_connected_path BACKEND · see ns-purchase-order-path-1-sales-order-connected.html i PATH 2 · INVENTORY OR ASSEMBLY BUILD — Summary. See ns-purchase-order-path-2-inventory-or-build.html for field-level depth. WORKFLOW po_lifecycle_inventory_or_build_path (sub-contract dispatched from master) SUMMARY PO entered for replenishment / assembly components / packaging / general purchasing → vendor confirms → product arrives at facility → loading dock receives → Item Receipt → inventory added at correct location → Finance alert → Finance reviews → VendBill processed → paid → PO closed THREADING No customer PO# threading. memo carries internal req# or vendor invoice#. STATUS: REAL PATH 2 · PO for inventory or assembly build no SO link · memo = internal req# / vendor invoice# replenishment · assembly components · packaging dock receives → inventory_balance += qty at location closes when all items received + billed + paid PATH 2 · po_lifecycle_inventory_or_build_path DATABASE · see ns-purchase-order-path-2-inventory-or-build.html i ITEM RECEIPT — universal step (when applicable). Direct drop ship may skip warehouse on Path 1. NS RECORD: ItemRcpt FIELDS WRITTEN bodyFields.createdfrom = po.id (e.g. "PO-7833") bodyFields.status = "Received" bodyFields.received_at = datetime bodyFields.location = location_id (Path 2; Path 1 may be N/A for direct dropship) INVENTORY EFFECT Path 1: transient (crossdock case only) Path 2: inventory_balance.quantityavailable += received qty at location STATUS: REAL Item Receipt (universal when applicable) createdfrom = po.id · status = "Received" BACKEND · Path 1 may skip (direct dropship); Path 2 credits inventory i ★ AUTOMATION ALERT → FINANCE — universal handoff. TRIGGER Item Receipt completed (or fulfillment on Path 1 direct dropship) WHY Receiving happens at the dock, but billing/payment is Finance-owned. Alert tells Finance: review PO + Item Receipt + supporting docs before processing payment. ACTION fire: NS workflow / scheduled script alert to Finance fire: events row po.finance_alert_fired STATUS: STUB — platform-side event fires (REAL); NS workflow build pending (Mike to deploy) ★ automation alert → Finance STUB · REAL once NS automation deployed FINANCE · emits po.finance_alert_fired event i FINANCE REVIEW — Finance reviews PO + Item Receipt + supporting docs. CHECKS received qty matches expected vendor pricing matches PO packing slip / vendor invoice attached any shortages / damages / substitutions flagged TABLES read: purchase_orders · item_receipts · vendor terms STATUS: REAL · Finance-driven Finance review qty · pricing · docs · subs CLOUD · Finance role i VENDOR BILL ENTERED — Finance processes against the PO. NS RECORD: VendBill FIELDS WRITTEN bodyFields.createdfrom = po.id (e.g. "PO-7833") bodyFields.entity = vendor_id Path 1: bodyFields.memo = customer_po_number ("72622") Path 1: bodyFields.tranid = bodyFields.initialtranid = " / " ("1217 / 72622") Path 2: bodyFields.memo = internal req# / vendor invoice# (no customer PO#) STATUS: REAL Vendor Bill entered VendBill · createdfrom = po.id MESSAGEBUS · threading on Path 1 i VENDOR BILL PAID per vendor terms. NS RECORD: VendPymt FIELDS bodyFields.appliedto = vendor_bill.id (e.g. "VB-2244") bodyFields.amount = vendor cost TABLES write: vendor_payments · transactions (VendPymt) STATUS: REAL VendBill paid VendPymt · per terms DATABASE · VendPymt i RESOLVE SHORTAGES / DAMAGES / SUBS / PRICING — before close. CHECKS any short-ships → vendor credit / rebill damages → claim filed substitutions → operator approves pricing variance → reconcile vs PO rate TABLES write: proposed_actions (vendor_credit_draft, claim_open, etc.) STATUS: REAL · HITL for any non-clean close resolve issues shortages · damages · subs · pricing SECURITY · HITL i CLOSE PO — once items received + billed + paid + issues resolved. ACTION status: PO → 'Closed' fire: events.po.closed TABLES write: purchase_orders (status) · events STATUS: REAL close PO status = 'Closed' · events fire BACKEND · po.closed i PATH 1 SIDECAR — customer-side invoice + payment cycle runs in parallel. DETAIL Path 1 closes the SO once vendor side + customer side both complete. Customer invoice + dunning + payment apply happen on the SO chain, not the PO chain. SEE ns-sales-order-path-3-dropship.html for customer-side detail STATUS: REAL Path 1 sidecar · customer-side cycle SO invoice + dunning + payment in parallel DATABASE · cross-link to SO Path 3 i → Path 1 (sub-contract) → Path 2 (sub-contract) both paths converge → Item Receipt ★ automation alert LEGEND Path 1 SO-connected Path 2 inventory / build ★ Finance handoff HITL / issue resolve CUSTOMER PO# THREADING (PATH 1 only) · trace one customer PO across SO + PO + VendBill chain (sample: customer PO# 72622, NS SO# 1217) SO · otherrefnum "72622" / tranid "1217" PO · memo + lineFields.links[].tranid "72622" / "1217 / 72622" Item Receipt · createdfrom po.id → inherits memo VendBill · memo + tranid + initialtranid "72622" / "1217 / 72622" / "1217 / 72622" PATH 2 · no customer PO# threading memo = internal req# / vendor invoice#

Phase detail — Mike's actual 2-path process

01 Trigger origin — SO-connected OR purchasing need REAL

POs originate one of two ways. Path 1 is triggered by a Sales Order needing drop ship or customer-specific fulfillment. Path 2 is triggered by a purchasing need: inventory replenishment, assembly build components, packaging supply, or general purchasing.
Path 1 trigger
SO has items.dropship = true OR items.special_order = true
Path 2 trigger
inventory reorder point hit · assembly BOM upcoming · packaging restock · general purchasing
Vendor selection
vendors.status = 'active' · terms known · item purchasable

02 Enter PO in NetSuite REAL

PO posted as system-of-record. D1 mirrors on next 2-min hot-tier sync. Path 1 captures customer PO# threading on memo + createdfrom = so.id.
NS record
PurchOrd
Tables write
purchase_orders · purchase_order_lines · transactions (PurchOrd)
Path 1 fields
memo = customer_po_number · createdfrom = so.id · lineFields.links[].tranid = "<so.tranid> / <customer_po>"
Path 2 fields
memo = internal req# / vendor invoice# · createdfrom = NULL

03 Transmit PO · vendor confirms REAL (manual) EDI auto STUB

PO is transmitted to the vendor (today manual via email PDF; EDI 850 outbound is a STUB). Vendor confirms order acceptance, pricing, and expected ship/delivery date.
Channels
email PDF (today) · EDI 850 outbound (STUB)
Captured
order accepted · pricing locked · expected ship/delivery date · subs/shortages flagged

04 Classify origin · dispatch sub-contract REAL

Master inspects PO origin and dispatches into one of two path sub-contracts.
Path 1
createdfrom IS NOT NULL AND parent is a Sales Order → po_lifecycle_sales_order_connected_path
Path 2
no SO link → po_lifecycle_inventory_or_build_path

P1 Option 1 — PO connected to a Sales Order PATH 1 REAL

For drop ship vendor orders or customer-specific fulfillment. Vendor ships product (direct to customer or to GFS for crossdock). Item Receipt closes the PO line (may be skipped on direct drop ship). SO Item Fulfillment ships to customer. Carries customer PO# threading end-to-end.
NS records
PurchOrd · ItemRcpt · ItemShip · VendBill · VendPymt
Threading
PO.memo = SO.otherrefnum · VendBill.memo/tranid/initialtranid carry customer PO#
SO sidecar
customer-side invoice + dunning + payment apply runs in parallel on the SO chain

P2 Option 2 — PO for inventory or assembly build PATH 2 REAL

For inventory replenishment, assembly build components, packaging supply, or general purchasing. Product arrives at the facility, loading dock receives, Item Receipt completed, inventory added at the correct location. No customer-side cycle.
NS records
PurchOrd · ItemRcpt · VendBill · VendPymt
Inventory effect
inventory_balance.quantityavailable += received qty at location_id
Threading
none — no customer PO#. memo carries internal req# or vendor invoice#

05 Item Receipt (universal) REAL

Both paths converge here when receiving is applicable. Path 1 may skip on direct drop ship. Path 2 always lands here and credits inventory.
NS record
ItemRcpt (createdfrom = po.id)
Tables write
item_receipts · inventory_balance (Path 2)

06 ★ Automation alert → Finance REAL (concept) NS workflow build pending

Universal handoff. Receiving happens at the dock but billing/payment is Finance-owned. Alert tells Finance to review PO + Item Receipt + supporting docs before processing payment.
Trigger
Item Receipt completed (or fulfillment on Path 1 direct dropship)
Event
events.po.finance_alert_fired
STUB
NS workflow trigger config pending Mike's deploy

07 Finance review · Vendor Bill · pay REAL

Finance reviews PO + Item Receipt + supporting docs (packing slip, vendor invoice). Vendor Bill entered against the PO. Paid per vendor terms.
NS records
VendBill · VendPymt
Path 1 threading
memo = customer_po · tranid = initialtranid = "<so.tranid> / <customer_po>"
Path 2 fields
memo = internal req# / vendor invoice# (no customer PO#)

08 Resolve issues · close PO REAL

PO closes once items received + billed + paid + any shortages/damages/substitutions/pricing issues resolved.
NS status
PurchOrd'Closed'
Event
events.po.closed

Tables, endpoints, code paths

kindnamepurpose
D1 tablepurchase_ordersPO header mirror · createdfrom, memo, entity, status
D1 tablepurchase_order_linesPO line-level mirror · item, qty, rate, lineFields.links[].tranid
D1 tableitem_receiptsvendor receipt closes PO line · createdfrom = po.id
D1 tableitem_fulfillmentsPath 1 sidecar · ships SO line to customer
D1 tablevendor_billsVendBill mirror · Path 1 carries customer PO# on memo/tranid/initialtranid
D1 tablevendor_paymentsVendPymt mirror · appliedto = vendor_bill.id
D1 tableinventory_balancePath 2 credits qty at location_id on Item Receipt
D1 tabletransactionsPurchOrd / ItemRcpt / VendBill / VendPymt rows
D1 tableeventspo.transmitted · po.finance_alert_fired · po.closed
Workflowpurchase_order_lifecyclemaster dispatcher (risk 2)
Workflowpo_lifecycle_sales_order_connected_pathPath 1 sub-contract (risk 3)
Workflowpo_lifecycle_inventory_or_build_pathPath 2 sub-contract (risk 2)
NS recordPurchOrd · ItemRcpt · VendBill · VendPymtsystem-of-record across the 2 paths

Open gaps — honest punch list

  • EDI 850 outbound STUB: PO transmission is manual email PDF today. EDI 850 outbound would auto-transmit POs (+ threading) to EDI-enabled vendors.
  • Auto-classify origin STUB: Path classification is operator/system-driven today via createdfrom. Stable but worth surfacing on intake.
  • ★ Automation alert → Finance: Platform fires events.po.finance_alert_fired. NS workflow trigger config pending Mike's deploy.
  • Vendor-side EDI 855 (PO acknowledgement) and EDI 856 (ASN inbound) not wired — vendor confirmations / ETAs are captured manually.
  • Auto-match VendBill by tranid combo (Path 1) — when vendor invoice references the "<so.tranid> / <customer_po>" combo, auto-match against originating PO without manual keying.

Path detail docs