# purchase-order-detail

## Overview

Displays full details of a purchase order including header information, line items, and order total. Available actions depend on the current status of the purchase order.

## Screen Type

DetailView

### DetailView Details

#### Displayed Fields

- **Header**
  - Document Number
  - Status
  - Receipt Status
  - Revision Number
  - Supplier (name + ID)
  - Currency *(pending — schema gap)*
  - Receiving Site
  - Order Date
  - External Supplier Order Reference
  - Created Date
  - Confirmed At *(pending — schema gap)*
  - Closed At *(pending — schema gap)*
  - Close Reason *(visible only when status is CLOSED; omitted if not set)*
- **Receipt totals** (`data-testid="po-receipt-totals"`, card above Line Items)
  - **Visibility:** the whole **Receipt Summary** card is **not** rendered when **orderStatus** is **DRAFT** or **SUBMITTED** (`orderStatus === DRAFT || orderStatus === SUBMITTED`) — there are no receipts to summarise until the PO transitions to **ORDERED** after approval.
  - **Data sources:** PO-level totals and per-line received aggregates on this page are computed from the PO's posted inbound shipments (only **POSTED** inbound shipments are counted). The **Line Items** table reads per-line Received numbers by resolving the PO lines and summing posted received quantity across inbound shipments.
  - **Card title** is static (**Receipt Summary**). **Description** beneath it (`text-sm`, muted foreground) is receive-context copy from **PO-level posted received vs ordered qty**: **“All ordered quantity received”** when posted received qty meets or exceeds ordered qty (numeric tolerance applied); otherwise **“N units still to receive”** (**“unit”** when N is exactly 1).
  - **Body:** a **three-column** HTML table (`data-testid="po-receipt-totals-table"`): a **thead** labels the middle column **Quantity** and the last column **Value** (first column header is visually empty with **Stage** sr-only); an **sr-only** `caption` also describes the table. **No** top border on the table (first data row sits below the title with spacing from the title’s bottom padding only). Data rows **Ordered** | **Received (R)** where **R** = count of posted inbound shipments — middle column is quantity, last column is value. Horizontal rules appear **between** rows only; the **last row has no bottom border**. **Ordered** has no parenthetical doc-count adornment (line items live in the table below the card). When **R > 1**, the **(R)** fragment is interactive: **hover** opens the compact breakdown tooltip (**receipt #** links, units · $); **click** smooth-scrolls to the **Receipts** card. When **R ≤ 1**, the count is plain text (no tooltip, no scroll affordance).
  - **Ordered row:** ordered qty and ordered value (baseline).
  - **Received row — quantity:** when posted received qty differs from ordered (and received > 0), render `**±qty`** + **12px gap** + **received qty** (12px muted variance segment, `**+`/`−`** for direction — no directional icons), regardless of whether receive is complete. When received equals ordered or is 0, render qty only. **Received row — value:** em dash when posted received qty is zero; aligned with ordered total = **plain amount only** (no green check); different from ordered total = **±$** + **12px gap** + **amount**, regardless of whether receive is complete.
  - **Section-level error / skeleton:** while the receipt totals are loading, the Receipt Summary card renders a skeleton; if loading errors, an inline error card replaces the body (rest of the page stays usable).
- **Line Items** (table `data-testid="po-line-items-table"`)
  - Line #
  - Item (name + SKU)
  - Ordered Quantity
  - UoM
  - Unit Price
  - Subtotal
  - Received quantity (posted inbound shipments, per line): when received qty differs from ordered (and `received > 0`), render `**±qty`** + **12px gap** + `**received qty`** (**12px** muted proportional numerals vs body-sized main qty), in both directions (under and over). When received equals ordered or is 0, qty only.
  - Received value (per line; posted receipt qty × PO unit price)
- **Totals row** — **Totals** label in the first two columns; **ordered quantity total** (`poTotals.orderedQty`) under Ordered Qty; em dashes under UoM and Unit Price; column totals for subtotal, **received qty**, and **received value**. Received qty totals follow the same `±qty` rule as the per-line cells (delta when actual differs from ordered, in both directions). Received value follows the same variance rules as the line rows (**no** green check when values match references).
- **Receipts** (card + table; card has anchor id for scroll-from-**(R)** on **Received** row when R > 1)
  - Receipt #, status badge, **Posted qty** and **Received value** on this PO for **POSTED** receipts (qty and value at PO unit price; em dash for draft/cancelled or no posted qty on this PO). **Receipt date**; numeric column headers use **right** alignment. Row navigates to inbound shipment detail
  - **Create new receipt** — same rules as sidebar “Create Inbound Shipment” (ORDERED and not fully received); creates remaining-quantity draft inbound shipment via `createRemainingInboundShipment`

    Over-receipt IS modeled — `postInboundShipment` allows received qty to exceed ordered qty within the supplier's configured tolerance (`SupplierToleranceConfig.quantity{Absolute,Percentage}Tolerance`).

#### Available Actions


| Action                | Condition                                                              | Requires Input                                                        | Confirmation         |
| --------------------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------- | -------------------- |
| Edit                  | orderStatus is DRAFT                                                   | —                                                                     | —                    |
| Submit for Approval   | orderStatus is DRAFT                                                   | —                                                                     | dialog               |
| Approve               | orderStatus is SUBMITTED                                               | —                                                                     | dialog               |
| Reject                | orderStatus is SUBMITTED                                               | reason (String)                                                       | dialog               |
| Amend                 | orderStatus is ORDERED                                                 | —                                                                     | —                    |
| Create Inbound Shipment  | orderStatus is ORDERED, receiptStatus ≠ RECEIVED                       | —                                                                     | —                    |
| Close                 | orderStatus is ORDERED                                                 | closeReason (String, optional), writeOffRemaining (Boolean, optional) | dialog               |
| Cancel                | orderStatus is DRAFT, SUBMITTED, or ORDERED                            | —                                                                     | dialog               |
| View Revision History | — (always; **Actions** panel right column, immediately above **Back**) | —                                                                     | opens revision sheet |
| Create new receipt    | orderStatus is ORDERED, receiptStatus ≠ RECEIVED                       | —                                                                     | —                    |
| Back                  | —                                                                      | —                                                                     | —                    |


Action dialogs (triggered by status-gated actions):

- **Close Dialog** — when the Close action is triggered, opens a modal dialog with an optional close reason text input. Displays a reason text input (optional), a Cancel button, and a Confirm Close button (always enabled). Dialog state clears on cancel or successful submission.

## Access Control

- [purchaser](../actor/purchaser.md)
- [approver](../actor/approver.md)

