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',
|
||||
'version': '17.0.1.2',
|
||||
'version': '17.0.1.3',
|
||||
'category': 'Point of Sale',
|
||||
'summary': 'Integrate Odoo POS with Star/Epson Printers via QZ Tray.',
|
||||
'depends': ['point_of_sale'],
|
||||
|
||||
@ -4,6 +4,7 @@ class PosConfig(models.Model):
|
||||
_inherit = 'pos.config'
|
||||
|
||||
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_kitchen_printer_name = fields.Char("Kitchen Printer Name", help="Name of the kitchen printer mapped in QZ Tray")
|
||||
qz_paper_width = fields.Selection(
|
||||
|
||||
@ -5,6 +5,7 @@ class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
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_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)
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
import { patch } from "@web/core/utils/patch";
|
||||
import { ReceiptScreen } from "@point_of_sale/app/screens/receipt_screen/receipt_screen";
|
||||
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 GS = 0x1d;
|
||||
@ -17,6 +17,10 @@ function columnsFromConfig(config) {
|
||||
return Number.isFinite(value) ? value : DEFAULT_COLUMNS;
|
||||
}
|
||||
|
||||
function getBillingPrinterName(config) {
|
||||
return config?.qz_billing_printer_name || config?.qz_printer_name || "";
|
||||
}
|
||||
|
||||
function cleanText(value) {
|
||||
return String(value ?? "")
|
||||
.normalize("NFKD")
|
||||
@ -356,7 +360,8 @@ async function buildEscPosReceipt(order, pos) {
|
||||
|
||||
patch(ReceiptScreen.prototype, {
|
||||
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 {
|
||||
if (!window.qz) {
|
||||
console.error("QZ Tray library not loaded.");
|
||||
@ -367,7 +372,7 @@ patch(ReceiptScreen.prototype, {
|
||||
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",
|
||||
copies: 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 columns = columnsFromConfig(config);
|
||||
const builder = new EscPosBuilder(columns);
|
||||
const table = order.getTable?.() || order.table;
|
||||
const newLines = orderChange?.new || [];
|
||||
const cancelledLines = orderChange?.cancelled || [];
|
||||
|
||||
builder.init();
|
||||
builder.align("center");
|
||||
@ -412,19 +420,25 @@ async function buildEscPosKitchenTicket(order, pos, changes) {
|
||||
|
||||
builder.align("left");
|
||||
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}`);
|
||||
builder.line(`Time: ${new Date().toLocaleTimeString()}`);
|
||||
builder.separator("=");
|
||||
|
||||
for (const category of Object.values(changes.categories || {})) {
|
||||
if (category.name) {
|
||||
if (cancelled) {
|
||||
builder.bold(true);
|
||||
builder.line(category.name.toUpperCase());
|
||||
builder.line("ORDER CANCELLED");
|
||||
builder.bold(false);
|
||||
}
|
||||
for (const line of category.orderlines || []) {
|
||||
const qty = line.quantity;
|
||||
builder.separator("=");
|
||||
|
||||
function printSection(title, lines) {
|
||||
if (!lines.length) {
|
||||
return;
|
||||
}
|
||||
builder.bold(true);
|
||||
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+$/, "");
|
||||
addWrappedLine(builder, `${qtyText} x ${line.name}`);
|
||||
if (line.note) {
|
||||
@ -434,6 +448,9 @@ async function buildEscPosKitchenTicket(order, pos, changes) {
|
||||
builder.line("");
|
||||
}
|
||||
|
||||
printSection("NEW ITEMS", newLines);
|
||||
printSection("CANCELLED ITEMS", cancelledLines);
|
||||
|
||||
builder.feed(END_FEED_LINES);
|
||||
if (config.qz_enable_cutter !== false) {
|
||||
builder.cut(config.qz_cut_mode || "partial");
|
||||
@ -442,11 +459,14 @@ async function buildEscPosKitchenTicket(order, pos, changes) {
|
||||
return bytesToBase64(builder.bytes);
|
||||
}
|
||||
|
||||
patch(PosStore.prototype, {
|
||||
async sendOrderToPrinter(order) {
|
||||
if (this.config.use_qz_printer && this.config.qz_kitchen_printer_name) {
|
||||
const changes = order.computeChanges();
|
||||
if (Object.keys(changes.categories).length > 0) {
|
||||
patch(Order.prototype, {
|
||||
async printChanges(cancelled) {
|
||||
const printerName = this.pos.config.qz_kitchen_printer_name;
|
||||
if (this.pos.config.use_qz_printer && printerName) {
|
||||
const orderChange = this.changesToOrder(cancelled);
|
||||
if (!orderChange.new.length && !orderChange.cancelled.length) {
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
if (!window.qz) {
|
||||
console.error("QZ Tray library not loaded.");
|
||||
@ -455,28 +475,24 @@ patch(PosStore.prototype, {
|
||||
if (!qz.websocket.isActive()) {
|
||||
await qz.websocket.connect({ retries: 2, delay: 1 });
|
||||
}
|
||||
|
||||
const config = qz.configs.create(this.config.qz_kitchen_printer_name, {
|
||||
const config = qz.configs.create(printerName, {
|
||||
encoding: "CP437",
|
||||
copies: 1,
|
||||
spool: { size: 1 },
|
||||
});
|
||||
const payload = await buildEscPosKitchenTicket(order, this, changes);
|
||||
const payload = await buildEscPosKitchenTicket(this, this.pos, orderChange, cancelled);
|
||||
await qz.print(config, [{
|
||||
type: "raw",
|
||||
format: "command",
|
||||
flavor: "base64",
|
||||
data: payload,
|
||||
}]);
|
||||
order.saved_quantity = order.get_orderlines().reduce((acc, line) => acc + line.get_quantity(), 0);
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error("QZ Tray Kitchen Print Error:", err);
|
||||
// Fallback or show error
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return super.sendOrderToPrinter(...arguments);
|
||||
return super.printChanges(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user