Skip to main content

Orders

The order aggregate is the OMS header: lines, status history, notes, tags, payments on the order, and imports. It lives in commerce, not in the warehouse app.

Responsibilities​

  • Persist an order and its lines for a tenant and channel.
  • Enforce VALID_ORDER_TRANSITIONS on status changes.
  • Write OrderStatusHistory.
  • Enqueue outbox events in the same transaction as the write.
  • Leave fulfillment creation and wallet debit to the worker.

Architecture​

The HTTP response returns after the transaction commits, including the outbox insert. Fulfillment and the wallet debit are asynchronous.

Code map​

PiecePath
State machine used by commandorder/services/order-state-machine.ts
Duplicate state machineorder-status/domain/order-state-machine.ts
Fulfillment listenerorder-fulfillment.listener.ts
Outbound sync listeneroutbound-order-sync.listener.ts
Schema41-order.prisma

Controllers: order and order-tag under the order feature. Notes are a separate feature (/commerce/order-note). History is /commerce/order-status-history. There is no separate cancel subsystem. Cancellation is a transition to CANCELLED.

Data model​

Order, OrderItem, OrderStatusHistory, OrderNote. Status is OrderStatus. The database enum limits stored values. The TypeScript map limits transitions.

Channel.defaultFulfillmentMode defaults to OMS_MANAGED. The schema comment says Amazon AFN versus MFN is per order, and the channel default is a fallback. Whether AFN skips pick and pack was not found as a branch in this pass.

Business rules​

See Business rules and State machine. Short version: the map is the spec, the file header comment is incomplete, and isValidOrderTransition(null, to) returns true.

API surface​

Orders are under /v1/commerce/orders. Field-level contracts are OpenAPI. This page does not copy DTOs. Permission strings are whatever @RequirePermissions is on each handler. A handler without that decorator is allowed for any authenticated user.

Events and queues​

EventWhen
order.createdCreate paths, including ingestion when items exist and the outbox writer is present
order.confirmedConfirm paths
order.location.changedLocation changes
order.status.changedStatus service and return command
order.address.resolution_triggeredOrder command
stock.sync.requestedSome order-command paths

Address validation can also enqueue validate-pending-order on address_validation_queue after ingestion.

Integrations​

Marketplace pull creates orders through ingestion. Status push uses OutboundOrderSyncListener when the channel handler implements outbound and manageState is on. The listener’s exact guard was not line-read. Schema comment: manageState is the flag for pushing order status.

WhatsApp checkout can also create orders. That path is under automation, not MarketplaceRegistry.

Workflows​

Failure scenarios​

  • Throw inside the command transaction: order and outbox roll back together.
  • Wallet listener throws after confirm: order stays committed, outbox retries the debit. See Wallet debit.
  • Create idempotency key: not established. A client retry can create a second order unless a unique key is enforced in the service. That key was not confirmed.

Development​

Change transitions in both state-machine files and the state machine page. Keep side effects on OutboxWriterService.enqueue(tx, ...).

Troubleshooting​

If an order exists and no fulfillment order appears, check that the worker is running and that order.created is not DEAD. If the channel did not receive a status push, check manageState and whether the connector registered an outbound order service.