Fulfillment
Fulfillment turns an order into warehouse work: pick, pack, label, and ship. The warehouse app calls these APIs. It does not own a second engine.
Responsibilities
- Create and transition
FulfillmentOrderrows. - Allocate when stock arrives (
stock.received). - Produce labels through
fulfillment_queuejobbatch-labels. - React to order and shipment events.
Architecture
Code map
| Piece | Path |
|---|---|
| State machine | fulfillment-state-machine.ts |
| Workflow | fulfillment-workflow.service.ts |
| Order listener | order-fulfillment.listener.ts |
| Schema | 42-fulfillment.prisma |
Entities: FulfillmentOrder, FulfillmentLineItem, FulfillmentStatusHistory, Package, PackageItem, Manifest.
State machine
isValidTransition returns false when the current status is missing from the map. It does not use the order helper’s “null means allow any” shortcut.
| From | To |
|---|---|
OPEN | PICKING, CANCELLED, ON_HOLD, LABEL_CREATING |
PICKING | PICKED, ON_HOLD, LABEL_CREATING |
PICKED | PACKING, ON_HOLD, LABEL_CREATING |
PACKING | PACKED, LABEL_CREATING |
PACKED | LABEL_CREATED, ON_HOLD, LABEL_CREATING |
LABEL_CREATING | LABEL_CREATED, LABEL_CREATION_FAILED, CANCELLED, ON_HOLD |
LABEL_CREATION_FAILED | LABEL_CREATING, CANCELLED, ON_HOLD, OPEN |
LABEL_CREATED | PACKED, READY_TO_SHIP, LABEL_CREATING |
READY_TO_SHIP | PACKED, PARTIALLY_SHIPPED, SHIPPED, LABEL_CREATING |
PARTIALLY_SHIPPED | PARTIALLY_SHIPPED, SHIPPED, ON_HOLD, LABEL_CREATING |
SHIPPED | DELIVERED |
DELIVERED, CANCELLED | none |
ON_HOLD | OPEN, CANCELLED |
The source comment on PARTIALLY_SHIPPED says a fulfillment order may ship in several consignments and stays there until the last backordered line goes out. The self-edge is in the map.
OPEN can jump to LABEL_CREATING without PICKING. That is an implemented transition. Whether the warehouse UI always picks first is unknown.
Events and queues
Listeners: order.created, order.status.changed, order.location.changed, shipment.status.changed, stock.received.
Queue: fulfillment_queue / batch-labels, concurrency 5, default 3 attempts with exponential backoff 2 seconds.
Stock side effects from fulfillment enqueue stock.sync.requested.
Integrations
Label files go to the courier strategy. When the courier returns a file, it is stored in S3. Carrier HTTP is not in the fulfillment database transaction. See Labels and Couriers.
Failure scenarios
LABEL_CREATION_FAILED is an operational status, not only a log line. It can return to LABEL_CREATING, CANCELLED, ON_HOLD, or OPEN.
If the courier accepted the shipment and the local transaction did not commit, the two sides diverge. Automatic compensating cancel was not verified per carrier.
Workflows
Development
Call isValidTransition before writing a new status. Enqueue stock sync in the same transaction as the stock mutation.