# Domain — orders and receipt notices

> Field lists below are a snapshot of SDK **0.20.0** for orientation. The authority is the
> installed SDK (`@ezytail/ezyflow-sdk` types and `canonicalSchemas`), `docs/ezyflow/push.md`,
> and — above all — the **activity's composed schema** (`canonical.getEventSchema`, or the MCP
> tool), which can require fields the canonical schema leaves optional. Ask the catalog; do not assume.

## Envelope (every pushed event)

| Field | Notes |
| --- | --- |
| `id` | Envelope id; the key the entity is read back by. Stable per business object → retries are idempotent (`docs/ezyflow/api/idempotency.md`) |
| `type` | e.g. `order.created` |
| `source` | The system the push comes from (for EzyOrder: e.g. `ezyorder`) |
| `event_date_time` | ISO 8601, stamped server-side at push time |
| `activity` | **Never set it.** The SDK client adds the App's one activity (ADR 0010) |
| `snapshot` | The entity state |

## Order entry → `order.created`

Snapshot (main fields, as used by the reference App's `src/server/push.ts`):

| Field | Required | Notes |
| --- | --- | --- |
| `reference` | yes | Business order number |
| `customer_reference` | no | `null` when blank — never `""` |
| `channel` | no | `null` when blank — an empty string is refused by rules |
| `currency` | yes | ISO 4217, 3 letters |
| `total` | yes | `{ amount, currency }` |
| `has_pickup_point` | yes | boolean |
| `source_system` | yes | Same value as envelope `source` for EzyOrder-originated data |
| `lines[]` | yes | `sku`, `quantity_ordered`, `is_delivery`, `is_downpayment`, `is_service` |
| `parcels[]` | yes | may be empty |

Related: `order.cancelled` (pushable). Reads: `listOrder`, `getOrder`, `getOrderTimeline`,
`getOrderGraph`, `getOrderRelated`.

## Receipt notice entry → `procurement.created`

`ProcurementSnapshot`:

| Field | Required | Notes |
| --- | --- | --- |
| `reference` | yes | Receipt notice / PO number |
| `supplier_reference` | no | Supplier's code |
| `supplier_name` | no | |
| `date_planned` | no | Expected arrival date (ISO 8601) |
| `amount_total` | no | `Money` `{ amount, currency }` |
| `incoterm` | no | |
| `state` | no | |
| `fulfilment_location` | no | `LocationSnapshot` — warehouse receiving the goods |
| `quality_grade` | no | |
| `source_system` | yes | |
| `created_at` | yes | ISO 8601 |
| `updated_at` | no | |
| `lines[]` | yes | `quantity_ordered` (req.), `sku`, `product_reference`, `quantity_invoiced`, `sequence` |
| `activity` | — | Present in the type; filled by the client — never from user input |

Related: `receipt.confirmed` (pushable) closes the loop when goods are received.
Reads: `listProcurement`, `getProcurement`, `getProcurementTimeline`, `getProcurementGraph`,
`getProcurementRelated`. Delivered-only follow-ups: `procurement_line.added|updated|removed`.

## Other pushable types (contract 0.20.0)

`delivery.confirmed`, `order.cancelled`, `order.created`, `partner.archived`, `partner.created`,
`partner.updated`, `procurement.created`, `product_variant.archived`, `product_variant.created`,
`product_variant.updated`, `product.archived`, `receipt.confirmed`.
Which of these **this activity** accepts: `canonical.getActivityEventCatalog`.

## Projected entities

`order`, `partner`, `procurement`, `product`, `product_variant`, `stock_level` — each with
`list*`, `get*`, `get*Timeline`, `get*Graph`, `get*Related`. See `docs/ezyflow/projections.md`.

## Push lifecycle (UI contract)

```
form (TanStack Form + Zod)
  → server fn: requireAdmin → build body → pushEvent { dry_run: true }   (Rehearse)
      ├─ Problem → show ProblemPanel (title, detail, trace id, dictionary link)
      └─ OK (Dry-Run: true header) → enable "Push"
  → server fn: requireAdmin → same body → pushEvent                        (Push)
      └─ accepted → toast + link to /orders/$key or /procurements/$key
```

A rehearsal is mandatory before the real push in the UI. Refusals carry a `phase`
(guard / schema / rule); `docs/ezyflow/problems.md` and `docs/ezyflow/api/problem-*.md` say
what each means.
