# Orders-Scan — Contracts

> Produced by the Reversa **Writer** (phase: generation) · doc_level: `complete`
> Generated on 2026-09-21

**Confidence scale:** 🟢 CONFIRMED · 🟡 INFERRED · 🔴 GAP

External HTTP contract exposed by the `orders-scan` unit — the single route `GET /orders/scan` (`OrderController::scan`, no route name) under the admin group (`['web','admin']`, empty admin prefix), declared **before** `resource('/orders')` so it is not shadowed by the resource `show` route. It is a read-only **JSON** order picker; it requires an authenticated admin session; an unauthenticated request gets `302 → auth/login`. 🟢 (`routes/web.php:72`, `OrderController.php:419-437`)

---

## GET `/orders/scan` — order/draft picker 🟢 (`:419-437`)

- **Auth:** required; anonymous → `302 auth/login`. 🟢 (`routes/web.php:24-28`)
- **Request (query string):**

  | Field | Type | Required | Notes |
  |-------|------|----------|-------|
  | `q` | string | ❌ | When truthy, filters to orders whose **related customer** has `phone LIKE %q%` OR `fullname LIKE %q%` (OR grouped inside a `whereHas` EXISTS). Customerless orders never match. `%`/`_` act as LIKE wildcards (parameter-bound, no injection). 🟢 (`:422-426`) |
  | `status` | enum `draft`\|`done` | ❌ | Exact `status` filter; `?status=draft` is the POS resume-draft case. 🟢 (`:427-429`) |

- **Response — `200 application/json`:** a JSON **array** (possibly empty `[]`) of up to 10 `Order` objects, ordered `id` desc. Each object:

  | Field | Type | Meaning |
  |-------|------|---------|
  | *(all `orders` columns)* | mixed | `id`, `customer_id`, `subtotal`, `total`, `discount_amount`, `earned_point`, `paid`, `debt_amount`, `points_awarded_at`, `count`, `status`, `notes`, timestamps. 🟢 |
  | `code` | string (appended) | `#QT78-{id}`. 🟢 |
  | `is_editable` | bool (appended) | draft, or done within 24h of `updated_at`. 🟢 |
  | `debt_locked` | bool (appended) | a `pos_debt` ledger row exists. 🟢 |
  | `customer` | object\|null | the full eager-loaded `Customer` (null for a walk-in). 🟢 |
  | `products` | array | each `Product` with a `pivot` `{qty, price, unit_id, conversion_qty}`. 🟢 |

- **Status codes:** `200` (array, possibly empty) · `302 → auth/login` (unauthenticated). The `response()->json(null, 204)` fallback is **dead code** — a `Collection` is always truthy, so `204` is never emitted. No `404`; an empty match is a valid `[]`. 🟢 (`:432-436`)
- **CSRF:** not applicable (`GET`). 🟢

---

## Consumed contracts (owned by other units)

`orders-scan` only reads the `orders`/`order_product`/`customers` tables through Eloquent; it calls no other unit's HTTP endpoint and no external service. 🟢

| Reads | Owner unit | Purpose |
|-------|------------|---------|
| `orders` + appended attributes | `orders-crud` | the order records and their editability flags |
| `order_product` pivot + `Product` | `orders-crud` / `products-catalog` | the line items to rehydrate a cart |
| `customers` (via `Order::customer` + `whereHas`) | `customers-crud` | the buyer and the `q` search target |

---

## Producer/consumer relationships

| This unit is… | Counterparty | Contract |
|---------------|--------------|----------|
| **Producer** | `pos-terminal` | serves `orders/scan?status=draft` so the POS client can list and rehydrate resumable drafts (customer + product lines in one payload). 🟢 |
| **Consumer** | `orders-crud` | reads the `Order` model, its appended attributes, and its relations owned by that unit. 🟢 |
| **Consumer** | `customers-crud` | the `q` search resolves against `Customer.phone`/`fullname`. 🟢 |

---

## Cross-cutting contract notes

- **Method surface:** a single `GET`; no other verbs on `/orders/scan`. 🟢 (`routes/web.php:72`)
- **Content type:** `application/json` (an array), not HTML — contrast the `orders-crud` `index` HTML list. 🟢 (`:433`)
- **Discriminator:** unlike `pos-scan`'s `is_barcode` union, the payload is a plain array; consumers branch on array length. 🟢
- **Read-only:** `scan()` never mutates state. 🟢 (`:419-437`)
- **Cap:** at most 10 rows, no pagination; it is a picker, not a report. 🟢 (`:430`)
- **Search grouping:** the phone/name OR is inside the `whereHas`, so no OR-leak against `status`. ⚠ Reviewer 2026-09-22: removed the stale "unlike `orders-crud` `index`" contrast — that search was grouped/fixed 2026-09-21. 🟢 (`:425-427`)
- **Search scope:** `q` matches only orders with a related customer; walk-in orders are excluded from `q` results. 🟡 (`:423`)
- **Authorization:** authentication only; any admin may enumerate recent orders. 🟡 (ADR-0009)
- **No observability:** the lookup emits no telemetry. 🔴 (`:419-437`, absence)
