qz printer error handling and format updated

This commit is contained in:
Alaguraj0361 2026-04-28 10:10:37 +05:30
parent 1cccf85702
commit 2115f20e95
10 changed files with 237 additions and 117 deletions

View File

@ -4,7 +4,7 @@
height: 100vh !important; height: 100vh !important;
width: 100vw !important; width: 100vw !important;
overflow: hidden; overflow: hidden;
background: url('/dine360_theme_chennora/static/src/img/chen-banner-2.webp') !important; background: linear-gradient(rgba(0, 0, 0, 0.6), rgba(0, 0, 0, 0.6)), url('/dine360_theme_chennora/static/src/img/chen-banner-2.webp') !important;
background-repeat: no-repeat !important; background-repeat: no-repeat !important;
background-position: center center !important; background-position: center center !important;
background-size: cover !important; background-size: cover !important;
@ -111,8 +111,8 @@
} }
.form-control { .form-control {
background: rgba(255, 255, 255, 0.05) !important; background: rgba(255, 255, 255, 0.1) !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important; border: 1px solid rgba(255, 255, 255, 0.3) !important;
border-radius: 12px !important; border-radius: 12px !important;
padding: 12px 15px !important; padding: 12px 15px !important;
height: auto !important; height: auto !important;
@ -120,7 +120,14 @@
} }
.form-control::placeholder { .form-control::placeholder {
color: rgba(255, 255, 255, 0.3) !important; color: rgba(255, 255, 255, 0.6) !important;
}
/* Accessibility contrast for error messages */
.alert-danger {
background-color: rgba(220, 53, 69, 0.9) !important;
color: white !important;
border: none !important;
} }
/* Login Button with Gradient */ /* Login Button with Gradient */
@ -157,7 +164,16 @@
.o_login_right_side { .o_login_right_side {
background: #0f172a !important; background: #0f172a !important;
/* Keep dark background on mobile */ }
.o_login_card_wrapper {
width: 90% !important;
}
.oe_website_login_container .oe_login_form,
.oe_website_login_container .oe_signup_form,
.oe_website_login_container .oe_reset_password_form {
width: 90% !important;
} }
} }

View File

@ -106,17 +106,7 @@
object-fit: contain !important; object-fit: contain !important;
} }
/* "Rush Mode" Label next to logo */ /* "Rush Mode" Label removed for production */
.pos .pos-logo::after {
content: "RUSH MODE";
font-size: 14px;
font-weight: 800;
color: #ef4444;
letter-spacing: 2px;
border-left: 2px solid #e5e7eb;
padding-left: 20px;
margin-left: 10px;
}
/* 3. Search Bar - Teal Theme */ /* 3. Search Bar - Teal Theme */
.pos .search-bar { .pos .search-bar {
@ -365,7 +355,8 @@
.pos-receipt .pos-receipt-order-data { .pos-receipt .pos-receipt-order-data {
color: #94a3b8 !important; color: #94a3b8 !important;
font-size: 11px !important; font-size: 12px !important;
margin-top: 10px !important;
} }
.pos-receipt .receipt-orderlines { .pos-receipt .receipt-orderlines {
@ -375,11 +366,27 @@
.pos-receipt .orderline { .pos-receipt .orderline {
border: none !important; border: none !important;
margin: 5px 0 !important; margin: 0 !important;
padding: 10px 0 !important; padding: 8px 0 !important;
border-bottom: 1px dashed #e2e8f0 !important; border-bottom: 1px dashed #e2e8f0 !important;
display: flex !important; display: flex !important;
flex-wrap: wrap !important;
justify-content: space-between !important; justify-content: space-between !important;
align-items: baseline !important;
}
.pos-receipt .orderline .product-name {
flex: 1 1 65% !important;
font-weight: 700 !important;
white-space: normal !important;
word-break: break-word !important;
padding-right: 5px !important;
}
.pos-receipt .orderline .pos-receipt-right-align {
flex: 0 0 auto !important;
text-align: right !important;
font-weight: 800 !important;
} }
.pos-receipt .pos-receipt-total { .pos-receipt .pos-receipt-total {
@ -393,3 +400,47 @@
.pos-receipt-amount { .pos-receipt-amount {
font-weight: 800 !important; font-weight: 800 !important;
} }
/* ========================================
RESPONSIVE MEDIA QUERIES
======================================== */
@media (max-width: 1024px) {
.pos .leftpane {
width: 380px !important;
}
.pos .product {
width: 160px !important;
height: 180px !important;
}
.pos .product .product-img {
height: 110px !important;
}
.pos .search-bar {
width: 280px !important;
}
}
@media (max-width: 768px) {
.product-screen {
flex-direction: column !important;
height: 100vh !important;
}
.pos .leftpane {
width: 100% !important;
height: 50vh !important;
border-left: none !important;
border-top: 2px solid #f1f5f9 !important;
}
.pos .rightpane {
height: 50vh !important;
}
.pos .pos-topheader {
height: auto !important;
flex-wrap: wrap !important;
padding: 10px !important;
}
.pos .search-bar {
width: 100% !important;
margin-top: 10px !important;
}
}

View File

@ -19,14 +19,6 @@
<i class="fa fa-globe"/> <i class="fa fa-globe"/>
</a> </a>
<a href="#" class="o_top_item" title="AI Assistant">
<span class="o_ai_icon">AI</span>
</a>
<a href="#" class="o_top_item" title="Search">
<i class="fa fa-search"/>
</a>
<a href="/web#action=mail.action_discuss" class="o_top_item" title="Messages"> <a href="/web#action=mail.action_discuss" class="o_top_item" title="Messages">
<i class="fa fa-comments-o"/> <i class="fa fa-comments-o"/>
<span class="badge_dot"/> <span class="badge_dot"/>

View File

@ -3,14 +3,14 @@
/* ============================================ */ /* ============================================ */
.online-orders-screen { .online-orders-screen {
background: #1a1a2e; background: #171422;
color: #eee; color: #eee;
font-family: 'Inter', 'Segoe UI', sans-serif; font-family: 'Inter', 'Segoe UI', sans-serif;
} }
/* Header */ /* Header */
.online-orders-header { .online-orders-header {
background: linear-gradient(135deg, #16213e 0%, #0f3460 100%); background: #171422;
border-bottom: 2px solid rgba(255, 255, 255, 0.1); border-bottom: 2px solid rgba(255, 255, 255, 0.1);
min-height: 60px; min-height: 60px;
} }
@ -58,7 +58,7 @@
/* Body */ /* Body */
.online-orders-body { .online-orders-body {
background: #1a1a2e; background: #171422;
} }
/* Left Panel - Orders List */ /* Left Panel - Orders List */
@ -87,9 +87,9 @@
} }
.order-card.selected { .order-card.selected {
background: rgba(15, 52, 96, 0.5); background: rgba(214, 17, 30, 0.1);
border-color: #e94560; border-color: #d6111e;
box-shadow: 0 0 15px rgba(233, 69, 96, 0.2); box-shadow: 0 0 15px rgba(214, 17, 30, 0.2);
} }
.order-ref { .order-ref {
@ -164,7 +164,7 @@
width: 44px; width: 44px;
height: 44px; height: 44px;
border-radius: 50%; border-radius: 50%;
background: linear-gradient(135deg, #e94560, #0f3460); background: linear-gradient(135deg, #d6111e, #1a1d23);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -229,8 +229,8 @@
} }
.kitchen-badge.active { .kitchen-badge.active {
background: rgba(233, 69, 96, 0.2); background: rgba(214, 17, 30, 0.2);
color: #e94560; color: #d6111e;
} }
/* Detail Action Buttons */ /* Detail Action Buttons */
@ -265,9 +265,9 @@
/* Navbar Button */ /* Navbar Button */
.online-orders-nav-btn { .online-orders-nav-btn {
background: rgba(233, 69, 96, 0.15); background: rgba(214, 17, 30, 0.15);
border: 1px solid rgba(233, 69, 96, 0.3); border: 1px solid rgba(214, 17, 30, 0.3);
color: #e94560; color: #d6111e;
border-radius: 10px; border-radius: 10px;
padding: 6px 14px; padding: 6px 14px;
font-weight: 600; font-weight: 600;
@ -276,9 +276,9 @@
} }
.online-orders-nav-btn:hover { .online-orders-nav-btn:hover {
background: #e94560; background: #d6111e;
color: #fff; color: #fff;
border-color: #e94560; border-color: #d6111e;
} }
/* Scrollbar */ /* Scrollbar */
@ -300,7 +300,7 @@
/* Spinner */ /* Spinner */
.spinner-border { .spinner-border {
color: #e94560 !important; color: #d6111e !important;
} }
/* Responsive */ /* Responsive */

View File

@ -25,6 +25,7 @@ export class OnlineOrdersScreen extends Component {
loading: true, loading: true,
selectedOrder: null, selectedOrder: null,
confirmingId: null, confirmingId: null,
error: null,
}); });
console.log("[OnlineOrders] Services obtained:", { console.log("[OnlineOrders] Services obtained:", {
@ -83,6 +84,7 @@ export class OnlineOrdersScreen extends Component {
async loadOnlineOrders() { async loadOnlineOrders() {
try { try {
this.state.loading = true; this.state.loading = true;
this.state.error = null;
const orders = await this.orm.call( const orders = await this.orm.call(
"pos.order", "pos.order",
"get_online_orders", "get_online_orders",
@ -94,6 +96,7 @@ export class OnlineOrdersScreen extends Component {
} catch (error) { } catch (error) {
console.error("[OnlineOrders] Error loading orders:", error); console.error("[OnlineOrders] Error loading orders:", error);
this.state.loading = false; this.state.loading = false;
this.state.error = "Failed to load orders. Please check your connection.";
} }
} }
@ -104,6 +107,7 @@ export class OnlineOrdersScreen extends Component {
async confirmOrder(orderId) { async confirmOrder(orderId) {
try { try {
this.state.confirmingId = orderId; this.state.confirmingId = orderId;
this.state.error = null;
await this.orm.call( await this.orm.call(
"pos.order", "pos.order",
"action_confirm_online_order", "action_confirm_online_order",
@ -119,12 +123,14 @@ export class OnlineOrdersScreen extends Component {
} catch (error) { } catch (error) {
console.error("[OnlineOrders] Confirm error:", error); console.error("[OnlineOrders] Confirm error:", error);
this.notification.add("Failed to confirm order"); this.notification.add("Failed to confirm order");
this.state.error = "Failed to confirm order. It might have been modified.";
this.state.confirmingId = null; this.state.confirmingId = null;
} }
} }
async rejectOrder(orderId) { async rejectOrder(orderId) {
try { try {
this.state.error = null;
await this.orm.call( await this.orm.call(
"pos.order", "pos.order",
"action_reject_online_order", "action_reject_online_order",
@ -138,6 +144,7 @@ export class OnlineOrdersScreen extends Component {
} catch (error) { } catch (error) {
console.error("[OnlineOrders] Reject error:", error); console.error("[OnlineOrders] Reject error:", error);
this.notification.add("Failed to reject order"); this.notification.add("Failed to reject order");
this.state.error = "Failed to reject order.";
} }
} }

View File

@ -24,7 +24,21 @@
</div> </div>
<!-- Content --> <!-- Content -->
<div class="online-orders-body flex-grow-1 d-flex overflow-hidden"> <div class="online-orders-body flex-grow-1 d-flex overflow-hidden position-relative">
<!-- Error Alert -->
<div t-if="state.error" class="position-absolute w-100 p-3" style="z-index: 1050; top: 0;">
<div class="alert alert-danger d-flex justify-content-between align-items-center m-0 shadow">
<div>
<i class="fa fa-exclamation-triangle me-2"/>
<span t-esc="state.error"/>
</div>
<button class="btn btn-sm btn-outline-danger border-0" t-on-click="() => this.state.error = null">
<i class="fa fa-times"/>
</button>
</div>
</div>
<!-- Loading State --> <!-- Loading State -->
<div t-if="state.loading" class="d-flex align-items-center justify-content-center w-100"> <div t-if="state.loading" class="d-flex align-items-center justify-content-center w-100">
<div class="text-center"> <div class="text-center">

View File

@ -20,25 +20,26 @@ export class ChannelPanel extends Component {
this.dialog = useService("dialog"); this.dialog = useService("dialog");
this.SOURCE_LABELS = { this.SOURCE_LABELS = {
walk_in: '🚶 Walk-In', walk_in: 'Walk-In',
phone: '📞 Phone', phone: 'Phone',
whatsapp: '💬 WhatsApp', whatsapp: 'WhatsApp',
social_media: '📱 Social', social_media: 'Social',
online: '🌐 Online', online: 'Online',
kiosk: '📟 Kiosk', kiosk: 'Kiosk',
qr: '📷 QR Code', qr: 'QR Code',
platform: '🛒 Platform', platform: 'Platform',
}; };
this.FULFILMENT_LABELS = { this.FULFILMENT_LABELS = {
dine_in: '🍽️ Dine-In', dine_in: 'Dine-In',
pickup: '🛍️ Pickup', pickup: 'Pickup',
delivery: '🚚 Delivery', delivery: 'Delivery',
}; };
this.state = useState({ this.state = useState({
showDelivery: false, showDelivery: false,
showDetails: false, // For manual fields toggle showDetails: false, // For manual fields toggle
isCollapsed: false, // Panel collapse state
searchQuery: '', searchQuery: '',
searchResults: [], searchResults: [],
searching: false, searching: false,

View File

@ -4,11 +4,20 @@
<!-- ChannelPanel Component --> <!-- ChannelPanel Component -->
<t t-name="dine360_order_channels.ChannelPanel" owl="1"> <t t-name="dine360_order_channels.ChannelPanel" owl="1">
<t t-if="showPanel and currentOrder"> <t t-if="showPanel and currentOrder">
<div class="channel-panel d-flex flex-column gap-2 p-2 border-bottom"> <div class="channel-panel p-2 border-bottom">
<!-- Header with Collapse Toggle -->
<div class="d-flex justify-content-between align-items-center mb-1" style="cursor: pointer;" t-on-click="() => this.state.isCollapsed = !this.state.isCollapsed">
<span class="fw-bold small text-muted"><i class="fa fa-cogs me-1"/> Order Channels</span>
<button class="btn btn-sm btn-link p-0 text-decoration-none shadow-none text-muted">
<i t-attf-class="fa #{this.state.isCollapsed ? 'fa-chevron-down' : 'fa-chevron-up'}"/>
</button>
</div>
<div t-if="!this.state.isCollapsed" class="d-flex flex-column gap-2 mt-2">
<!-- Order Source Row --> <!-- Order Source Row -->
<div class="channel-section"> <div class="channel-section">
<div class="channel-label mb-1">📋 ORDER SOURCE</div> <div class="channel-label mb-1"><i class="fa fa-list-alt me-1"></i> ORDER SOURCE</div>
<div class="d-flex flex-wrap gap-1"> <div class="d-flex flex-wrap gap-1">
<t t-foreach="Object.entries(SOURCE_LABELS)" t-as="entry" t-key="entry[0]"> <t t-foreach="Object.entries(SOURCE_LABELS)" t-as="entry" t-key="entry[0]">
<button <button
@ -23,7 +32,7 @@
<!-- Phone number field --> <!-- Phone number field -->
<div t-if="orderSource === 'phone'" class="channel-extra"> <div t-if="orderSource === 'phone'" class="channel-extra">
<input type="text" class="form-control form-control-sm" <input type="text" class="form-control form-control-sm"
placeholder="📞 Phone Number" placeholder="Phone Number"
t-att-value="currentOrder.telephone_number" t-att-value="currentOrder.telephone_number"
t-on-change="(ev) => { currentOrder.telephone_number = ev.target.value; }"/> t-on-change="(ev) => { currentOrder.telephone_number = ev.target.value; }"/>
</div> </div>
@ -31,7 +40,7 @@
<!-- WhatsApp number field --> <!-- WhatsApp number field -->
<div t-if="orderSource === 'whatsapp'" class="channel-extra"> <div t-if="orderSource === 'whatsapp'" class="channel-extra">
<input type="text" class="form-control form-control-sm" <input type="text" class="form-control form-control-sm"
placeholder="📱 WhatsApp Number" placeholder="WhatsApp Number"
t-att-value="currentOrder.whatsapp_number" t-att-value="currentOrder.whatsapp_number"
t-on-change="(ev) => { currentOrder.whatsapp_number = ev.target.value; }"/> t-on-change="(ev) => { currentOrder.whatsapp_number = ev.target.value; }"/>
</div> </div>
@ -39,14 +48,14 @@
<!-- Social media ref field --> <!-- Social media ref field -->
<div t-if="orderSource === 'social_media'" class="channel-extra"> <div t-if="orderSource === 'social_media'" class="channel-extra">
<input type="text" class="form-control form-control-sm" <input type="text" class="form-control form-control-sm"
placeholder="📲 Post / Message Reference" placeholder="Post / Message Reference"
t-att-value="currentOrder.social_ref" t-att-value="currentOrder.social_ref"
t-on-change="(ev) => { currentOrder.social_ref = ev.target.value; }"/> t-on-change="(ev) => { currentOrder.social_ref = ev.target.value; }"/>
</div> </div>
<!-- Fulfilment Row --> <!-- Fulfilment Row -->
<div class="channel-section mt-1"> <div class="channel-section mt-1">
<div class="channel-label mb-1">🚀 FULFILMENT TYPE</div> <div class="channel-label mb-1"><i class="fa fa-rocket me-1"></i> FULFILMENT TYPE</div>
<div class="btn-group w-100" role="group"> <div class="btn-group w-100" role="group">
<t t-foreach="Object.entries(FULFILMENT_LABELS)" t-as="entry" t-key="entry[0]"> <t t-foreach="Object.entries(FULFILMENT_LABELS)" t-as="entry" t-key="entry[0]">
<button <button
@ -126,6 +135,7 @@
</div> </div>
</div> </div>
</div>
</t> </t>
</t> </t>

View File

@ -9,22 +9,3 @@
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3) !important; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3) !important;
} }
/* Category Sidebar Red Background */
.pos .category-button {
background: #d61112 !important;
color: white !important;
border: 1px solid rgba(255, 255, 255, 0.1) !important;
transition: all 0.2s ease;
}
.pos .category-button:hover {
background: #a00d0e !important;
transform: scale(1.02);
}
.pos .category-button.active {
background: #111 !important;
/* Contrast active category */
box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.5) !important;
border: 1px solid #FECD4F !important;
}

View File

@ -52,48 +52,95 @@ function buildReceiptLinesFromOrder(order) {
const lines = []; const lines = [];
const company = order?.pos?.company; const company = order?.pos?.company;
const client = order?.get_partner?.(); const client = order?.get_partner?.();
const table = order?.table;
const cashier = order?.employee || order?.pos?.get_cashier?.();
// 1. HEADER (Centered-ish)
if (company?.name) { if (company?.name) {
lines.push(company.name); lines.push(company.name.toUpperCase());
} }
if (company?.phone) { if (company?.street) lines.push(company.street);
lines.push(company.phone); if (company?.city) lines.push(company.city);
if (company?.phone) lines.push(`Tel: ${company.phone}`);
// Custom Odoo Header
if (order?.pos?.config?.receipt_header) {
lines.push(order.pos.config.receipt_header);
} }
lines.push("-".repeat(RECEIPT_COLUMNS)); lines.push("-".repeat(RECEIPT_COLUMNS));
const receiptNumber = order?.name || order?.uid || ""; // 2. ORDER INFO
if (receiptNumber) { const receiptNumber = order?.name || "";
lines.push(`Receipt: ${receiptNumber}`); if (receiptNumber) lines.push(`Order: ${receiptNumber}`);
if (table) {
lines.push(`TABLE: ${table.name}`.padEnd(20) + `GUESTS: ${order.customer_count || 1}`);
} }
lines.push(new Date().toLocaleString()); if (cashier) {
if (client?.name) { lines.push(`SERVER: ${cashier.name}`);
lines.push(`Customer: ${client.name}`);
} }
lines.push(`DATE: ${new Date().toLocaleString()}`);
if (client) {
lines.push(`CUSTOMER: ${client.name}`);
}
lines.push("=".repeat(RECEIPT_COLUMNS));
lines.push(leftRight("ITEM", "PRICE"));
lines.push("-".repeat(RECEIPT_COLUMNS)); lines.push("-".repeat(RECEIPT_COLUMNS));
// 3. ORDER LINES
for (const orderline of order?.get_orderlines?.() || []) { for (const orderline of order?.get_orderlines?.() || []) {
const product = orderline.get_product?.(); const product = orderline.get_product?.();
const name = product?.display_name || product?.name || ""; const name = product?.display_name || product?.name || "";
const qty = orderline.get_quantity?.() || 0; const qty = orderline.get_quantity?.() || 0;
const total = orderline.get_price_with_tax?.() ?? orderline.get_display_price?.() ?? 0; const priceUnit = orderline.get_unit_display_price?.() || 0;
for (const wrapped of wrapLine(name, RECEIPT_COLUMNS)) { const total = orderline.get_price_with_tax?.() || 0;
lines.push(wrapped);
// "Qty x Name" on left, "Total" on right
const itemLabel = `${qty} x ${name}`;
const itemPrice = money(total, currency);
if (itemLabel.length + itemPrice.length + 1 > RECEIPT_COLUMNS) {
lines.push(itemLabel);
lines.push(itemPrice.padStart(RECEIPT_COLUMNS));
} else {
lines.push(leftRight(itemLabel, itemPrice));
} }
lines.push(leftRight(` ${qty} x`, money(total, currency)));
// Show unit price if qty > 1
if (qty > 1) {
lines.push(` @ ${money(priceUnit, currency)}`);
}
}
// 4. TOTALS
lines.push("=".repeat(RECEIPT_COLUMNS));
lines.push(leftRight("SUBTOTAL", money(order?.get_total_without_tax?.(), currency)));
const tax = order?.get_total_tax?.();
if (tax) {
lines.push(leftRight("TAX", money(tax, currency)));
} }
lines.push("-".repeat(RECEIPT_COLUMNS)); lines.push("-".repeat(RECEIPT_COLUMNS));
lines.push(leftRight("TOTAL", money(order?.get_total_with_tax?.(), currency))); lines.push(leftRight("TOTAL", money(order?.get_total_with_tax?.(), currency)));
const tax = order?.get_total_tax?.();
if (tax) {
lines.push(leftRight("Tax", money(tax, currency)));
}
const change = order?.get_change?.(); const change = order?.get_change?.();
if (change) { if (change) {
lines.push(leftRight("Change", money(change, currency))); lines.push(leftRight("CHANGE", money(change, currency)));
} }
lines.push("-".repeat(RECEIPT_COLUMNS)); lines.push("-".repeat(RECEIPT_COLUMNS));
lines.push("Thank you");
// Custom Odoo Footer
if (order?.pos?.config?.receipt_footer) {
lines.push(order.pos.config.receipt_footer);
}
lines.push("THANK YOU FOR DINING WITH US!");
lines.push("PLEASE VISIT AGAIN");
lines.push(NEWLINE);
return lines; return lines;
} }
@ -108,7 +155,16 @@ function buildReceiptLines(receiptElement, order) {
function buildEscPosReceipt(receiptElement, order) { function buildEscPosReceipt(receiptElement, order) {
const ESC = "\x1B"; const ESC = "\x1B";
const GS = "\x1D"; const GS = "\x1D";
const lines = buildReceiptLines(receiptElement, order); let lines = buildReceiptLines(receiptElement, order);
// Safety mechanism: limit receipt length to prevent runaway printing
if (lines.length > 150) {
console.warn("Receipt is suspiciously long, truncating to 150 lines to prevent runaway printing.");
lines.length = 150;
lines.push("-".repeat(RECEIPT_COLUMNS));
lines.push("TRUNCATED FOR SAFETY");
}
const body = lines.flatMap((line) => wrapLine(line)).join(NEWLINE); const body = lines.flatMap((line) => wrapLine(line)).join(NEWLINE);
return [ return [
@ -123,14 +179,6 @@ function buildEscPosReceipt(receiptElement, order) {
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_printer_name) {
const rawPrintingEnabled = window.localStorage?.getItem("dine360_qz_raw_enabled") === "1";
if (!rawPrintingEnabled) {
console.warn(
"QZ raw receipt printing is disabled. Set localStorage.dine360_qz_raw_enabled = '1' only while testing a raw ESC/POS printer queue."
);
return super.printReceipt(...arguments);
}
try { try {
if (!window.qz) { if (!window.qz) {
console.error("QZ Tray library not loaded."); console.error("QZ Tray library not loaded.");