refactor: improve printer cut reliability, add buffer flushing, and enhance receipt metadata parsing for Odoo 19
This commit is contained in:
parent
e71f979e3b
commit
3a28e7ac08
@ -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);
|
||||
}
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
@ -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}`;
|
||||
} else {
|
||||
tableName = `Table ${table.table_number}`;
|
||||
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 {
|
||||
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
|
||||
|
||||
Loading…
Reference in New Issue
Block a user