# Automation 2 — Fulfillment generation (at close)

**Scope item 5:** when the customization window closes, generate the correct fulfillment
orders per customer from their saved selections. Line items reflect swaps, additions, and
tier upgrades. Orders tagged/flagged per membership tier for packing & shipping.

## Approach: Worker task on Cloudflare Cron (runs at `close_at`)

Keep the logic in the one backend. A daily Cron Trigger checks for a cycle whose
`close_at` is today and `status = open`, then processes it and flips it to `closed`/`fulfilled`.

```
GET /apps/wineclub/tasks/fulfill      (Cloudflare Cron, daily)
  1. cycle = open cycle with close_at == today; if none → exit
  2. for each active member on that cycle:
       sel = customer.metafields.wineclub.selection[cycle]
       if sel && sel.fulfillmentStatus == 'pending':
           createOrderFromSelection(sel)        # swaps + add-ons
           sel.fulfillmentStatus = 'generated'
       else:
           createOrderFromDefault(member.tierPool)   # fallback (automation 3)
           sel.fulfillmentStatus = 'default_applied'
  3. cycle.status = 'fulfilled'
```

### Order creation

- Use `orderCreate` (or draft → complete) with:
  - Line items = the saved box bottles (variantId × 1) + add-on bottles.
  - **Tags:** `wineclub`, `tier:<key>`, `cycle:<handle>`, and a packing flag e.g.
    `pack:classic-3`. These drive packing/shipping filters and any 3PL export.
  - Note: membership tier + "customized" vs "default".
- The base subscription charge is handled by Appstle's renewal; this order represents the
  **shipment composition**. Coordinate with Appstle so you don't double-charge:
  - If Appstle already generates the renewal order, prefer **editing that order's line
    items** to match the selection instead of creating a parallel order.
  - Decision needed (see open questions): compose onto the Appstle order vs. separate
    fulfillment order. This is the single most important integration detail to confirm
    with Appstle's behavior on this store.

## Idempotency

- Guard with `fulfillmentStatus` so re-runs don't duplicate orders.
- Tag orders with `cycle:<handle>` and check for an existing order with that tag +
  customer before creating.
