diff --git a/static/src/js/escpos_generator.js b/static/src/js/escpos_generator.js index 0c7a513..820b002 100755 --- a/static/src/js/escpos_generator.js +++ b/static/src/js/escpos_generator.js @@ -207,6 +207,7 @@ export class EscPosGenerator { // ── Initialize ──────────────────────────────────────────────────── cmds.push(...this.initialize()); + cmds.push(...this.setAlignment('left')); // ── Header ──────────────────────────────────────────────────────── // On basic receipt (table checker): skip company header, print only minimal info @@ -353,6 +354,25 @@ export class EscPosGenerator { cmds.push(...this.addLine(this.divider())); } + // ── Order Notes (Basic Receipt) ─────────────────────────────────── + if (receiptData.isBasicReceipt && receiptData.orderData) { + const o = receiptData.orderData; + if (o.internalNote) { + const noteLines = String(o.internalNote).split('\n'); + cmds.push(...this.addLine('Order Note:', { height: 2, bold: true })); + noteLines.forEach(l => { + cmds.push(...this.addLine(` ${l}`, { height: 2, bold: true })); + }); + } + if (o.customerNote) { + const noteLines = String(o.customerNote).split('\n'); + cmds.push(...this.addLine('Cust Note:', { height: 2, bold: true })); + noteLines.forEach(l => { + cmds.push(...this.addLine(` ${l}`, { height: 2, bold: true })); + }); + } + } + // ── Totals ──────────────────────────────────────────────────────── // Hidden on basic receipt — matches t-if="!props.basic_receipt" in order_receipt.xml if (!receiptData.isBasicReceipt && receiptData.totals) { diff --git a/static/src/js/pos_receipt_printer.js b/static/src/js/pos_receipt_printer.js index 0725c9c..88b4c0e 100755 --- a/static/src/js/pos_receipt_printer.js +++ b/static/src/js/pos_receipt_printer.js @@ -248,11 +248,14 @@ patch(PosPrinterService.prototype, { if (isMobile) { console.log('[BluetoothPrint] Mobile or WebView environment detected. Falling back to native printWeb on main window.'); try { - this.printWeb(el); + console.log('[BluetoothPrint] Invoking await this.printWeb(el)...'); + await this.printWeb(el); + console.log('[BluetoothPrint] printWeb has finished executing.'); return true; } catch (e) { console.error('[BluetoothPrint] printWeb failed, calling window.print directly:', e); window.print(el); + console.log('[BluetoothPrint] direct window.print has returned.'); return true; } } @@ -260,6 +263,7 @@ patch(PosPrinterService.prototype, { return new Promise((resolve) => { // Create a hidden iframe for printing const printFrame = document.createElement('iframe'); + printFrame.className = 'pos-print-iframe'; printFrame.style.position = 'fixed'; printFrame.style.right = '0'; printFrame.style.bottom = '0'; @@ -655,6 +659,7 @@ patch(PosPrinterService.prototype, { _buildReceiptDataFromOrder(order) { console.log('[BluetoothPrint] _buildReceiptDataFromOrder called for:', order?.pos_reference); + const isBasic = !!this._currentPrintBasic; const pos = this.env?.services?.pos; const company = order.company || pos?.company || {}; const config = order.config || pos?.config || {}; @@ -716,6 +721,8 @@ patch(PosPrinterService.prototype, { cashier: formatFirstLastName((typeof order.getCashierName === 'function' ? order.getCashierName() : '') || ''), customer: order.partner_id?.name || null, tableName, + internalNote: isBasic ? (pos?.getStrNotes ? pos.getStrNotes(order.internal_note || '[]') : (typeof order.internal_note === 'string' ? order.internal_note : '')) : '', + customerNote: isBasic ? (order.general_customer_note || '') : '', }; // ── Order lines ──────────────────────────────────────────────────────── @@ -723,7 +730,6 @@ patch(PosPrinterService.prototype, { ? order.getOrderlines() : (order.lines || []); - const isBasic = !!this._currentPrintBasic; const lines = orderlines .filter(line => { if (line.combo_parent_id) return false; // skip combo sub-lines @@ -747,8 +753,22 @@ patch(PosPrinterService.prototype, { ? line.displayPriceUnit : (qty !== 0 ? lineTotal / qty : line.price_unit || 0); - // Customer-facing note (always shown, even on basic receipt) - const note = line.customer_note || ''; + // Construct combined line note if basic receipt, else empty string + let note = ''; + if (isBasic) { + const noteParts = []; + const customerNote = line.customer_note || line.customerNote || ''; + if (customerNote) { + noteParts.push(customerNote); + } + if (line.note) { + const internalNote = pos?.getStrNotes ? pos.getStrNotes(line.note) : (typeof line.note === 'string' ? line.note : ''); + if (internalNote) { + noteParts.push(internalNote); + } + } + note = noteParts.join(' | '); + } // Find combo sub-lines linked to this line const comboSubLines = orderlines diff --git a/static/src/tests/escpos_properties.test.js b/static/src/tests/escpos_properties.test.js index 376f7d1..8e924f4 100755 --- a/static/src/tests/escpos_properties.test.js +++ b/static/src/tests/escpos_properties.test.js @@ -31,14 +31,17 @@ describe('ESC/POS Conversion Properties', () => { orderName: fc.string({ minLength: 1, maxLength: 50 }), date: fc.date().map(d => d.toISOString()), cashier: fc.string({ minLength: 1, maxLength: 50 }), - customer: fc.option(fc.string({ minLength: 1, maxLength: 100 })) + customer: fc.option(fc.string({ minLength: 1, maxLength: 100 })), + internalNote: fc.option(fc.string({ maxLength: 100 })), + customerNote: fc.option(fc.string({ maxLength: 100 })) }), lines: fc.array( fc.record({ productName: fc.string({ minLength: 1, maxLength: 100 }), quantity: fc.float({ min: Math.fround(0.01), max: Math.fround(1000), noNaN: true }), price: fc.float({ min: Math.fround(0.01), max: Math.fround(100000), noNaN: true }), - total: fc.float({ min: Math.fround(0.01), max: Math.fround(100000), noNaN: true }) + total: fc.float({ min: Math.fround(0.01), max: Math.fround(100000), noNaN: true }), + note: fc.option(fc.string({ maxLength: 100 })) }), { minLength: 1, maxLength: 50 } ), @@ -56,7 +59,8 @@ describe('ESC/POS Conversion Properties', () => { footerData: fc.record({ message: fc.string({ maxLength: 200 }), barcode: fc.option(fc.string({ minLength: 1, maxLength: 50 })) - }) + }), + isBasicReceipt: fc.boolean() }); };