# Operations Playbook

How a warehouse runs on this system. Written for ops managers and supervisors; the API calls behind each step are in `docs/API.md`.

## 1. Master data you must have before go-live

| Item | Owner | Notes |
|------|-------|-------|
| Warehouses, zones, locations with pick/putaway sequences | Ops | Pick sequence = walking order from the pack area; putaway sequence = walking order from the dock. Mark docks `is_receivable`, pick faces `forward_pick`, set `max_handling_units` on single-pallet rack positions. Set `settings.default_pack_location_id` per warehouse |
| Clients with warehouse links, allocation strategy, min shelf life | Ops + Commercial | `default_allocation_strategy`: FEFO for anything with expiry, FIFO otherwise. `settings.receiving.over_receipt_tolerance_pct`, `settings.orders.allow_partial_shipment` |
| Products with UoMs, barcodes, tracking flags | Client / Ops | `is_lot_tracked`, `is_expiry_tracked`, `is_serial_tracked`, `qc_required_on_receipt`, temperature, hazmat, weight/volume (used by capacity and storage billing) |
| Pick faces (product_location_assignments) | Ops | min/max in base units; the hourly scan creates replenishment tasks |
| Putaway rules | Ops | Priority-ordered; default policy applies when no rule matches |
| Reason codes | Ops | Seeded; approvals flagged per code |
| Rate cards | Finance | One active card per client; charge codes listed in `docs/DATA-MODEL.md` |
| Users and roles | IT | Operators restricted to warehouses; client users to their client |
| AI policies | Ops | Start every use case in `advise`; move to `auto` only after reviewing accept/reject history |

## 2. A day in the life

**06:00 Inbound.** Confirmed ASNs with dock appointments arrive; the gate checks the trailer in (`yard/check-in`), moves it to a door. Receivers open a receipt from the ASN and scan each pallet: barcode → quantity → new LPN → lot/expiry if tracked. Over-receipts beyond the client tolerance need a supervisor (`force_over_receipt`). Damaged goods are received as damaged and routed to the damaged zone. Products flagged for QC land in quarantine; QC inspects and releases. Completing the receipt closes the ASN (short lines raise a `receiving_discrepancy` exception) and triggers inbound billing.

**Putaway.** Each received LPN becomes a putaway task with a suggested location from the rules (and, when enabled, an AI re-ranking). Operators pull the next task on RF, go to the suggestion, scan the location. Putting away elsewhere requires an override reason, which supervisors review in the audit trail.

**09:00 Outbound planning.** Orders arrive via API/portal. Confirm + allocate (FIFO/FEFO per product/client, min shelf life enforced). Supervisors preview a wave (by client, priority, cut-off, carrier), choose the pick method:
- *discrete* for large orders, *batch* for many small single-SKU orders, *zone* for pick-and-pass, *cluster* for e-commerce multi-order carts.
Releasing the wave creates pick tasks in walking order and sets orders to `picking`.

**Picking.** RF shows the next task; the operator scans a tote, then for each line: location → LPN (if any) → quantity. A short pick needs a reason; the system releases the shortfall, tries to re-allocate from another location (appending a line), and raises a `short_pick` exception for the supervisor (AI triage proposes a root cause and next step).

**Packing and shipping.** At the pack station, open a carton for the order, pack lines from the tote(s), close with weight/dims/tracking. Shipments group closed cartons per consignee/carrier; `packed` verifies completeness (partial allowed only if the order permits), `manifest` records the tracking/freight reference, `dispatch` moves stock out of the ledger and fires `order.shipped` (client webhook + billing).

**Continuous.** The hourly scan tops up pick faces from reserve (FIFO/FEFO from bulk). Expired lots are quarantined nightly. Cycle counts generated from plans are counted blind on RF.

**17:00 Supervisor review.** `GET /reports/dashboard`: late orders, open exceptions by severity, pending AI recommendations. Review count variances (four-eyes: the counter cannot approve), approve adjustments requiring approval, accept/reject AI recommendations.

## 3. Exceptions and what to do

| Exception | Raised when | Resolution |
|-----------|-------------|------------|
| `receiving_discrepancy` | over/under/unexpected/damaged on receipt | Confirm with client; adjust ASN expectations or accept; resolve with notes |
| `qc_failed` | inspection fails | Disposition the damaged stock (scrap adjustment / return to vendor) |
| `short_pick` | pick confirms less than requested | Check the location (AI suggests where), cycle count it, re-allocate the order |
| `count_variance` | count posted with variances | Investigate patterns (AI count anomaly report), tighten processes |
| `expired_stock` | lots within warning window | Prioritise sale/return; after expiry stock moves to `expired` and must be written off via adjustment |
| `replen_no_reserve` | pick face below min, nothing in reserve | Inbound planning with the client |
| `reconciliation_drift` | balance ≠ ledger | **Critical.** Stop writes to the affected balance, inspect `inventory_transactions` for the balance id, contact engineering. Should never happen |
| `unpriced_activity` | activity without a rate card line | Finance adds the charge code to the rate card |
| `ai_failure` | provider errors or cost cap | Operations continue on rules; check provider status / raise the cap |

## 4. Inventory integrity routine

- Nightly `wms:inventory:reconcile` must be drift-free; it is the proof that balances equal the ledger.
- Adjustments: reasons flagged `requires_approval` must be approved by a different user than the requester.
- Holds: placing a hold on allocated stock requires `cancel_allocations=true` explicitly; affected orders go back to `confirmed` and need re-allocation.
- Counts: stock that moved during a count invalidates the line (recount), never a blind adjustment.
- Whole-LPN moves carry allocations; loose moves of allocated stock are refused.

## 5. Billing close (monthly)

1. Finance checks `GET /billing/events?status=open&from=&to=` and `v_billable_summary`; resolves `unpriced_activity` exceptions.
2. Storage charges come from daily `storage_snapshots` (pallet-day, cbm-day, location-day; price the ones the client's rate card defines).
3. `POST /billing/periods {client_id, period_start, period_end}` → `/close` assigns open events, applies the minimum monthly charge, totals by charge code.
4. Export to SAP (`/export {export_reference}`); the `billing.period_closed` event is also available to the integration layer.

## 6. AI governance routine

- Weekly: review `GET /ai/usage` (cost, invocation statuses) and the accept/reject ratio per use case in `ai_recommendations`.
- Promote a use case to `auto` per client only when ≥ 90% of recommendations above the threshold were accepted for 4 weeks; start with `putaway_advisor`.
- Keep `nl_query` in `advise` (it only reads); keep `asn_extraction` in `advise` permanently (a person confirms every draft ASN).
- Prompt versions are in code; any prompt change bumps the version so old recommendations stay attributable.
