Skip to main content

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 FulfillmentOrder rows.
  • Allocate when stock arrives (stock.received).
  • Produce labels through fulfillment_queue job batch-labels.
  • React to order and shipment events.

Architecture​

Code map​

PiecePath
State machinefulfillment-state-machine.ts
Workflowfulfillment-workflow.service.ts
Order listenerorder-fulfillment.listener.ts
Schema42-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.

FromTo
OPENPICKING, CANCELLED, ON_HOLD, LABEL_CREATING
PICKINGPICKED, ON_HOLD, LABEL_CREATING
PICKEDPACKING, ON_HOLD, LABEL_CREATING
PACKINGPACKED, LABEL_CREATING
PACKEDLABEL_CREATED, ON_HOLD, LABEL_CREATING
LABEL_CREATINGLABEL_CREATED, LABEL_CREATION_FAILED, CANCELLED, ON_HOLD
LABEL_CREATION_FAILEDLABEL_CREATING, CANCELLED, ON_HOLD, OPEN
LABEL_CREATEDPACKED, READY_TO_SHIP, LABEL_CREATING
READY_TO_SHIPPACKED, PARTIALLY_SHIPPED, SHIPPED, LABEL_CREATING
PARTIALLY_SHIPPEDPARTIALLY_SHIPPED, SHIPPED, ON_HOLD, LABEL_CREATING
SHIPPEDDELIVERED
DELIVERED, CANCELLEDnone
ON_HOLDOPEN, 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.

Troubleshooting​

Labels and integrations.