Orders & fulfilment
Draft Orders
Indicative price, not an offer: €24/mo; support for quote versioning and expired-quote audits
Generated from docs/plan/paid/draft-orders/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
A quoting system for B2B and sales teams to stage orders with line items and track quote expiry. Merchants create drafts, send PDF quotes to customers, and convert accepted quotes to real orders.
The problem
B2B and sales teams need to quote before payment and track which quotes convert to orders. Without a quoting system, they resort to manual emails or spreadsheets, losing visibility into quote-to-order conversion.
What it does
- Create draft order with line items, quantities, prices
- Apply manual discounts or coupons to draft
- Calculate tax on draft (if tax module is available)
- Set quote expiry date (e.g., 30 days from creation)
- Email draft quote as PDF to customer
- Customer can accept quote (converts to real order) or merchant can close/discard
- Audit trail: draft created, sent to customer, accepted/declined, expired
- Merchant can edit draft at any time before quote expiry
- Track which quotes convert to orders (quote-to-order ratio KPI)
- Versioning: if draft is updated, old version is archived
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.
- Auto-renewal of quotes — quotes expire and are not auto-renewed. Reason: requires standing-order infrastructure (paid feature).
- Multi-currency support — draft is in shop currency only. Reason: per-country obligation; paid feature.
- Margin calculation — core does not show profit margin on draft. Reason: requires cost data; paid feature.
- Approval workflow — no manager approval step before quote is sent. Reason: organizational workflow; paid feature.
Data model
New tables: DraftOrder {id, customerId (optional), email, lineItems: [{productId, quantity, priceOverride}], tax, totalBeforeTax, totalAfterTax, discountAmount, expiresAt, status (‘draft’|‘sent’|‘accepted’|‘declined’|‘expired’), createdAt, sentAt, acceptedAt}; DraftOrderVersion {id, draftOrderId, version, lineItems (snapshot), totalAfterTax (snapshot), createdAt}; DraftOrderAuditLog {id, draftOrderId, action, details, timestamp}. No migration.
API
- POST /api/draft-orders
- GET /api/draft-orders/:id
- PUT /api/draft-orders/:id
- POST /api/draft-orders/:id/send
- POST /api/draft-orders/:id/accept
- POST /api/draft-orders/:id/decline
- DELETE /api/draft-orders/:id
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Create draft order form: select/search customer, add line items, set expiry, apply discount. Draft list: pending/sent/accepted/declined, expiry countdown, quick actions (send, convert, delete). Draft detail: show quote PDF, version history, email resend.
The seam — why this is paid
Core owns: basic order/quote structure. Paid owns: quote versioning, multi-currency, approval workflows, quote templates.
Support commitment: quote versioning, tax recalculation on refresh, PDF generation
Dependencies
- Product/pricing data
- Customer database (optional; draft can be email-only)
- Email system (send PDF quote)
- Tax system (if paid; otherwise core assumes tax=0)
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.
- Draft created with email=‘customer@example.com’ and 2 line items shows totalAfterTax correctly
- Editing draft increments version; old version archived with snapshot of prices/items
- Quote expires: if date is in past, status auto-transitions to ‘expired’ (on next query or via scheduler)
- Sending draft emails PDF quote to customer with accept/decline link; email must be sent (audit log recorded)
- Customer clicks accept link, real order is created with same line items, status ‘pending’ (not ‘paid’ yet)
- Audit log shows created, sent, accepted with timestamps and user/system action source
Risks
Price staleness: product price changes after draft created but before order accepted; mitigation: freeze price on draft; order uses frozen price on accept. Lost quotes: draft expires without customer action; merchant unaware; mitigation: email reminder 3 days before expiry (optional feature).
Commercial context
| Suggested price | €24/mo; support for quote versioning and expired-quote audits |
| Rival anchor | Shopify: free (core); Magento: ~€500 extension |
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.