Plain-English summary
Printers connect receipt printers (for customer receipts at the counter) and kitchen printers (for order tickets in the kitchen). In the new stack, printing is a separate local-broker service (foodops.app.printing) — a desktop print client that runs on-site and talks to the physical printers; the counter app sends jobs to it over a local bridge. Printer hardware selection (which OS printer a station uses) happens in the broker / counter, not in the admin backoffice. The admin outlet page's Register and Kitchen forms only capture a Name — they do not carry printer/IP/category-routing fields.
When you'd use this
- Initial outlet setup — connect the thermal receipt printer at the counter
- Adding a kitchen station — route "food" categories to the hot-kitchen printer, "drinks" to the bar printer
- Troubleshooting "receipt didn't print" — check printer config, network, and category routing
- Replacing hardware — swap the old printer for a new one, update IP or USB port
How to access
Warning: Printer setup is NOT in the admin backoffice (new stack) In the retired
app.foodops.iomonolith, printer config was embedded in the outlet's Registers/Kitchens blocks. In the new stack that is gone: the admin backoffice Kitchen form (KitchenFormDialog.tsx) collects only a Name. The Register form (RegisterFormDialog.tsx) collects a Name plus auto-print toggles (KOT/receipt/receipt-on-new-order), invoice/credit-note/payment-receipt template selectors, receipt footer content, and a "use receipt printer template" toggle (plus, on the Salesmade brand only, an auto-print-mode selector with Off/Standard Printer/Thermal Printer options) — but still no printer IP, no paper-width, and no category-routing fields.[code: foodops-admin-app/apps/admin/src/components/settings/restaurant/KitchenFormDialog.tsx — single Name field @ main; RegisterFormDialog.tsx — expanded with template/receipt-print settings as of 93e9085..35c11eb]
Printer hardware is configured in the local print broker, not the web app:
- Print broker (desktop): the on-site
foodops.app.printingclient maps a station to an OS printer.[code: foodops.app.printing/Haafai.Printing/Components/PrinterStationEditor.razor — "Station: @Settings.StationName" + "Select a printer..." bound to Settings.AvailablePrinters @ master] - Counter app: prints by sending jobs to the local broker over a bridge; surfaces a connection state (
PrinterDisconnectBanner).[code: foodops.counter.app/src/components/printer/PrinterDisconnectBanner.tsx; src/services/printing/{brokerService,brokerQueueService,receiptBrokerBridge,kotMapper,receiptMapper}.ts @ main]

Printer addressing in the new stack — and what happens when a printer's IP changes
Code-derived from
foodops.app.printing@f6eb32a(master) andfoodops.counter.app@8faa421(main).
How a print job reaches paper
Two delivery paths, both ending at a named Windows printer on the outlet machine that runs the print client:
- Counter → local print broker: the counter posts pre-rendered KOT/receipt/cancellation jobs over HTTP to a broker on the outlet LAN (
/print/kot,/print/receipt,/print/cancelled-kot; base URL defaults tohttp://localhost:7070, with a per-outlet URL + enabled flag delivered by the session bootstrap)[code: foodops.counter.app/src/services/printing/brokerService.ts:24-110,478-536 @ main]. Manual receipt prints try the broker first and fall back to the legacy chain (cloud direct-receipt or browser print) when the broker is disabled or unreachable[code: src/services/printing/receiptBrokerBridge.ts:1-16 @ main]. - Cloud Print API → desktop print client: the on-site print client (
Haafai.Printing) keeps a SignalR connection to the Print API; aNewPrintJobpush makes it fetch the job, print it, and mark it done[code: foodops.app.printing/Haafai.Printing/Services/PrintJobService.cs:100-260 @ master; App.xaml.cs:85-86 starts the listener on app launch].
Printers are selected by Windows printer NAME — never by IP
The print client's Printer Settings page lists the machine's installed Windows printers and saves the selections locally on that machine:
- Kitchen stations: one dropdown per station (single printer), saved as
Selected{Station}Printer[code: Haafai.Printing/Components/Pages/Home.razor:80-94 (dropdown), 266-276 (list = PrinterSettings.InstalledPrinters), 310-315 (save) @ master]. - Registers (bills/receipts): a checkbox list per register — one register can fan bills out to multiple printers
[code: Home.razor:52-75 (checkboxes), 222-243 (list + saved selection), 318-323 (save) @ master].
At print time the client renders the job through the Windows printer driver (GDI PrintDocument drawing) and addresses the printer purely by that saved name (pd.PrinterSettings.PrinterName) [code: PrintJobService.cs:537 (KOT), 751 (bill) @ master].
FoodOps stores no printer IP address anywhere in the new stack [code: VERIFIED-ABSENT — grep of foodops.app.printing@master and foodops.counter.app@main printing services for IP-address / port-9100 / network-printer configuration returns no matching code]. The printer's network address lives only in the Windows printer definition (its driver + TCP/IP port), which is OS configuration outside FoodOps.
So what breaks when the printer's IP changes — and how to fix it
- FoodOps configuration is untouched. The saved printer name still matches; what broke is the Windows printer's port, which still points at the old address — so jobs the print client sends stop coming out of the physical printer.
- Fix it at the OS/network level: update the Windows printer's port to the new IP (or re-add the printer), and ideally give the printer a static IP or DHCP reservation so it can't drift again. No FoodOps-side change is needed as long as the Windows printer name is unchanged.
- If the printer was reinstalled under a different Windows name: the saved selection now points at a name that no longer exists. Open the print client's Printer Settings, reselect the printer for the affected station/register, and Save Changes
[code: Home.razor:308-323 — selections are keyed by printer name in the client's local storage]. - There is no rediscovery or auto-repair: the client simply prints to the saved name every time. Jobs pushed while printing is broken are not silently recovered — a job whose station/register has no printer selected is skipped, and a job whose print attempt errors is not marked printed
[code: PrintJobService.cs:225-252 (skip when unconfigured; job status updated only after a successful print), 686-692 (print errors rethrown)]. No automatic reprint loop exists in the shipped client, so once the printer is fixed, re-issue the print from the counter (reprint KOT / print the receipt again)[code: counter reprint support — src/services/printing/brokerService.ts:505-524 @ main].
The legacy IP-printer-per-register / category-routing-on-the-kitchen-block model below describes the retired monolith. The new model is broker-based; the steps below are kept for conceptual context and need a fresh broker-based walkthrough.
Prerequisites
- Physical printer connected to the network (IP-based) or USB
- Printer supports ESC/POS protocol (standard for thermal receipt printers — Star, Epson, Bixolon, etc.)
- For kitchen printers: at least one kitchen station configured in the outlet
Receipt printer setup
- In the outlet's Registers block, click the register that should use this printer
- Assign the printer by IP address or name (depending on the driver configuration)
- Set paper width (80mm standard for receipts, 58mm for compact)
- Test print — verify logo, layout, and text
Receipt layout is controlled by the PDF Templates settings. The thermal print adapter converts the template to ESC/POS commands.
Kitchen printer setup
- In the outlet's Kitchens block, click Add Kitchen (or edit an existing one)
- Name the kitchen station (e.g., "Hot Kitchen", "Bar", "Grill")
- Assign a printer to the station (IP or name)
- Configure Category Routing — assign product categories to this kitchen
- When an order containing items in those categories is saved, a kitchen ticket auto-prints at this printer
Info: Category routing drives BOTH KDS and kitchen printing The same routing configuration that puts items on the Kitchen Display also controls which kitchen printer gets the ticket. They're two outputs of the same routing decision.
Key concepts
- ESC/POS — the industry-standard protocol for thermal receipt printers. Most commercial POS printers speak it; non-ESC/POS printers (like office inkjets) won't work.
- Category routing — the mapping of product categories to kitchen stations/printers. "Shawarmas → Hot Kitchen" means orders containing shawarmas generate a ticket on the hot-kitchen printer.
- Receipt printer vs kitchen printer — receipt is customer-facing (at the counter, post-payment); kitchen is staff-facing (in the kitchen, post-order-acceptance). Different formats, different printers, different triggers.
- Paper width — 80mm is standard for receipts; 58mm for compact/handheld printers. Template rendering adapts to the width.
- Auto-print — kitchen tickets print automatically on order acceptance. Receipts print on-demand (cashier clicks "Print Receipt") or auto-print based on outlet settings.
Common questions
Q: What printer brands work with Foodops? A: Any ESC/POS-compatible thermal printer. Commonly tested: Epson TM-series, Star TSP-series, Bixolon SRP-series, and most generic 80mm Ethernet/USB thermal printers.
Q: Can I use a regular office printer for receipts? A: Not natively — Foodops generates ESC/POS commands, not PCL/PostScript. For emailed/PDF receipts, use the PDF Templates feature instead.
Q: My receipt isn't printing — what's the most common cause? A: In order: (1) printer is powered off / disconnected, (2) IP address changed (DHCP reassignment), (3) paper roll is out, (4) printer isn't assigned to the register in outlet settings.
Q: Can one kitchen station have two printers? A: One station = one printer. If you need redundancy, create a second station with the same category routing pointed at the backup printer.
Q: Do kitchen tickets show modifiers? A: Yes — each line item on the kitchen ticket includes its modifier selections (e.g., "Chicken Shawarma — Spice: Hot, Extra: Cheese"). Critical for kitchen accuracy.
Q: Can I customize what appears on the kitchen ticket? A: Kitchen ticket layout is less customizable than receipt layout — it's optimized for readability in a busy kitchen (large font, item names, modifiers, order number, table number). Custom fields via PDF Templates don't apply to kitchen tickets.
Q: My printer's IP address changed and bills stopped printing — where do I fix that in FoodOps? A: Nowhere in FoodOps — the new stack never stores the printer's IP. The print client targets the printer by its Windows printer name, and the IP lives only in that Windows printer's port settings. Fix the Windows printer's port (point it at the new IP, or re-add the printer) and give the printer a static IP/DHCP reservation so it can't happen again. If the printer was re-installed under a new Windows name, reselect it in the print client's Printer Settings and save. Nothing needs changing in the admin app or the counter, and there's no auto-reprint of jobs that failed in the meantime — re-issue those from the counter.
Q: Can one register print bills to two printers at once (new stack)? A: Yes — in the print client's Printer Settings, register printers are a checkbox list, so a register can fan a bill out to multiple Windows printers simultaneously. Kitchen stations remain single-printer (a dropdown per station).
Edge cases and known issues
Warning: Printer shows offline despite being connected Symptom: Printer is physically on, network-connected, but Foodops says "offline" Cause: IP changed (DHCP lease expired), or a firewall rule blocking the port Workaround: Set a static IP for the printer on your router; verify with
ping <ip>from the register device
Warning: Kitchen ticket printing for wrong kitchen station Symptom: "Bar" orders printing on the "Hot Kitchen" printer Cause: Category routing misconfigured — the "Beverages" category is assigned to "Hot Kitchen" instead of "Bar" Workaround: Settings → Outlets → Kitchens → edit the station → fix category assignments
Plan limits
Plan tiers and their limits are not listed here — they are DB-backed and change with the lineup. See Pricing for the live catalogue.
What changed since 2026-04-15 (NEW 2026-05)
Counter-app printer connection debug + UX polish (commits
44f1dba2026-04-23 and16606a92026-04-24, repofoodops.counter.app) — the printer-connection banner / debug surface in the React counter app received fixes, and the silent-background-refresh feature touched printer state propagation. Blocked 2026-05-04: the PrinterDisconnectBanner appears only when a printer is configured and disconnected — the E2E org has no printer configured, so the banner state could not be triggered or verified. Verify against an outlet with a printer assigned where the printer can be physically disconnected during a session.No changes since 2026-04-15 to the Razor-monolith printer-config paths (
Areas/Settings/Registers,Areas/Settings/RestaurantKitchens,Printing/Hubs). PrintHub last touched on 2026-04-09 (6992edfSignalR/caching refactor — pre-cutoff).2026-08-20 — counter.app extended the session close-summary thermal print (feature added 2026-07-31, commits 5468530..5ac2982, still not yet folded into this entry's main body) with two further changes, commit range 964ff7e..fe34047, repo foodops.counter.app (commit subjects: 'discard log tab + export, route reprint by session register'; 'mode-gated discard review (acknowledge vs approve/reject)'). (1)
PrintSessionSummaryRequest(src/services/printing/brokerService.ts) gains an optionaldiscardLog?: DiscardLogfield, andtoPrintSessionSummaryRequest(sessionSummaryMapper.ts) now copiessummary.discardLogonto it — so the printed/broker-submitted session summary can now carry the shift's discard (void-bin) log alongside the existing tenders/sales totals. (2)printSessionSummaryThermal(sessionSummaryActions.ts) now setssalesRegisterId: summary.salesRegisterId || snap?.registerIdinstead of unconditionally using the live session snapshot's register id — so printing/reprinting a summary for a session correctly routes to THAT session's register rather than whatever register the current live snapshot happens to be on (matters for reprints after a register/session switch). Both changes are user-facing only through the not-yet-documented broker-based session-summary print feature; they don't touch any printer/IP/routing surface this entry currently documents (KOT/receipt/cancelled-kot broker jobs, Windows-printer-name selection, no-IP-stored claim), so no edit to the current entry text is warranted. Worth folding in — alongside the 2026-07-31 session-summary note — once the broker-based session-summary walkthrough is written. (auto-applied by the truth pipeline; adversarially gated)2026-08-06 — counter.app fixed a manual-reprint tax-display bug on the broker receipt path (commits db87fa0..3f286e8, 'fix(receipt): restore multi-tax split on manual reprint', repo foodops.counter.app). Previously, manually reprinting a receipt for an already-synced/paid order could collapse a multi-component tax (e.g. CGST/SGST) into a single aggregate 'Tax Total' line, because the create-time
_offline_taxessnapshot is wiped on sync and the resolver's detail cache is cold for a paid order.buildReceiptOrderFromLocalOrder(src/services/printing/receiptBrokerBridge.ts) now falls back to a new best-effort recovery step when the stored breakdown is empty: it loads each line item's variant tax components from the menu cache and, via the newrecoverUniformTaxBreakdown/splitTaxTotalhelpers (src/services/printing/recoverTaxBreakdown.ts), proportionally re-splits taxTotal across those components — but ONLY when every taxed line shares the identical 2+-component tax signature; mixed tax schemes or single-tax orders still print one aggregate line, and any menu-lookup failure is swallowed (logs a warning, falls back to the empty split) rather than breaking the receipt. This is user-facing: staff manually reprinting a receipt on the broker path will now see the correct multi-line tax breakdown instead of a collapsed single line, for outlets with a uniform multi-component tax setup. Worth a line once the broker-based receipt walkthrough is written (alongside the existing multi-currency-settlement and session-summary notes in this section) — no printer/IP/routing UI or documented printer-config claim is affected, so no edit to the current entry text is warranted. (auto-applied by the truth pipeline; adversarially gated)2026-07-31 — counter.app added session close-summary thermal printing via the broker (commit range 5468530..5ac2982, repo foodops.counter.app, feat commit 'session close-summary print plumbing — types, offline cache, broker mapper + submitSessionSummary'). New broker method
brokerService.submitSessionSummary()POSTs aPrintSessionSummaryRequest(jobId, optional salesRegisterId/printerId override, copies, printedBy, plus the full session-close payload — reference, outlet/register names, opened/closed-by + timestamps, currency, tenders[] with opening/expected/counted/difference per tender, salesCounts[], salesTotals[]) to a NEW broker endpoint/print/session-summary, idempotent on jobId — a fourth job type alongside the documented/print/kot,/print/receipt,/print/cancelled-kot. The new actionprintSessionSummaryThermal(sessionId)(src/services/printing/sessionSummaryActions.ts) refuses to run if no broker is configured (toasts 'No print broker is configured on this device.'), otherwise fetches the close summary LIVE (getCloseSummary(sessionId, true)) so the printed slip matches the on-screen Day Summary, and submits it via the broker with a fresh client-issued jobId. A parallel non-thermal path,saveSessionSummaryPdf(sessionId, reference), downloads a server-rendered PDF of the same summary via an anchor click (no broker involved). The mappertoPrintSessionSummaryRequest(sessionSummaryMapper.ts) is a straight 1:1 field copy from the server's SessionCloseSummary DTO. This is new user-facing behavior — staff closing a register session can now print (or PDF-save) a physical copy of the session/day summary — worth a line under 'How a print job reaches paper' (as a fourth broker job type) and a new Common-questions entry once the broker-based walkthrough is written. Not yet reflected inverified_against_commitfor foodops.counter.app (currently pinned at8faa421, which predates this range). (auto-applied by the truth pipeline; adversarially gated)2026-07-17 (doc-as-test, live UI walk on counter.salesmade.io + admin — sprint Track C): PASS (web-walkable claims) / N-A (desktop-client claims, as this entry expects). The load-bearing claim — no printer IP field anywhere in the web UI — confirmed: counter Settings → Print Broker tab shows a display-only broker URL (default
http://localhost:7070, Status + Recheck + per-OS installer downloads) with no IP input; the POS Register Options dialog and admin outlet/Register edit forms enumerate no host/IP/port field. A live console line[BrokerUpdate] print broker not reachable at http://localhost:7070/...independently confirmed the documented default broker base URL. Windows-desktop-client specifics (name-based printer picker) remain N-A — no web surface exists, exactly as the frontmatter notes. Screenshots 220-224. (Platform-map note, separate correction:admin.salesmade.iodoes NOT resolve — the live admin app isapp.salesmade.io, getent-confirmed.)2026-07-16 (codebase, gap-mining sprint — TRUE_GAP: bill printing after a printer's IP address changes): added section "Printer addressing in the new stack — and what happens when a printer's IP changes" + 2 Q&A. Verified against
foodops.app.printing@f6eb32a(master) andfoodops.counter.app@8faa421(main): the shipped print client selects printers fromPrinterSettings.InstalledPrintersby Windows printer NAME (station = single dropdown saved asSelected{Station}Printer; register = multi-select checkbox list saved asRegister{id}Printers) and prints via GDIPrintDocumentwithPrinterSettings.PrinterName; NO IP-address configuration exists anywhere in the new-stack printing code (verified-absent), so an IP change is a Windows-printer-port problem, not a FoodOps-config problem; jobs are pushed via SignalRNewPrintJob, skipped when no printer is selected, and left un-marked when the print attempt errors; no retry/reprint loop exists in the shipped client. Counter side re-confirmed at 8faa421: broker base URL resolution (per-outlet session-bootstrap config, default localhost:7070), broker-first-with-legacy-fallback receipt policy, reprint-KOT support. Repinned counter.app; extended the printing glob withComponents/Pages/Home.razor. Claim sidecar seeded (_meta/claims/FoodOps__settings__printers.yml).2026-07-11 — counter.app receipt broker now supports per-tender multi-currency settlement (ADR-0011, commits e4e7aaa..b120e71). Previously every payment line on a broker-printed receipt rendered in the outlet's single base currency (
buildSettlementFromLocalOrderappliedMath.min(raw, remaining)directly against the base grand total). NowtryReceiptViaBrokerbuilds acurrencyByIdmap from the session'senabledCurrencies(currencyId → {symbol, exchangeRate}), and each payment line inbuildSettlementFromLocalOrderprints in its OWN native currency: a foreign tender (e.g. USD Cash) shows the native tendered amount and its own currency symbol, carries newuseDifferentCurrency/exchangeRatefields, while the running balance/change/outstanding calculation still tracks and clamps in the outlet's base currency (native × configured fixed rate). This is a new user-facing receipt behavior for multi-currency outlets — worth a line under 'What changed' or 'Key concepts' once the broker-based receipt walkthrough is written, since staff working a multi-currency till will now see native-currency amounts + exchange rate on printed receipts rather than a single base-currency figure. Not yet reflected inverified_against_commitfor foodops.counter.app (currently pinned at047ad31, which predates this range). (auto-applied by the truth pipeline; adversarially gated)2026-07-06 — counter.app broker submission got materially more resilient (commits b813f61..047ad31, esp.
fix(print)andfeat(broker): fail-fast KOT submit via a circuit breaker). Previously a down/unreachable print broker meant every KOT submission burned the full 4s connect timeout (and reported a confusing raw 'signal is aborted without reason' error) before falling back to the offline queue. NowbrokerService.tstracks broker reachability with a 10s circuit breaker: a timeout orTypeError: Failed to fetchopens it, any successful HTTP reply (including 5xx) or a health() probe closes it. While open,submitKotWriteAhead(brokerQueueService.ts) skips the network call entirely and immediately re-arms the write-ahead row for the background drainer — so the counter no longer stalls per-order during a broker outage, and timeout errors surfaced to logs/toasts now read 'Broker timeout after 4000ms' instead of the opaque AbortController message. This doesn't change any documented setup/config step (no printer/IP/routing UI touched), but is worth a line in 'Edge cases and known issues' or the broker-outage troubleshooting guidance once the broker-based walkthrough is written, since it changes what staff actually observe (fail-fast + auto-retry) when the print broker goes offline mid-shift. (auto-applied by the truth pipeline; adversarially gated)
Warning: Unverified — status:
draftVerified_by:unverified· Last verified: not recorded This entry was written from Settings UI observation (outlet Kitchens block visible in screenshots) and codebase knowledge, but no physical printer test was performed. Printer connection, ESC/POS rendering, and auto-print trigger behaviour are documented from product knowledge, not from a live test session. Flag discrepancies to the KB maintainer.
Note: Code-verified 2026-05-04 · codebase Verified_by:
codebase· Last verified: 2026-05-04 ·haafai.app.foodops:6992edf+foodops.counter.app:16606a9Curatedsource_repos[]across two repos: the Razor monolith config paths (Haafai.Restaurant.App/{Areas/Settings/{Registers,RestaurantKitchens},Printing/**}) where printer assignment to registers + kitchen routing lives, and the React counter-app printer surface (foodops.counter.app:src/components/printer/**) where the connection banner and debug UX live. Printer-config Settings UI in the monolith is unchanged since 2026-04-15; counter-app printer UI saw44f1dba2026-04-23 (printer connection debug) and16606a92026-04-24 (silent refresh) — see "What changed" above. HIGH-drift flag was partly real (counter-app side) and partly false-positive (config side); curated globs distinguish the two.
Note: Re-pointed after backend microservice split 2026-06-03 · codebase Verified_by:
codebase· Last verified: 2026-06-03 ·haafai.app.foodops:bf7c9d2+foodops.app.printing:f6eb32a+foodops.counter.app:e002374The 2026-05-02 backend split stood up a dedicated printing service,haafai/foodops.app.printing(prod branchmaster) — MAUI desktop print client (Haafai.Printing) + Print API (Haafai.Printing.Api). Added it tosource_repos[]with a precise glob covering the printer-station config UI (Haafai.Printing/Components/PrinterStationEditor.razor), the receipt/job print services (Haafai.Printing/Services/**), the API client layer (Haafai.Printing/Clients/**), and the Print API (Haafai.Printing.Api/{Controllers,Settings}/**). All paths verified viagh api ...?ref=master. The existing monolith glob (Registers/RestaurantKitchens/Printing/**— where printer→register assignment + category routing + the SignalRPrintHublive) still resolves onmain; re-pinnedhaafai.app.foodopsto prod HEADbf7c9d2. Counter-app printer globs (src/components/printer/**,src/services/printer/**,src/services/printing/**) all still resolve onmain; pin already at prod HEADe002374. Did NOT addfoodops.api(legacy) orhubits.microservices(separate ERP effort).
Note: Re-verified 2026-06-04 · codebase — no user-facing change Verified_by:
codebase·haafai.app.foodops:bf7c9d2+foodops.app.printing:f6eb32a+foodops.counter.app:e002374Re-checked all printer-config source paths against prod HEADs; all pins already current.foodops.app.printing@masterHEAD =f6eb32a(pin); only in-window commit is a backend "Fix print for salesmade" (3bdeae3, 2026-05-12), no printer-config UI change. counter.app printer paths: zero commits since cutoff. MonolithRegistersControllersawe789e90(mandatory-cover-selection toggle — register cover setting, not printer config) andfe6cf10("Logo printing issue" — backend logo-render fix); neither changes printer setup/assignment behaviour. No content edit needed.
Important: Re-verified 2026-06-29 · codebase — NEW-STACK re-point (legacy app.foodops retired) Verified_by:
codebase·foodops.app.printing:f6eb32a(master) +foodops.counter.app:32669a9(main) +foodops-admin-app:f6ffd0b(main) Legacyhaafai.app.foodopsremoved fromsource_repos. KEY CORRECTION: printer setup is no longer in the web app. The admin-app Register/Kitchen dialogs are Name-only (no printer/IP/paper-width/category-routing fields) — confirmed by readingRegisterFormDialog.tsx+KitchenFormDialog.tsx. Printer hardware selection lives in the local print-broker (foodops.app.printingPrinterStationEditor.razor: station → OS printer) and the counter prints via a broker bridge (src/services/printing/*,PrinterDisconnectBanner). Re-pointed "How to access" accordingly. RESIDUAL: the ESC/POS / paper-width / category-routing details below are legacy-monolith-derived and need a fresh broker-based walkthrough; where (if anywhere) kitchen category→station routing is configured in the new stack is not first-party-confirmed — flag for re-check. Entry remainsdraft/low-confidence.
Related
- Settings Overview — outlet configuration where printers are managed
- Kitchen Display (KDS) — shares category routing with kitchen printers
- POS Takeaway Order — receipt printing post-payment
- PDF Templates & Transaction Series — receipt visual customization