Plain-English summary
A Kitchen Order Ticket (KOT) is the slip — printed on a kitchen printer or shown on a kitchen-display screen — that tells the kitchen what to prepare. It is created when a cashier or server taps Send KOT on an order ticket, locking those lines into a "sent" state. The KOT carries the order's items, modifiers, prep notes, and order context (table number, takeaway flag, customer name where relevant) but not pricing — the kitchen does not need amounts. KOTs are also the unit the kitchen marks as prepared; that mark flows back to the order ticket so the front-of-house knows what is ready.
When you'd use this
- A cashier taking a counter or dine-in order needs to push items to the kitchen — Send KOT is how that happens.
- A server splitting a course across two prep cycles wants to send a KOT for starters now and another for mains later.
- A KOT printer is offline and orders are queueing — understanding what a KOT is helps debug whether the issue is with the print job, the kitchen display, or the order itself.
- A new staff member is being trained on the kitchen-display workflow and asks "what's a KOT?".
- A drift-detection scenario where the order's "sent" line state diverges from what the kitchen actually received.
What appears on a KOT
A standard Foodops KOT carries [code: hubits-api-sales/Hubits.Sales.Api/Controllers/OrderPrintController.cs:172-276 GetKOTPrintData @ main (the print-data payload); rendered by the local print broker foodops.app.printing/Haafai.Printing/Services/PrintJobService.cs:615-705 ProcessKotPrintJob @ master]:
- Order reference — the order number/ID. Lets the kitchen call out the order when ready, and lets the front-of-house tie a finished plate back to the right ticket. (KOT print payload carries both the KOT
referenceand the parent order'sreference.) - Order type — Dine-in / Takeaway / Delivery, plus the relevant context: table number for dine-in, customer name + mobile, and shipping address for delivery. See order types.
[code: OrderPrintController.cs:81-131 ManualPrintKitchenOrderTicket — restaurantOrderType / restaurantTable / customer.name+mobile / shippingAddress @ main] - Items, quantity, and modifiers — line by line, including item-level note, allergy flag, and a per-item takeaway flag. Modifiers are listed under each item ("no onions", "extra cheese").
[code: OrderPrintController.cs:143-163 — quantity / productName / modifiers / note / isAllergic / allergies / forTakeAway @ main] - Timestamp — the order date / created date is carried in the print payload.
- No prices — pricing is intentionally absent. The KOT print-data endpoints (
GetKOTPrintData/ManualPrintKitchenOrderTicket) carry no price or tax fields, and the broker's KOT slip shows no price column; per-item amounts on the auto-print path are explicitly zeroed.[code: OrderPrintController.cs:172-276 GetKOTPrintData / :57-169 ManualPrintKitchenOrderTicket — no price/tax fields @ main; :1607-1608 auto-print item Rate=0, Amount=0 @ main; contrast GetReceiptPrintData:284-564 which carries grossTotal/taxTotal/taxes @ main]
The exact layout depends on the printer's column width (usually 80mm thermal) and on outlet-level configuration. Foodops supports both a "by-order" KOT layout (one ticket per order) and an "itemwise" layout (one physical slip per quantity unit of each item) — gated by the org setting EnableItemwiseKotPrinting, applied when the KOT print jobs are generated. [code: hubits-api-sales/Hubits.Sales.Api/Controllers/OrderPrintController.cs:1524-1530 itemwise = EnableItemwiseKotPrinting setting, :1619 itemwise branch fans out per-unit jobs @ main] (Itemwise is independent of multi-station routing — see Multi-printer section.)
Note (changed at the July-10 launch): the food / "Drinks" within-slip split was a legacy
haafai.app.foodopsRazor-view rendering (_KitchenOrderTicketPrint.cshtml), driven by the per-itemShowOnBeverageOrderTicketflag. That legacy view is retired. The flag is still carried in the KOT print-data payload (OrderPrintController.cs:156,264 showOnBeverageOrderTicket @ main), but the new print broker does not render a separate Drinks section —PrintJobService.ProcessKotPrintJobdraws one item list per KOT with no beverage subsection (foodops.app.printing/Haafai.Printing/Services/PrintJobService.cs:615-705 @ master— no beverage/drinks handling). So on the new stack a KOT slip is a single item list; physical separation of bar drinks from food is done only by kitchen-set /ProductVariantKitchenrouting (see Multi-printer section), never by a within-slip Drinks subsection.
When does a KOT fire
The KOT is created the moment the cashier or server taps Send KOT on the order-taking screen. Effects of sending:
- A KOT record is created server-side and attached to the order.
[code: hubits-api-sales/Hubits.Sales.Api/Services/Counter/SalesOrders/SalesOrderHelper.cs:580-938 CreateKitchenOrderTicket @ main] - Items are routed to the kitchen(s) they belong to and a KOT is created per kitchen-set (see Multi-printer section below).
- The KOT is delivered to its destination(s):
- Kitchen printer — if a printer is configured and the register has
AutoPrintKOTenabled, the KOT prints automatically. Multi-printer setups (one per prep station) can split a single order across multiple physical prints.[code: hubits-api-sales/Hubits.Sales.Api/Controllers/OrderPrintController.cs:1362+ AutoPrintKitchenOrderTicket @ main; CancelledKotPrintService.cs:74 gated on register.AutoPrintKOT @ main] - Kitchen Display — KOTs also appear on screen-based kitchen displays in real time via SignalR (
KitchenOrderAddedevents), regardless of printer status.[code: hubits-api-sales/Hubits.Sales.Api/Controllers/OrdersController.cs:3819-3826 KitchenOrderAdded SignalR push to organization group after CreateKitchenOrderTicket (line 3782); a parallel KitchenOrderUpdated fires at :3829-3835 for edits @ main]
- Kitchen printer — if a printer is configured and the register has
A single order can have multiple KOTs over its lifetime — the cashier can add more lines after the first send and tap Send KOT again. Each Send creates a new KOT for the new lines only, not a duplicate of the previous KOT: CreateKitchenOrderTicket diffs the existing (non-cancelled) KOT items against the current order line items and only generates KOT items for the added lines/quantities. [code: hubits-api-sales/Hubits.Sales.Api/Services/Counter/SalesOrders/SalesOrderHelper.cs:634-720 previous-vs-current KOT-item diff @ main]
Voids, edits, and re-sends
Once a line is on a KOT, the kitchen has either prepared it or is about to. Editing or removing the line means the kitchen has either wasted ingredients or needs to know to stop. Two patterns:
- Void a sent line — voiding/removing a line does NOT delete the KOT from the kitchen's record. It flips the corresponding KOT item to
IsCancelled = true(withCancelledAt); the parent KOT is itself cancelled in two cases — when the sum of its item quantities reaches zero, or when all of its items are cancelled and prep hasn't started (!HasPreparingStarted).[code: hubits-api-sales/Hubits.Sales.Api/Services/Counter/SalesOrders/SalesOrderHelper.cs:663-693 removed-item → IsCancelled/CancelledAt (672-673); KOT cancelled if items-quantity-sum == 0 (678-684) OR all items cancelled && !HasPreparingStarted (686-692) @ main]The kitchen is told about the cancellation in two ways: (1) a cancelled-KOT slip prints at each kitchen the KOT routes to — but only when the register hasAutoPrintKOTenabled and a printer is connected[code: hubits-api-sales/Hubits.Sales.Api/Services/Counter/Printing/CancelledKotPrintService.cs:44-170 FirePrintsForRecentlyCancelledAsync, gated on register.AutoPrintKOT + connected printer @ main]; and (2) the Kitchen Display is signalled to refresh via SignalR so the cancelled state shows there too[code: hubits-api-sales/Hubits.Sales.Api/Controllers/OrdersController.cs:2962-2998 OrderCancelled + KitchenOrderAdded SignalR push @ main]. The kitchen needs to physically discard or redirect whatever they were preparing. - Modify a sent line — changing an already-sent line (qty, modifiers, note, allergy) no longer prints a cancel slip; the kitchen gets one consolidated MODIFIED slip carrying the specific change (Modifiers / Note / Allergy / Qty). In the Reprint dropdown, KOTs whose items are all superseded by the modify (modify-replaced / decrease-consumed / removed) are hidden, so only the current MODIFIED slip shows.
[code: foodops.counter.app/src/components/orders/OrderDetailsPanel.tsx:2008-2009 hide all-superseded KOTs; src/offline/kot/diffKot.ts + src/services/printing/kotMapper.ts MODIFIED-slip mapping @ main] - Add new lines — the new lines stay un-sent until the next Send KOT tap. The original KOT is unchanged.
Re-sending an entire KOT (e.g., because the printer was offline) is a separate action — Reprint KOT, available from the order detail panel. Reprinting is a print-only operation; it mints a fresh reprint job (rendered with a *** REPRINT *** banner) and does not create a new KOT record or notify the kitchen of "new" items. [code: foodops.counter.app/src/components/orders/OrderDetailsPanel.tsx:254-360 handleReprintKot / tryReprintViaBroker @ main; foodops.counter.app/src/offline/actions/printActions.ts:136-183 queueKotPrint → POST /orders/auto-print-kot (reuses existing kotId, no new KOT) @ main]
Kitchen marks items prepared
When the kitchen marks an item or whole KOT as prepared (ready) on the Kitchen Display, that signal flows back to the front-of-house: [code: hubits-api-sales/Hubits.Sales.Api/Controllers/OrderKitchenController.cs:483-620 MarkMultipleReady @ main]
- The ready flag is mirrored from the KOT item onto the underlying
SalesOrderLineItem(IsReady/ReadyAt), so the counter order list and KDS both see the prepared state.[code: OrderKitchenController.cs:519-523 mirror IsReady onto SalesOrderLineItem @ main] - When every (non-cancelled) item on a KOT is ready, the KOT is marked completed, and when an order's prep is done its status advances to
Ready(setsHasPrepared,PreparedTime).[code: OrderKitchenController.cs:559-585 ticket IsCompleted + order → RestaurantOrderOperationStatus.Ready / HasPrepared @ main] - A real-time
KitchenStatusUpdatedSignalR event is pushed to the organization so servers see which dishes are ready to pick up.[code: OrderKitchenController.cs:615-631 KitchenStatusUpdated SignalR push to organization-{orgId} group @ main]
This back-channel is part of why KOTs are not just print jobs — they are first-class objects the system tracks through their prep lifecycle.
KOT vs receipt
Easy confusion. Both are printed slips; both come out of a printer near the cashier; both relate to the same order. The differences:
| KOT | Receipt | |
|---|---|---|
| Audience | Kitchen / prep staff | Customer |
| Contents | Items, modifiers, prep notes, order context | Items, prices, taxes, discounts, payment method, change |
| Triggered by | Send KOT (mid-flow, before payment) | Pay & Close (after payment) |
| Printer | Kitchen printer (thermal, often in the back) | Counter receipt printer |
| Pricing | Never | Always |
A single order generates at least one KOT (when items are sent to prep) and at least one receipt (at payment). They are not interchangeable.
Note (multi-GST, 2026-06-02): the
bc04a77multi-GST change adds a per-component tax breakdown (e.g. one row each for CGST / SGST / GST) to the receipt print data, summing to the existing tax total[code: hubits-api-sales/Hubits.Sales.Api/Controllers/OrderPrintController.cs:497-518 GetReceiptPrintData @ 5274891]. KOTs are unaffected — the KOT print endpoints carry no tax or price fields, consistent with the no-prices rule above[code: OrderPrintController.cs:56-162 ManualPrintKitchenOrderTicket / GetKOTPrintData @ 5274891].
Multi-printer / multi-station setups
In larger kitchens, prep is often split across stations (e.g., grill, pastry, bar). Foodops supports routing different parts of an order to different kitchens/printers: [code: hubits-api-sales/Hubits.Sales.Api/Services/Counter/SalesOrders/SalesOrderHelper.cs:794-980 CreateKitchenOrderTicket kitchen-set routing @ main]
- Each product variant is mapped to one or more RestaurantKitchens (
ProductVariantKitchen), which is how an item is tagged with its target station(s).[code: SalesOrderHelper.cs:799-815 resolve item's RestaurantKitchen set via ProductVariantKitchen @ main] - When Send KOT fires, items are grouped by their (sorted) kitchen-set, and one KOT is created per kitchen-set with every routed kitchen linked via
KitchenOrderTicketKitchens— so each kitchen's printer gets its own copy.[code: SalesOrderHelper.cs:815-938 group by kitchen-set, KOT per group, KitchenOrderTicketKitchens links @ main] - Each printer prints only its relevant lines — the grill printer does not get the bar's drinks. (The legacy within-slip food/"Drinks" layout split is retired with the old Razor view; the new broker renders one item list per KOT — see the Note above.)
The split is configured at the product/menu level (kitchen assignment) plus at the outlet level (which kitchens belong to the outlet). Misconfiguration produces "missing items in the kitchen" symptoms — the items printed somewhere, just not where the cook expected. The kitchen display shows the full picture and can be useful for triangulating.
Key concepts
- KOT (Kitchen Order Ticket) — the printed or digital slip sent to the kitchen at Send KOT, containing items + modifiers + context but no pricing.
- Send KOT — the cashier action that creates a KOT and locks the corresponding ticket lines.
- Kitchen Display — screen-based alternative or supplement to printers; receives KOTs in real time. See Kitchen Display.
- Itemwise KOT — alternative layout that splits one ticket per item or per category (vs the default by-order layout). Useful for multi-station prep.
- Void — marking a sent line as cancelled. Notifies the kitchen; does not delete the KOT.
- Reprint — printing the same KOT again on the same or different printer. Print-only; no new KOT object.
- Prepared flag — the back-channel signal from kitchen to ticket indicating an item is ready.
Common questions
Q: Why does the KOT not show prices? A: The kitchen prepares food, not bills. Prices on a KOT are noise that slows prep and creates discounts/waste-tracking confusion. The receipt the customer gets has prices; the KOT does not.
Q: Can I edit an item after I've sent the KOT? A: Only by voiding it. The line is locked once sent because the kitchen has either started prep or queued it for prep. To change quantity or modifiers, void the existing line and add a new one — the kitchen sees both events.
Q: I sent a KOT but the kitchen says they didn't get it. What's wrong? A: Three usual suspects: (1) the kitchen printer is offline or out of paper — check the printer; (2) item-to-printer routing is wrong, so the KOT printed elsewhere — check the menu's printer assignment; (3) the kitchen-display device is disconnected — check the network and reconnect. The KOT itself almost always exists server-side; the question is delivery.
Q: Can I reprint a KOT?
A: Yes — via the Reprint KOT action in the order detail panel. Reprinting does not create a new KOT object; it just re-runs the print job for the existing KOT (printed with a *** REPRINT *** banner). [code: foodops.counter.app/src/components/orders/OrderDetailsPanel.tsx:254-360 handleReprintKot @ main; foodops.counter.app/src/offline/actions/printActions.ts:136-183 reuses existing kotId @ main]
Q: What's the difference between a KOT and a receipt? A: KOT goes to the kitchen and lists what to make; receipt goes to the customer and shows what they paid. KOT has no prices; receipt has prices.
Q: How does a KOT show up on the kitchen display? A: Each new KOT appears as a card or row on the Kitchen Display, with items, modifiers, and the order context. The kitchen ticks items off as they are prepared; the ticked status flows back to the order ticket.
Q: Can one order have multiple KOTs?
A: Yes — if the cashier adds more lines after sending the first KOT, the next Send KOT fires a second KOT for just the new lines. Common when courses are sent to the kitchen in waves. [code: hubits-api-sales/Hubits.Sales.Api/Services/Counter/SalesOrders/SalesOrderHelper.cs:634-720 KOT-item diff = new lines only @ main]
Q: Does Foodops support a single order being printed at multiple stations (e.g., grill and bar)?
A: Yes — items are routed to kitchens based on each product's kitchen assignment, and a single Send KOT produces one KOT per kitchen-set, so each station's printer gets its own copy. [code: hubits-api-sales/Hubits.Sales.Api/Services/Counter/SalesOrders/SalesOrderHelper.cs:794-938 kitchen-set routing, KitchenOrderTicketKitchens @ main]
Q: What happens to KOTs when an order is cancelled?
A: The order's KOT items flip to cancelled (IsCancelled/CancelledAt); a cancelled-KOT slip prints at each routed kitchen (when the register has AutoPrintKOT on and a printer is connected), and the kitchen display is signalled to refresh via SignalR so the cancellation shows there too. The cancelled KOT items remain in the record (not deleted), so it stays clear what was sent before the cancel. [code: hubits-api-sales/Hubits.Sales.Api/Controllers/OrdersController.cs:2940-2998 cancel → IsCancelled + cancelled-KOT prints + OrderCancelled/KitchenOrderAdded SignalR @ main]
Edge cases and known issues
Send KOT button does not respond
- Symptom: tapping Send KOT produces no apparent action.
- Cause: typically a network drop, occasionally a print queue backed up. The button should re-enable after the request times out.
- Workaround: refresh the order page once. If the lines are now in "sent" state, the KOT went through despite the UI not updating. If they are still editable, the send failed — try again.
KOT prints but kitchen display does not show it
- Symptom: the printer produces the KOT successfully but the screen-based display has no record.
- Cause: kitchen-display device is offline or not subscribed to the SignalR notification stream. KOT delivery to the KDS is via SignalR (
KitchenOrderAdded/KitchenStatusUpdatedto the organization group), which is independent of the print path.[code: hubits-api-sales/Hubits.Sales.Api/Controllers/OrdersController.cs:3819-3826 KitchenOrderAdded on KOT creation @ main; OrderKitchenController.cs:615-631 KitchenStatusUpdated @ main] - Workaround: refresh the kitchen display device; check its network connection. The KOT exists server-side regardless.
Wrong items end up at wrong printer
- Symptom: bar drinks print at the grill station and vice versa.
- Cause: a product's kitchen assignment (
ProductVariantKitchen) is misconfigured, so items route to the wrong RestaurantKitchen / printer.[code: hubits-api-sales/Hubits.Sales.Api/Services/Counter/SalesOrders/SalesOrderHelper.cs:799-815 routing by ProductVariantKitchen @ main] - Workaround: a manager fixes the product's kitchen assignment. Until fixed, prep staff need to physically walk tickets between stations.
KOT shows pricing despite the no-prices rule
- Symptom: a KOT printout includes a price column.
- Cause: not a per-line configuration option in current code — the KOT print-data payloads (
GetKOTPrintData/ManualPrintKitchenOrderTicket) carry no price or tax fields, the auto-print path zeroes item Rate/Amount, and the broker's KOT slip draws no price column. There is no "show prices on KOT" toggle. If a slip with a per-line price column reaches the kitchen it is a receipt mis-routed to the kitchen printer, not a priced KOT.[code: hubits-api-sales/Hubits.Sales.Api/Controllers/OrderPrintController.cs:172-276 GetKOTPrintData — no price/tax @ main; :1607-1608,1865-1866 auto-print item Rate=0/Amount=0 @ main; foodops.app.printing/Haafai.Printing/Services/PrintJobService.cs:615-705 ProcessKotPrintJob draws items only, no RATE/AMOUNT @ master] - Workaround: check what is actually being routed to the kitchen printer — if it shows a per-line price column, it is a receipt, so review the printer/document routing rather than looking for a KOT price toggle (there is none).
Related
POS - Complete Takeaway Order Flow — the order-taking flow that culminates in Send KOT
Kitchen Display — the screen-based KOT consumer
Order Types — the dine-in / takeaway / delivery context that shapes KOT content
Tables (Dine-in) — table number context that appears on dine-in KOTs
Sales Sessions — the session inside which orders (and KOTs) are created
Close Paid Order — the order lifecycle KOTs sit within
Counter (internal-docs) — the screen where Send KOT is tapped
2026-08-23 — codebase (hubits-api-sales ed3c7d6..0f25183, main). Diff shares base ed3c7d6 with the already-logged 2026-08-19 entry. NOT NEW: the MarkMultiplePreparing tenant-scoping addition (k.KitchenOrderTicket.ApplicationOrganisationId == CallContext.ApplicationOrganisationId) and the non-kitchen-product no-op acknowledgment path (items looked up against SalesOrderLineItems, logged as 'preparing is a no-op') are a verbatim repeat of the change already logged 2026-08-19 — MarkMultiplePreparing remains a distinct endpoint from the documented mark-ready handler (MarkMultipleReady, OrderKitchenController.cs:483-620) that this entry cites, and this diff doesn't touch that handler. GENUINELY NEW, both outside this entry's documented KOT surface: (1) OrdersController.cs adds a new POST {orderId}/change-discount endpoint (ChangeOrderDiscount) that applies a bill-level discount to an open, unpaid order with no existing discount/loyalty redemption, reconstructing UpdateOrderDto with LineItems=null so the update pipeline redistributes the discount across existing lines. This is order-level pricing/billing, not kitchen routing or ticket content — it does not touch OrderPrintController.cs's KOT print-data endpoints (GetKOTPrintData/ManualPrintKitchenOrderTicket), so the entry's 'no prices on KOT' rule is unaffected, same pattern as the 2026-06-02 multi-GST note (discount changes flow to the receipt, not the KOT). (2) KitchenController.cs's GetKitchenTickets ProductName-fallback resolution (for legacy KOT items missing the denormalised ProductName column) is restructured from a nested EF collection projection into two flat, index-covered queries, per the added comment citing a prod perf problem (190 of 252 slow queries in a two-hour window at 500-700ms each on the most-polled endpoint). This is a pure query-plan fix — the same fallback resolution outcome (unresolved-name items get their ProductName filled from Products) is preserved, just computed more efficiently; it does not change what a KOT/KDS ticket list returns, so it doesn't touch this entry's documented multi-kitchen-routing or KOT-content claims. Neither change is added to the entry body. (auto-applied by the truth pipeline; adversarially gated) (auto-applied by the truth pipeline; adversarially gated)
2026-08-23 — codebase (foodops.counter.app a3fe6a1..83f0da4, main). NEW INFO, continuing the open question flagged 2026-08-05 for 'Voids, edits, and re-sends' / 'What happens to KOTs when an order is cancelled?': the void-bin cancel-order path (
handleVoidBinCancelin OrderDetailsPanel.tsx) now runs through a newrunQuickUndoable/quickUndoActiveengine ('quick undo' pattern, kind: 'discard') instead of committingvoidOrderToBin()immediately. When a session'squickUndoconfig has 'discard' active, the actualvoidOrderToBin()call (and its success toast/close) is deferred/holdable — the success toast is suppressed while held, its text is now 'moved to the discard bin' (was 'moved to the void bin'), and the panel closes immediately (0ms) instead of after a 1s delay when held. This is a frontend UX change to a cancellation entry point this entry does not document by name (the entry's cancel-order claims are the backend path: IsCancelled/CancelledAt flip, cancelled-KOT print gated on AutoPrintKOT, OrderCancelled/KitchenOrderAdded SignalR refresh, hubits-api-sales OrdersController.cs:2940-2998) — this diff is frontend-only and does not show whether the deferredvoidOrderToBin()still ultimately calls that same backend endpoint, so no documented claim is confirmed, extended, or contradicted. Still not added to the entry body pending (a) confirmation that void-bin/discard-bin cancellation reaches the documented backend KOT-cancellation back-channel, and (b) rollout status of the quick-undo/discard-bin naming. (auto-applied by the truth pipeline; adversarially gated)2026-08-19 — codebase (hubits-api-sales ed3c7d6..98c202d, main). Diff is mostly cumulative repeats of already-logged changes sharing base ed3c7d6: OrdersController.cs's GetOrdersList/Invoices RecompileHintInterceptor.Tag plan-cache opt-outs (logged 2026-08-14), CancelOrder's SaveChangesAsync-before-BOM-reversal reordering via BomDeductionRunner.ReverseIsolatedAsync for the 2026-08-10 shared-ingredient/64-retry incident (logged 2026-08-15), and ChangeStatus's KnownOperationStatusIds validation rejecting unknown status ids with a 400 (logged 2026-08-15) — none touch this entry's cited KOT paths (CreateKitchenOrderTicket, the creation-path KitchenOrderAdded push at OrdersController.cs:3819-3826, MarkMultipleReady at OrderKitchenController.cs:483-620, or the cancel-path push at :2940-2998). GENUINELY NEW: MarkMultiplePreparing (OrderKitchenController.cs) — a distinct endpoint from the documented mark-ready handler, as its own new comment notes ('see the mark-ready handler') — gained (1) explicit tenant scoping on its kitchenItems query (k.KitchenOrderTicket.ApplicationOrganisationId == CallContext.ApplicationOrganisationId), closing a cross-org read gap, and (2) a no-op acknowledgment path for item ids with no matching KOT item (non-kitchen products): these are now looked up against SalesOrderLineItems and logged as an intentional no-op ('preparing is a no-op (non-kitchen product)') instead of just a 'not found' warning, so a client's mark-preparing queue for a non-kitchen product drains instead of retrying forever. Both changes are scoped entirely to mark-PREPARING, which this entry has repeatedly (2026-07-04 onward) treated as an out-of-scope inventory/BOM mechanism distinct from the documented mark-READY back-channel (MarkMultipleReady, :483-620) — neither changes KOT contents, printing, Send KOT, void/cancel, or the mark-ready flow this entry documents. Not added to the entry body. Also unrelated: two new commits in this range (fix(inventory) Hangfire low-stock-suggestion crash guard; feat(chart-of-accounts) batch deposit-account resolution) touch no files in this entry's cited surface. (auto-applied by the truth pipeline; adversarially gated) (auto-applied by the truth pipeline; adversarially gated)
2026-08-15 — codebase (hubits-api-sales ed3c7d6..a56cf10, main). Two of the three touched areas are cumulative repeats already logged 2026-08-14 against the same base (ed3c7d6): OrderKitchenController.cs's MarkMultiplePreparing BOM-deduction restructure (SaveChangesAsync before per-order isolated deduction via BomDeductionRunner.DeductIsolatedAsync, commit 'fix(kitchen): stop shared-ingredient recipes corrupting the mark-preparing save') and OrdersController.cs's GetOrdersList/Invoices RecompileHint plan-cache opt-outs — neither is new and neither touches this entry's cited KOT paths (CreateKitchenOrderTicket, the creation-path KitchenOrderAdded push at :3819-3826, MarkMultipleReady at OrderKitchenController.cs:483-620). GENUINELY NEW: CancelOrder in OrdersController.cs now calls SaveChangesAsync (persisting the order's status flip, table release, and KOT cancellation flags) BEFORE attempting BOM/recipe stock reversal, and the reversal itself now runs through a new isolated BomDeductionRunner.ReverseIsolatedAsync (own identity map, cleared after). Per the added comment, this fixes a real 2026-08-10 incident where a shared-ingredient identity conflict left the change-tracker poisoned during BOM reversal, causing the trailing SaveChangesAsync to throw a 500 and the same cancel request to be retried ~64 times by a POS terminal. This does not contradict the documented cancel-path claims this entry cites (IsCancelled/CancelledAt flip, cancelled-KOT print gated on AutoPrintKOT, OrderCancelled/KitchenOrderAdded SignalR refresh, OrdersController.cs:2940-2998) — that code is untouched by this diff and, per the reordering, its effects are now saved MORE reliably (independent of whether the unrelated BOM reversal succeeds), not less. Separately, OrdersController.cs's ChangeStatus endpoint gained validation rejecting unknown RestaurantOrderOperationStatus ids with a 400 instead of letting an invalid id reach SaveChanges as a raw FK violation — unrelated to Send KOT, mark-ready, or any KOT print/cancel path this entry documents. Net: no documented claim in this entry is touched or contradicted; the CancelOrder reliability fix is a positive-direction note (fewer 500-retry storms on cancel) but is an inventory/reliability mechanism, not a KOT behavior change, so not added to the entry body. (auto-applied by the truth pipeline; adversarially gated)
2026-08-14 — codebase (hubits-api-sales ed3c7d6..04ad21d, main). NEW_INFO, none of it touches this entry's documented KOT surface. (1) OrderKitchenController.cs's MarkMultiplePreparing — a distinct endpoint from the MarkMultipleReady mark-ready back-channel this entry cites at :483-620 — restructures the BOM/recipe stock-deduction step first added 2026-07-04 (already flagged then as out-of-scope for this KOT entry): the preparing-status SaveChangesAsync now runs BEFORE deduction, and deduction itself runs per-order via a new BomDeductionRunner.DeductIsolatedAsync in an isolated identity map, per the commit subject 'fix(kitchen): stop shared-ingredient recipes corrupting the mark-preparing save' — i.e. a shared-ingredient recipe's deduction failure could previously corrupt/roll back the whole mark-preparing batch save, not just its own order. This is a bug fix to an already out-of-scope inventory mechanism (mark-PREPARING stock deduction), not a change to Send KOT, CreateKitchenOrderTicket, the mark-READY back-channel, or any KOT print/cancel path. (2) OrdersController.cs's GetOrdersList and its Invoices sub-queries gained .TagWith(RecompileHintInterceptor.Tag) plan-cache opt-outs to fix a 2026-08-09 storm-induced query-timeout — a pure performance change with no behavioral effect, and these queries are not the KitchenOrderAdded/OrderCancelled paths this entry cites (:3819-3826, :2940-2998). Net: no documented claim in this entry is touched; the 2026-07-04 routing question (whether mark-preparing BOM deduction belongs in a separate inventory/stock KB entry) still stands, now with an added failure-isolation fix. Not added to this entry's body. (auto-applied by the truth pipeline; adversarially gated) (auto-applied by the truth pipeline; adversarially gated)
2026-08-14 — codebase (foodops.counter.app f52b6dc..5b426a9, main). GENUINELY NEW, relevant to 'What appears on a KOT': KOTPrintLayout.tsx (the KOT print-tab component this entry cites for what renders on a slip) fixes a timestamp-rendering bug, not a payload change. GetKOTPrintData's orderDate is raw UTC with no 'Z' suffix — unlike the broker's org-local endpoints — so the old
new Date(dateString)read it as terminal-local and could print the wrong hour (e.g. offset outlets like Malé). formatDate/formatDateTime now use parseServerDate to force a UTC read, then render via an explicit orgTimeZone (from kotMapper.getOrgTimeZone, persisted to localStorage for cold-loaded print tabs); formatTime also gained a NaN guard. This does not change the 'Timestamp — the order date/created date is carried in the print payload' claim (the field is unchanged; only its client-side display timezone is now correct) — no edit needed, just a fidelity improvement worth a footnote if this section is expanded. NOT NEW: the OrderDetailsPanel.tsx hunk in this diff (enableVoidBin flag → handleVoidBinCancel → voidOrderToBin, bypassing the confirm dialog) is a cumulative repeat of the void-bin alternate-cancellation path already logged in the 2026-08-05 verification-history entry — same open question stands (frontend-only diff still doesn't show whether voidOrderToBin calls the documented backend CancelOrder/KOT-cancellation path). The diffKot.ts hunk only adds temporaryconsole.logdiagnostics (explicitly labeled 'remove after diagnosis') around the previously-fixed (2026-07-22) prevLineQuantityById capping logic — no functional change. kotMapper.ts adds a test-onlyresetOrgTimeZoneForTestshelper — not user-facing. (auto-applied by the truth pipeline; adversarially gated)2026-08-08 — codebase (hubits-api-sales bb69312..cdb3b32, main). Most of this diff is a cumulative repeat of changes already logged in the 2026-08-01 and 2026-08-05 verification-history entries: KitchenController.cs's GetKitchenTickets/GetKitchenTicketCounts bucket-membership centralization and its restaurantKitchenId-vs-KitchenOrderTicketKitchens multi-kitchen join fix, and OrdersController.cs's CreateOrder/UpdateOrderInternal/UpdateCustomer OrderAdded/OrderUpdated broadcasts migrating to the new _orderEvents.PublishAsync/IOrderEventPublisher envelope, plus OrderKitchenController.cs's KitchenStatusUpdated gaining additive salesOrderId/sessionId/scope fields — none of that is new, and none of it touches the KOT-specific paths this entry cites (CreateKitchenOrderTicket, KOT print endpoints, the creation-path KitchenOrderAdded push at OrdersController.cs:3819-3826, the cancel-path push at :2940-2998, or MarkMultipleReady at OrderKitchenController.cs:483-620 — none of these ranges appear in this diff). GENUINELY NEW: OrderKitchenController.cs adds a private TryCloseSettledOrderAsync(SalesOrder order) helper, called from MarkMultipleServed (the mark-SERVED endpoint — distinct from the mark-READY endpoint this entry documents). When an order reaches a terminal service status while already financially settled (Outstanding <= 0.01 or IsCreditSale), it is auto-transitioned to the Closed SalesOrderStatus and, for dine-in orders, its table is freed to Vacant. This is order-lifecycle/table-management behavior triggered downstream of marking items served, not a change to KOT contents, printing, or the documented mark-ready back-channel (IsReady mirror onto SalesOrderLineItem, ticket IsCompleted, order→Ready, :483-620) — it doesn't contradict any claim in this entry. Worth a documentation decision on whether 'order auto-closes when marked served while already paid' belongs in a table/order-lifecycle KB entry rather than this one, since it concerns post-KOT order closure, not the ticket itself — not added to this entry's body. (auto-applied by the truth pipeline; adversarially gated)
2026-08-05 — codebase (hubits-api-sales bb69312..9351716, main). NEW INFO, none of it contradicts documented KOT claims. (1) KitchenController.cs's GetKitchenTickets/GetKitchenTicketCounts (the KDS ticket-list/count endpoints referenced in source_repos but not cited with line-level detail in this entry) had their Open/Preparing/Completed/Cancelled bucket predicates centralized into shared expression trees to stop the list and its count badges from disagreeing. Per the new code comments this also documents, for the first time in first-party form, that Open and Preparing tabs intentionally OVERLAP: a ticket with one item already started and one still untouched shows on BOTH tabs simultaneously — this is pre-existing behavior being fixed for consistency, not a new feature, but it's a KDS-board-surface detail this entry doesn't cover (it belongs more naturally in kitchen-display.md, which this entry links out to). The restaurantKitchenId-vs-KitchenOrderTicketKitchens multi-kitchen join fix also appears in this diff, but it's the same fix already logged in the 2026-08-01 verification-history entry (diff base bb69312 is shared, so it's a cumulative-diff repeat, not new). (2) OrderKitchenController.cs's KitchenStatusUpdated SignalR broadcasts (mark-served/mark-preparing/mark-ready) gained a new IOrderEventPublisher-style DI dependency and additive salesOrderId/sessionId/scope routing fields so terminals can filter by session instead of reloading — the existing payload fields (itemId/isServed/status/updatedAt, ticketId, updatedOrder) and the documented mark-ready mechanics this entry cites (mirror IsReady onto SalesOrderLineItem, ticket IsCompleted, order→Ready, KitchenStatusUpdated push, OrderKitchenController.cs:483-620) are untouched. (3) OrdersController.cs's CreateOrder/UpdateOrderInternal/UpdateCustomer OrderAdded/OrderUpdated broadcasts were migrated to the same new _orderEvents.PublishAsync publisher, but these are distinct events from the KitchenOrderAdded (KOT creation, :3819-3826) and OrderCancelled/KitchenOrderAdded (cancel, :2940-2998) paths this entry documents — neither of those code paths appears in this diff. Worth a documentation decision on whether the Open/Preparing tab-overlap detail belongs in kitchen-display.md rather than here; nothing added to this entry's body. (auto-applied by the truth pipeline; adversarially gated) (auto-applied by the truth pipeline; adversarially gated)
2026-08-05 — codebase (foodops.counter.app f52b6dc..37ad39c, main). NEW INFO relevant to 'Voids, edits, and re-sends' and 'What happens to KOTs when an order is cancelled?': a new session-scoped
enableVoidBinflag (read viagetCurrentSession(), defaults false pending session load) introduces an alternate order-cancellation path. When enabled, tapping Cancel Order no longer opens the existing confirm dialog (setShowCancelDialog(true)/handleCancelConfirm) — it callshandleVoidBinCancel(), which invokes a newvoidOrderToBin()action (offline-first, Dexie-backed per the commit subjects: 'void bin offline plumbing — enableVoidBin flag + Dexie v26 voidBinEntries + sync actions') and immediately marks the order cancelled client-side, with the cancellation reason captured via a new persistent, app-wide snackbar instead of the dialog. Companion commits add void-bin discard buttons, a read-only void-bin panel, and a live badge on the dine-in view. This diff is frontend-only (foodops.counter.app), so it does NOT show whethervoidOrderToBinstill calls the same backend cancel endpoint this entry documents (hubits-api-sales OrdersController.cs CancelOrder, IsCancelled/CancelledAt flip, cancelled-KOT print gated on AutoPrintKOT, OrderCancelled/KitchenOrderAdded SignalR refresh, :2940-2998) or takes a different server-side route — so the existing cancel-order claims are not contradicted, only potentially incomplete once this flag is live. Worth a documentation decision once (a) it's confirmed whether void-bin cancellation still triggers the documented KOT-cancellation back-channel, and (b)enableVoidBinrollout status is known — not yet added to the entry body. (auto-applied by the truth pipeline; adversarially gated)2026-08-01 — codebase (hubits-api-sales bb69312..0d94f42, main). NEW INFO relevant to 'Multi-printer / multi-station setups' and the 'Wrong items end up at wrong printer' edge case: KitchenController.cs's GetKitchenTickets and GetKitchenTicketCounts (the KDS ticket-list/count endpoints — not previously in this entry's source_repos) were filtering strictly on a ticket's singular RestaurantKitchenId column. Per the diff's own comment, a ticket routed to several kitchens 'carries only its FIRST kitchen in the singular column,' so filtering the ticket list/counts by any non-first kitchen in a multi-kitchen-set KOT would silently omit it — a real gap in the documented kitchen-set routing model (one KOT per kitchen-set, all routed kitchens linked via KitchenOrderTicketKitchens, SalesOrderHelper.cs:794-980). Both endpoints now also match against the KitchenOrderTicketKitchens join, so a KDS screen filtered to any routed kitchen (not just the first) sees the ticket. This is a bug FIX confirming/completing the entry's existing multi-kitchen-routing claim, not a contradiction — worth a line in 'Multi-printer / multi-station setups' or the 'Wrong items end up at wrong printer' edge case noting that per-kitchen KDS filtering now correctly covers every routed kitchen, not just the first. Separately, no documented behavior changed: OrderKitchenController.cs's KitchenStatusUpdated SignalR broadcasts (mark-served/mark-preparing/mark-ready) gained added salesOrderId/sessionId/scope routing fields via a new IOrderEventPublisher-style envelope (an efficiency change so terminals can filter by session instead of reloading on every event) — the mirror-to-SalesOrderLineItem, ticket-completion, and order→Ready transition logic this entry cites (OrderKitchenController.cs:519-585) is untouched. OrdersController.cs's CreateOrder/UpdateOrderInternal/UpdateCustomer OrderAdded/OrderUpdated broadcasts were similarly migrated to the new publisher, but these are distinct events from the KitchenOrderAdded (creation, :3819-3826) and OrderCancelled/KitchenOrderAdded (cancel, :2940-2998) paths this entry documents, neither of which appears in this diff. (auto-applied by the truth pipeline; adversarially gated)
2026-07-22 (second pass) — codebase (foodops.counter.app 85feff4..dea0b59, main): fixed a client-side bug (SO-6662) in diffKot.ts affecting the modify-diff math behind the entry's documented 'consolidated MODIFIED slip' behavior (Voids, edits, and re-sends section). Previously, a doubled local previous-KOT-quantity baseline — arising when the same order line appeared on two KOTs after a local/server KOT-id divergence — could sum to more than the order line's actual prior quantity and fabricate a phantom 'Qty from N to M' entry on the next edit's MODIFIED slip, even though the line's quantity hadn't really decreased. The fix caps each paired line's previous-KOT quantity at the order line's own authoritative prior quantity (from a new ctx.prevLineQuantityById map) before the modify/decrease/removal split runs — it only caps downward, never inflates, and leaves the kotItems arrays used for cancel/decrease bookkeeping untouched. This does not change any documented mechanism this entry describes (MODIFIED slip fires instead of a cancel slip on qty/modifier/note/allergy edits; diffKot.ts + kotMapper.ts do the mapping) — it corrects an edge case where the printed Qty-change value on that slip could be wrong. Worth a future footnote in the 'Modify a sent line' bullet noting that phantom Qty-change artifacts on the MODIFIED slip were a known, now-fixed issue, rather than a change to when/why a MODIFIED slip fires. (auto-applied by the truth pipeline; adversarially gated)
2026-07-22 — codebase (hubits-api-sales 86e7d20..f26ba1f, main): CancelOrder (OrdersController.cs, inserted before the existing write path ~line 3255-3282) now enforces a
SalesOrders.Deletepermission check — read directly from org-scopedUserRoles(not the 3-min-cachedUser.IsInRole, so it also covers the API-key path) — before any cancellation logic runs. A user/API caller lacking that role now gets a 403 ("You do not have permission to cancel orders") and the cancel is rejected outright; none of the downstream KOT-cancellation behavior this entry documents (IsCancelled/CancelledAt flip, cancelled-KOT print gating on AutoPrintKOT, OrderCancelled/KitchenOrderAdded SignalR refresh, OrdersController.cs:2940-2998) is touched or contradicted — it's a new precondition gating whether cancellation (and thus KOT cancellation) happens at all. Worth a line in the 'What happens to KOTs when an order is cancelled?' Q&A or edge cases noting that a permission-denied cancel attempt leaves the order and its KOTs completely unchanged (no partial cancellation), since staff without SalesOrders.Delete will now see a new failure mode there. Not yet added to the entry body — flagging for a documentation decision rather than auto-editing, since this is additive scope (permissions/authorization) rather than a correction to an existing claim. (auto-applied by the truth pipeline; adversarially gated)2026-07-04 (second pass, same day) — codebase (hubits-api-sales 01cd718..3005390): CancelOrder in OrdersController.cs now also reverses BOM/recipe ingredient stock that was deducted at the 'mark preparing' step (complementing the mark-preparing deduction flagged earlier today), attaching a BomStockReversalLog to the cancel response; the reversal is wrapped in try/catch so a failure is logged, not thrown, and does not block the cancel. This diff does not touch the KOT-cancellation surface this entry documents (IsCancelled/CancelledAt item flip, cancelled-KOT print gating on AutoPrintKOT, or the OrderCancelled/KitchenOrderAdded SignalR refresh) — it is an inventory side-effect of order cancellation, not a change to KOT contents, print behavior, or KDS delivery. The diff also includes an unrelated Day Summary export change to the reports/tray pipeline. Same routing question as the earlier note: document as a new inventory/stock-accounting KB entry (paired with the mark-preparing deduction), rather than folding into this KOT entry, since neither touches the ticket itself. (auto-applied by the truth pipeline; adversarially gated)
2026-07-04 — codebase (hubits-api-sales 30a126a..a4c6ced): a new commit ('deduct recipe/BOM ingredient stock on KDS mark-preparing and at payment (idempotent)') adds a BOM/recipe ingredient stock deduction inside MarkMultiplePreparing (OrderKitchenController.cs, ~line 420-425) when an order transitions to RestaurantOrderOperationStatus.Preparing — wrapped in try/catch so a deduction failure is logged, not thrown, and doesn't block the status change. This is a distinct action from Send KOT (KOT creation) and from MarkMultipleReady (documented in this entry's 'Kitchen marks items prepared' section, lines 483-620) — neither of those cited code paths is touched by this diff, so no existing KOT claim is contradicted. The new behavior is an inventory side-effect of a kitchen 'mark preparing' step, not a change to KOT contents, print behavior, or the ready back-channel. Flagging for a decision: document as a new subsection here (if 'mark preparing' is a kitchen/KDS-facing action worth explaining alongside Send KOT / mark-ready), or route it to a dedicated inventory/stock KB entry instead, since it's about ingredient stock accounting rather than the ticket itself. (auto-applied by the truth pipeline; adversarially gated)
2026-06-29 — codebase (July-10 launch re-verify against new-stack prod HEADs:
foodops.counter.app@32669a9main,hubits-api-sales@19190b9main,foodops.app.printing@f6eb32amaster). UPHELD against current code: KOT print-data has items + context + no prices (OrderPrintController.cs:172-276 GetKOTPrintData,:57-169 ManualPrintKitchenOrderTicket; auto-print zeroes Rate/Amount:1607-1608,1865-1866); the broker renders the slip (foodops.app.printing/Haafai.Printing/Services/PrintJobService.cs:615-705 ProcessKotPrintJob— orderType/kotType, KOT #, table, item list with modifiers + allergy, no price column, no Drinks subsection, no order-note line); new-lines-only diff + void→IsCancelled + KOT-cancel gates (SalesOrderHelper.cs:633-690); MODIFIED-slip model confirmed in counter.app (src/offline/kot/diffKot.ts—source: 'removed'fires the paper cancel slip,'decrease-consumed'/'modify-replaced'update KDS only;kitchenChangeNote= "Modifiers/Note/Allergy updated" or "Qty from X to Y"); Reprint hides all-superseded KOTs +*** REPRINT ***banner (OrderDetailsPanel.tsx:2116-2117, 343-360); mark-ready back-channel (OrderKitchenController.cs:484 MarkMultipleReady, mirror IsReady→SalesOrderLineItem:520-521, ticket IsCompleted:556-558, KitchenStatusUpdated SignalR); creation-pathKitchenOrderAddedSignalR; multi-kitchen routing viaProductVariantKitchen/KitchenOrderTicketKitchens. LEGACY RE-POINT (launch): allhaafai.app.foodopsRazor KOT views (_KitchenOrderTicketPrint.cshtml,_KitchenOrderTicketItemwisePrint.cshtml, etc.) are retired — re-pointed "what appears on a KOT" to the brokerPrintJobService.cs, itemwise toOrderPrintController.cs:1524-1619(EnableItemwiseKotPrintingsetting — still a real feature), and the food/"Drinks" within-slip split to RETIRED: theShowOnBeverageOrderTicketflag still rides the print-data payload (:156,264) but the new broker renders one item list with no beverage subsection (corrected 2 places). Droppedhaafai.app.foodopsfrom frontmatter;source_reposre-pointed to the counter KOT modules + broker. Frontmatter SHAs + last_verified bumped.2026-05-04 — codebase: haafai/foodops.counter.app:b9677b0, haafai/haafai.app.foodops:46a80a3 (Views/Orders/_KitchenOrderTicketPrint.cshtml, _KitchenOrderTickets.cshtml, _KitchenOrderTicketItemwisePrint.cshtml, Services/Kitchen/KitchenOrdersService.cs confirmed); UI not yet verified
2026-06-03 — content re-verification against
hubits-api-sales@5274891(incl. multi-GSTbc04a77) andfoodops.counter.app@e002374. KOT behavior is UNCHANGED: the KOT print endpoints (ManualPrintKitchenOrderTicket,GetKOTPrintData) carry items + context but no tax/price fields, so the multi-GST change does not touch KOTs — the "no prices" rule holds. Multi-GST adds a per-component tax breakdown to the receipt only; added a one-line note in the KOT-vs-receipt section. NOTE (path drift, no behavior change):source_reposlistsfoodops.counter.app/src/pages/KitchenPage.tsx, which has been removed — KDS now lives atsrc/kds/pages/KdsDisplayPage.tsxand the KOT print surface atsrc/components/kitchen/KOTPrintLayout.tsx. Glob should be re-pointed in a future curation pass; no user-facing KOT behavior change.2026-06-14 — TRUTH AUDIT (codebase, prod branches per
_meta/repo-prod-branches.md): verified every behavior claim againsthubits-api-sales@main,foodops.counter.app@main,haafai.app.foodops@main. Added[code: …]citations to all confirmed claims (KOT contents/no-prices, new-lines-only diff, void→IsCancelled, mark-ready back-channel, multi-kitchen routing, reprint=print-only, itemwise=slip-per-unit, SignalR KDS delivery). CORRECTIONS: (1) "voiding surfaces a void notification on the kitchen display" → actual behavior is a printed cancelled-KOT slip (gated onregister.AutoPrintKOT+ connected printer) PLUS a SignalR refresh signal to kitchen views; (2) "KOT shows pricing via outlet-level template override" → FABRICATED, no such override exists; KOT payloads/views have no price fields at all, so a priced slip = a mis-routed receipt — rewrote the edge case. DELETED 2 bullets from "What appears on a KOT" (order-level note shown at top; cashier/server name) — not present in the standard KOT print payloads. FLAGGED unverifiable (HTML comments): Send-KOT button UX, "lines lock to sent" UI behavior, order-status-advances-on-Send-KOT (only confirmed on mark-ready), audit-log row on void. Verified-against HEADs: counter.app, hubits-api-sales, haafai.app.foodops on prod (main).2026-06-15 — ADVERSARIAL 2ND-PASS truth audit (re-verified the 2026-06-14 corrections against the same prod branches, refute-bias). UPHELD: KOT contents (order+KOT ref, type/table/customer+mobile/shipping, items+qty+modifiers+note+allergy+forTakeAway, timestamp), no-prices in print-data endpoints + views, itemwise=slip-per-unit, CreateKitchenOrderTicket record, new-lines-only diff, void→IsCancelled, cancelled-KOT print gating (AutoPrintKOT + connected printer), mark-ready back-channel (mirror IsReady → SalesOrderLineItem, ticket IsCompleted, order→Ready/HasPrepared/PreparedTime, KitchenStatusUpdated), multi-kitchen routing via ProductVariantKitchen + KitchenOrderTicketKitchens, reprint=print-only (reuses kotId, *** REPRINT *** banner, order detail panel), cancel-path SignalR. CORRECTIONS APPLIED: (1) the new-KOT KDS-delivery
KitchenOrderAddedwas mis-cited to the CANCEL path (OrdersController.cs:2986-2998); the creation-path push is at :3819-3826 after CreateKitchenOrderTicket (:3782) — re-cited in 2 places (When-does-a-KOT-fire + edge case). (2) "food vs beverage split ... routes the bar's drinks to a separate beverage printer" was OVERSTATED —ShowOnBeverageOrderTicketis display-only (a "Drinks" section on the SAME slip, cshtml :333-340); no backend printer routing reads it; physical routing is the kitchen-set/ProductVariantKitchen mechanism — tightened in 2 places. (3) "KOT cancelled only if all items cancelled and prep not started" omitted the second gate — KOT also cancels when item-quantity-sum reaches 0 (SalesOrderHelper.cs:678-684), tightened. (4) "no price/tax fields at all" scoped to the print-data endpoints + views (the auto-print Receipt envelope carries order totals but zeroes item Rate/Amount at :1532-1533). RE-CONFIRMED the deleted order-note/cashier bullets (absent from the cshtml KOT view) with a caveat that the auto-print broker path does carry Description/CreatedByUserName. Citation line-ranges tightened (GetKOTPrintData 172-276, KitchenStatusUpdated 615-631). Net: no fabrications survived; corrections were directionally sound, 4 over/mis-stated points tightened to exactly what prod code shows.