Print QZ receipts as raw ESC POS

This commit is contained in:
metatroncubeswdev 2026-04-24 21:13:31 -04:00
parent 66af3e010a
commit 43284d9f1a

View File

@ -3,6 +3,55 @@
import { patch } from "@web/core/utils/patch";
import { ReceiptScreen } from "@point_of_sale/app/screens/receipt_screen/receipt_screen";
const RECEIPT_COLUMNS = 42;
function normalizeReceiptText(text) {
return (text || "")
.replace(/\u00a0/g, " ")
.replace(/[ \t]+/g, " ")
.split("\n")
.map((line) => line.trim())
.filter(Boolean);
}
function wrapLine(line, width = RECEIPT_COLUMNS) {
if (line.length <= width) {
return [line];
}
const wrapped = [];
let remaining = line;
while (remaining.length > width) {
let breakpoint = remaining.lastIndexOf(" ", width);
if (breakpoint <= 0) {
breakpoint = width;
}
wrapped.push(remaining.slice(0, breakpoint).trimEnd());
remaining = remaining.slice(breakpoint).trimStart();
}
if (remaining) {
wrapped.push(remaining);
}
return wrapped;
}
function buildEscPosReceipt(receiptElement) {
const ESC = "\x1B";
const GS = "\x1D";
const lines = normalizeReceiptText(receiptElement.innerText);
const body = lines.flatMap((line) => wrapLine(line)).join("\n");
return [
ESC + "@", // Initialize printer
ESC + "a" + "\x01", // Center
ESC + "E" + "\x01", // Bold on
body,
ESC + "E" + "\x00", // Bold off
ESC + "a" + "\x00", // Left align
"\n\n\n",
GS + "V" + "\x00", // Full cut on Epson-compatible printers
].join("");
}
patch(ReceiptScreen.prototype, {
async printReceipt() {
if (this.pos.config.use_qz_printer && this.pos.config.qz_printer_name) {
@ -17,52 +66,24 @@ patch(ReceiptScreen.prototype, {
}
const printerName = this.pos.config.qz_printer_name;
const config = qz.configs.create(printerName);
const config = qz.configs.create(printerName, {
encoding: "CP437",
spool: { end: "\n" },
});
// Get inner HTML of receipt
const receiptElement = document.querySelector('.pos-receipt-container');
const receiptElement = document.querySelector(".pos-receipt") || document.querySelector(".pos-receipt-container");
if (!receiptElement) {
return false;
}
const receiptHtml = receiptElement.innerHTML;
// Odoo receipt styling is necessary for QZ pixel print
const printData = `
<html>
<head>
<style>
@page { margin: 0; }
body {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
width: 300px; /* Adjust based on printer paper width, 80mm = ~300px */
margin: 0;
padding: 10px;
}
* { box-sizing: border-box; }
.pos-receipt { width: 100%; text-align: center; }
.pos-receipt .pos-receipt-logo { max-width: 50%; margin: 0 auto; }
.pos-receipt .pos-receipt-contact { text-align: center; font-size: 12px; margin-bottom: 10px; }
.pos-receipt .receipt-orderlines { width: 100%; text-align: left; }
.pos-receipt .receipt-orderlines td { padding: 2px 0; }
.pos-receipt .receipt-total { width: 100%; font-weight: bold; font-size: 16px; margin-top: 10px; text-align: right; }
</style>
</head>
<body>
<div class="pos-receipt">
${receiptHtml}
</div>
</body>
</html>
`;
const printData = buildEscPosReceipt(receiptElement);
await qz.print(config, [
{
type: 'pixel',
format: 'html',
flavor: 'plain',
data: printData
type: "raw",
format: "plain",
flavor: "plain",
data: printData,
}
]);