# Orders Note Design

## Data Model

Single column mutation on the `orders` table. No schema changes are introduced by this unit.

| Column | Type | Nullable | Notes |
|--------|------|----------|-------|
| `orders.notes` | string | yes | The only column written by this unit |

Mass-assignment is bounded by `Order::$fillable = ['notes', 'debt_amount']` (`Order.php:11`). Because `updateNote` validates only `notes`, `fill($valided)` can never touch `debt_amount` or any other column — the fillable list is effectively the write boundary enforced at the model layer.

No migration is required: the `notes` column already exists.

## Internal Flows

### Happy path

```
PUT /orders/{order}/note
        │
        ▼
[admin group middleware — web + admin]
        │  anonymous → 302 auth/login
        ▼
OrderController::updateNote($request, $id)
        │
        ├─ validate(['notes' => 'nullable|string'])
        │       non-string → redirect()->back()->withErrors(bag)
        │
        ├─ Order::findOrFail($id)
        │       unknown id → ModelNotFoundException → 404
        │
        ├─ $product->fill($valided)        ← $product holds an Order (copy-paste name)
        │
        └─ $product->save()
                │
                ├─ true  → admin_toastr('Cập nhật thành công')
                │           redirect()->back()                     [302]
                │
                └─ false → back()->withInput()
                            ->withErrors('Cập nhật thất bại')      [302]
```

### Note cleared

`notes` absent or empty string passes `nullable` validation; `fill` sets the column to `null`/empty with no error.

### Form rendering (client side)

Both reader screens emit:

```blade
Form::open(['route' => ['orders.update_note', '#id#'], 'method' => 'PUT'])
```

`#id#` is a placeholder replaced by JavaScript with the concrete order id when the per-order inline editor is opened. `Form::open` emits the CSRF token and the `_method=PUT` spoof field required by the `web` middleware stack.

Rendering screens:
- `resources/views/pages/customer-statis.blade.php:183` (customer statistics)
- `resources/views/pages/customer-orders.blade.php:121` (customer purchase history)

### Route registration constraint

The route `PUT /orders/{order}/note` must be declared **before** `resource('/orders')` in `routes/web.php`. The resource macro expands to `PUT /orders/{order}` for its `update` action; because the note route has a distinct path suffix (`/note`), shadowing only occurs if declaration order is reversed. Current registration at line 74, resource at line 75+.

## Technical Decisions

| Decision | Rationale | Evidence |
|----------|-----------|----------|
| Dedicated single-column endpoint, not routed through `orders-crud` update | `orders-crud` update recomputes totals, points, debt, and enforces `is_editable`; notes are free metadata and must bypass all of that | `OrderController.php:342-356` vs full update action |
| Redirect-back (PRG), not JSON | The callers are server-rendered Blade forms; no AJAX contract needed | `redirect()->back()` / `back()->withInput()` at `:353,355` |
| No status/editability guard | Notes are free metadata, not financial state; confirmed intentional 2026-09-25 (`questions.md#question-14`) | absence of `is_editable`/status check at `:342-356` |
| `fill($valided)` over explicit `$order->notes = …` | Delegates write-boundary enforcement to `Order::$fillable`; consistent with the project's mass-assignment convention | `Order.php:11`; `:349` |
| Authentication only, no per-record authorization | Any admin may edit any order's note (ADR-0009) | route group middleware; `updateNote` has no policy check |

## Notes

- **`$product` variable name (`:348`):** the loaded `Order` record is stored in a local variable named `$product` — a copy-paste artifact from the surrounding controller. It is harmless at runtime but is a readability trap; rename on reimplementation.
- **Effectively unreachable failure branch (`:355`):** `save()` returns `false` only when a `saving` Eloquent event halts persistence. `Order` defines no such event, so the `Cập nhật thất bại` path is dead code in practice. Retain for defensive parity or remove on reimplementation.
- **No observability:** the write path emits no log entry, metric, or trace — including on the failure branch. The only signal of a failed save is the user-facing flash.
- **CSRF surface:** the `web` middleware enforces CSRF on every request reaching this route; a missing or expired token returns `419` before the controller is invoked.
