From 317b79ed29a5379d9e2ebf55571a7c104ef85a6f Mon Sep 17 00:00:00 2001 From: Dave Date: Fri, 4 Sep 2026 13:28:45 +0200 Subject: [PATCH] refactor(pos): make buildEposDeliveryXml block-driven --- public/pos/epos-print.js | 68 ++++++++++++++++++++++++++-------------- public/pos/receipt.html | 17 +++++----- 2 files changed, 54 insertions(+), 31 deletions(-) diff --git a/public/pos/epos-print.js b/public/pos/epos-print.js index 59662ff..ef5daf0 100644 --- a/public/pos/epos-print.js +++ b/public/pos/epos-print.js @@ -172,29 +172,51 @@ window.EposPrint = (function () { } } - // Mirrors the delivery-note ESC/POS builder in receipt.html's - // printDelivery() — same fields, same "Include prices" toggle. - function buildEposDeliveryXml(s, showPrices) { - const parts = []; - parts.push('WFSALES POS '); - parts.push('Delivery Note '); - if (s.customer && s.customer.company) parts.push(`Company: ${xmlEscape(s.customer.company)} `); - parts.push(`Customer: ${xmlEscape((s.customer && s.customer.name) || '—')} `); - const d = new Date(s.createdAt); - parts.push(`Date: ${xmlEscape(d.toLocaleDateString('en-ZA'))} `); - parts.push('-------------------------------- '); - const itemLines = s.items.map(it => { - const qtyLine = ` ${it.qty} ${it.unit || 'ea'}`; - const priceSuffix = showPrices ? ` R ${((it.selling_price || 0) * it.qty).toFixed(2)}` : ''; - return `${xmlEscape(it.description || it.sku)} ${xmlEscape(qtyLine + priceSuffix)} `; - }).join(''); - parts.push(`${itemLines}`); - parts.push('-------------------------------- '); - if (showPrices) parts.push(`TOTAL: R ${s.total.toFixed(2)} `); - parts.push('Received by: __________ Driver: __________ '); - parts.push(''); - parts.push(''); - return `${parts.join('')}`; + function deliveryBlockRenderers(s, showPricing) { + return { + header_title: function (block) { + const title = block.text || 'WFSALES POS'; + return `${xmlEscape(title)} Delivery Note `; + }, + customer: function () { + let out = ''; + if (s.customer && s.customer.company) out += `Company: ${xmlEscape(s.customer.company)} `; + out += `Customer: ${xmlEscape((s.customer && s.customer.name) || '—')} `; + return out; + }, + date: function () { + const d = new Date(s.createdAt); + return `Date: ${xmlEscape(d.toLocaleDateString('en-ZA'))} `; + }, + items: function () { + const itemLines = s.items.map(function (it) { + const qtyLine = ` ${it.qty} ${it.unit || 'ea'}`; + const priceSuffix = showPricing ? ` R ${((it.selling_price || 0) * it.qty).toFixed(2)}` : ''; + return `${xmlEscape(it.description || it.sku)} ${xmlEscape(qtyLine + priceSuffix)} `; + }).join(''); + return `-------------------------------- ${itemLines}`; + }, + totals: function () { + let out = '-------------------------------- '; + if (showPricing) out += `TOTAL: R ${s.total.toFixed(2)} `; + return out; + }, + signature: function () { + return 'Received by: __________ Driver: __________ '; + }, + }; + } + + // `blocks` is the resolved block list for 'delivery'. `showPricing` + // is the combined "Show pricing" setting — it gates the per-item + // price and the totals block together (see slip-layout.js). + function buildEposDeliveryXml(s, blocks, showPricing) { + const renderers = deliveryBlockRenderers(s, showPricing); + const body = blocks + .filter(function (b) { return b.enabled; }) + .map(function (b) { return renderers[b.id] ? renderers[b.id](b) : ''; }) + .join(''); + return `${body}`; } // Mirrors the cashup page's "Print cash-up" button. Renders an diff --git a/public/pos/receipt.html b/public/pos/receipt.html index ba974b4..0f9a38a 100644 --- a/public/pos/receipt.html +++ b/public/pos/receipt.html @@ -532,12 +532,11 @@ async function printThermalNetwork(settings){ // / card / EFT get the standard sale slip. let xml, successLabel; if (sale.payment === 'account') { - // Logos don't always render cleanly on the TM-T20II at small - // sizes; the delivery note skips the logo so the address + items - // get the most space. If you want the logo back, set `logo` - // here and pass it to buildEposDeliveryXml the same way - // buildEposXml takes it. - xml = window.EposPrint.buildEposDeliveryXml(sale); + const letterheadId = sale.letterhead && sale.letterhead.id; + const deliveryBlocks = window.PosSlipLayout.getBlocksForPrint(letterheadId, 'delivery'); + const dnToggle = document.getElementById('dnShowPrices'); + const showPricing = dnToggle ? !!dnToggle.checked : window.PosSlipLayout.getShowPricingForPrint(letterheadId); + xml = window.EposPrint.buildEposDeliveryXml(sale, deliveryBlocks, showPricing); successLabel = 'Delivery note printed · '; } else { const logo = await window.EposPrint.imageToRaster('/wfdash-logo.png', 384).catch(()=>null); @@ -685,10 +684,12 @@ async function printDelivery(){ btn.disabled = false; return; } + const letterheadIdNet = sale.letterhead && sale.letterhead.id; const dnToggleNet = document.getElementById('dnShowPrices'); - const showPricesNet = dnToggleNet ? !!dnToggleNet.checked : true; + const showPricesNet = dnToggleNet ? !!dnToggleNet.checked : window.PosSlipLayout.getShowPricingForPrint(letterheadIdNet); try{ - const xml = window.EposPrint.buildEposDeliveryXml(sale, showPricesNet); + const deliveryBlocksNet = window.PosSlipLayout.getBlocksForPrint(letterheadIdNet, 'delivery'); + const xml = window.EposPrint.buildEposDeliveryXml(sale, deliveryBlocksNet, showPricesNet); await window.EposPrint.printViaNetwork(printerSettings.ip, xml); status.textContent = 'Delivery note printed · ' + printerSettings.ip; }catch(e){