refactor: improve printer cut reliability, add buffer flushing, and enhance receipt metadata parsing for Odoo 19

This commit is contained in:
Suherdy Yacob 2026-06-01 10:35:08 +07:00
parent e71f979e3b
commit 3a28e7ac08
3 changed files with 67 additions and 12 deletions

View File

@ -69,6 +69,7 @@ export class EscPosGenerator {
const cmds = [];
for (let i = 0; i < lines; i++) cmds.push(...FEED_LINE);
cmds.push(...CUT_PAPER);
cmds.push(...INIT); // Force printer buffer flush and reset
return new Uint8Array(cmds);
}
@ -337,8 +338,8 @@ export class EscPosGenerator {
}
}
// Feed and cut (set feed to 4 lines to ensure the footer clears the cutter blade before cutting)
cmds.push(...this.feedAndCut(4));
// Feed and cut (set feed to 6 lines to ensure the footer clears the tear bar)
cmds.push(...this.feedAndCut(6));
return new Uint8Array(cmds);
}

View File

@ -55,7 +55,7 @@ export class EscPosGraphics {
commands.push(...rasterCommands);
// Feed paper and cut
commands.push(...this._feedAndCut(4));
commands.push(...this._feedAndCut(6));
const result = new Uint8Array(commands);
@ -221,6 +221,9 @@ export class EscPosGraphics {
// m = 0 (full cut), 1 (partial cut)
commands.push(GS, 0x56, 0x00);
// Initialize printer to force buffer flush and reset
commands.push(ESC, 0x40);
return commands;
}

View File

@ -682,19 +682,31 @@ patch(PosPrinterService.prototype, {
} catch (_) { dateStr = new Date().toLocaleString(); }
// Table name — from pos_restaurant.ReceiptHeader patch
// order.table_id or order.self_ordering_table_id → table.table_number
let tableName = '';
const table = order.table_id || order.self_ordering_table_id || null;
let table = order.table_id || order.self_ordering_table_id || null;
if (table) {
if (order.customer_count) {
tableName = `Table ${table.table_number}, Guests: ${order.customer_count}`;
if (typeof table === 'number' || typeof table === 'string') {
const tableId = parseInt(table);
if (pos && pos.models && pos.models['restaurant.table']) {
table = pos.models['restaurant.table'].get(tableId) || { table_number: tableId };
} else if (pos && pos.tables) {
table = pos.tables.find(t => t.id === tableId) || { table_number: tableId };
} else {
tableName = `Table ${table.table_number}`;
table = { table_number: tableId };
}
}
const number = table.table_number || table.name || '';
if (number) {
if (order.customer_count) {
tableName = `Table ${number}, Guests: ${order.customer_count}`;
} else {
tableName = `Table ${number}`;
}
}
}
const orderData = {
orderName: order.pos_reference || order.name || '',
orderName: (typeof order.getName === 'function' ? order.getName() : '') || order.pos_reference || order.name || '',
date: dateStr || new Date().toLocaleString(),
cashier: formatFirstLastName((typeof order.getCashierName === 'function' ? order.getCashierName() : '') || ''),
customer: order.partner_id?.name || null,
@ -834,13 +846,52 @@ patch(PosPrinterService.prototype, {
taxId: getText('.pos-receipt-tax-id') || ''
};
// Helper to find text by pattern in any descendant element, prioritizing the most specific (deepest/shortest) match
const findTextByPattern = (pattern) => {
const walker = document.createTreeWalker(el, NodeFilter.SHOW_ELEMENT, null, false);
let bestText = '';
while (walker.nextNode()) {
const node = walker.currentNode;
// Exclude script, style, and large container elements
if (['SCRIPT', 'STYLE', 'UL', 'OL', 'TBODY', 'TABLE', 'HTML', 'BODY', 'DIV.pos-receipt'].includes(node.tagName)) {
continue;
}
const text = node.textContent.trim();
if (pattern.test(text)) {
// We want the most specific leaf node, which has the shortest text containing the pattern
if (!bestText || text.length < bestText.length) {
bestText = text;
}
}
}
return bestText;
};
// Parse order info
// Odoo 19: order reference is in .pos-receipt-vat, date in #order-date, cashier in .cashier
let orderName = getText('.pos-receipt-vat') || getText('.pos-receipt-order-name') || getText('.order-name') || getText('.pos-receipt-order-data') || '';
if (!orderName) {
const possibleOrderText = findTextByPattern(/\b(Order|Ticket|Ref|Invoice)\b/i);
if (possibleOrderText) {
orderName = possibleOrderText.split('\n')[0].trim();
}
}
let tableName = getText('.pos-receipt-table') || getText('.table-name') || '';
if (!tableName) {
const possibleTableText = findTextByPattern(/\bTable\s+\d+/i) || findTextByPattern(/\bTable\b/i);
if (possibleTableText) {
tableName = possibleTableText.split('\n')[0].trim();
}
}
const orderData = {
orderName: getText('.pos-receipt-vat') || getText('.pos-receipt-order-name') || getText('.order-name') || getText('.pos-receipt-order-data') || '',
orderName: orderName,
date: getText('#order-date') || getText('.pos-receipt-date') || new Date().toLocaleString(),
cashier: formatFirstLastName(getText('.cashier') || getText('.pos-receipt-cashier') || ''),
customer: getText('.pos-receipt-customer') || getText('.customer') || null
customer: getText('.pos-receipt-customer') || getText('.customer') || null,
tableName: tableName
};
// Parse order lines