# Flowchart — `pos`

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

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

## `PosController::index` — render terminal 🟢

```mermaid
flowchart TD
    A["GET /pos"] --> B["build admin URLs<br/>(pos/scan, customers/scan,<br/>orders/scan?status=draft, orders, customers.statis)"]
    B --> C{"request()->id set?"}
    C -->|yes| D["load Order draft<br/>+ customer + products.units.unit<br/>→ replace each product.units with buildUnitsPayload"]
    C -->|no| E["no draft"]
    D --> F["Admin::script(pos client JS ~630 lines)"]
    E --> F
    F --> G["view('pages.pos')"]
```

## `PosController::scan` — product lookup 🟢

```mermaid
flowchart TD
    A["GET /pos/scan?q="] --> B{"Product::code(q)->first() ?"}
    B -->|match| C["{ is_barcode:true,<br/>data: product + units payload }"]
    B -->|no match| D["Product name LIKE '%q%' take 10"]
    D --> E["{ is_barcode:false,<br/>data: [products + units payload] }"]
```

## Client cart — add / re-unit / submit (confirmed correct by the team against the live UI) 🟢

```mermaid
flowchart TD
    A["scan / autocomplete select"] --> B["addItem"]
    B --> C{"lineKey = code + '__' + (unit_id||'base')<br/>already in cart?"}
    C -->|yes| D["updateItem: qty += 1, re-price"]
    C -->|no| E["push line, qty=1, fetch /products/get-price"]
    D --> F["updatePOS: subtotal/discount/debt"]
    E --> F
    F --> G["change unit on a line"]
    G --> H{"target lineKey collides?"}
    H -->|yes| I["merge: qty added directly, remove old line"]
    H -->|no| J["relabel line, re-price (qty preserved)"]
    I --> K["submit"]
    J --> K
    K --> L{"debt > 0 and input not disabled?"}
    L -->|yes, no customer| M["🚫 block: must select customer"]
    L -->|yes, debt > total| N["🚫 block: debt cannot exceed total"]
    L -->|ok / debt=0| O["POST/PUT /orders (OrderController)"]
```

**Pricing rule (`Product::getPriceByCustomerType`):** `wholesale_prices[type]` if present else `sale_price`; divided by `conversion_qty` when a non-base unit is selected. 🟢

**Notes:**
- Debt input is locked (server ignores it) when the order is `done` or already has a `pos_debt` ledger entry (`debt_locked`). 🟢
- Persistence, totals, earned points and debt posting happen in `OrderController` (orders module). 🟡
