-
Stock Items (= count($data['items'] ?? []) ?>)
+
Stock Items (= count($data['items'] ?? []) ?>)
-
+
|
diff --git a/pages/barcode_pos_script.php b/pages/barcode_pos_script.php
index 7b4cf13..764941b 100644
--- a/pages/barcode_pos_script.php
+++ b/pages/barcode_pos_script.php
@@ -98,13 +98,13 @@
container.style.width = '100%';
container.style.maxWidth = '100%';
- container.style.lineHeight = '1.05';
+ container.style.lineHeight = '1';
container.style.marginBottom = '0';
if (arabicLine) {
arabicLine.style.display = 'block';
arabicLine.style.fontWeight = '700';
- arabicLine.style.fontSize = compact ? '0.98rem' : '1.10rem';
+ arabicLine.style.fontSize = compact ? '1.04rem' : '1.16rem';
arabicLine.style.direction = 'rtl';
arabicLine.style.whiteSpace = 'nowrap';
arabicLine.style.overflow = 'hidden';
@@ -114,7 +114,7 @@
if (englishLine) {
englishLine.style.display = 'block';
englishLine.style.fontWeight = '600';
- englishLine.style.fontSize = compact ? '0.84rem' : '0.95rem';
+ englishLine.style.fontSize = compact ? '0.90rem' : '0.99rem';
englishLine.style.whiteSpace = 'nowrap';
englishLine.style.overflow = 'hidden';
englishLine.style.textOverflow = 'ellipsis';
@@ -127,21 +127,21 @@
}
container.style.width = '100%';
- container.style.maxWidth = '240px';
+ container.style.maxWidth = '260px';
container.style.direction = 'ltr';
container.style.textAlign = 'center';
- container.style.lineHeight = '1.05';
+ container.style.lineHeight = '1';
container.style.fontWeight = '600';
- container.style.fontSize = compact ? '0.80rem' : '0.92rem';
+ container.style.fontSize = compact ? '0.88rem' : '0.98rem';
container.style.marginTop = '0';
const row = container.querySelector('.label-date-row');
if (row) {
- row.style.gap = compact ? '10px' : '14px';
+ row.style.gap = compact ? '8px' : '12px';
}
container.querySelectorAll('.label-date-item').forEach((item) => {
- item.style.gap = compact ? '4px' : '6px';
+ item.style.gap = compact ? '3px' : '5px';
});
}
@@ -186,7 +186,7 @@
priceContainer.textContent = label.price ? 'OMR ' + label.price : '';
priceContainer.style.display = label.price ? 'block' : 'none';
priceContainer.style.fontWeight = '700';
- priceContainer.style.fontSize = compact ? '0.98rem' : '1.08rem';
+ priceContainer.style.fontSize = compact ? '1.04rem' : '1.14rem';
priceContainer.style.lineHeight = '1';
priceContainer.style.marginTop = '0';
@@ -194,10 +194,10 @@
previewContainer.style.flexDirection = 'column';
previewContainer.style.alignItems = 'center';
previewContainer.style.justifyContent = 'center';
- previewContainer.style.gap = compact ? '4px' : '6px';
+ previewContainer.style.gap = compact ? '3px' : '4px';
previewContainer.style.width = Math.min(Math.max(width * 3.8, 185), 310) + 'px';
previewContainer.style.maxWidth = '100%';
- previewContainer.style.padding = height <= 25 ? '10px 12px' : '12px 14px';
+ previewContainer.style.padding = height <= 25 ? '9px 11px' : '11px 13px';
svg.innerHTML = '';
JsBarcode(svg, label.sku, getSingleBarcodeOptions(label.sku, width, height));
@@ -299,14 +299,14 @@
text-align: center;
overflow: hidden;
box-sizing: border-box;
- padding: 1.1mm 1.8mm 1.2mm;
- gap: 0.45mm;
+ padding: 0.9mm 1.55mm 1mm;
+ gap: 0.25mm;
}
.label-container:last-child { page-break-after: avoid; }
.label-name {
width: 100%;
max-width: 100%;
- line-height: 1.02;
+ line-height: 1;
}
.label-name-ar,
.label-name-en {
@@ -317,33 +317,33 @@
}
.label-name-ar {
font-weight: 700;
- font-size: 12px;
+ font-size: 13.4px;
direction: rtl;
}
.label-name-en {
- font-size: 10px;
+ font-size: 11px;
font-weight: 600;
}
.label-price {
- font-size: 12px;
+ font-size: 13px;
font-weight: 700;
line-height: 1;
white-space: nowrap;
}
.label-compact {
- padding: 0.85mm 1.3mm 0.95mm;
- gap: 0.35mm;
+ padding: 0.7mm 1.15mm 0.75mm;
+ gap: 0.2mm;
}
- .label-compact .label-name-ar { font-size: 10.5px; }
- .label-compact .label-name-en { font-size: 8.75px; }
- .label-compact .label-price { font-size: 10.5px; }
+ .label-compact .label-name-ar { font-size: 11.6px; }
+ .label-compact .label-name-en { font-size: 9.6px; }
+ .label-compact .label-price { font-size: 11.5px; }
.barcode-wrap {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
- padding: 0 1mm;
- margin: 0.15mm 0;
+ padding: 0 0.8mm;
+ margin: 0.1mm 0;
overflow: visible;
}
.barcode-wrap svg {
@@ -483,7 +483,7 @@
priceContainer.textContent = label.price ? 'OMR ' + label.price : '';
priceContainer.style.display = label.price ? 'block' : 'none';
priceContainer.style.fontWeight = '700';
- priceContainer.style.fontSize = compact ? '0.90rem' : '1.02rem';
+ priceContainer.style.fontSize = compact ? '0.98rem' : '1.08rem';
priceContainer.style.lineHeight = '1';
priceContainer.style.marginTop = '0';
}
@@ -492,10 +492,10 @@
previewContainer.style.flexDirection = 'column';
previewContainer.style.alignItems = 'center';
previewContainer.style.justifyContent = 'center';
- previewContainer.style.gap = compact ? '4px' : '5px';
+ previewContainer.style.gap = compact ? '3px' : '4px';
previewContainer.style.width = Math.min(Math.max(width * 4.0, 190), 320) + 'px';
previewContainer.style.maxWidth = '100%';
- previewContainer.style.padding = height <= 30 ? '10px 11px' : '11px 13px';
+ previewContainer.style.padding = height <= 30 ? '9px 10px' : '10px 12px';
svg.innerHTML = '';
JsBarcode(svg, label.sku, getDatedBarcodeOptions(label.sku, width, height));
@@ -625,13 +625,13 @@
}
.label-container:last-child { page-break-after: avoid; }
.dated-label {
- padding: 0.95mm 1.45mm 1mm;
- gap: 0.3mm;
+ padding: 0.78mm 1.25mm 0.8mm;
+ gap: 0.2mm;
}
.label-name {
width: 100%;
max-width: 100%;
- line-height: 1.02;
+ line-height: 1;
}
.label-name-ar,
.label-name-en {
@@ -642,11 +642,11 @@
}
.label-name-ar {
font-weight: 700;
- font-size: 11.5px;
+ font-size: 13px;
direction: rtl;
}
.label-name-en {
- font-size: 9.5px;
+ font-size: 10.6px;
font-weight: 600;
}
.label-meta {
@@ -655,8 +655,8 @@
flex-direction: column;
align-items: center;
justify-content: center;
- gap: 0.25mm;
- margin-top: 0.1mm;
+ gap: 0.15mm;
+ margin-top: 0;
}
.label-dates {
--label-date-row-gap: 1.2mm;
@@ -667,7 +667,7 @@
align-items: center;
text-align: center;
direction: ltr;
- font-size: 9px;
+ font-size: 10.1px;
font-weight: 600;
line-height: 1;
}
@@ -687,31 +687,31 @@
font-weight: 700;
}
.label-price {
- font-size: 10px;
+ font-size: 11.2px;
font-weight: 700;
line-height: 1;
white-space: nowrap;
}
.label-compact {
- padding: 0.75mm 1.05mm 0.8mm;
- gap: 0.25mm;
+ padding: 0.58mm 0.95mm 0.6mm;
+ gap: 0.15mm;
}
- .label-compact .label-name-ar { font-size: 10px; }
- .label-compact .label-name-en { font-size: 8.3px; }
- .label-compact .label-meta { gap: 0.2mm; }
+ .label-compact .label-name-ar { font-size: 11.1px; }
+ .label-compact .label-name-en { font-size: 9.1px; }
+ .label-compact .label-meta { gap: 0.12mm; }
.label-compact .label-dates {
--label-date-row-gap: 0.85mm;
--label-date-item-gap: 0.35mm;
- font-size: 7.8px;
+ font-size: 8.7px;
}
- .label-compact .label-price { font-size: 8.8px; }
+ .label-compact .label-price { font-size: 9.7px; }
.barcode-wrap {
width: 100%;
display: flex;
align-items: center;
justify-content: center;
- padding: 0 0.7mm;
- margin: 0.15mm 0;
+ padding: 0 0.55mm;
+ margin: 0.08mm 0;
overflow: visible;
}
.barcode-wrap svg {
diff --git a/tmp_barcode_pos_script_check.js b/tmp_barcode_pos_script_check.js
new file mode 100644
index 0000000..5a3cb37
--- /dev/null
+++ b/tmp_barcode_pos_script_check.js
@@ -0,0 +1,843 @@
+ function escapeBarcodeLabelHtml(value) {
+ return String(value || '')
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/"/g, '"')
+ .replace(/'/g, ''');
+ }
+
+ function getSingleBarcodeLabelData() {
+ return window.currentSingleBarcodeLabel || null;
+ }
+
+ function getSingleBarcodePrintDimensions() {
+ return {
+ width: Math.max(parseInt(document.getElementById('barcodeWidth').value, 10) || 50, 10),
+ height: Math.max(parseInt(document.getElementById('barcodeHeight').value, 10) || 30, 10)
+ };
+ }
+
+ function getSingleBarcodeScale(labelWidthMm, labelHeightMm) {
+ if (labelWidthMm <= 32 || labelHeightMm <= 20) {
+ return 0.96;
+ }
+ if (labelWidthMm <= 40 || labelHeightMm <= 25) {
+ return 0.92;
+ }
+ if (labelWidthMm <= 50 || labelHeightMm <= 30) {
+ return 0.88;
+ }
+ return 0.84;
+ }
+
+ function getSingleBarcodeOptions(sku, labelWidthMm, labelHeightMm) {
+ const skuText = String(sku || '');
+ const skuLength = skuText.length;
+ const compact = labelWidthMm <= 40 || labelHeightMm <= 25;
+ const extraCompact = labelWidthMm <= 32 || labelHeightMm <= 20;
+ const showValue = !extraCompact && labelHeightMm >= 24 && skuLength <= 18;
+
+ let moduleWidth = 1.8;
+ if (skuLength >= 18 || labelWidthMm <= 35) {
+ moduleWidth = 1.1;
+ } else if (skuLength >= 14 || labelWidthMm <= 40) {
+ moduleWidth = 1.3;
+ } else if (labelWidthMm <= 50) {
+ moduleWidth = 1.6;
+ }
+
+ let barcodeHeight = 56;
+ if (labelHeightMm <= 20) {
+ barcodeHeight = 30;
+ } else if (labelHeightMm <= 25) {
+ barcodeHeight = 38;
+ } else if (labelHeightMm <= 30) {
+ barcodeHeight = 46;
+ } else if (labelHeightMm <= 35) {
+ barcodeHeight = 56;
+ } else {
+ barcodeHeight = 64;
+ }
+
+ const quietZone = extraCompact ? 4 : compact ? 6 : 10;
+
+ return {
+ format: "CODE128",
+ lineColor: "#000",
+ width: moduleWidth,
+ height: barcodeHeight,
+ displayValue: showValue,
+ fontSize: compact ? 10 : 12,
+ textMargin: compact ? 2 : 4,
+ margin: quietZone,
+ marginTop: 0,
+ marginBottom: showValue ? 2 : 0,
+ fontOptions: "bold"
+ };
+ }
+
+ function buildSingleBarcodeNameHtml(nameAr, nameEn) {
+ const lines = [];
+ if (nameAr) {
+ lines.push('' + escapeBarcodeLabelHtml(nameAr) + '
');
+ }
+ if (nameEn) {
+ lines.push('' + escapeBarcodeLabelHtml(nameEn) + '
');
+ }
+ return lines.join('');
+ }
+
+ function applyBarcodePreviewNameStyles(container, compact) {
+ if (!container) {
+ return;
+ }
+
+ const arabicLine = container.querySelector('.label-name-ar');
+ const englishLine = container.querySelector('.label-name-en');
+
+ container.style.width = '100%';
+ container.style.maxWidth = '100%';
+ container.style.lineHeight = '1';
+ container.style.marginBottom = '0';
+
+ if (arabicLine) {
+ arabicLine.style.display = 'block';
+ arabicLine.style.fontWeight = '700';
+ arabicLine.style.fontSize = compact ? '1.04rem' : '1.16rem';
+ arabicLine.style.direction = 'rtl';
+ arabicLine.style.whiteSpace = 'nowrap';
+ arabicLine.style.overflow = 'hidden';
+ arabicLine.style.textOverflow = 'ellipsis';
+ }
+
+ if (englishLine) {
+ englishLine.style.display = 'block';
+ englishLine.style.fontWeight = '600';
+ englishLine.style.fontSize = compact ? '0.90rem' : '0.99rem';
+ englishLine.style.whiteSpace = 'nowrap';
+ englishLine.style.overflow = 'hidden';
+ englishLine.style.textOverflow = 'ellipsis';
+ }
+ }
+
+ function applyBarcodePreviewDateStyles(container, compact) {
+ if (!container) {
+ return;
+ }
+
+ container.style.width = '100%';
+ container.style.maxWidth = '260px';
+ container.style.direction = 'ltr';
+ container.style.textAlign = 'center';
+ container.style.lineHeight = '1';
+ container.style.fontWeight = '600';
+ container.style.fontSize = compact ? '0.88rem' : '0.98rem';
+ container.style.marginTop = '0';
+
+ const row = container.querySelector('.label-date-row');
+ if (row) {
+ row.style.gap = compact ? '8px' : '12px';
+ }
+
+ container.querySelectorAll('.label-date-item').forEach((item) => {
+ item.style.gap = compact ? '3px' : '5px';
+ });
+ }
+
+ function buildSingleBarcodeSvgMarkup(sku, labelWidthMm, labelHeightMm) {
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+ const scale = getSingleBarcodeScale(labelWidthMm, labelHeightMm);
+ JsBarcode(svg, sku, getSingleBarcodeOptions(sku, labelWidthMm, labelHeightMm));
+ svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
+ svg.setAttribute('shape-rendering', 'crispEdges');
+ svg.style.width = Math.round(scale * 100) + '%';
+ svg.style.maxWidth = '100%';
+ svg.style.height = 'auto';
+ svg.style.display = 'block';
+ svg.style.margin = '0 auto';
+ svg.style.shapeRendering = 'crispEdges';
+ return svg.outerHTML;
+ }
+
+ function renderSingleBarcodePreview() {
+ const label = getSingleBarcodeLabelData();
+ if (!label) {
+ return;
+ }
+
+ const dimensions = getSingleBarcodePrintDimensions();
+ const width = dimensions.width;
+ const height = dimensions.height;
+ const scale = getSingleBarcodeScale(width, height);
+ const compact = width <= 40 || height <= 25;
+ const nameContainer = document.getElementById('barcodeLabelName');
+ const priceContainer = document.getElementById('barcodeLabelPrice');
+ const previewContainer = document.getElementById('barcodeContainer');
+ const svg = document.getElementById('barcodeSvg');
+
+ if (!nameContainer || !priceContainer || !previewContainer || !svg) {
+ return;
+ }
+
+ nameContainer.innerHTML = buildSingleBarcodeNameHtml(label.nameAr, label.nameEn);
+ applyBarcodePreviewNameStyles(nameContainer, compact);
+
+ priceContainer.textContent = label.price ? 'OMR ' + label.price : '';
+ priceContainer.style.display = label.price ? 'block' : 'none';
+ priceContainer.style.fontWeight = '700';
+ priceContainer.style.fontSize = compact ? '1.04rem' : '1.14rem';
+ priceContainer.style.lineHeight = '1';
+ priceContainer.style.marginTop = '0';
+
+ previewContainer.style.display = 'flex';
+ previewContainer.style.flexDirection = 'column';
+ previewContainer.style.alignItems = 'center';
+ previewContainer.style.justifyContent = 'center';
+ previewContainer.style.gap = compact ? '3px' : '4px';
+ previewContainer.style.width = Math.min(Math.max(width * 3.8, 185), 310) + 'px';
+ previewContainer.style.maxWidth = '100%';
+ previewContainer.style.padding = height <= 25 ? '9px 11px' : '11px 13px';
+
+ svg.innerHTML = '';
+ JsBarcode(svg, label.sku, getSingleBarcodeOptions(label.sku, width, height));
+ svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
+ svg.setAttribute('shape-rendering', 'crispEdges');
+ svg.style.width = Math.round(scale * 100) + '%';
+ svg.style.maxWidth = '100%';
+ svg.style.height = 'auto';
+ svg.style.display = 'block';
+ svg.style.margin = '0 auto';
+ svg.style.shapeRendering = 'crispEdges';
+ }
+
+ function initSingleBarcodePreviewControls() {
+ ['barcodeWidth', 'barcodeHeight'].forEach((id) => {
+ const input = document.getElementById(id);
+ if (!input || input.dataset.barcodePreviewBound === '1') {
+ return;
+ }
+
+ input.dataset.barcodePreviewBound = '1';
+ input.addEventListener('input', renderSingleBarcodePreview);
+ input.addEventListener('change', renderSingleBarcodePreview);
+ });
+ }
+
+ initSingleBarcodePreviewControls();
+
+ window.printItemBarcode = function(sku, nameAr, nameEn, price) {
+ if (!sku) {
+ Swal.fire('Error', 'This item has no SKU/Barcode assigned.', 'error');
+ return;
+ }
+
+ window.currentSingleBarcodeLabel = {
+ sku: String(sku),
+ nameAr: nameAr || '',
+ nameEn: nameEn || '',
+ price: price || ''
+ };
+
+ renderSingleBarcodePreview();
+
+ const modal = new bootstrap.Modal(document.getElementById('barcodePrintModal'));
+ modal.show();
+ };
+
+ window.executeBarcodePrint = function() {
+ const qty = Math.max(parseInt(document.getElementById('barcodeQty').value, 10) || 1, 1);
+ const dimensions = getSingleBarcodePrintDimensions();
+ const width = dimensions.width;
+ const height = dimensions.height;
+ const label = getSingleBarcodeLabelData();
+
+ if (!label) {
+ return;
+ }
+
+ const nameHtml = buildSingleBarcodeNameHtml(label.nameAr, label.nameEn);
+ const price = label.price ? 'OMR ' + escapeBarcodeLabelHtml(label.price) : '';
+ const svg = buildSingleBarcodeSvgMarkup(label.sku, width, height);
+ const compactClass = width <= 40 || height <= 25 ? 'label-compact' : '';
+
+ const iframe = document.createElement('iframe');
+ iframe.style.position = 'absolute';
+ iframe.style.width = '0px';
+ iframe.style.height = '0px';
+ iframe.style.border = 'none';
+ document.body.appendChild(iframe);
+
+ const doc = iframe.contentWindow.document;
+
+ let labelsHtml = '';
+ for (let i = 0; i < qty; i++) {
+ labelsHtml += `
+
+ ${nameHtml ? `
${nameHtml}
` : ''}
+
${svg}
+ ${price ? `
${price}
` : ''}
+
+ `;
+ }
+
+ doc.open();
+ doc.write(`
+
+
+
+
+
+ ${labelsHtml}
+
+
+ `);
+ doc.close();
+
+ iframe.contentWindow.focus();
+ setTimeout(() => {
+ iframe.contentWindow.print();
+ setTimeout(() => {
+ document.body.removeChild(iframe);
+ }, 2000);
+ }, 500);
+ };
+
+ function formatBarcodeLabelDate(value) {
+ const dateText = String(value || '').trim();
+ const match = dateText.match(/^(\d{4})-(\d{2})-(\d{2})$/);
+ if (match) {
+ return `${match[3]}/${match[2]}/${match[1]}`;
+ }
+ return '';
+ }
+
+ function buildDatedBarcodeDateRowsHtml(productionDate, expiryDate) {
+ const productionText = formatBarcodeLabelDate(productionDate) || '--/--/----';
+ const expiryText = formatBarcodeLabelDate(expiryDate) || '--/--/----';
+
+ return `
+
+ P:${escapeBarcodeLabelHtml(productionText)}
+ E:${escapeBarcodeLabelHtml(expiryText)}
+
+ `;
+ }
+
+ function getDatedBarcodeLabelData() {
+ return window.currentDatedBarcodeLabel || null;
+ }
+
+ function getDatedBarcodePrintDimensions() {
+ return {
+ width: Math.max(parseInt(document.getElementById('datedBarcodeWidth').value, 10) || 50, 10),
+ height: Math.max(parseInt(document.getElementById('datedBarcodeHeight').value, 10) || 35, 10)
+ };
+ }
+
+ function getDatedBarcodeScale(labelWidthMm, labelHeightMm) {
+ if (labelWidthMm <= 32 || labelHeightMm <= 24) {
+ return 0.96;
+ }
+ if (labelWidthMm <= 40 || labelHeightMm <= 30) {
+ return 0.95;
+ }
+ if (labelWidthMm <= 50 || labelHeightMm <= 35) {
+ return 0.94;
+ }
+ return 0.90;
+ }
+
+ function getDatedBarcodeOptions(sku, labelWidthMm, labelHeightMm) {
+ const baseOptions = getSingleBarcodeOptions(sku, labelWidthMm, labelHeightMm);
+ const skuLength = String(sku || '').length;
+ const showValue = labelHeightMm >= 46 && labelWidthMm >= 50 && skuLength <= 16;
+
+ return {
+ ...baseOptions,
+ displayValue: showValue,
+ fontSize: showValue ? 10 : 8,
+ textMargin: showValue ? 2 : 0,
+ height: Math.max(Math.round(baseOptions.height * (showValue ? 0.88 : 0.96)), 28),
+ margin: Math.max(baseOptions.margin, 6),
+ marginBottom: showValue ? 1 : 0
+ };
+ }
+
+ function buildDatedBarcodeSvgMarkup(sku, labelWidthMm, labelHeightMm) {
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
+ const scale = getDatedBarcodeScale(labelWidthMm, labelHeightMm);
+ JsBarcode(svg, sku, getDatedBarcodeOptions(sku, labelWidthMm, labelHeightMm));
+ svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
+ svg.setAttribute('shape-rendering', 'crispEdges');
+ svg.style.width = Math.round(scale * 100) + '%';
+ svg.style.maxWidth = '100%';
+ svg.style.height = 'auto';
+ svg.style.display = 'block';
+ svg.style.margin = '0 auto';
+ svg.style.shapeRendering = 'crispEdges';
+ return svg.outerHTML;
+ }
+
+ function renderDatedBarcodePreview() {
+ const label = getDatedBarcodeLabelData();
+ if (!label) {
+ return;
+ }
+
+ const dimensions = getDatedBarcodePrintDimensions();
+ const width = dimensions.width;
+ const height = dimensions.height;
+ const scale = getDatedBarcodeScale(width, height);
+ const compact = width <= 40 || height <= 30;
+ const nameContainer = document.getElementById('datedBarcodeLabelName');
+ const datesContainer = document.getElementById('datedBarcodeLabelDates');
+ const priceContainer = document.getElementById('datedBarcodeLabelPrice');
+ const previewContainer = document.getElementById('datedBarcodeContainer');
+ const svg = document.getElementById('datedBarcodeSvg');
+ const productionInput = document.getElementById('datedBarcodeProductionDate');
+ const expiryInput = document.getElementById('datedBarcodeExpiryDate');
+
+ if (!nameContainer || !datesContainer || !previewContainer || !svg) {
+ return;
+ }
+
+ nameContainer.innerHTML = buildSingleBarcodeNameHtml(label.nameAr, label.nameEn);
+ applyBarcodePreviewNameStyles(nameContainer, compact);
+
+ datesContainer.innerHTML = buildDatedBarcodeDateRowsHtml(
+ productionInput ? productionInput.value : '',
+ expiryInput ? expiryInput.value : ''
+ );
+ applyBarcodePreviewDateStyles(datesContainer, compact);
+
+ if (priceContainer) {
+ priceContainer.textContent = label.price ? 'OMR ' + label.price : '';
+ priceContainer.style.display = label.price ? 'block' : 'none';
+ priceContainer.style.fontWeight = '700';
+ priceContainer.style.fontSize = compact ? '0.98rem' : '1.08rem';
+ priceContainer.style.lineHeight = '1';
+ priceContainer.style.marginTop = '0';
+ }
+
+ previewContainer.style.display = 'flex';
+ previewContainer.style.flexDirection = 'column';
+ previewContainer.style.alignItems = 'center';
+ previewContainer.style.justifyContent = 'center';
+ previewContainer.style.gap = compact ? '3px' : '4px';
+ previewContainer.style.width = Math.min(Math.max(width * 4.0, 190), 320) + 'px';
+ previewContainer.style.maxWidth = '100%';
+ previewContainer.style.padding = height <= 30 ? '9px 10px' : '10px 12px';
+
+ svg.innerHTML = '';
+ JsBarcode(svg, label.sku, getDatedBarcodeOptions(label.sku, width, height));
+ svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
+ svg.setAttribute('shape-rendering', 'crispEdges');
+ svg.style.width = Math.round(scale * 100) + '%';
+ svg.style.maxWidth = '100%';
+ svg.style.height = 'auto';
+ svg.style.display = 'block';
+ svg.style.margin = '0 auto';
+ svg.style.shapeRendering = 'crispEdges';
+ }
+
+ function initDatedBarcodePreviewControls() {
+ ['datedBarcodeWidth', 'datedBarcodeHeight', 'datedBarcodeProductionDate', 'datedBarcodeExpiryDate'].forEach((id) => {
+ const input = document.getElementById(id);
+ if (!input || input.dataset.barcodePreviewBound === '1') {
+ return;
+ }
+
+ input.dataset.barcodePreviewBound = '1';
+ input.addEventListener('input', renderDatedBarcodePreview);
+ input.addEventListener('change', renderDatedBarcodePreview);
+ });
+ }
+
+ initDatedBarcodePreviewControls();
+
+ window.printItemBarcodeWithDates = function(sku, nameAr, nameEn, price, defaultExpiryDate) {
+ if (!sku) {
+ Swal.fire('Error', 'This item has no SKU/Barcode assigned.', 'error');
+ return;
+ }
+
+ window.currentDatedBarcodeLabel = {
+ sku: String(sku),
+ nameAr: nameAr || '',
+ nameEn: nameEn || '',
+ price: price || ''
+ };
+
+ const productionInput = document.getElementById('datedBarcodeProductionDate');
+ const expiryInput = document.getElementById('datedBarcodeExpiryDate');
+ if (productionInput) {
+ productionInput.value = '';
+ }
+ if (expiryInput) {
+ expiryInput.value = defaultExpiryDate || '';
+ }
+
+ renderDatedBarcodePreview();
+
+ const modal = new bootstrap.Modal(document.getElementById('datedBarcodePrintModal'));
+ modal.show();
+ };
+
+ window.executeDatedBarcodePrint = function() {
+ const qty = Math.max(parseInt(document.getElementById('datedBarcodeQty').value, 10) || 1, 1);
+ const dimensions = getDatedBarcodePrintDimensions();
+ const width = dimensions.width;
+ const height = dimensions.height;
+ const label = getDatedBarcodeLabelData();
+ const productionDate = document.getElementById('datedBarcodeProductionDate').value || '';
+ const expiryDate = document.getElementById('datedBarcodeExpiryDate').value || '';
+
+ if (!label) {
+ return;
+ }
+
+ if (!productionDate || !expiryDate) {
+ Swal.fire('Missing dates', 'Please select both production and expiry dates before printing.', 'warning');
+ return;
+ }
+
+ if (expiryDate < productionDate) {
+ Swal.fire('Invalid dates', 'Expiry date must be the same as or later than the production date.', 'warning');
+ return;
+ }
+
+ const nameHtml = buildSingleBarcodeNameHtml(label.nameAr, label.nameEn);
+ const dateHtml = buildDatedBarcodeDateRowsHtml(productionDate, expiryDate);
+ const priceHtml = label.price ? `OMR ${escapeBarcodeLabelHtml(label.price)}
` : '';
+ const svg = buildDatedBarcodeSvgMarkup(label.sku, width, height);
+ const compactClass = width <= 40 || height <= 30 ? 'label-compact' : '';
+
+ const iframe = document.createElement('iframe');
+ iframe.style.position = 'absolute';
+ iframe.style.width = '0px';
+ iframe.style.height = '0px';
+ iframe.style.border = 'none';
+ document.body.appendChild(iframe);
+
+ const doc = iframe.contentWindow.document;
+
+ let labelsHtml = '';
+ for (let i = 0; i < qty; i++) {
+ labelsHtml += `
+
+ ${nameHtml ? `
${nameHtml}
` : ''}
+
${svg}
+
+
+ `;
+ }
+
+ doc.open();
+ doc.write(`
+
+
+
+
+
+ ${labelsHtml}
+
+
+ `);
+ doc.close();
+
+ iframe.contentWindow.focus();
+ setTimeout(() => {
+ iframe.contentWindow.print();
+ setTimeout(() => {
+ document.body.removeChild(iframe);
+ }, 2000);
+ }, 500);
+ };
+
+
+ window.printPosReceiptFromInvoice = function(inv) {
+ const container = document.getElementById('posReceiptContent');
+ const itemsHtml = inv.items.map(item => {
+ const itemTotal = item.unit_price * item.quantity;
+ const vatRate = parseFloat(item.vat_rate !== undefined && item.vat_rate !== null ? item.vat_rate : 0);
+ const vatAmount = itemTotal * (vatRate / (100 + vatRate));
+ return `
+
+ ${item.name_en} / ${item.name_ar} ${item.quantity} x ${parseFloat(item.unit_price).toFixed(3)} |
+ ${vatAmount.toFixed(2)} |
+ ${itemTotal.toFixed(3)} |
+
+ `;
+ }).join('');
+
+ const totalVat = inv.items.reduce((sum, item) => {
+ const itemTotal = item.unit_price * item.quantity;
+ const vatRate = parseFloat(item.vat_rate !== undefined && item.vat_rate !== null ? item.vat_rate : 0);
+ return sum + (itemTotal * (vatRate / (100 + vatRate)));
+ }, 0);
+ const subtotal = inv.items.reduce((sum, item) => sum + (item.unit_price * item.quantity), 0);
+
+ const companyName = "= htmlspecialchars($data['settings']['company_name'] ?? 'Accounting System') ?>";
+ const outletName = "= htmlspecialchars($data['settings']['current_outlet_name'] ?? '') ?>";
+ const companyPhone = "= htmlspecialchars($data['settings']['company_phone'] ?? '') ?>";
+ const companyVat = "= htmlspecialchars($data['settings']['vat_number'] ?? '') ?>";
+ const companyLogo = "= htmlspecialchars($data['settings']['company_logo'] ?? '') ?>";
+
+ container.innerHTML = `
+
+
+ ${companyLogo ? `

` : ''}
+
${companyName}
+ ${inv.outlet_name ? `
${inv.outlet_name}
` : ''}
+ ${companyPhone ? `
Tel: ${companyPhone}
` : ''}
+ ${companyVat ? `
VAT: ${companyVat}
` : ''}
+
+
TAX INVOICE / فاتورة ضريبية
+
Inv / رقم: INV-${inv.id.toString().padStart(5, '0')}
+
Date / التاريخ: ${inv.invoice_date}
+
+
+
+ Customer / العميل: ${inv.customer_name || 'Walk-in / عميل عابر'}
+
+
+
+
+
+ | ITEM / الصنف |
+ VAT / الضريبة |
+ TOTAL / الإجمالي |
+
+
+
+ ${itemsHtml}
+
+
+
+
+ Subtotal (Excl. VAT) / المجموع الفرعي (دون الضريبة)
+ = __('currency') ?> ${(subtotal - totalVat).toFixed(3)}
+
+
+ VAT / الضريبة
+ = __('currency') ?> ${totalVat.toFixed(2)}
+
+
+ TOTAL (Incl. VAT) / الإجمالي (شامل الضريبة)
+ = __('currency') ?> ${subtotal.toFixed(3)}
+
+
+ PAID / المدفوع
+ = __('currency') ?> ${parseFloat(inv.paid_amount).toFixed(3)}
+
+
+ BALANCE / الرصيد
+ = __('currency') ?> ${(subtotal - inv.paid_amount).toFixed(3)}
+
+
+
+
Thank You for your business! / شكراً لتعاملكم معنا!
+
+
+ `;
+
+ const posModal = new bootstrap.Modal(document.getElementById('posReceiptModal'));
+ posModal.show();
+ };
+
+ function printPosReceipt() {
+ const content = document.getElementById('posReceiptContent').innerHTML;
+ const printArea = document.getElementById('posPrintArea');
+ printArea.innerHTML = `${content}
`;
+
+ document.body.classList.add('printing-receipt');
+ window.print();
+ document.body.classList.remove('printing-receipt');
+ location.reload();
+ }
+