Skip to main content
Version: v1.0.0(int)

Sales orders

Header + line items + timeline notes + documents. Two status fields: order_status (org.order_status ENUM) and fulfillment_status (org.fulfillment_status ENUM), both stored directly on the row.

ER diagram

org.sales_orders

ColumnTypeNotes
idUUID PK
org_idUUID FKplatform.organizations(id).
customer_idUUID FKNOT NULL. → org.customers(id) ON DELETE RESTRICT.
so_numberVARCHAR(100)Optional. Partial UNIQUE (org_id, so_number) WHERE so_number IS NOT NULL.
order_channel_idUUID FKNOT NULL. → org.order_channels(id) ON DELETE RESTRICT.
order_statusorder_statusNOT NULL. ENUM. See values below.
fulfillment_statusfulfillment_statusNOT NULL. ENUM. See values below.
fulfillment_location_idUUID FKorg.locations(id) ON DELETE SET NULL.
broker_idUUID FKorg.brokers(id) ON DELETE SET NULL. Snapshot — defaulted from customer but overridable per SO.
broker_fee_percentNUMERIC(10,6)CHECK 0..100. Snapshot — defaulted from customer.
broker_feesNUMERIC(18,6)NOT NULL. CHECK ≥ 0. Default 0. User-entered; may differ from the calculated order_value × broker_fee_percent / 100.
fulfillment_costsNUMERIC(18,6)NOT NULL. CHECK ≥ 0. Default 0.
freight_costsNUMERIC(18,6)NOT NULL. CHECK ≥ 0. Default 0.
promo_costsNUMERIC(18,6)NOT NULL. CHECK ≥ 0. Default 0.
other_costsNUMERIC(18,6)NOT NULL. CHECK ≥ 0. Default 0.
mabdDATENOT NULL. "Must Arrive By Date" — the customer-facing delivery commitment.
placed_atDATENOT NULL. Default CURRENT_DATE. Matches Received status.
entered_atDATEMatches Entered status.
shipped_atDATEMatches Shipped status.
invoiced_atDATEMatches Invoiced status.
paid_atDATEMatches Paid status.
rejected_atDATEMatches Rejected status.
notesTEXT
qbo_idVARCHAR(100)UNIQUE per (org_id, qbo_id). QBO sync identifier.
created_atTIMESTAMPTZNOT NULL. Default NOW().
updated_atTIMESTAMPTZNOT NULL. Default NOW(). Auto-updated by trigger.

org.sales_order_items

ColumnTypeNotes
idUUID PK
org_idUUID FK
sales_order_idUUID FKNOT NULL. → org.sales_orders(id) ON DELETE CASCADE.
sku_idUUID FKNOT NULL. → org.skus(id) ON DELETE RESTRICT.
quantityINTEGERNOT NULL. CHECK ≥ 1. Default 1. SKU sales-unit integer.
unit_priceNUMERIC(18,6)NOT NULL. CHECK ≥ 0. Default 0.

UNIQUE (sales_order_id, sku_id) — one line per SKU per SO.

org.sales_order_timeline_notes

Append-only audit-style notes pinned to a status.

ColumnTypeNotes
idUUID PK
org_idUUID FK
sales_order_idUUID FKNOT NULL. → org.sales_orders(id) ON DELETE CASCADE.
order_statusorder_statusNOT NULL. ENUM. The status the note was pinned to.
noteTEXTNOT NULL.
created_atTIMESTAMPTZNOT NULL. Default NOW().

Documents

Files attached to a sales order are stored in the polymorphic org.documents table with entity_type = 'sales_order' and entity_id = sales_orders.id. Same shape used by purchase orders and work orders.

Status values

Two independent ENUMs.

  • org.order_status: ReceivedEnteredShippedInvoicedPaid, with branches Rejected, In-Dispute, Completed.
  • org.fulfillment_status: PendingPartialFulfilled.

order_status tracks the commercial lifecycle; fulfillment_status tracks the warehouse lifecycle.

Cross-references

Notes

  • No is_active — terminal states live on order_status (Rejected, Completed).
  • Aggregate order value is computed on read — no order_value column. SUM(sales_order_items.quantity * sales_order_items.unit_price) per API response.
  • Inventory cost (COGS) is computed on read from the matching FG ledger CONSUME events — no inventory_cost column on the header.
  • Lifecycle date rules:
    • Milestones (Received, Entered, Shipped, Invoiced, Paid) — each has a date column.
    • Single-event branches (Rejected) — rejected_at.
    • Transient / multi-occurrence states (In-Dispute) — no date column. The SO can enter and exit dispute multiple times; history is captured in sales_order_timeline_notes.
    • Terminal archived state (Completed) — no date column. Exact moment isn't load-bearing; check timeline notes if needed.