feat: implement order and line notes for basic receipts in ESC/POS generator and update receipt data mapping
This commit is contained in:
parent
e80bce30fa
commit
5524546dab
@ -207,6 +207,7 @@ export class EscPosGenerator {
|
|||||||
|
|
||||||
// ── Initialize ────────────────────────────────────────────────────
|
// ── Initialize ────────────────────────────────────────────────────
|
||||||
cmds.push(...this.initialize());
|
cmds.push(...this.initialize());
|
||||||
|
cmds.push(...this.setAlignment('left'));
|
||||||
|
|
||||||
// ── Header ────────────────────────────────────────────────────────
|
// ── Header ────────────────────────────────────────────────────────
|
||||||
// On basic receipt (table checker): skip company header, print only minimal info
|
// 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()));
|
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 ────────────────────────────────────────────────────────
|
// ── Totals ────────────────────────────────────────────────────────
|
||||||
// Hidden on basic receipt — matches t-if="!props.basic_receipt" in order_receipt.xml
|
// Hidden on basic receipt — matches t-if="!props.basic_receipt" in order_receipt.xml
|
||||||
if (!receiptData.isBasicReceipt && receiptData.totals) {
|
if (!receiptData.isBasicReceipt && receiptData.totals) {
|
||||||
|
|||||||
@ -248,11 +248,14 @@ patch(PosPrinterService.prototype, {
|
|||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
console.log('[BluetoothPrint] Mobile or WebView environment detected. Falling back to native printWeb on main window.');
|
console.log('[BluetoothPrint] Mobile or WebView environment detected. Falling back to native printWeb on main window.');
|
||||||
try {
|
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;
|
return true;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('[BluetoothPrint] printWeb failed, calling window.print directly:', e);
|
console.error('[BluetoothPrint] printWeb failed, calling window.print directly:', e);
|
||||||
window.print(el);
|
window.print(el);
|
||||||
|
console.log('[BluetoothPrint] direct window.print has returned.');
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -260,6 +263,7 @@ patch(PosPrinterService.prototype, {
|
|||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
// Create a hidden iframe for printing
|
// Create a hidden iframe for printing
|
||||||
const printFrame = document.createElement('iframe');
|
const printFrame = document.createElement('iframe');
|
||||||
|
printFrame.className = 'pos-print-iframe';
|
||||||
printFrame.style.position = 'fixed';
|
printFrame.style.position = 'fixed';
|
||||||
printFrame.style.right = '0';
|
printFrame.style.right = '0';
|
||||||
printFrame.style.bottom = '0';
|
printFrame.style.bottom = '0';
|
||||||
@ -655,6 +659,7 @@ patch(PosPrinterService.prototype, {
|
|||||||
_buildReceiptDataFromOrder(order) {
|
_buildReceiptDataFromOrder(order) {
|
||||||
console.log('[BluetoothPrint] _buildReceiptDataFromOrder called for:', order?.pos_reference);
|
console.log('[BluetoothPrint] _buildReceiptDataFromOrder called for:', order?.pos_reference);
|
||||||
|
|
||||||
|
const isBasic = !!this._currentPrintBasic;
|
||||||
const pos = this.env?.services?.pos;
|
const pos = this.env?.services?.pos;
|
||||||
const company = order.company || pos?.company || {};
|
const company = order.company || pos?.company || {};
|
||||||
const config = order.config || pos?.config || {};
|
const config = order.config || pos?.config || {};
|
||||||
@ -716,6 +721,8 @@ patch(PosPrinterService.prototype, {
|
|||||||
cashier: formatFirstLastName((typeof order.getCashierName === 'function' ? order.getCashierName() : '') || ''),
|
cashier: formatFirstLastName((typeof order.getCashierName === 'function' ? order.getCashierName() : '') || ''),
|
||||||
customer: order.partner_id?.name || null,
|
customer: order.partner_id?.name || null,
|
||||||
tableName,
|
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 ────────────────────────────────────────────────────────
|
// ── Order lines ────────────────────────────────────────────────────────
|
||||||
@ -723,7 +730,6 @@ patch(PosPrinterService.prototype, {
|
|||||||
? order.getOrderlines()
|
? order.getOrderlines()
|
||||||
: (order.lines || []);
|
: (order.lines || []);
|
||||||
|
|
||||||
const isBasic = !!this._currentPrintBasic;
|
|
||||||
const lines = orderlines
|
const lines = orderlines
|
||||||
.filter(line => {
|
.filter(line => {
|
||||||
if (line.combo_parent_id) return false; // skip combo sub-lines
|
if (line.combo_parent_id) return false; // skip combo sub-lines
|
||||||
@ -747,8 +753,22 @@ patch(PosPrinterService.prototype, {
|
|||||||
? line.displayPriceUnit
|
? line.displayPriceUnit
|
||||||
: (qty !== 0 ? lineTotal / qty : line.price_unit || 0);
|
: (qty !== 0 ? lineTotal / qty : line.price_unit || 0);
|
||||||
|
|
||||||
// Customer-facing note (always shown, even on basic receipt)
|
// Construct combined line note if basic receipt, else empty string
|
||||||
const note = line.customer_note || '';
|
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
|
// Find combo sub-lines linked to this line
|
||||||
const comboSubLines = orderlines
|
const comboSubLines = orderlines
|
||||||
|
|||||||
@ -31,14 +31,17 @@ describe('ESC/POS Conversion Properties', () => {
|
|||||||
orderName: fc.string({ minLength: 1, maxLength: 50 }),
|
orderName: fc.string({ minLength: 1, maxLength: 50 }),
|
||||||
date: fc.date().map(d => d.toISOString()),
|
date: fc.date().map(d => d.toISOString()),
|
||||||
cashier: fc.string({ minLength: 1, maxLength: 50 }),
|
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(
|
lines: fc.array(
|
||||||
fc.record({
|
fc.record({
|
||||||
productName: fc.string({ minLength: 1, maxLength: 100 }),
|
productName: fc.string({ minLength: 1, maxLength: 100 }),
|
||||||
quantity: fc.float({ min: Math.fround(0.01), max: Math.fround(1000), noNaN: true }),
|
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 }),
|
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 }
|
{ minLength: 1, maxLength: 50 }
|
||||||
),
|
),
|
||||||
@ -56,7 +59,8 @@ describe('ESC/POS Conversion Properties', () => {
|
|||||||
footerData: fc.record({
|
footerData: fc.record({
|
||||||
message: fc.string({ maxLength: 200 }),
|
message: fc.string({ maxLength: 200 }),
|
||||||
barcode: fc.option(fc.string({ minLength: 1, maxLength: 50 }))
|
barcode: fc.option(fc.string({ minLength: 1, maxLength: 50 }))
|
||||||
})
|
}),
|
||||||
|
isBasicReceipt: fc.boolean()
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user