Use this guide when a restaurant wants cancelled kitchen order ticket (KOT) items to remain in the database and appear in a POS report.
Important: in current Saleculator local databases, removed-line attributes in LINESREMOVED.ATTRIBUTES are stored as Java properties text, for example {sendstatus=Cancel, kotnum=KOT1, ...}. Do not use MySQL ExtractValue() for this report; it will return blank and the report will print zero rows.
What the feature does #
event.removelinemarks already-sent KOT lines assendstatus=Cancel.Script.SendOrderprints the KOT change slip and saves removed lines throughsales.keepLine(line).sales.keepLine(line)writes to theLINESREMOVEDtable with the active cash, ticket number, product name, units, price, attributes, and user.
Create or update the POS report resource #
- Open Administration > Resources.
- Create or open
POS.Report.CancelledKOTItems. - Paste this full XML:
<?xml version="1.0" encoding="UTF-8"?>
<output>
<sql><![CDATA[
SELECT
LR.DATENEW,
LR.TICKETID,
LR.PRODUCT,
LR.UNITS,
(LR.UNITS * LR.PRICE) AS AMOUNT,
LR.PERSON,
TRIM(SUBSTRING_INDEX(SUBSTRING_INDEX(CAST(LR.ATTRIBUTES AS CHAR), 'kotnum=', -1), ',', 1)) AS KOTNUM
FROM LINESREMOVED LR
WHERE LR.DATENEW >= $startDate
AND LR.DATENEW <= $endDate
AND CAST(LR.ATTRIBUTES AS CHAR) LIKE '%sendstatus=Cancel%'
ORDER BY LR.DATENEW, LR.TICKETID, LR.PRODUCT
]]></sql>
<ticket>
<image>Printer.Ticket.Logo</image>
<line></line>
<line size="1">
<text align="center" length="48" bold="true">Cancelled KOT Items</text>
</line>
<line></line>
<line>
<text length="12">Start Date:</text>
<text length="36">$startDate</text>
</line>
<line>
<text length="12">End Date:</text>
<text length="36">$endDate</text>
</line>
<line>
<text>------------------------------------------------</text>
</line>
<line>
<text align="left" length="5">Bill</text>
<text align="left" length="14">Date</text>
<text align="left" length="15">Item</text>
<text align="right" length="5">Qty</text>
<text align="right" length="9">Amount</text>
</line>
<line>
<text>------------------------------------------------</text>
</line>
#foreach ($line in $posreport.getResult())
<line>
<text align="left" length="5">${line.printValue(1)}</text>
<text align="left" length="14">${line.printDate(0)}</text>
<text align="left" length="15">${line.printValue(2)}</text>
<text align="right" length="5">${line.printValue(3)}</text>
<text align="right" length="9">${line.printCurrency(4)}</text>
</line>
<line>
<text align="left" length="8">KOT:</text>
<text align="left" length="12">${line.printValue(6)}</text>
<text align="left" length="8">User:</text>
<text align="left" length="20">${line.printValue(5)}</text>
</line>
#end
<line>
<text>------------------------------------------------</text>
</line>
<line>
<text align="left" length="20" bold="true">Cancelled rows</text>
<text align="right" length="28" bold="true">$posreport.printCount()</text>
</line>
</ticket>
</output>
Add the report button #
- Open the
POS.Reportsresource. - Add this button inside the
<configuration>block:
<button key="button.print" titlekey="Cancelled KOT Items" template="POS.Report.CancelledKOTItems"/>
After saving, reopen Sales > POS Reports, choose a date range that includes the cancellation time, and run Cancelled KOT Items.
Quick SQL check #
SELECT DATENEW, TICKETID, PRODUCT, UNITS, PRICE, PERSON,
CAST(ATTRIBUTES AS CHAR) AS ATTRIBUTES_TEXT
FROM LINESREMOVED
WHERE DATENEW >= '2026-08-11 00:00:00'
AND DATENEW <= '2026-08-12 00:00:00'
AND CAST(ATTRIBUTES AS CHAR) LIKE '%sendstatus=Cancel%'
ORDER BY DATENEW;
Troubleshooting #
- If rows exist in
LINESREMOVEDbut the report is empty, verify the report SQL usesCAST(ATTRIBUTES AS CHAR) LIKE '%sendstatus=Cancel%'. - If rows exist but the date range misses them, set the start date before the cancellation time and the end date after it.
- If the button does not show, confirm the button line is inside
POS.Reportsand restart or reopen the POS Reports screen.