WebSocket Monitor & Print Job History
Scope: PWA settings screen for live Socket.IO diagnostics and recent thermal print queues. See also: KDS & printing model (three tracking entities), Print Bridge spec (agent lifecycle), Document print module (receipt/FEL jobs).
Purpose
The WebSocket Monitor (/settings/websockets) helps operators and developers at a location:
- See who is connected on the
/restaurantand/kdsSocket.IO namespaces. - Inspect recent kitchen thermal print jobs (
print_job) — the table behindKitchenPrintJobsTabContent. - Inspect recent document print jobs (
document_print_job) — receipts, bills, FEL, etc. - Preview ticket payloads and match device records to live socket sessions.
Route: apps/frontend-pwa/src/pages/settings/websockets/WebSocketMonitorPage.tsx
Also linked from kitchen station settings (StationSocketsPanel → “Open WebSocket monitor”).
Data sources on the page
| Section | TanStack Query key (approx.) | HTTP endpoint | Auth |
|---|---|---|---|
| Live connections | websocketStatus | GET /websocket-status?businessId=&locationId= | Firebase |
| Kitchen print jobs tab | kitchenPrintJobHistory | GET /print-jobs/history?locationId=&limit=20 | Firebase |
| Document print jobs tab | documentPrintJobHistory | GET /document-print-jobs/history?businessId=&locationId=&limit=20 | Firebase |
| KDS devices | kds-devices-location | GET /restaurant/kitchen/devices?... | Firebase |
| Kitchen stations | kitchen-stations-ws-monitor | GET /kitchen-stations?... | Firebase |
Poll intervals: WebSocket status and kitchen history 10 s; document history 15 s.
Implementation references:
- Frontend service:
apps/frontend-pwa/src/services/websocketStatusService.ts - Kitchen history controller:
apps/backend/src/restaurant/interfaces/kitchen.controller.ts(GET print-jobs/history) - Kitchen history query:
apps/backend/src/restaurant/infrastructure/print-job.repository.ts→findRecentByLocationId - Document history:
apps/backend/src/document-print/interfaces/document-print.controller.ts→GET history - WebSocket status:
apps/backend/src/restaurant/interfaces/websocket-status.controller.ts
Kitchen print jobs tab (print_job)
What each row represents
One row = one print_job record: a thermal kitchen ticket queued for a kitchen_station, usually tied to an order_item.
This is not the same as kitchen_ticket (KDS acknowledgement). The monitor shows printer queue state only. For the full KDS vs printer split, see KDS & printing model.
Test prints: When print_job.order_item_id is NULL, the UI sets isTestPrint: true and shows a test-print label instead of a product name (station test from hardware settings).
UI column → data source
| Column | Source |
|---|---|
| Job ID | print_job.id |
| Product | product.name via order_item.product_id, or test-print badge |
| Status | print_job.status (pending, sent, printed, failed) |
| Station | kitchen_station.name |
| Order / Table | order.document_number, dining_table.table_number |
| Error details | print_job.last_error |
| Created | print_job.created_at |
| Printed | print_job.printed_at |
| Preview | Client-side ticket preview from ticketData in API response |
Tables joined by findRecentByLocationId
Primary query (Kysely table names → PostgreSQL):
print_job
INNER JOIN kitchen_station ON kitchen_station.id = print_job.station_id
LEFT JOIN order_item ON order_item.id = print_job.order_item_id
LEFT JOIN order ON order.id = order_item.order_id
LEFT JOIN dining_table ON dining_table.id = order.table_id
LEFT JOIN product ON product.id = order_item.product_id
WHERE kitchen_station.location_id = :locationId
ORDER BY print_job.created_at DESC
LIMIT :limit -- default 20, max 100
Secondary query for modifiers (per distinct order_item_id in the result set):
order_item_modifier
WHERE order_item_id IN (...)
AND show_on_kitchen_ticket_snapshot IS NOT FALSE
Mapped through mapKitchenModifierLines() in @flowpos-workspace/receipt-layout (same helper as KDS print payload and document-print kitchen lines). See Kitchen ticket modifier rendering in menus-modifier-groups.
Entity relationship (kitchen history)
erDiagram
print_job ||--o| order_item : order_item_id
print_job }o--|| kitchen_station : station_id
kitchen_station }o--|| location : location_id
order_item }o--|| order : order_id
order_item }o--|| product : product_id
order }o--o| dining_table : table_id
order_item ||--o{ order_item_modifier : order_item_id
Related tables not loaded by this endpoint
| Table | Role | Why omitted |
|---|---|---|
kitchen_ticket | KDS bump/recall/void | Separate lifecycle; admin KDS uses tickets, not this history API |
product_station_assignment | Routing at send-to-kitchen | Used when jobs are created, not when listing history |
kds_device | Paired screens | Loaded separately on the monitor for socket ↔ device matching |
Bridge/agent operations use GET /print-jobs?status=pending&stationId= and PATCH /print-jobs/:id (device token), documented in Print Bridge spec and restaurant-api-postman.
Document print jobs tab (document_print_job)
Separate queue for structured receipt documents (not kitchen line items).
| Aspect | Detail |
|---|---|
| Table | document_print_job only (no joins in history list) |
| Scope | business_id + location_id |
| Module docs | Document print |
| Bridge | List/patch via device token; WebSocket document_print_job.new |
Columns on the monitor map directly to DocumentPrintJobSummary (jobKind, sourceType, sourceId, targetPrinterId, claimedBy, status timestamps, etc.).
Live WebSocket status
GET /websocket-status returns connection metadata for:
| Namespace | Typical clients | Room filter |
|---|---|---|
/restaurant | PWA POS, Print Bridge agent | Business + location rooms |
/kds | KDS kiosk / admin KDS | rst:{businessId}:{locationId}:all and station rooms |
Each client entry may include socketId, userId, userEmail, deviceId, deviceName, stationId, rooms, connectedAt. The monitor correlates deviceId with kds_device rows to show whether a paired device currently has an active socket.
Event payloads for real-time print (not the same as history REST):
| Event | Namespace | When |
|---|---|---|
print_job.new | /restaurant | Kitchen job created (send-to-kitchen) |
print_job.updated | /restaurant | Job status patched (bridge or network printer) |
document_print_job.new | /restaurant | Document job enqueued |
API quick reference
Kitchen history (Firebase — monitor page)
curl -X GET '{{BASE_URL}}/print-jobs/history?locationId={{locationId}}&limit=20' \
-H 'Authorization: Bearer {{ID_TOKEN}}'
- Required:
locationId - Optional:
limit(1–100, default 20) - Response: Array of jobs with
ticketData,stationName,isTestPrint, timestamps,lastError
Document history (Firebase — monitor page)
curl -X GET '{{BASE_URL}}/document-print-jobs/history?businessId={{businessId}}&locationId={{locationId}}&limit=20' \
-H 'Authorization: Bearer {{ID_TOKEN}}'
WebSocket status (Firebase)
curl -X GET '{{BASE_URL}}/websocket-status?businessId={{businessId}}&locationId={{locationId}}' \
-H 'Authorization: Bearer {{ID_TOKEN}}'
Debugging checklist
- Job ID in monitor but nothing printed — Check
print_job.statusandlast_error; confirm Print Bridge subscribed toprint_job.newfor thatstation_id; see Print Bridge spec. - KDS shows ticket, monitor empty for kitchen tab — KDS-only station may have
kitchen_ticketwithoutprint_job; expected per KDS model. - Wrong product/table on row — Trace
order_item_id→order/product/dining_table; modifier text comes fromorder_item_modifiersnapshots, not live catalog names. - Stale list — History is poll-based (10 s); live status changes also emit
print_job.updatedon/restaurant.
Related documentation
| Topic | Document |
|---|---|
print_job vs kitchen_ticket | kds-tracking-model.md |
Schema DDL for print_job | Restaurant.md |
| cURL examples (list/patch jobs) | restaurant-api-postman.md |
| Modifier snapshots on kitchen tickets | menus-modifier-groups.md |
| Document print queue | document-print/index.md |
Last updated: 2026-05-21