Skip to main content
Order fulfillment in Shopper is designed around the concept of shipments, physical packages sent to customers. A single order can have multiple shipments, each with its own carrier, tracking number, and set of items.

Architecture

The fulfillment system connects four models:
  • Order has many shipments (OrderShipping) and tracks overall shipping_status
  • Each shipment has its own status (ShipmentStatus) and a timeline of events (OrderShippingEvent)
  • Each item tracks its own fulfillment status independently

Database Schema

OrderShipping Table

OrderShippingEvent Table

OrderItem Fulfillment Columns

Fulfillment Status

The FulfillmentStatus enum defines the lifecycle of each item:

Status Flow

Shipping Status (Order Level)

The ShippingStatus enum lives on the Order model and reflects the combined state of all its shipments. It answers “has this order been shipped/delivered?”.

Shipment Status (Package Level)

The ShipmentStatus enum lives on the OrderShipping model and tracks the delivery progress of a single package. Each shipment goes through this lifecycle independently.

Shipment Transitions

The HasFulfillmentTransitions trait on OrderShipping enforces valid state transitions:

Transitioning Shipment Status

Use transitionTo() to update the shipment status. It validates the transition and automatically logs a tracking event:
This creates an OrderShippingEvent record with the status, timestamp, and optional location data.

Tracking Events

Each shipment has a timeline of events that record its journey:

Relationships

Order → Shipments

OrderShipping → Items & Events

OrderItem → Shipment

Creating Shipments

Single Shipment (All Items Together)

When all items ship in one package:

Partial Shipment (Split Across Packages)

When items ship in multiple packages with different carriers:

Marking as Delivered

Dropshipping Workflow

For external products sourced from suppliers, the fulfillment flow includes an additional step where the order is forwarded to the supplier who ships directly to the customer.
Dropshipping requires the Supplier feature to be enabled. External products must be linked to a supplier.

Complete Dropshipping Flow

Mixed Orders (Own Products + Supplier Products)

An order can contain both your own products and dropshipped products:

Querying Fulfillment Data

Items by Status

Orders Needing Attention

Shipment History

Handling Returns

Actions

Shopper provides dedicated action classes for fulfillment operations. These handle side effects like syncing order statuses, updating item states, and dispatching events automatically.

RecordShipmentEventAction

The recommended way to record shipment events. It validates the transition, updates the shipment status, logs the event, and handles side effects based on the new status:
When a shipment transitions to PickedUp, the action automatically updates all items in the shipment to FulfillmentStatus::Shipped and changes the order status from New to Processing. When a shipment transitions to Delivered, the action updates all items to FulfillmentStatus::Delivered and completes the order if all items across all shipments are delivered. When a shipment transitions to Returned, the action updates all items to FulfillmentStatus::Cancelled.

MarkShipmentDeliveredAction

A convenience action for marking a shipment as delivered. It validates that the shipment can transition to Delivered and dispatches the OrderShipmentDelivered event:

SyncOrderShippingStatusAction

This action computes the order-level shipping_status from the fulfillment statuses of all its items. It runs automatically after every shipment event, but you can call it manually if you update item statuses directly:
The algorithm counts items by fulfillment status and determines the order shipping status: When the status transitions to Shipped, the OrderShipped event is dispatched.

Events

Fulfillment events are dispatched automatically by the actions above:

Storefront Example

Order Tracking Page

Best Practices