NetSuite logistics review flow workflow contract R595

workflow_type: logistics_review_workflow · owner: logistics team · risk 2 · trigger: item lands on logistics queue from freight_routing_check

The operational workflow for the logistics team. When freight_routing_check classifies a SO or PO as freight-relevant (LTL / FTL / parcel-with-special-handling / dropship vendor freight / freight-collect), the record lands on the logistics queue with freight_status = 'pending_logistics_review'. This workflow is what happens next: team reviews, selects carrier, schedules pickup, generates BOL, captures tracking, notifies the SO/PO owner. Verify checks at +24h post-pickup (tracking active) and +7d post-pickup (delivery confirmed) close the loop. Customer PO# threads end-to-end: SO.otherrefnum = "72622"BOL.shipper_reflogistics_shipments.customer_po_numberInvoice.otherrefnum.

downstream of freight check D1-canonical · NS push via NS_PUSH_QUEUE 4 HITL gates 2 verify_checks (+24h, +7d) custbody_* TBD pending Mike

Customer PO# threading · sample SO #1217 / customer PO 72622

SO.otherrefnum
72622
freight_status_mirror
customer_po_number = 72622
proposed_actions
payload.customer_po = 72622
BOL.shipper_ref
72622
logistics_shipments
customer_po_number = 72622
Invoice.otherrefnum
72622

One field threads the entire freight chain end-to-end. If a customer calls about "PO 72622", every record on the route is reachable from that one value.

Pipeline — 7-stage logistics review flow (animated)

idle
layers
WHAT THIS DOES: This is the ns logistics review flow — inbound from freight_routing_check → review → carrier select (hitl) → schedule pickup (hitl) → generate bol (hitl) → capture tracking + notify owner → verify tracking +24h → verify delivery +7d → pod captured → event ledger handoff to finance + sales · plus 5 issue escalation branches 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: NS logistics review flow — inbound from freight_routing_check → review → carrier select (HITL) → schedule pickup (HITL) → generate BOL (HITL) → capture tracking + notify owner → verify tracking +24h → verify delivery +7d → POD captured → event ledger handoff to finance + sales · plus 5 issue escalation branches 00 / Inbound from freight_routing_check 01 / Logistics team review · queue surface 02 / Select carrier (HITL) · 03 / Schedule pickup (HITL) 04 / Generate BOL (HITL) · 05 / Capture tracking + notify owner 06 / Verify tracking active (+24h) · 07 / Verify delivery confirmed (+7d) 08 / POD captured + reflexion + event handoff to finance + sales ESCALATION / Issue branches off the main flow (5 issue types) WHAT THIS DOES: This is the inbound from freight_routing_check — workflow handoff. 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: INBOUND FROM freight_routing_check — workflow handoff. TRIGGER freight.routed_to_logistics event fired by ns-freight-routing-check SOURCE RECORD source_record_type: SalesOrd OR PurchOrd source_record_id: e.g. 1217 (SO) or 8843 (PO) PAYLOAD classification (LTL / FTL / parcel / dropship_vendor_freight / customer_pickup / freight_collect) special_handling_json { refrigerated, frozen, hazmat, oversize } estimated_cost (number — finalized at carrier select) target_ship_date, origin_zip, destination_zip customer_po_number (threads from SO.otherrefnum or PO.memo) — e.g. "72622" WORKFLOW STATE freight_status_mirror.freight_status = 'pending_logistics_review' STATUS: REAL · queue surfaces at /logistics-review.html (build pending) inbound from freight_routing_check event: freight.routed_to_logistics payload: classification + special_handling + estimated_cost customer_po_number = "72622" threads in freight_status_mirror = 'pending_logistics_review' MESSAGEBUS · workflow handoff i WHAT THIS DOES: This is the queue surface — what logistics opens each morning. 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: QUEUE SURFACE — what logistics opens each morning. URL: /logistics-review.html (TBD — pending build · SOT fields below) VIEW SELECT * FROM freight_status_mirror WHERE freight_status IN ('pending_logistics_review','awaiting_pickup','in_transit') ORDER BY target_ship_date ASC DASH CARDS pending_review · awaiting_pickup · in_transit · by_carrier · issues_open STATUS: REAL (D1 query) · STUB (UI build) queue surface · /logistics-review.html pending_review · awaiting_pickup · in_transit by_carrier · issues_open ordered by target_ship_date ASC D1 query REAL · UI build STUB CLOUD · pages function i WHAT THIS DOES: This is the source record joins — pull customer/vendor + items + addresses. 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: SOURCE RECORD JOINS — pull customer/vendor + items + addresses. SQL SELECT t.id, t.tranid, t.otherrefnum, t.memo, t.shipmethod, t.shipcarrier, t.shipdate, t.shipaddress, c.companyname, c.id AS customer_id FROM transactions t LEFT JOIN customers c ON c.id = t.entity WHERE t.id = ? ALSO transaction_lines (item + qty + weight) items (refrigerated · frozen · hazmat · oversize re-confirm) PURPOSE Build the review surface with full context. STATUS: REAL source record joins transactions + customers/vendors transaction_lines + items otherrefnum = "72622" carried through special_handling re-confirmed item-level DATABASE · context build i WHAT THIS DOES: This is the stage 01 — logistics team review. 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: STAGE 01 — LOGISTICS TEAM REVIEW. ACTION Team opens the entry from queue. Reviews: - source record (SO #1217 / PO #8843) - classification carried from check (e.g. LTL with refrigerated) - customer/vendor address - items, weight, volume, special handling flags - target ship date DECISION confirm + advance to stage 02 (Select carrier) OR escalate: re-classify (back to freight check), reject, hold HITL: implicit — team-driven, not stage_proposed_action yet SAMPLE SO #1217 · customer "ACME Catering" · PO 72622 Class LTL · refrigerated true · weight 1,840 lbs · ship_date 2026-06-02 STATUS: REAL (operator) 01 · logistics team review confirm classification + special handling review address · items · weight · ship_date SO #1217 · ACME Catering · PO 72622 · LTL refrigerated advance · escalate · re-classify · hold CLOUD · team-driven review i WHAT THIS DOES: This is the lane history lookup — drives carrier suggestion. 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: LANE HISTORY LOOKUP — drives carrier suggestion. SQL SELECT carrier, AVG(cost) AS avg_cost, COUNT(*) AS shipments, AVG(transit_days) AS avg_transit, SUM(on_time) * 1.0 / COUNT(*) AS on_time_pct FROM logistics_shipments WHERE lane_origin = ? AND lane_dest = ? AND ship_date > date('now', '-90 days') GROUP BY carrier ORDER BY on_time_pct DESC, avg_cost ASC EFFECT - prior preferred carriers surface first - on-time + cost-effective carriers ranked higher STATUS: REAL · logistics_shipments + freight_lane_history lane history lookup logistics_shipments · 90-day window carrier · avg_cost · on_time_pct · avg_transit ranks by on_time DESC, cost ASC feeds Stage 02 carrier suggestion DATABASE · lane intelligence i WHAT THIS DOES: This is the carrier qualifier filter — narrows shortlist by special handling. 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: CARRIER QUALIFIER FILTER — narrows shortlist by special handling. LOGIC IF refrigerated OR frozen → only reefer-qualified carriers IF hazmat → only carriers with HazMat certification IF oversize → only FTL or specialized freight carriers TABLES carriers WHERE qualifications & required_quals = required_quals JOIN prior lane history results filtered by qualifier set OUTPUT qualified_carriers[] ranked STATUS: REAL carrier qualifier filter reefer · hazmat cert · oversize carriers WHERE quals satisfy required_quals joins lane history → qualified ranked list e.g. R+L (LTL reefer) · XPO · Saia DATABASE · shortlist build i WHAT THIS DOES: This is the stage 02 — select carrier (hitl). 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: STAGE 02 — SELECT CARRIER (HITL). ACTION stage_proposed_action(action_type='logistics_carrier_select', risk_level=2) PAYLOAD source_record_type, source_record_id, customer_po_number = "72622" proposed_carrier (top of qualified_carriers list) proposed_cost (lane avg + qualifier adjustments) proposed_transit_days ALT CARRIERS list[3-5] alternatives with cost + transit + on_time HITL GATE /logistics-review.html → proposed_actions → Mike (or logistics lead) approves ADR-031 invariant: every write-back to NS/business state HITL-required WRITES (on approval) freight_status_mirror.carrier_selected = ? freight_status_mirror.freight_status = 'awaiting_pickup' STATUS: REAL 02 · select carrier (HITL) stage_proposed_action(action_type='logistics_carrier_select') proposed_carrier + cost + transit · alt list[3-5] HITL: Mike / logistics lead approves at /logistics-review.html on approval → freight_status = 'awaiting_pickup' DECISION · HITL gate · ADR-031 i WHAT THIS DOES: This is the stage 03 — schedule pickup (hitl). 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: STAGE 03 — SCHEDULE PICKUP (HITL). PRECONDITIONS - carrier_selected (Stage 02 approved) - freight is ready: Item Fulfillment created (SO) or Item Receipt received (PO) - BOL ready for Stage 04 ACTION stage_proposed_action(action_type='logistics_pickup_schedule', risk_level=2) NS WRITE-BACK (on HITL approval — via NS_PUSH_QUEUE) UPDATE salesorder SET shipdate = ?, custbody_pickup_scheduled = ? WHERE id = ? ALSO pickup_window (e.g. 2026-06-02 08:00–12:00) pickup_location_id, dock_door, contact phone STATUS: REAL (D1) · STUB (NS push pending custbody_pickup_scheduled confirm) 03 · schedule pickup (HITL) preconditions: carrier approved + freight ready + BOL ready stage_proposed_action(logistics_pickup_schedule) NS write-back via NS_PUSH_QUEUE: shipdate + custbody_pickup_scheduled pickup_window · dock_door · contact phone DECISION · HITL gate · NS push pending i WHAT THIS DOES: This is the stage 04 — generate bol (hitl). 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: STAGE 04 — GENERATE BOL (HITL). TEMPLATE GFS standard BOL — PDF, 2 copies for carrier + 1 for file HEADERS shipper = GFS (Heartland or origin location) consignee = customer ship_address (or vendor on PO) shipper_ref = SO.bodyFields.otherrefnum = "72622" (customer PO# threading) consignee_ref = customer's reference if provided LINES per-line items + NMFC freight class codes (LTL only) total weight · total piece count · pallet count SPECIAL HANDLING refrigerated → temp-range printed on BOL hazmat → SECOND PDF: Shipper's Declaration of Dangerous Goods (49 CFR) HITL stage_proposed_action(action_type='logistics_bol_generate', risk_level=2) Mike approves before BOL is final and printed STORE R2: logistics-bol/{shipment_id}/{date}.pdf · SuiteAttach to NS STATUS: REAL (PDF gen) · HITL gate ADR-031 04 · generate BOL (HITL) GFS standard template · PDF · 2 copies for carrier BOL.shipper_ref = SO.otherrefnum = "72622" (PO# threading) hazmat → second PDF: 49 CFR DG declaration R2: logistics-bol/{id}/{date}.pdf · SuiteAttach to NS DECISION · HITL gate · 49 CFR if hazmat i WHAT THIS DOES: This is the stage 05 — capture tracking + notify owner (hitl on email send). 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: STAGE 05 — CAPTURE TRACKING + NOTIFY OWNER (HITL on email send). ON CARRIER PICKUP Capture tracking number from carrier (call / portal / API if integrated) Update freight_status = 'shipped' · ship_actual_datetime = ? NS WRITE-BACK (via NS_PUSH_QUEUE) UPDATE salesorder SET custbody_tracking_number = ?, custbody_carrier = ? WHERE id = ? OWNER EMAIL DRAFT subject: "Shipped — SO#{tranid} — PO {customer_po} — tracking {tracking}" to: SO owner (sales) or PO owner (purchasing) · HITL approve send CUSTOMER EMAIL DRAFT (SO + standard freight terms) subject: "Your GFS order has shipped — PO {customer_po}" body: tracking link, ETA, carrier name, BOL pdf attach SAMPLE PRO# 12345678 · carrier "R+L Carriers" · ETA 2026-06-04 STATUS: REAL (capture · D1) · STUB (carrier API integration) 05 · capture tracking + notify owner capture tracking# on pickup · freight_status = 'shipped' NS push: custbody_tracking_number + custbody_carrier owner email draft (HITL send) · sales / purchasing customer email draft (SO) · tracking link + ETA + BOL pdf DECISION · HITL on email send · carrier API STUB i WHAT THIS DOES: This is the stage 06 — verify tracking active (+24h). 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: STAGE 06 — VERIFY TRACKING ACTIVE (+24h). SCHEDULER Cron / Durable Object scheduled +24h after pickup_actual_datetime CHECK If carrier API integrated: GET /carrier-api/tracking/{number} → expect status IN ('picked_up','in_transit','out_for_delivery') ELSE manual confirm by logistics WRITE freight_status_mirror.verify_24h_status = 'pass' | 'fail' ISSUE PATH status = 'unknown' OR 'not_found' after +24h → stage_proposed_action(action_type='logistics_issue', issue_type='pickup_missed') → branches to issue escalation lane below STATUS: REAL (manual fallback) · STUB (carrier APIs) 06 · verify tracking active (+24h) scheduled +24h after pickup_actual_datetime carrier API: status IN (picked_up, in_transit, out_for_delivery) fallback: manual confirm by logistics fail → issue_type='pickup_missed' branches BACKEND · verify_check +24h · issue branch i WHAT THIS DOES: This is the stage 07 — verify delivery confirmed (+7d). 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: STAGE 07 — VERIFY DELIVERY CONFIRMED (+7d). SCHEDULER Cron / Durable Object scheduled +7d after pickup_actual_datetime CHECK If carrier API: GET /carrier-api/tracking/{number} → expect status='delivered' ELSE poll for POD upload from carrier portal / EDI 214 POD CAPTURE PDF or image stored at R2: logistics-pod/{shipment_id}/{date}.pdf Linked from NS via SuiteAttach WRITES freight_status_mirror.freight_status = 'delivered' freight_status_mirror.delivery_date = ? freight_status_mirror.pod_url = ? NS WRITE-BACK (via NS_PUSH_QUEUE) custbody_delivery_date · custbody_pod_url ISSUE PATH delivery NOT confirmed by +7d → issue_type='delay_in_transit' or 'lost_shipment' STATUS: REAL · POD via R2 + SuiteAttach 07 · verify delivery confirmed (+7d) scheduled +7d after pickup · POD via carrier API or portal R2: logistics-pod/{id}/{date}.pdf · SuiteAttach to NS freight_status = 'delivered' · NS push custbody_delivery_date fail → issue_type='delay_in_transit' or 'lost_shipment' BACKEND · verify_check +7d · POD captured i WHAT THIS DOES: This is the pod captured + reflexion — workflow 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: POD CAPTURED + REFLEXION — workflow close. POD STORE R2 bucket: logistics-pod/{shipment_id}/{ISO date}.pdf SuiteAttach RESTlet links it back to NS record (SO or PO) REFLEXION Did the carrier choice work? reflexion_writer.write({ workflow_type: 'logistics_review_workflow', carrier, lane, transit_days_actual, on_time, damage, refused, cost_actual, customer_po_number }) LANE HISTORY UPDATE INSERT INTO freight_lane_history (carrier, lane, transit_days, on_time, cost, ...) Feeds next freight_routing_check's lane intelligence — closed loop. WRITES logistics_shipments row inserted (final scoreboard row) STATUS: REAL 08 · POD captured + reflexion R2: logistics-pod/{id}/{date}.pdf · SuiteAttach to NS reflexion: carrier worked? transit_actual? on_time? freight_lane_history updated · closed loop to next check BACKEND · POD + reflexion + lane history i WHAT THIS DOES: This is the ★ event handoff → finance + sales (workflow terminus). 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: ★ EVENT HANDOFF → finance + sales (workflow terminus). EVENT FIRED logistics.shipment_delivered PAYLOAD source_record_type, source_record_id, customer_po_number = "72622" carrier, delivered_at, pod_url, on_time, damage_flag SUBSCRIBERS finance_handoff_workflow (R594 planned) — triggers invoice sales_daily_summary — included in tomorrow's owner digest customer_followup_workflow — optional NPS / satisfaction (TBD) PO# THREADING (end-to-end) SO.otherrefnum → BOL.shipper_ref → logistics_shipments.customer_po_number → Invoice.otherrefnum → event_ledger.customer_po_number = "72622" STATUS: REAL · event ledger live ★ event handoff to finance + sales event: logistics.shipment_delivered (workflow terminus) subscribers: finance_handoff · sales_daily_summary PO# 72622 threads to Invoice.otherrefnum MESSAGEBUS · event ledger · workflow terminus i WHAT THIS DOES: This is the issue — pickup missed. 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: ISSUE — PICKUP MISSED. TRIGGER verify +24h fails OR carrier no-show reported ACTION Re-schedule pickup (loop back to Stage 03) Log lane reliability penalty (freight_lane_history.on_time -=) stage_proposed_action(action_type='logistics_issue', issue_type='pickup_missed') NOTIFY SO/PO owner + logistics lead SQL UPDATE freight_status_mirror SET freight_status='pickup_missed', issue_open=1 WHERE source_record_id=? STATUS: REAL issue · pickup missed verify +24h fails or carrier no-show → reschedule pickup + lane reliability penalty notify: SO owner + logistics ISSUE · pickup_missed i WHAT THIS DOES: This is the issue — delay in transit. 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: ISSUE — DELAY IN TRANSIT. TRIGGER tracking shows no movement > 48h OR carrier ETA slips beyond target ACTION Carrier call to confirm location / ETA Customer pre-notify (proactive) Update ETA in NS + customer email NOTIFY SO owner + customer SQL UPDATE freight_status_mirror SET freight_status='delay_in_transit', issue_open=1, eta_revised=? WHERE source_record_id=? STATUS: REAL issue · delay in transit no movement > 48h or ETA slips → carrier call + customer pre-notify notify: SO owner + customer ISSUE · delay_in_transit i WHAT THIS DOES: This is the issue — damage on delivery. 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: ISSUE — DAMAGE ON DELIVERY. TRIGGER POD notes damage OR customer reports damage within 48h of receipt ACTION Open carrier claim (claim# + photos) Document with photos to R2 (logistics-claims/{id}/photos) Replacement order if needed (new SO from sales) NOTIFY SO owner + customer service + carrier claims STATUS: REAL · claim portal STUB (manual today) issue · damage on delivery POD notes damage or customer reports <48h → open carrier claim photos to R2 + replacement notify: SO owner + CS + claims ISSUE · damage_on_delivery i WHAT THIS DOES: This is the issue — refused delivery. 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: ISSUE — REFUSED DELIVERY. TRIGGER carrier returns shipment to GFS INVESTIGATE Why refused? Reasons: wrong item · late · customer cancelled · damage on arrival quality issue · spec deviation · operator error ACTION Hold returned inventory at receiving location Escalate to sales lead (decide: retry, credit, restock) IF wrong item / sales operator error → loop back to SO source NOTIFY SO owner + customer + sales lead STATUS: REAL issue · refused delivery carrier returns shipment investigate reason → hold inventory + escalate to sales lead notify: SO owner + customer ISSUE · refused_delivery i WHAT THIS DOES: This is the issue — lost shipment. 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: ISSUE — LOST SHIPMENT. TRIGGER tracking stuck > 7 days OR carrier confirms loss ACTION Open formal loss claim with carrier (+ supporting docs) Ship replacement (new SO if customer needs it urgent) Recover from carrier per BOL terms + insurance NOTIFY Finance + sales + customer SQL UPDATE freight_status_mirror SET freight_status='lost_shipment', issue_open=1, claim_amount=? WHERE source_record_id=? STATUS: REAL · formal claim doc STUB issue · lost shipment tracking stuck > 7d or carrier confirms loss → formal claim filed + replacement SO + recover notify: Finance + sales + customer ISSUE · lost_shipment i step step step step step key flow key flow key flow step step step key flow key flow key flow step step step step step step LEGEND Logistics flow lanes verify_check lane HITL gate Issue escalation issue branch (dashed)
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:

What logistics sees on the dashboard (mock)

Mock of the queue surface the logistics team opens each morning. TBD URL: https://gfs-netsuite.pages.dev/logistics-review.html (pending build — SVG above is the SOT).

Pending review
14
+3 since yesterday
Awaiting pickup
8
2 overdue
In transit
22
5 delivering today
By carrier
LTL · 11
FTL · 4
Parcel · 28
past 7 days
Issues open
3
delay · damage · refused

Stage-by-stage workflow (7 stages)

01 Logistics team review team-driven

Team opens the queue, reviews each new entry: source record (SO/PO), classification from freight_routing_check, customer/vendor address, items, weight, volume, special handling flags. Confirm classification is correct (escalate back to check if not).

Source record
SO or PO · classification carried from check
NS fields read
bodyFields.shipmethod · bodyFields.shipcarrier · bodyFields.shipaddress · bodyFields.shipdate · lineFields.shippingcost
Custom fields
custbody_freight_status · custbody_freight_classification TBD pending Mike
Outcome
Either confirm + advance to stage 02, or escalate (re-classify, reject, or hold)

02 Select carrier HITL

Pick carrier based on classification + lane history. LTL → preferred LTL carrier per lane. FTL → broker quote. Parcel → UPS/FedEx. Customer pickup → mark as such. Dropship vendor freight → vendor selects, we record. Freight collect → customer's carrier.

Inputs
classification + ship_address + total_weight + total_volume + ship_date + lane history
HITL
Proposed carrier lands in proposed_actions. Mike (or logistics lead) approves. ADR-031 invariant.
Lane history lookup
SELECT carrier, AVG(cost), COUNT(*), AVG(transit_days) FROM logistics_shipments WHERE lane_origin=? AND lane_dest=? AND ship_date > date('now','-90 days')
Sample value
customer PO# 72622 carries into payload

03 Schedule pickup HITL

Once carrier is approved + freight is ready (inventory pulled, assembly built, or PO received), schedule the pickup. Write the pickup datetime back to NS as bodyFields.shipdate + custom custbody_pickup_scheduled.

Preconditions
Item Fulfillment created (for SO) or Item Receipt received (for PO) · carrier confirmed · BOL ready
NS write
UPDATE salesorder SET shipdate=?, custbody_pickup_scheduled=? WHERE id=? (through NS_PUSH_QUEUE, HITL-gated)
Pickup window
e.g. 2026-06-02 08:00–12:00 · dock door + contact phone captured

04 Generate BOL HITL

Generate Bill of Lading PDF using the standard GFS template. Includes shipper, consignee, items (with NMFC freight class codes for LTL), weight, special handling (refrigerated / frozen / hazmat). Customer PO# threads through — BOL header carries SO.otherrefnum for tracing.

Template
GFS standard BOL · ships with the load · PDF + 2 copies for carrier
PO# threading
BOL.shipper_ref = SO.bodyFields.otherrefnum · e.g. "72622"
Hazmat handling
If special_handling = 'hazmat', generate Shipper's Declaration of Dangerous Goods (49 CFR) as second PDF
Storage
R2 logistics-bol/{shipment_id}/{date}.pdf · linked from NS via SuiteAttach

05 Capture tracking + notify owner HITL on email send

When carrier picks up, capture tracking number + update freight_status = 'shipped'. Email goes out to SO owner (sales) or PO owner (purchasing) with tracking link. Customer gets shipment confirmation if SO + standard.

Tracking write
UPDATE salesorder SET custbody_tracking_number=?, custbody_carrier=?, freight_status='shipped' WHERE id=?
Owner email
subject: "Shipped — SO#{tranid} — PO {customer_po} — tracking {tracking}"
Customer email
subject: "Your GFS order has shipped — PO {customer_po}" with tracking link + ETA + BOL pdf attach
Sample
PRO# 12345678 · carrier "R+L Carriers" · ETA 2026-06-04 · customer PO# 72622

06 Verify tracking active verify_check +24h

24 hours after pickup, verify the carrier's tracking system shows the shipment as picked up and in transit. If not, flag as a potential pickup-failure issue and re-escalate to logistics.

Check
If carrier API available: GET /carrier-api/tracking/{number} → expect status in (picked_up · in_transit · out_for_delivery)
Fallback
Manual confirm by logistics if no carrier API integration yet
Issue path
If status = 'unknown' or 'not_found' after +24h, stage an issue HITL for logistics review (issue_type='pickup_missed')

07 Verify delivery confirmed verify_check +7d

7 days post-pickup, expect delivery to be confirmed. Pull POD (proof of delivery) signature if carrier provides it. Update freight_status = 'delivered'. Close the workflow run. If delivery NOT confirmed, escalate as a delay issue.

POD capture
PDF or image stored in R2 at logistics-pod/{shipment_id}/{date}.pdf · linked from NS via SuiteAttach
Final write
freight_status = 'delivered' · custbody_delivery_date = ? · custbody_pod_url = ?
Reflexion
Run reflexion: did the carrier choice work? Update lane history scores for the next freight check on the same lane.
Event fired
logistics.shipment_delivered — subscribers: finance + sales daily summary

Issue escalation paths

Issue typeTriggerActionNotify
pickup_missedverify +24h fails OR carrier no-show reportedRe-schedule pickup, log lane reliability penaltySO/PO owner + logistics lead
delay_in_transittracking shows no movement > 48h OR carrier ETA slipsCarrier call, customer pre-notify, ETA updateSO owner + customer
damage_on_deliveryPOD notes damage OR customer reports damage within 48hOpen carrier claim, document with photos, replacement order if neededSO owner + customer service + carrier claims
refused_deliverycarrier returns shipment to GFSInvestigate reason: wrong item, late, customer cancelled, etc. Hold inventory, escalate to sales.SO owner + customer + sales lead
lost_shipmenttracking stuck > 7 days OR carrier confirms lossOpen formal loss claim, ship replacement, recover from carrierFinance + sales + customer

Handoff back to sales + finance

When freight_status = 'delivered' is written, an event lands on the event ledger (logistics.shipment_delivered) which triggers the universal Finance handoff (R594 NS automation, planned). Finance sees the delivered shipment + invoice goes out. Sales gets the delivery confirmation in their daily summary. The customer PO# threads all the way through: SO.otherrefnumBOL.shipper_refInvoice.otherrefnum → logistics dashboard → event ledger entry.

Tables, endpoints, code paths

kindnamepurpose
D1 tablefreight_status_mirrorworkflow state — freight_status per (source_record_type, source_record_id) · customer_po_number threads
D1 tablelogistics_shipmentsfinal shipment scoreboard row — carrier, lane, transit_actual, on_time, cost, damage_flag, customer_po_number
D1 tablefreight_lane_historyupdated by reflexion at workflow close · feeds next freight_routing_check
D1 tablecarrierscarrier qualifications (refrigerated, hazmat cert, oversize)
D1 tableproposed_actions4 HITL gates: carrier_select · pickup_schedule · bol_generate · issue stages
D1 tableeventslogistics.shipment_delivered · workflow.completed · logistics.issue_opened
R2 bucketlogistics-bol/{id}/{date}.pdfBOL pdf storage · SuiteAttach to NS
R2 bucketlogistics-pod/{id}/{date}.pdfPOD pdf storage · SuiteAttach to NS
Workflow contractlogistics_review_workflowthis flow · risk 2 · 4 HITL gates · 2 verify_checks (+24h, +7d)
Workflow contractfreight_routing_checkupstream · hands off via freight.routed_to_logistics event
NS field (write)custbody_carrier · custbody_tracking_number · custbody_pickup_scheduled · custbody_delivery_date · custbody_pod_urlvia NS_PUSH_QUEUE · HITL-gated
NS field (write, TBD)custbody_freight_status · custbody_freight_classificationpending Mike review
Endpoint/logistics-review.htmlqueue surface UI (build pending — SVG above is SOT)

Open gaps — honest punch list

Related docs