# Orders-Print — Implementation Tasks

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

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

## Prerequisites

- [ ] `Order` model with appended `code` (`#QT78-{id}`), `products()` (`order_product` withPivot `qty,price,unit_id,conversion_qty`), `customer()`, `total`, `discount_amount`, `created_at`. 🟢 (`app/Models/Order.php:39-51`)
- [ ] `Unit` model available for the per-line label lookup (`Unit::find(pivot.unit_id)->name`). 🟢 (`units` unit)
- [ ] `pages.pos-print` view + `admin::index` layout and partials present. 🟢 (`resources/views/pages/pos-print.blade.php`)
- [ ] Admin route group `['web','admin']`; `/orders/{order}/print` registered **before** `resource('/orders')`. 🟢 (`routes/web.php:73-75`)

## Tasks

> Each task references the legacy file the behaviour was extracted from.

- [ ] T-01, Register `GET /orders/{order}/print` → `OrderController@printOrder` in the admin group, **before** the `/orders` resource so it is not shadowed by the resource `show` route.
  - Legacy origin: `routes/web.php:73`
  - Done when: `GET /orders/{id}/print` resolves to `printOrder()` for an authenticated admin; anonymous → `302` to `auth/login`.
  - Confidence: 🟢

- [x] T-02, Implement `printOrder($id)`: load the order, set header `In đơn hàng` + breadcrumb, and render `pages.pos-print` with `order`.
  - Legacy origin: `OrderController.php:394-402`
  - Done when: a valid order renders the receipt view with its data.
  - Confidence: 🟢 — ✅ Fixed 2026-09-21, folded into T-06 (`findOrFail` adopted).

- [ ] T-03, Render the receipt body (`pages.pos-print`): store identity, title "Hoá đơn", `Số HĐ: {code}`, `Ngày: {created_at d-m-Y H:i}`, conditional customer block (`fullname`/`phone`/`number_format(points,1)`), per-line block, discount row, total row, footer + bank details.
  - Legacy origin: `resources/views/pages/pos-print.blade.php:32-97`
  - Done when: the receipt matches legacy layout for a customer order and a walk-in order.
  - Confidence: 🟢

- [ ] T-04, Render each product line: unit label = `Unit::find(pivot.unit_id)->name` when `pivot.unit_id` else product `unit`; print `qty`, `number_format(pivot.price,0)`, and `number_format(qty*pivot.price,0)`.
  - Legacy origin: `pos-print.blade.php:55-67`
  - Done when: converted-unit and base-unit lines both label and total correctly at 0 decimals.
  - Confidence: 🟢

- [ ] T-05, Render the `.noPrint` action bar: `?ref=orders` → `history.back()` ("Trở lại"), else a link to `/pos` ("Tạo đơn mới"); plus a `window.print()` button. Ensure `@media print` hides `.noPrint` and chrome and sizes the receipt to ~72–80mm.
  - Legacy origin: `pos-print.blade.php:98-114,177-196`
  - Done when: the back link switches on `?ref`, the print button prints, and printed output shows only the receipt.
  - Confidence: 🟢

- [x] T-06, Harden the unknown-id case: use `findOrFail` (or guard the view) so a missing order returns a clean `404` instead of a fatal null-dereference in the view.
  - Legacy origin: `OrderController.php:396`; `pos-print.blade.php:43-55`
  - Done when: `GET /orders/{unknown}/print` returns `404`, not a `500`.
  - Confidence: 🟢 — ✅ Fixed 2026-09-21 (`Order::find` → `Order::findOrFail`).

- [ ] T-07, (Improvement, not in legacy) Remove the per-line `Unit::find` N+1 by eager-loading units for the printed lines.
  - Legacy origin: `pos-print.blade.php:57`
  - Done when: printing an order runs a bounded number of queries independent of line count.
  - Confidence: 🟡

- [ ] T-08, (Improvement, not in legacy) Add observability — log/metric on the print path.
  - Legacy origin: `OrderController.php:392-400` (absence)
  - Done when: each print request emits a structured log or metric.
  - Confidence: 🔴

## Test Tasks

- [ ] TT-01, Happy path: a customer order with lines → `GET /orders/{id}/print` returns `200` with the store header, `#QT78-{id}`, date, customer block, per-line `qty × price = total`, discount, and grand total (see `requirements.md`, Acceptance Criteria).
- [ ] TT-02, Walk-in: an order with no customer prints without the customer block.
- [ ] TT-03, Unit label: a converted-unit line shows the unit name; a base-unit line shows the product `unit`.
- [ ] TT-04, `?ref`: `?ref=orders` yields a history-back control; absence yields the `/pos` "new order" link.
- [x] TT-05, Unknown id: `GET /orders/{unknown}/print` returns a clean `404` (regression guard for the T-06 `findOrFail` fix, 2026-09-21).
- [ ] TT-06, Auth: anonymous request → `302` to `auth/login`.

## Data Migration Tasks (if applicable)

- None. Read-only projection over `orders`/`order_product`/`customers`/`units`; introduces no schema. 🟢 (`data-dictionary.md:200-237,320-325`)

## Suggested Order

1. T-01 → T-02 (route + controller action).
2. T-03 → T-05 (receipt view, lines, action bar).
3. T-06 → T-08 (unknown-id hardening, N+1 removal, observability) as improvements.

## Pending Gaps (🔴)

- **No observability (T-08):** the print path emits no signal.
- **N+1 unit lookups (T-07):** one `Unit::find` per line — bound it on reimplementation.
- **Draft printing / live-points-on-history / 0-decimal display:** confirm these conventions with the business before reimplementation. (`pos-print.blade.php:48,57,64-81`)
