The check that fires when a SO or PO has freight relevance, classifies the freight type, and routes the record into the logistics review queue. Without this gate, freight would silently fall through and the logistics team wouldn't know to plan it.
Freight is the moment GFS hands physical goods to a carrier. When a SO is fulfilled by inventory or assembly, when a PO arrives at a dock, when a dropship vendor ships direct to the customer — freight has to be planned, carrier picked, BOL generated, tracking captured. This check is the dispatcher: it decides whether freight needs human routing (parcel-with-handling, LTL, FTL, dropship vendor freight, freight collect) or can be auto-handled (standard parcel, customer pickup, freight already arranged). When human routing is needed, the record lands on the logistics review queue.
A v2 workflow contract that fires on:
freight_status on the SO body.It does NOT fire on: SOs marked customer pickup, POs where vendor pays freight, parcel orders < threshold, or records already past Item Fulfillment / Item Receipt.
| Trigger | Source | Condition |
|---|---|---|
| SO entered | NS SuiteFlow on SO save | shipmethod IS NOT NULL AND shipmethod != 'pickup' AND freight_status IS NULL |
| PO created | NS SuiteFlow on PO save | shipmethod IS NOT NULL AND PO is dropship OR vendor-freight-prepay-collect AND freight_status IS NULL |
| Manual trigger | Logistics team button | "Re-check freight" on any source record to re-run classification |
proposed_actions row, HITL-gated per ADR-031.freight_status = 'pending_logistics_review'. NS push when approved.SO.otherrefnum or PO.memo) carries forward to the logistics workflow| Mode | Symptom | Recovery |
|---|---|---|
| Missing ship address | Precondition fails, record HOLDS, owner gets email | Owner adds address, re-trigger |
| Classification ambiguous | Two classifications tie (e.g. could be LTL or FTL) | Logistics team picks in stage 02 of logistics workflow |
| No rate card for lane | estimate_freight_cost returns null | Stage manual_quote HITL, logistics gets RFQ |
| Hazmat without permit | Item flagged hazmat but no GFS hazmat carrier on file | Hold + escalate to compliance |
workflow_type: freight_routing_check risk_level: 2 trigger: SuiteFlow on SO/PO save (conditional) inputs_required: source_record_type: ['so', 'po'] source_record_id: number ship_method: string | null ship_carrier: string | null shipping_cost: number | null total_weight: number | null total_volume: number | null ship_address: object context_to_load: - customer_or_vendor_address - line_items (with shipping_cost) - special_handling_flags preconditions: - source_record_exists - ship_address_valid - freight_relevance_check (block if no shipping_cost AND no ship_method) fan_out_targets: - classify_freight_type (chat_tool) - assess_special_handling (chat_tool) - estimate_freight_cost (chat_tool) - flag_for_logistics_review (stage_proposed_action) - notify_logistics (hitl_email_draft) - update_so_or_po (d1_write) post_actions: - log_run - reflexion - event(workflow.completed, 'freight_routing_check') verify_checks: - logistics_review_completed (+24h) - freight_cost_finalized (+48h)
| Date | Round | Change | By |
|---|---|---|---|
2026-05-26 | R594 | Built freight-routing-check + logistics-review-flow workflows. NS custom field names (custbody_freight_status, custbody_freight_classification) flagged TBD pending Mike review. | Mike + Claude |
| Record | Customer PO# field | Carries through to |
|---|---|---|
| SO | bodyFields.otherrefnum | BOL.shipper_ref · logistics queue entry · Invoice.otherrefnum |
| PO (SO-connected) | bodyFields.memo + lineFields.links[].tranid | BOL when dropship · VendBill.memo |
| PO (inventory) | internal req# in bodyFields.memo — does NOT carry customer PO# | VendBill.memo (internal ref only) |
| Logistics queue | source_po_or_otherrefnum (mirrored from source record) | BOL · tracking · POD |
| NS custom fields TBD | custbody_freight_status · custbody_freight_classification | Pending Mike confirmation |
Apply migration:
npx wrangler d1 execute gfs-netsuite --remote --file migrations/schema/134_freight_logistics_workflows.sql
Verify contracts:
npx wrangler d1 execute gfs-netsuite --remote --command "SELECT workflow_type, description, risk_level FROM workflow_definitions WHERE workflow_type LIKE 'freight%' OR workflow_type LIKE 'logistics%';"
Manual trigger (test):
POST /api/workflow/run
{ "workflow_type": "freight_routing_check", "inputs": { "source_record_type": "so", "source_record_id": 12345 } }
https://gfs-netsuite.pages.dev/logistics-review.html — build