feat: enhance basic receipt layout with double-height printing and conditional header skipping

This commit is contained in:
Suherdy Yacob 2026-06-01 15:09:38 +07:00
parent e667ef3bac
commit d1df33bdb2

View File

@ -209,13 +209,13 @@ export class EscPosGenerator {
cmds.push(...this.initialize()); cmds.push(...this.initialize());
// ── Header ──────────────────────────────────────────────────────── // ── Header ────────────────────────────────────────────────────────
if (receiptData.headerData) { // On basic receipt (table checker): skip company header, print only minimal info
if (!receiptData.isBasicReceipt && receiptData.headerData) {
const h = receiptData.headerData; const h = receiptData.headerData;
if (h.companyName) { if (h.companyName) {
cmds.push(...this.addLine(h.companyName, { align: 'center', bold: true, width: 2, height: 2 })); cmds.push(...this.addLine(h.companyName, { align: 'center', bold: true, width: 2, height: 2 }));
} }
if (h.address) { if (h.address) {
// Long addresses should wrap — print as-is (printer wraps automatically)
cmds.push(...this.addLine(h.address, { align: 'center' })); cmds.push(...this.addLine(h.address, { align: 'center' }));
} }
if (h.phone) { if (h.phone) {
@ -230,12 +230,20 @@ export class EscPosGenerator {
// ── Order info ──────────────────────────────────────────────────── // ── Order info ────────────────────────────────────────────────────
if (receiptData.orderData) { if (receiptData.orderData) {
const o = receiptData.orderData; const o = receiptData.orderData;
if (receiptData.isBasicReceipt) {
// Basic receipt (table checker): all info at double height for readability
if (o.orderName) cmds.push(...this.addLine(`Order: ${o.orderName}`, { height: 2, bold: true }));
if (o.date) cmds.push(...this.addLine(o.date, { height: 2 }));
if (o.cashier) cmds.push(...this.addLine(`By: ${o.cashier}`, { height: 2 }));
if (o.tableName) cmds.push(...this.addLine(o.tableName, { bold: true, height: 2, align: 'center' }));
if (o.customer) cmds.push(...this.addLine(`Cust: ${o.customer}`, { height: 2 }));
} else {
if (o.orderName) cmds.push(...this.addLine(`Order: ${o.orderName}`)); if (o.orderName) cmds.push(...this.addLine(`Order: ${o.orderName}`));
if (o.date) cmds.push(...this.addLine(`Date: ${o.date}`)); if (o.date) cmds.push(...this.addLine(`Date: ${o.date}`));
if (o.cashier) cmds.push(...this.addLine(`Cashier: ${o.cashier}`)); if (o.cashier) cmds.push(...this.addLine(`Cashier: ${o.cashier}`));
// Table name — printed after cashier, matching pos_restaurant ReceiptHeader
if (o.tableName) cmds.push(...this.addLine(o.tableName, { bold: true, align: 'center' })); if (o.tableName) cmds.push(...this.addLine(o.tableName, { bold: true, align: 'center' }));
if (o.customer) cmds.push(...this.addLine(`Customer: ${o.customer}`)); if (o.customer) cmds.push(...this.addLine(`Customer: ${o.customer}`));
}
cmds.push(...this.addLine(this.divider())); cmds.push(...this.addLine(this.divider()));
} }
@ -248,15 +256,16 @@ export class EscPosGenerator {
if (receiptData.isBasicReceipt) { if (receiptData.isBasicReceipt) {
// ── Basic receipt / table checker ────────────────────────── // ── Basic receipt / table checker ──────────────────────────
// Show only qty + product name (no price/total), matching // Show only qty + product name (no price/total), at double-width
// Odoo's basic_receipt behaviour (vals.price = false). // double-height for maximum readability on 58mm paper.
// Format: "2 Mie Ayam Geprek" // At width=2, each char is 2x wide, so effective cols = W/2 = 16
const qtyLabel = qtyStr.padEnd(3); const halfW = Math.floor(W / 2);
const nameMaxLen = W - qtyLabel.length; const qtyLabel = qtyStr.padEnd(2) + ' ';
const nameMaxLen = halfW - qtyLabel.length;
const displayName = name.length > nameMaxLen const displayName = name.length > nameMaxLen
? name.substring(0, nameMaxLen - 1) + '.' ? name.substring(0, nameMaxLen - 1) + '.'
: name; : name;
cmds.push(...this.addLine(qtyLabel + displayName, { bold: true, height: 2 })); cmds.push(...this.addLine(qtyLabel + displayName, { bold: true, width: 2, height: 2 }));
} else { } else {
// ── Full receipt ─────────────────────────────────────────── // ── Full receipt ───────────────────────────────────────────
// Line 1: product name // Line 1: product name
@ -279,14 +288,24 @@ export class EscPosGenerator {
const subQty = sub.quantity || 0; const subQty = sub.quantity || 0;
const subQtyStr = subQty % 1 === 0 ? subQty.toFixed(0) : subQty.toFixed(2); const subQtyStr = subQty % 1 === 0 ? subQty.toFixed(0) : subQty.toFixed(2);
if (receiptData.isBasicReceipt) {
// Double-height sub-lines on basic receipt
const halfW = Math.floor(W / 2);
const prefix = ` ${subQtyStr}x `;
const maxSubLen = halfW - prefix.length;
const displaySub = subName.length > maxSubLen
? subName.substring(0, maxSubLen - 1) + '.'
: subName;
cmds.push(...this.addLine(prefix + displaySub, { height: 2 }));
} else {
// Format: " - 1x Mie Goreng" // Format: " - 1x Mie Goreng"
const prefix = ` - ${subQtyStr}x `; const prefix = ` - ${subQtyStr}x `;
const maxSubLen = W - prefix.length; const maxSubLen = W - prefix.length;
const displaySub = subName.length > maxSubLen
const displayName = subName.length > maxSubLen
? subName.substring(0, maxSubLen - 1) + '.' ? subName.substring(0, maxSubLen - 1) + '.'
: subName; : subName;
cmds.push(...this.addLine(prefix + displayName, { bold: true })); cmds.push(...this.addLine(prefix + displaySub, { bold: true }));
}
}); });
} }
@ -323,7 +342,11 @@ export class EscPosGenerator {
wrappedLines.forEach((l, i) => { wrappedLines.forEach((l, i) => {
const prefix = i === 0 ? '* ' : ' '; const prefix = i === 0 ? '* ' : ' ';
if (receiptData.isBasicReceipt) {
cmds.push(...this.addLine(prefix + l, { bold: true, height: 2 }));
} else {
cmds.push(...this.addLine(prefix + l, { bold: true })); cmds.push(...this.addLine(prefix + l, { bold: true }));
}
}); });
} }
}); });