Core
Orders
Draft Orders

Draft Orders

Staging buffer between message-sourced orders (AI parser, catalog picks) and confirmed Order rows. Human-in-the-loop review + edit before an Order is created.

1. Overview

Orders from WhatsApp/Instagram messages, the AI parser, or a catalog quick-pick are not inserted directly into Order. They land in DraftOrder with status pending_review. A seller reviews, edits, and either approves (→ Order) or rejects (→ archived). Drafts are the unit of human control for message-driven commerce.

2. Architecture

3. Data model

See schema.prisma:L478-L539 for DraftOrder, DraftOrderItem, and DraftOrderStatus enum (pending_review | approved | rejected | expired).

4. Key flows

4.1 Approval → Order (single transaction)

The eager status flip to approved before the Order insert is the guard against duplicate conversion on concurrent approves.

4.2 Creation from a message

5. Lifecycle

All terminal. No reopening.

6. Key files

The race-condition fix (tx + status flip) landed in commit 68bbf56 ("Added draft-order fixes").

7. Env vars & config

No dedicated env vars. Inherits DB, JWT, and WhatsApp credentials.

8. Gotchas & troubleshooting

  • expiresAt is passive → set to now + 24h but no scheduler calls cleanupExpiredDrafts. Only trigger is manual POST /cleanup/expired. Drafts pile up forever.
  • Unauthenticated debug endpointGET /pending-test at draft-orders.ts:L510-L545 has no auth, hardcoded userId. Remove before prod exposure.
  • customer is denormalized JSON → not an FK to Customer. Editing the Customer record won't retroactively update drafts.
  • DraftOrderItem.productId may be null → happens when parser can't match. Approval must fill these in or the Order insert fails.
  • PATCH/PUT recompute sets totalAmount = subtotal → tax and shipping ignored on edit. Real money bug if drafts with tax are edited post-parse.
  • WhatsApp confirm on approve is best-effort → try/catch + swallow. Failures silent; Order still created. Check BE logs if sellers report missing confirmations.
  • Five helpers create drafts → canonical createDraftOrder plus four local helpers in webhook files. Logic drift is real — route new code through OrderCreationService.createDraftOrderFromMessage.

9. Extension points

  • New draft source → call OrderCreationService.createDraftOrderFromMessage(parsed). Don't inline a new helper.
  • Auto-approve for trusted customers → rule layer before createDraftOrder; on match call approveDraftOrder immediately.
  • Expiry scheduler → add a cron in schedulers/ calling cleanupExpiredDrafts daily.

10. Related docs