Scan Console
Operator scanning, identifier management, and label printing.
Overview
The scanning layer turns a scanned code — a barcode, QR code, or asset number — into a platform action. It resolves the code to an asset, flightcase, or catalogue item, then hands it to a context handler that drives the right workflow: dispatching kit, receiving returns, counting a stocktake, receiving a transfer, or just looking something up. Every scan is recorded, whatever the outcome.
Scanning UI routes:
| Route | Purpose |
|---|---|
/scanning/console |
Operator scan console |
/scanning/identifiers |
Scannable identifier index |
/scanning/generation-rules |
Identifier generation rules |
/scanning/generation-rules/create |
Create a generation rule |
/scanning/generation-rules/{rule}/edit |
Edit a generation rule |
/scanning/sessions |
Scan session history |
/scanning/sessions/{scanSession} |
Session detail with event feed |
The scan console
The console is the operator's main surface. Pick a context, choose a warehouse and (where relevant) a target, and start scanning. The context picker is data-driven — new context handlers, including plugin-provided ones, appear automatically without any console change.
The console provides:
- an always-focused input for keyboard-wedge scanners (it refocuses on blur and submits on Enter),
- a live, most-recent-first outcome feed — success, warning, blocked (with a human reason), or unknown, each with the resolved entity and a timestamp,
- expected-set progress (X of Y, remaining and unexpected) using per-context wording (counted / received / scanned),
- a loose quantity prompt for stocktake counts that writes one authoritative count,
- lookup cards with live status, current rental, equipment test due state, and deep links, and
- close / abandon / new session controls with a final session summary.
Every mutation flows through the same action classes the rest of the platform uses, so all guard gates apply. A refusal — for example a blocked dispatch gate — surfaces as an outcome row with a reason, never a raw error.
Contexts
| Context | What a scan does |
|---|---|
| Check-out | Allocates if needed and dispatches through the real dispatch actions (all gates apply) |
| Check-in | Returns items, including flightcase verify flows |
| Stocktake | Feeds counts into the same seam as manual entry |
| Transfer | Records line receipts at the destination |
| Lookup | Read-only entity card — no changes made |
| Prep | Shows due/overdue equipment-tests with a perform deep link |
| Sub-hire receiving | Receives against a virtual stock intake |
Identifiers & labels
A scannable identifier is a unique code tied to an asset, flightcase, or catalogue item. From the identifier surfaces you can assign, replace, and deactivate identifiers (history is retained), and generate identifiers singly or in bulk against a generation rule that defines the prefix and sequence pattern.
Labels print through the Document Template Engine — a label document type on custom page dimensions renders dependency-free Code128 barcodes, printed one at a time or in batches through the normal PDF pipeline. Identifier panels appear on the asset, flightcase, and catalogue item detail pages.
Plugins
Two contracts let plugins extend scanning without touching core: RfidReaderContract (a plugin implements only the reader; the core batch processor dedupes, resolves, dispatches, and records) and LabelPrinterContract (a plugin consumes rendered labels and returns typed results). See the Plugin SDK.
Stale session expiry
The scheduled command signals:expire-scan-sessions runs hourly. Open sessions whose started_at is older than the configured staleness window (scanning.session_expiry_hours, default 24) are automatically abandoned.
Permissions
| Permission | Grants |
|---|---|
scanning.access |
Use the console and read sessions/identifiers |
scanning.scan |
Submit scans |
scanning.manage_sessions |
Open, close, and abandon sessions |
API
See the Scanning API for session and identifier endpoints.