Orders & fulfilment
Fulfillment Status
Generated from docs/plan/core/fulfillment-status/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Merchants can’t mark orders as fulfilled, so they don’t know which orders are ready to ship or already shipped. This feature adds a fulfillment workflow state to orders.
The problem
I don’t have a way to say ‘this order is ready to ship’ or ‘this order is fulfilled’. I manually track readiness in my head or notes. I need a status that shows fulfillment progress.
What it does
- Add fulfillment_status enum to orders: unfulfilled, partially_fulfilled, fulfilled, cancelled
- Auto-update fulfillment_status based on shipments: if all items shipped, status → fulfilled
- Allow manual status override: staff can mark order as fulfilled even if no shipment created
- Display fulfillment status as badge in order list and detail (different colors: gray unfulfilled, yellow partial, green fulfilled)
- Filter order list by fulfillment status (e.g., show only unfulfilled orders)
- Show fulfillment % in order detail: (items_shipped / total_items) * 100
- Support bulk status change: select multiple orders and mark as fulfilled at once
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Fulfillment workflow automation (e.g., ‘when payment captured, auto-set status to ready’) — that is automation, separate feature
- Backorder or pre-order fulfillment states — that is inventory system, separate feature
- Fulfillment notifications to warehouse (e.g., print pick list) — that is warehouse system, separate feature
Data model
orders.fulfillment_status (enum: unfulfilled, partially_fulfilled, fulfilled, cancelled). orders.fulfillment_updated_at (timestamp). No migration if orders schema is extensible; otherwise migration to add fields.
API
- PATCH /api/admin/orders/:orderId/fulfillment-status — update fulfillment status
- GET /api/admin/orders?fulfillment_status=unfulfilled — filter orders by fulfillment status
- POST /api/admin/orders/bulk-action — bulk update fulfillment status
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Order list includes ‘Fulfillment’ column showing status badge. Order detail shows fulfillment status badge and % progress bar. Button ‘Mark as Fulfilled’ changes status. Bulk actions sidebar: select multiple orders, dropdown ‘Bulk Action’ → ‘Mark Fulfilled’. Filter sidebar: checkbox ‘Unfulfilled’, ‘Partially Fulfilled’, ‘Fulfilled’.
The seam — why this is core
Core owns fulfillment status field, workflow, and UI. No paid seam — fulfillment tracking is infrastructure.
Core owns the interface + honest fulfillment tracker; order workflow is infrastructure, not a support commitment or credential.
Dependencies
- Existing orders collection (needs fulfillment_status field)
- shipment-management-tracking (for auto-updating status based on shipments)
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- Create order with 5 items and 1 shipment with 2 items; confirm fulfillment_status=‘partially_fulfilled’ and progress=40%
- Add second shipment with remaining 3 items; confirm fulfillment_status auto-updates to ‘fulfilled’
- Manually mark order as fulfilled; confirm fulfillment_status=‘fulfilled’ regardless of shipments
- Filter order list by fulfillment_status=‘unfulfilled’ and confirm only unfulfilled orders are shown
- Bulk-select 5 orders and mark as fulfilled; confirm all 5 orders update status
Risks
Auto-updating fulfillment_status based on shipments can create race conditions if shipment and fulfillment updates happen concurrently. Use database transactions to ensure consistency. Bulk status updates on large order sets (1000+) can timeout; implement async batch processing.
Commercial context
| Suggested price | free (core) |
| Rival anchor | Shopify: included; Magento: included |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.