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 = [];
|
const cmds = [];
|
||||||
for (let i = 0; i < lines; i++) cmds.push(...FEED_LINE);
|
for (let i = 0; i < lines; i++) cmds.push(...FEED_LINE);
|
||||||
cmds.push(...CUT_PAPER);
|
cmds.push(...CUT_PAPER);
|
||||||
|
cmds.push(...INIT); // Force printer buffer flush and reset
|
||||||
return new Uint8Array(cmds);
|
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)
|
// Feed and cut (set feed to 6 lines to ensure the footer clears the tear bar)
|
||||||
cmds.push(...this.feedAndCut(4));
|
cmds.push(...this.feedAndCut(6));
|
||||||
|
|
||||||
return new Uint8Array(cmds);
|
return new Uint8Array(cmds);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -55,7 +55,7 @@ export class EscPosGraphics {
|
|||||||
commands.push(...rasterCommands);
|
commands.push(...rasterCommands);
|
||||||
|
|
||||||
// Feed paper and cut
|
// Feed paper and cut
|
||||||
commands.push(...this._feedAndCut(4));
|
commands.push(...this._feedAndCut(6));
|
||||||
|
|
||||||
const result = new Uint8Array(commands);
|
const result = new Uint8Array(commands);
|
||||||
|
|
||||||
@ -221,6 +221,9 @@ export class EscPosGraphics {
|
|||||||
// m = 0 (full cut), 1 (partial cut)
|
// m = 0 (full cut), 1 (partial cut)
|
||||||
commands.push(GS, 0x56, 0x00);
|
commands.push(GS, 0x56, 0x00);
|
||||||
|
|
||||||
|
// Initialize printer to force buffer flush and reset
|
||||||
|
commands.push(ESC, 0x40);
|
||||||
|
|
||||||
return commands;
|
return commands;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -682,19 +682,31 @@ patch(PosPrinterService.prototype, {
|
|||||||
} catch (_) { dateStr = new Date().toLocaleString(); }
|
} catch (_) { dateStr = new Date().toLocaleString(); }
|
||||||
|
|
||||||
// Table name — from pos_restaurant.ReceiptHeader patch
|
// Table name — from pos_restaurant.ReceiptHeader patch
|
||||||
// order.table_id or order.self_ordering_table_id → table.table_number
|
|
||||||
let tableName = '';
|
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 (table) {
|
||||||
if (order.customer_count) {
|
if (typeof table === 'number' || typeof table === 'string') {
|
||||||
tableName = `Table ${table.table_number}, Guests: ${order.customer_count}`;
|
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 {
|
} 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 = {
|
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(),
|
date: dateStr || new Date().toLocaleString(),
|
||||||
cashier: formatFirstLastName((typeof order.getCashierName === 'function' ? order.getCashierName() : '') || ''),
|
cashier: formatFirstLastName((typeof order.getCashierName === 'function' ? order.getCashierName() : '') || ''),
|
||||||
customer: order.partner_id?.name || null,
|
customer: order.partner_id?.name || null,
|
||||||
@ -834,13 +846,52 @@ patch(PosPrinterService.prototype, {
|
|||||||
taxId: getText('.pos-receipt-tax-id') || ''
|
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
|
// Parse order info
|
||||||
// Odoo 19: order reference is in .pos-receipt-vat, date in #order-date, cashier in .cashier
|
// 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 = {
|
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(),
|
date: getText('#order-date') || getText('.pos-receipt-date') || new Date().toLocaleString(),
|
||||||
cashier: formatFirstLastName(getText('.cashier') || getText('.pos-receipt-cashier') || ''),
|
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
|
// Parse order lines
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user