# Orders Print Specification

## Purpose

The Orders Print capability exposes a single read-only HTML endpoint that renders a printable thermal receipt for an existing order. It serves both as a standalone re-print route reached from the order list and as the shared receipt template used by the order-creation flow.

## Requirements

### Requirement: Admin Authentication Required

The system SHALL restrict `GET /orders/{order}/print` to authenticated admin sessions. An unauthenticated request SHALL receive an HTTP `302` redirect to `auth/login` without rendering any receipt content. (Implemented in `routes/web.php:24-28,73`)

#### Scenario: Authenticated admin accesses print route

- **GIVEN** a valid admin session is present
- **WHEN** `GET /orders/{order}/print` is requested
- **THEN** the server returns `200 text/html` with the receipt page

#### Scenario: Unauthenticated request to print route

- **GIVEN** no admin session exists
- **WHEN** `GET /orders/{order}/print` is requested
- **THEN** the server returns `302` redirecting to `auth/login`

---

### Requirement: Unknown Order Returns 404

The system SHALL return HTTP `404` when the `order` path parameter does not match any existing order record. No partial or error receipt SHALL be rendered. (Implemented in `app/Http/Controllers/OrderController.php:396`)

#### Scenario: Non-existent order id

- **GIVEN** no order exists with the given id
- **WHEN** `GET /orders/{order}/print` is requested by an authenticated admin
- **THEN** the server returns `404` with no receipt body

---

### Requirement: Any Order Status Is Printable

The system SHALL render the receipt for any order regardless of its status. No guard on `draft` or `done` status SHALL block the print route. (Implemented in `app/Http/Controllers/OrderController.php:394-402`; confirmed intentional 2026-09-25)

#### Scenario: Printing a draft order

- **GIVEN** an order with `status = draft` exists
- **WHEN** `GET /orders/{order}/print` is requested by an authenticated admin
- **THEN** the server returns `200` with the receipt rendered for that order

#### Scenario: Printing a finalised order

- **GIVEN** an order with `status = done` exists
- **WHEN** `GET /orders/{order}/print` is requested by an authenticated admin
- **THEN** the server returns `200` with the receipt rendered for that order

---

### Requirement: Receipt Displays Order Identity

The system SHALL render the receipt with the order's human-facing code (`#QT78-{id}`) and its creation timestamp formatted as `d-m-Y H:i`. (Implemented in `resources/views/pages/pos-print.blade.php:43`)

#### Scenario: Order code and date on receipt

- **GIVEN** an existing order with id `42` created on 2026-09-21 at 14:30
- **WHEN** `GET /orders/42/print` returns `200`
- **THEN** the receipt body contains `#QT78-42` and `21-09-2026 14:30`

---

### Requirement: Customer Block Is Conditional

The system SHALL render a customer block containing the customer's full name, phone number, and current loyalty points when the order is associated with a customer. The system SHALL omit the customer block entirely for walk-in orders that have no associated customer. (Implemented in `resources/views/pages/pos-print.blade.php:45-49`)

#### Scenario: Order with customer

- **GIVEN** an existing order linked to a customer record
- **WHEN** `GET /orders/{order}/print` returns `200`
- **THEN** the receipt contains the customer's `fullname`, `phone`, and formatted `points`

#### Scenario: Walk-in order with no customer

- **GIVEN** an existing order with no associated customer
- **WHEN** `GET /orders/{order}/print` returns `200`
- **THEN** the receipt contains no customer block

---

### Requirement: Customer Points Reflect Current Balance

The system SHALL display the customer's loyalty points as the live balance at the time the receipt page is rendered, not the balance recorded at the time of sale. (Implemented in `resources/views/pages/pos-print.blade.php:48`)

#### Scenario: Customer points shown on receipt

- **GIVEN** a customer whose point balance has changed since the order was created
- **WHEN** the receipt is rendered
- **THEN** the displayed points equal the customer's current balance, not the historical sale-time balance

---

### Requirement: Per-Line Unit Label Resolution

For each product line on the receipt, the system SHALL display the unit label resolved from the selling unit captured at sale time (`pivot.unit_id`). When no selling unit was captured for a line, the system SHALL fall back to the product's base unit string. (Implemented in `resources/views/pages/pos-print.blade.php:56-60`)

#### Scenario: Line with a captured selling unit

- **GIVEN** an order line whose `pivot.unit_id` references a unit record
- **WHEN** the receipt is rendered
- **THEN** that line displays the name of the resolved `Unit` record

#### Scenario: Line without a selling unit

- **GIVEN** an order line with no `pivot.unit_id`
- **WHEN** the receipt is rendered
- **THEN** that line displays the product's base `unit` string

---

### Requirement: Line Amounts Display at Zero Decimals

The system SHALL render each product line's unit price and computed line total (`qty × pivot.price`) as whole numbers with no decimal places. The discount amount and grand total SHALL also be rendered at zero decimals. (Implemented in `resources/views/pages/pos-print.blade.php:64-81`)

#### Scenario: Line and totals formatted as whole numbers

- **GIVEN** an order with a line at unit price `15000.50` and qty `2`, a discount of `5000.25`, and a total of `25000.75`
- **WHEN** the receipt is rendered
- **THEN** the line price shows `15001` or `15000` (zero-decimal rounding), the line total shows the zero-decimal product, the discount shows `5000`, and the grand total shows `25001` or `25000` (zero-decimal rounding per `number_format`)

---

### Requirement: Discount and Grand Total Are Displayed

The system SHALL render a discount row showing the order's `discount_amount` and a grand total row showing the order's `total` on every receipt. (Implemented in `resources/views/pages/pos-print.blade.php:73,81`)

#### Scenario: Discount and total rows present

- **GIVEN** an order with a non-zero `discount_amount` and a `total`
- **WHEN** the receipt is rendered
- **THEN** the receipt contains a discount row prefixed with `-` and a grand total row

---

### Requirement: Back Navigation Controlled by `ref` Parameter

The system SHALL render the receipt's back control as a `history.back()` trigger when the `ref` query parameter equals `orders`. When `ref` is absent or any other value, the system SHALL render a navigation link to `/pos` instead. (Implemented in `resources/views/pages/pos-print.blade.php:100-108`)

#### Scenario: Back control with `?ref=orders`

- **GIVEN** the receipt URL includes `?ref=orders`
- **WHEN** the page is loaded
- **THEN** the back control invokes `history.back()` on activation

#### Scenario: Back control without `?ref=orders`

- **GIVEN** the receipt URL has no `ref` parameter or `ref` is not `orders`
- **WHEN** the page is loaded
- **THEN** the back control is a link to `/pos`

---

### Requirement: Print Is Triggered Client-Side

The system SHALL provide a browser-side print button that invokes `window.print()`. The server SHALL NOT perform any server-side print operation. The receipt's non-printable chrome (action bar and layout elements) SHALL be hidden when the browser's print dialog is active. (Implemented in `resources/views/pages/pos-print.blade.php:110-112,177-196`)

#### Scenario: Print button triggers browser print

- **GIVEN** the receipt page is loaded in a browser
- **WHEN** the print button is activated
- **THEN** the browser's native print dialog is invoked and the action bar is not included in the printed output

---

### Requirement: Receipt Is Read-Only

The system SHALL NOT modify any order, customer, or product record when rendering the print receipt. A `GET /orders/{order}/print` request SHALL produce no state changes. (Implemented in `app/Http/Controllers/OrderController.php:394-402`)

#### Scenario: No state change on print

- **GIVEN** an existing order in any state
- **WHEN** `GET /orders/{order}/print` is called any number of times
- **THEN** the order record, its line items, and the associated customer record are unchanged after each call
