forked from alaguraj/odoo-testing-addons
enabled multi-printer configuration for billing and kitchen printers
This commit is contained in:
parent
8fc4ae447a
commit
e7c67653e4
@ -4,7 +4,8 @@ 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("QZ Printer Name", help="Name of the 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_paper_width = fields.Selection(
|
qz_paper_width = fields.Selection(
|
||||||
[('42', '58mm / 42 columns'), ('48', '80mm / 48 columns')],
|
[('42', '58mm / 42 columns'), ('48', '80mm / 48 columns')],
|
||||||
string="QZ Receipt Width",
|
string="QZ Receipt Width",
|
||||||
|
|||||||
@ -5,7 +5,8 @@ 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_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)
|
||||||
qz_print_logo = fields.Boolean(related='pos_config_id.qz_print_logo', readonly=False)
|
qz_print_logo = fields.Boolean(related='pos_config_id.qz_print_logo', readonly=False)
|
||||||
qz_enable_cutter = fields.Boolean(related='pos_config_id.qz_enable_cutter', readonly=False)
|
qz_enable_cutter = fields.Boolean(related='pos_config_id.qz_enable_cutter', readonly=False)
|
||||||
|
|||||||
@ -3,6 +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";
|
||||||
|
|
||||||
const ESC = 0x1b;
|
const ESC = 0x1b;
|
||||||
const GS = 0x1d;
|
const GS = 0x1d;
|
||||||
@ -355,7 +356,7 @@ 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_printer_name) {
|
if (this.pos.config.use_qz_printer && this.pos.config.qz_billing_printer_name) {
|
||||||
try {
|
try {
|
||||||
if (!window.qz) {
|
if (!window.qz) {
|
||||||
console.error("QZ Tray library not loaded.");
|
console.error("QZ Tray library not loaded.");
|
||||||
@ -366,7 +367,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_printer_name, {
|
const config = qz.configs.create(this.pos.config.qz_billing_printer_name, {
|
||||||
encoding: "CP437",
|
encoding: "CP437",
|
||||||
copies: 1,
|
copies: 1,
|
||||||
spool: { size: 1 },
|
spool: { size: 1 },
|
||||||
@ -395,3 +396,87 @@ patch(ReceiptScreen.prototype, {
|
|||||||
return super.printReceipt(...arguments);
|
return super.printReceipt(...arguments);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function buildEscPosKitchenTicket(order, pos, changes) {
|
||||||
|
const config = pos?.config || {};
|
||||||
|
const columns = columnsFromConfig(config);
|
||||||
|
const builder = new EscPosBuilder(columns);
|
||||||
|
|
||||||
|
builder.init();
|
||||||
|
builder.align("center");
|
||||||
|
builder.bold(true);
|
||||||
|
builder.size("doubleHeight");
|
||||||
|
builder.line("KITCHEN ORDER");
|
||||||
|
builder.size("normal");
|
||||||
|
builder.line("");
|
||||||
|
|
||||||
|
builder.align("left");
|
||||||
|
if (order.name) builder.line(`Order: ${order.name}`);
|
||||||
|
if (order.table?.name) builder.line(`Table: ${order.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) {
|
||||||
|
builder.bold(true);
|
||||||
|
builder.line(category.name.toUpperCase());
|
||||||
|
builder.bold(false);
|
||||||
|
}
|
||||||
|
for (const line of category.orderlines || []) {
|
||||||
|
const qty = line.quantity;
|
||||||
|
const qtyText = Number.isInteger(qty) ? String(qty) : qty.toFixed(2).replace(/\.?0+$/, "");
|
||||||
|
addWrappedLine(builder, `${qtyText} x ${line.name}`);
|
||||||
|
if (line.note) {
|
||||||
|
addWrappedLine(builder, `Note: ${line.note}`, "", 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
builder.line("");
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.feed(END_FEED_LINES);
|
||||||
|
if (config.qz_enable_cutter !== false) {
|
||||||
|
builder.cut(config.qz_cut_mode || "partial");
|
||||||
|
}
|
||||||
|
builder.feed(1);
|
||||||
|
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) {
|
||||||
|
try {
|
||||||
|
if (!window.qz) {
|
||||||
|
console.error("QZ Tray library not loaded.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!qz.websocket.isActive()) {
|
||||||
|
await qz.websocket.connect({ retries: 2, delay: 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const config = qz.configs.create(this.config.qz_kitchen_printer_name, {
|
||||||
|
encoding: "CP437",
|
||||||
|
copies: 1,
|
||||||
|
spool: { size: 1 },
|
||||||
|
});
|
||||||
|
const payload = await buildEscPosKitchenTicket(order, this, changes);
|
||||||
|
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 true;
|
||||||
|
}
|
||||||
|
return super.sendOrderToPrinter(...arguments);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|||||||
@ -11,8 +11,12 @@
|
|||||||
<field name="use_qz_printer"/>
|
<field name="use_qz_printer"/>
|
||||||
<div class="content-group" invisible="not use_qz_printer">
|
<div class="content-group" invisible="not use_qz_printer">
|
||||||
<div class="row mt16">
|
<div class="row mt16">
|
||||||
<label string="Printer Name" for="qz_printer_name" class="col-lg-3 o_light_label"/>
|
<label string="Billing Printer" for="qz_billing_printer_name" class="col-lg-3 o_light_label"/>
|
||||||
<field name="qz_printer_name"/>
|
<field name="qz_billing_printer_name"/>
|
||||||
|
</div>
|
||||||
|
<div class="row mt16">
|
||||||
|
<label string="Kitchen Printer" for="qz_kitchen_printer_name" class="col-lg-3 o_light_label"/>
|
||||||
|
<field name="qz_kitchen_printer_name"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt16">
|
<div class="row mt16">
|
||||||
<label string="Receipt Width" for="qz_paper_width" class="col-lg-3 o_light_label"/>
|
<label string="Receipt Width" for="qz_paper_width" class="col-lg-3 o_light_label"/>
|
||||||
@ -46,8 +50,12 @@
|
|||||||
<field name="use_qz_printer"/>
|
<field name="use_qz_printer"/>
|
||||||
<div class="content-group" invisible="not use_qz_printer">
|
<div class="content-group" invisible="not use_qz_printer">
|
||||||
<div class="row mt16">
|
<div class="row mt16">
|
||||||
<label string="Printer Name" for="qz_printer_name" class="col-lg-3 o_light_label"/>
|
<label string="Billing Printer" for="qz_billing_printer_name" class="col-lg-3 o_light_label"/>
|
||||||
<field name="qz_printer_name"/>
|
<field name="qz_billing_printer_name"/>
|
||||||
|
</div>
|
||||||
|
<div class="row mt16">
|
||||||
|
<label string="Kitchen Printer" for="qz_kitchen_printer_name" class="col-lg-3 o_light_label"/>
|
||||||
|
<field name="qz_kitchen_printer_name"/>
|
||||||
</div>
|
</div>
|
||||||
<div class="row mt16">
|
<div class="row mt16">
|
||||||
<label string="Receipt Width" for="qz_paper_width" class="col-lg-3 o_light_label"/>
|
<label string="Receipt Width" for="qz_paper_width" class="col-lg-3 o_light_label"/>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user