# Flowchart — `orders`

> Produced by the Reversa **Archaeologist** (phase: excavation) · doc_level: `complete`
> Generated on 2026-09-17

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

## `OrderController::store` — create order + totalling engine 🟢

```mermaid
flowchart TD
    A["POST /orders (from POS cart)"] --> B["validate items[], customer.phone,<br/>discount_amount, debt_amount, status"]
    B --> C["customer = Customer::code(phone)->first()"]
    C --> D["for each item: find Product by code"]
    D --> E["resolve unit_id → ProductUnit (null if missing)<br/>price = getPriceByCustomerType(type, unit_id)<br/>subtotal += round(price,1)*qty<br/>earned_point += round(reward_point,1)*qty<br/>count++ (lines)"]
    E --> F["total = subtotal - discount_amount; paid = total"]
    F --> G{"debt_amount > 0?"}
    G -->|no customer| H["🚫 redirect: customer required"]
    G -->|debt > total| I["🚫 redirect: debt exceeds total"]
    G -->|ok / 0| J["save order + attach order_product pivots"]
    J --> K{"status == 'done' and customer?"}
    K -->|yes| L["customer.points += earned_point<br/>points_awarded_at = now()"]
    K -->|no| M["skip points"]
    L --> N{"done and debt > 0?"}
    M --> N
    N -->|yes| O["CustomerDebt::record(pos_debt)"]
    N -->|no| P["skip"]
    O --> Q{"status?"}
    P --> Q
    Q -->|draft| R["toastr 'Saved draft' → redirect POS"]
    Q -->|done| S["render pos-print"]
```

## `OrderController::update` — edit / finalise draft 🟢

```mermaid
flowchart TD
    A["PUT /orders/id"] --> B["order = findOrFail; debtLocked = order.debt_locked"]
    B --> C{"create_now_mode?"}
    C -->|yes| D["rebuild items[] from existing pivots<br/>(finalise stored draft without cart)"]
    C -->|no| E["use submitted items"]
    D --> F
    E --> F{"order.is_editable?"}
    F -->|no (done > 24h)| G["🚫 back: cannot edit"]
    F -->|yes| H["recompute totals (same engine as store)<br/>detach + re-attach pivots"]
    H --> I{"debtLocked?"}
    I -->|yes| J["freeze debt_amount (never re-post)"]
    I -->|no| K["apply debt rules (customer req, ≤ total)"]
    J --> L{"done and !points_awarded_at?"}
    K --> L
    L -->|yes| M["award points once, set points_awarded_at"]
    L -->|no| N["skip"]
    M --> O["save; if done & !locked & debt>0 → record pos_debt"]
    N --> O
    O --> P{"draft? → redirect POS · done? → pos-print"}
```

## `OrderController::destroy` — delete with reversals 🟢

```mermaid
flowchart TD
    A["DELETE /orders/id"] --> B["DB::transaction"]
    B --> C["Customer::reversePointsForOrder<br/>(points -= earned_point, clamped ≥ 0, if awarded)"]
    C --> D["CustomerDebt::voidForOrder<br/>(insert debt_void entries, reduce debt_total by<br/>min(entry, balance), flag shortfall in note)"]
    D --> E["order->delete() (hard — no SoftDeletes)"]
    E --> F["order_product cascades; customer_debts.order_id → null"]
    F --> G["JSON status"]
```

**Notes:**
- `count` counts distinct **lines**, not summed quantity. 🟢
- Products with an unknown `code` are silently skipped during totalling. 🟢
- `is_editable` = draft, or `done` within `Order::$limit_hours_editable` (24h) of `updated_at`. 🟢
- `debt_locked` = a `pos_debt` ledger row already exists → debt frozen across `done→draft→done` re-finalisation (posts exactly once). 🟢
- Nightly `Order::summaryLogging()` (scheduler + artisan) rebuilds `customer_order_summary` (`orders_count`, `amount_total`, `points_total`, `categories_statistic` — no `owe_total`, which was removed as unused).
