Skip to content

Store operations

A connected physical-goods demo: stock adjustments, partial fulfillment, delivery, returns, and refund records.

Open the operations workspace

Follow a complete order journey

  1. Inventory shows on-hand, reserved, and available units. Record a reasoned adjustment; stock cannot drop below paid orders’ reservations.
  2. Choose a fully paid order in Fulfillment. Select quantities, carrier, and tracking to record a partial or full shipment. Shipment quantities reduce stock and release the corresponding reservation.
  3. Record delivery. Only delivered units are eligible for return.
  4. Request a return, approve it, receive it with or without restocking, then record its refund. Receiving restocks once; refunding never restocks again. Rejected requests release return eligibility.
  5. Review the activity trail and preview the matching shipping, return-approval, or refund email. Messages are never sent by this demo.

State and transaction boundary

The studio store uses a separate physical-goods collection; the original Orders demo contains software licenses and is unchanged. The operations aggregate includes inventory, order-line price snapshots, captured amounts, shipments, return cases, command receipts, and an event ledger. Amounts are integer USD cents. This example excludes tax, discounts, shipping charges, exchanges, and payment processing; production pricing and accounting remain application concerns.

Reads use useOne through the active data provider. Commands use an executor. The session-memory executor serializes commands per provider/workspace, checks expected revision, applies an immutable transition, and writes the aggregate once. The same idempotency key and payload cannot apply twice; a reused key with different content fails. Failed writes keep the form draft and do not announce success. This is not a durable, multi-process transaction system.

// src/lib/commerce/adapter.ts
const demo = createDemoOperationsExecutor(provider);

// Replace the demo executor when connecting your backend:
const execute = createHttpOperationsExecutor("/api/commerce/commands", () => ({
  "X-CSRF-Token": session.csrfToken,
}));

await execute({
  workspaceId: "studio",
  expectedRevision: workspace.revision,
  commandId: crypto.randomUUID(),
  command: { kind: "adjust-stock", sku: "NOTE-01", delta: 5, reason: "Delivery received" },
}, actor);
// The HTTP adapter deliberately does not serialize actor.
// Derive trusted identity, workspace access, and permissions on the server.

Server contract

  • Authenticate, verify CSRF where cookies are used, derive actor and tenant from the session, and authorize products.manage or orders.manage. Client demo roles are presentation controls, not authentication.
  • Validate the request, lock or compare-and-swap the workspace revision, and enforce a unique workspace/idempotency key with payload fingerprint in durable storage. Cross-workspace commands must fail.
  • Run the domain transition and ledger write in one transaction. Return 409 for revision or idempotency conflicts. Return the complete committed workspace as JSON.
  • Integrate real carrier and payment providers server-side. Treat payment webhooks as authoritative and use the payment provider’s idempotency support. Only record a real refund after the payment operation is confirmed; a client click must not manufacture success.
  • Enqueue customer emails through a transactional outbox after the event commits. Unique delivery keys prevent duplicates. Previewing or downloading a template does not send it.

Adapt the source

The framework-independent model, command adapters, email bindings, fixture, and tests are included in the starter. The full dashboard adds the operations UI and deep links using view/order query parameters. Reloading the demo resets in-memory writes. Each draft remains editable after validation, network, or conflict errors.

Edit src/lib/commerce/model.ts for transitions, adapter.ts for command transport, emails.ts for event-to-template mapping, and fixture.json for the fictional store. The fixture emitter includes this collection. The main UI is src/components/commerce/operations-workspace.tsx.