commerce

Orders, carts, shipping records, and fulfillments for e-commerce workflows.

Commerce

Orders with line items, shopping carts, shipping tracking, and fulfillment records. All monetary amounts are stored in the smallest currency unit (e.g. cents for USD). Products are referenced from the billing skill.

Tables

orders

ColumnTypeDescription
iduuidPrimary key, auto-generated.
org_iduuidReferences organizations. Cascade delete.
contact_iduuidReferences contacts. Set NULL on contact delete.
numbertextHuman-readable order number, e.g. ORD-2026-001.
statusorder_statusCurrent order state. Defaults to pending.
currencytextISO currency code. Defaults to USD.
subtotalnumericAmount before tax and shipping, in smallest currency unit. Defaults to 0.
taxnumericTax amount in smallest currency unit. Defaults to 0.
shipping_costnumericShipping cost in smallest currency unit. Defaults to 0.
totalnumericTotal amount in smallest currency unit. Defaults to 0.
shipping_addressjsonbShipping address as JSON.
billing_addressjsonbBilling address as JSON.
placed_attimestamptzWhen the order was placed.
created_attimestamptzRow creation timestamp.
updated_attimestamptzAuto-updated via trigger.
metadatajsonbArbitrary key-value data. Defaults to empty object.

order_items

ColumnTypeDescription
iduuidPrimary key, auto-generated.
org_iduuidReferences organizations. Cascade delete.
order_iduuidReferences orders. Cascade delete.
product_iduuidReferences products. Set NULL on product delete.
nametextProduct name at time of purchase.
quantityintegerNumber of units. Defaults to 1.
unit_pricenumericPrice per unit in smallest currency unit.
totalnumericLine total in smallest currency unit (quantity * unit_price).
created_attimestamptzRow creation timestamp.
updated_attimestamptzAuto-updated via trigger.
metadatajsonbArbitrary key-value data. Defaults to empty object.

carts

ColumnTypeDescription
iduuidPrimary key, auto-generated.
org_iduuidReferences organizations. Cascade delete.
contact_iduuidReferences contacts. Set NULL on contact delete.
statuscart_statusCart state. Defaults to active.
currencytextISO currency code. Defaults to USD.
expires_attimestamptzWhen the cart expires and becomes abandoned.
created_attimestamptzRow creation timestamp.
updated_attimestamptzAuto-updated via trigger.
metadatajsonbArbitrary key-value data. Defaults to empty object.

cart_items

ColumnTypeDescription
iduuidPrimary key, auto-generated.
org_iduuidReferences organizations. Cascade delete.
cart_iduuidReferences carts. Cascade delete.
product_iduuidReferences products. Set NULL on product delete.
quantityintegerNumber of units. Defaults to 1.
unit_pricenumericPrice per unit in smallest currency unit.
created_attimestamptzRow creation timestamp.
updated_attimestamptzAuto-updated via trigger.
metadatajsonbArbitrary key-value data. Defaults to empty object.

shipping_records

ColumnTypeDescription
iduuidPrimary key, auto-generated.
org_iduuidReferences organizations. Cascade delete.
order_iduuidReferences orders. Cascade delete.
carriertextShipping carrier name, e.g. UPS, FedEx, USPS.
tracking_numbertextCarrier tracking number.
statusshipping_statusShipping state. Defaults to label_created.
shipped_attimestamptzWhen the shipment was dispatched.
delivered_attimestamptzWhen the shipment was delivered.
addressjsonbDelivery address as JSON.
created_attimestamptzRow creation timestamp.
updated_attimestamptzAuto-updated via trigger.
metadatajsonbArbitrary key-value data. Defaults to empty object.

fulfillments

ColumnTypeDescription
iduuidPrimary key, auto-generated.
org_iduuidReferences organizations. Cascade delete.
order_iduuidReferences orders. Cascade delete.
statusfulfillment_statusFulfillment state. Defaults to pending.
fulfilled_byuuidReferences users. Set NULL on user delete.
fulfilled_attimestamptzWhen the fulfillment was completed.
notestextInternal notes about the fulfillment.
created_attimestamptzRow creation timestamp.
updated_attimestamptzAuto-updated via trigger.
metadatajsonbArbitrary key-value data. Defaults to empty object.

Enums

order_status

ValueDescription
pendingOrder created but not yet confirmed.
confirmedOrder confirmed and awaiting processing.
processingOrder is being prepared.
shippedOrder has been shipped.
deliveredOrder has been delivered.
canceledOrder has been canceled.
refundedOrder has been refunded.

cart_status

ValueDescription
activeCart is active and items can be added.
convertedCart was converted into an order.
abandonedCart expired or was abandoned.

shipping_status

ValueDescription
label_createdShipping label has been created.
in_transitShipment is in transit.
out_for_deliveryShipment is out for delivery.
deliveredShipment has been delivered.
returnedShipment was returned to sender.
exceptionA delivery exception occurred.

fulfillment_status

ValueDescription
pendingFulfillment has not started.
processingItems are being packed or prepared.
fulfilledAll items have been fulfilled.
canceledFulfillment was canceled.

Row-Level Security

All six tables are scoped to the current user's organization via get_user_org_id(). Any org member can select, insert, and update rows. Only admins (checked via is_admin()) can delete rows.

Dependencies

  • billing -- products
  • crm (transitive via billing) -- contacts
  • identity (transitive via crm) -- organizations, users, get_user_org_id(), is_admin(), set_updated_at()

Example Queries

Calculate total revenue by order status:

SELECT
  status,
  count(*) AS order_count,
  sum(total) / 100.0 AS total_dollars
FROM orders
GROUP BY status
ORDER BY total_dollars DESC;

Show cart value for all active carts:

SELECT
  c.id AS cart_id,
  con.first_name || ' ' || con.last_name AS contact,
  count(ci.id) AS item_count,
  sum(ci.quantity * ci.unit_price) / 100.0 AS cart_value_dollars,
  c.expires_at
FROM carts c
LEFT JOIN contacts con ON con.id = c.contact_id
LEFT JOIN cart_items ci ON ci.cart_id = c.id
WHERE c.status = 'active'
GROUP BY c.id, con.first_name, con.last_name, c.expires_at
ORDER BY cart_value_dollars DESC;

Track shipping status for an order:

SELECT
  o.number AS order_number,
  sr.carrier,
  sr.tracking_number,
  sr.status AS shipping_status,
  sr.shipped_at,
  sr.delivered_at
FROM shipping_records sr
JOIN orders o ON o.id = sr.order_id
WHERE o.id = '<order_id>'
ORDER BY sr.created_at DESC;

List orders awaiting fulfillment:

SELECT
  o.number AS order_number,
  o.status AS order_status,
  f.status AS fulfillment_status,
  o.total / 100.0 AS total_dollars,
  o.placed_at,
  f.notes
FROM orders o
JOIN fulfillments f ON f.order_id = o.id
WHERE f.status IN ('pending', 'processing')
ORDER BY o.placed_at ASC;

Revenue per product from completed orders:

SELECT
  oi.name AS product,
  sum(oi.quantity) AS units_sold,
  sum(oi.total) / 100.0 AS revenue_dollars
FROM order_items oi
JOIN orders o ON o.id = oi.order_id
WHERE o.status IN ('shipped', 'delivered')
GROUP BY oi.name
ORDER BY revenue_dollars DESC;

Identify abandoned carts older than 24 hours:

SELECT
  c.id AS cart_id,
  con.email,
  sum(ci.quantity * ci.unit_price) / 100.0 AS cart_value_dollars,
  c.created_at,
  c.expires_at
FROM carts c
LEFT JOIN contacts con ON con.id = c.contact_id
LEFT JOIN cart_items ci ON ci.cart_id = c.id
WHERE c.status = 'active'
  AND c.created_at < now() - interval '24 hours'
GROUP BY c.id, con.email, c.created_at, c.expires_at
ORDER BY cart_value_dollars DESC;