forked from alaguraj/odoo-testing-addons
Fix QZ multi-printer fallback and kitchen hook
This commit is contained in:
parent
e7c67653e4
commit
c94c1f06eb
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
'name': 'Dine360 QZ Tray Printer',
|
'name': 'Dine360 QZ Tray Printer',
|
||||||
'version': '17.0.1.2',
|
'version': '17.0.1.3',
|
||||||
'category': 'Point of Sale',
|
'category': 'Point of Sale',
|
||||||
'summary': 'Integrate Odoo POS with Star/Epson Printers via QZ Tray.',
|
'summary': 'Integrate Odoo POS with Star/Epson Printers via QZ Tray.',
|
||||||
'depends': ['point_of_sale'],
|
'depends': ['point_of_sale'],
|
||||||
|
|||||||
@ -4,6 +4,7 @@ class PosConfig(models.Model):
|
|||||||
_inherit = 'pos.config'
|
_inherit = 'pos.config'
|
||||||
|
|
||||||
use_qz_printer = fields.Boolean("Use QZ Tray Printer", help="Print directly using QZ Tray locally")
|
use_qz_printer = fields.Boolean("Use QZ Tray Printer", help="Print directly using QZ Tray locally")
|
||||||
|
qz_printer_name = fields.Char("Legacy QZ Printer Name", help="Previous single-printer setting kept for compatibility")
|
||||||
qz_billing_printer_name = fields.Char("Billing Printer Name", help="Name of the billing printer mapped in QZ Tray")
|
qz_billing_printer_name = fields.Char("Billing Printer Name", help="Name of the billing printer mapped in QZ Tray")
|
||||||
qz_kitchen_printer_name = fields.Char("Kitchen Printer Name", help="Name of the kitchen printer mapped in QZ Tray")
|
qz_kitchen_printer_name = fields.Char("Kitchen Printer Name", help="Name of the kitchen printer mapped in QZ Tray")
|
||||||
qz_paper_width = fields.Selection(
|
qz_paper_width = fields.Selection(
|
||||||
|
|||||||
@ -5,6 +5,7 @@ class ResConfigSettings(models.TransientModel):
|
|||||||
_inherit = 'res.config.settings'
|
_inherit = 'res.config.settings'
|
||||||
|
|
||||||
use_qz_printer = fields.Boolean(related='pos_config_id.use_qz_printer', readonly=False)
|
use_qz_printer = fields.Boolean(related='pos_config_id.use_qz_printer', readonly=False)
|
||||||
|
qz_printer_name = fields.Char(related='pos_config_id.qz_printer_name', readonly=False)
|
||||||
qz_billing_printer_name = fields.Char(related='pos_config_id.qz_billing_printer_name', readonly=False)
|
qz_billing_printer_name = fields.Char(related='pos_config_id.qz_billing_printer_name', readonly=False)
|
||||||
qz_kitchen_printer_name = fields.Char(related='pos_config_id.qz_kitchen_printer_name', readonly=False)
|
qz_kitchen_printer_name = fields.Char(related='pos_config_id.qz_kitchen_printer_name', readonly=False)
|
||||||
qz_paper_width = fields.Selection(related='pos_config_id.qz_paper_width', readonly=False)
|
qz_paper_width = fields.Selection(related='pos_config_id.qz_paper_width', readonly=False)
|
||||||
|
|||||||
@ -3,7 +3,7 @@
|
|||||||
import { patch } from "@web/core/utils/patch";
|
import { patch } from "@web/core/utils/patch";
|
||||||
import { ReceiptScreen } from "@point_of_sale/app/screens/receipt_screen/receipt_screen";
|
import { ReceiptScreen } from "@point_of_sale/app/screens/receipt_screen/receipt_screen";
|
||||||
import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup";
|
import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup";
|
||||||
import { PosStore } from "@point_of_sale/app/store/pos_store";
|
import { Order } from "@point_of_sale/app/store/models";
|
||||||
|
|
||||||
const ESC = 0x1b;
|
const ESC = 0x1b;
|
||||||
const GS = 0x1d;
|
const GS = 0x1d;
|
||||||
@ -17,6 +17,10 @@ function columnsFromConfig(config) {
|
|||||||
return Number.isFinite(value) ? value : DEFAULT_COLUMNS;
|
return Number.isFinite(value) ? value : DEFAULT_COLUMNS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getBillingPrinterName(config) {
|
||||||
|
return config?.qz_billing_printer_name || config?.qz_printer_name || "";
|
||||||
|
}
|
||||||
|
|
||||||
function cleanText(value) {
|
function cleanText(value) {
|
||||||
return String(value ?? "")
|
return String(value ?? "")
|
||||||
.normalize("NFKD")
|
.normalize("NFKD")
|
||||||
@ -356,7 +360,8 @@ async function buildEscPosReceipt(order, pos) {
|
|||||||
|
|
||||||
patch(ReceiptScreen.prototype, {
|
patch(ReceiptScreen.prototype, {
|
||||||
async printReceipt() {
|
async printReceipt() {
|
||||||
if (this.pos.config.use_qz_printer && this.pos.config.qz_billing_printer_name) {
|
const printerName = getBillingPrinterName(this.pos.config);
|
||||||
|
if (this.pos.config.use_qz_printer && printerName) {
|
||||||
try {
|
try {
|
||||||
if (!window.qz) {
|
if (!window.qz) {
|
||||||
console.error("QZ Tray library not loaded.");
|
console.error("QZ Tray library not loaded.");
|
||||||
@ -367,7 +372,7 @@ patch(ReceiptScreen.prototype, {
|
|||||||
await qz.websocket.connect({ retries: 2, delay: 1 });
|
await qz.websocket.connect({ retries: 2, delay: 1 });
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = qz.configs.create(this.pos.config.qz_billing_printer_name, {
|
const config = qz.configs.create(printerName, {
|
||||||
encoding: "CP437",
|
encoding: "CP437",
|
||||||
copies: 1,
|
copies: 1,
|
||||||
spool: { size: 1 },
|
spool: { size: 1 },
|
||||||
@ -397,10 +402,13 @@ patch(ReceiptScreen.prototype, {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
async function buildEscPosKitchenTicket(order, pos, changes) {
|
async function buildEscPosKitchenTicket(order, pos, orderChange, cancelled = false) {
|
||||||
const config = pos?.config || {};
|
const config = pos?.config || {};
|
||||||
const columns = columnsFromConfig(config);
|
const columns = columnsFromConfig(config);
|
||||||
const builder = new EscPosBuilder(columns);
|
const builder = new EscPosBuilder(columns);
|
||||||
|
const table = order.getTable?.() || order.table;
|
||||||
|
const newLines = orderChange?.new || [];
|
||||||
|
const cancelledLines = orderChange?.cancelled || [];
|
||||||
|
|
||||||
builder.init();
|
builder.init();
|
||||||
builder.align("center");
|
builder.align("center");
|
||||||
@ -412,19 +420,25 @@ async function buildEscPosKitchenTicket(order, pos, changes) {
|
|||||||
|
|
||||||
builder.align("left");
|
builder.align("left");
|
||||||
if (order.name) builder.line(`Order: ${order.name}`);
|
if (order.name) builder.line(`Order: ${order.name}`);
|
||||||
if (order.table?.name) builder.line(`Table: ${order.table.name}`);
|
if (table?.name) builder.line(`Table: ${table.name}`);
|
||||||
if (order.customer_count) builder.line(`Guests: ${order.customer_count}`);
|
if (order.customer_count) builder.line(`Guests: ${order.customer_count}`);
|
||||||
builder.line(`Time: ${new Date().toLocaleTimeString()}`);
|
builder.line(`Time: ${new Date().toLocaleTimeString()}`);
|
||||||
|
if (cancelled) {
|
||||||
|
builder.bold(true);
|
||||||
|
builder.line("ORDER CANCELLED");
|
||||||
|
builder.bold(false);
|
||||||
|
}
|
||||||
builder.separator("=");
|
builder.separator("=");
|
||||||
|
|
||||||
for (const category of Object.values(changes.categories || {})) {
|
function printSection(title, lines) {
|
||||||
if (category.name) {
|
if (!lines.length) {
|
||||||
builder.bold(true);
|
return;
|
||||||
builder.line(category.name.toUpperCase());
|
|
||||||
builder.bold(false);
|
|
||||||
}
|
}
|
||||||
for (const line of category.orderlines || []) {
|
builder.bold(true);
|
||||||
const qty = line.quantity;
|
builder.line(title);
|
||||||
|
builder.bold(false);
|
||||||
|
for (const line of lines) {
|
||||||
|
const qty = Number(line.quantity || 0);
|
||||||
const qtyText = Number.isInteger(qty) ? String(qty) : qty.toFixed(2).replace(/\.?0+$/, "");
|
const qtyText = Number.isInteger(qty) ? String(qty) : qty.toFixed(2).replace(/\.?0+$/, "");
|
||||||
addWrappedLine(builder, `${qtyText} x ${line.name}`);
|
addWrappedLine(builder, `${qtyText} x ${line.name}`);
|
||||||
if (line.note) {
|
if (line.note) {
|
||||||
@ -434,6 +448,9 @@ async function buildEscPosKitchenTicket(order, pos, changes) {
|
|||||||
builder.line("");
|
builder.line("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
printSection("NEW ITEMS", newLines);
|
||||||
|
printSection("CANCELLED ITEMS", cancelledLines);
|
||||||
|
|
||||||
builder.feed(END_FEED_LINES);
|
builder.feed(END_FEED_LINES);
|
||||||
if (config.qz_enable_cutter !== false) {
|
if (config.qz_enable_cutter !== false) {
|
||||||
builder.cut(config.qz_cut_mode || "partial");
|
builder.cut(config.qz_cut_mode || "partial");
|
||||||
@ -442,41 +459,40 @@ async function buildEscPosKitchenTicket(order, pos, changes) {
|
|||||||
return bytesToBase64(builder.bytes);
|
return bytesToBase64(builder.bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
patch(PosStore.prototype, {
|
patch(Order.prototype, {
|
||||||
async sendOrderToPrinter(order) {
|
async printChanges(cancelled) {
|
||||||
if (this.config.use_qz_printer && this.config.qz_kitchen_printer_name) {
|
const printerName = this.pos.config.qz_kitchen_printer_name;
|
||||||
const changes = order.computeChanges();
|
if (this.pos.config.use_qz_printer && printerName) {
|
||||||
if (Object.keys(changes.categories).length > 0) {
|
const orderChange = this.changesToOrder(cancelled);
|
||||||
try {
|
if (!orderChange.new.length && !orderChange.cancelled.length) {
|
||||||
if (!window.qz) {
|
return true;
|
||||||
console.error("QZ Tray library not loaded.");
|
}
|
||||||
return false;
|
try {
|
||||||
}
|
if (!window.qz) {
|
||||||
if (!qz.websocket.isActive()) {
|
console.error("QZ Tray library not loaded.");
|
||||||
await qz.websocket.connect({ retries: 2, delay: 1 });
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!qz.websocket.isActive()) {
|
||||||
const config = qz.configs.create(this.config.qz_kitchen_printer_name, {
|
await qz.websocket.connect({ retries: 2, delay: 1 });
|
||||||
encoding: "CP437",
|
}
|
||||||
copies: 1,
|
const config = qz.configs.create(printerName, {
|
||||||
spool: { size: 1 },
|
encoding: "CP437",
|
||||||
});
|
copies: 1,
|
||||||
const payload = await buildEscPosKitchenTicket(order, this, changes);
|
spool: { size: 1 },
|
||||||
await qz.print(config, [{
|
});
|
||||||
type: "raw",
|
const payload = await buildEscPosKitchenTicket(this, this.pos, orderChange, cancelled);
|
||||||
format: "command",
|
await qz.print(config, [{
|
||||||
flavor: "base64",
|
type: "raw",
|
||||||
data: payload,
|
format: "command",
|
||||||
}]);
|
flavor: "base64",
|
||||||
order.saved_quantity = order.get_orderlines().reduce((acc, line) => acc + line.get_quantity(), 0);
|
data: payload,
|
||||||
return true;
|
}]);
|
||||||
} catch (err) {
|
return true;
|
||||||
console.error("QZ Tray Kitchen Print Error:", err);
|
} catch (err) {
|
||||||
// Fallback or show error
|
console.error("QZ Tray Kitchen Print Error:", err);
|
||||||
}
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
return super.sendOrderToPrinter(...arguments);
|
return super.printChanges(...arguments);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user