Architecture
The fulfillment system connects four models:- Order has many shipments (
OrderShipping) and tracks overallshipping_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
| Column | Type | Nullable | Description |
|---|---|---|---|
id | bigint | no | Primary key |
order_id | bigint | no | FK to order |
carrier_id | bigint | yes | FK to carrier |
status | string(32) | yes | Shipment status enum |
shipped_at | datetime | yes | When the shipment was dispatched |
received_at | datetime | yes | When the customer received it |
returned_at | datetime | yes | If the shipment was returned |
tracking_number | string | yes | Carrier tracking number |
tracking_url | string | yes | URL for customer tracking |
voucher | json | yes | Shipping label/voucher data |
OrderShippingEvent Table
| Column | Type | Nullable | Description |
|---|---|---|---|
id | bigint | no | Primary key |
order_shipping_id | bigint | no | FK to shipment |
status | string(32) | no | Shipment status at this event |
description | text | yes | Event description |
location | string | yes | Location name |
latitude | decimal(10,7) | yes | GPS latitude |
longitude | decimal(10,7) | yes | GPS longitude |
occurred_at | datetime | yes | When the event occurred |
metadata | jsonb | yes | Additional event data |
created_at | timestamp | yes | Creation timestamp |
OrderItem Fulfillment Columns
| Column | Type | Nullable | Description |
|---|---|---|---|
order_shipping_id | bigint | yes | FK to the shipment this item belongs to |
fulfillment_status | string | yes | Current fulfillment state of this item |
Fulfillment Status
TheFulfillmentStatus enum defines the lifecycle of each item:
Status Flow
| From | Possible Transitions |
|---|---|
| Pending | Processing, ForwardedToSupplier, Cancelled |
| ForwardedToSupplier | Processing, Shipped, Cancelled |
| Processing | Shipped, Cancelled |
| Shipped | Delivered |
| Delivered | (terminal state) |
| Cancelled | (terminal state) |
Shipping Status (Order Level)
TheShippingStatus 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)
TheShipmentStatus enum lives on the OrderShipping model and tracks the delivery progress of a single package. Each shipment goes through this lifecycle independently.
Shipment Transitions
TheHasFulfillmentTransitions trait on OrderShipping enforces valid state transitions:
| From | Possible Transitions |
|---|---|
| Pending | PickedUp, Returned |
| PickedUp | InTransit, DeliveryFailed, Returned |
| InTransit | AtSortingCenter, OutForDelivery, DeliveryFailed, Returned |
| AtSortingCenter | InTransit, OutForDelivery, DeliveryFailed, Returned |
| OutForDelivery | Delivered, DeliveryFailed, Returned |
| Delivered | Returned |
| DeliveryFailed | InTransit, OutForDelivery, Returned |
| Returned | (terminal state) |
Transitioning Shipment Status
UsetransitionTo() to update the shipment status. It validates the transition and automatically logs a tracking event:
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: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 toDelivered and dispatches the OrderShipmentDelivered event:
SyncOrderShippingStatusAction
This action computes the order-levelshipping_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:
| Condition | Order Shipping Status |
|---|---|
| All items cancelled | Returned |
| Some cancelled, rest shipped/delivered | PartiallyReturned |
| No items shipped or delivered | Unfulfilled |
| All items delivered | Delivered |
| Some items delivered | PartiallyDelivered |
| All items shipped or delivered | Shipped |
| Otherwise | PartiallyShipped |
Shipped, the OrderShipped event is dispatched.
Events
Fulfillment events are dispatched automatically by the actions above:| Event | Dispatched When | Properties |
|---|---|---|
OrderShipmentCreated | A new shipment is created for an order | Order $order, OrderShipping $shipment |
OrderShipped | Order shipping_status transitions to Shipped | Order $order |
OrderShipmentDelivered | A shipment is marked as delivered | Order $order, OrderShipping $shipment |
Storefront Example
Order Tracking Page
Best Practices
| Practice | Description |
|---|---|
| Update statuses atomically | Always update order_shipping_id and fulfillment_status together when shipping |
| Complete orders automatically | Check if all items are delivered and update the order status accordingly |
| Group items by carrier | Items from the same supplier or warehouse should share a shipment |
| Store tracking URLs | Always provide tracking_url so customers can track packages directly |
| Handle mixed orders | Process your own products and supplier products as separate shipments |