diff --git a/index.php b/index.php
index eec9f69..eb95421 100644
--- a/index.php
+++ b/index.php
@@ -7277,11 +7277,20 @@ runtime_debug_mark('page:rendering', ['page' => (string)$page]);
= !empty($item['expiry_date']) ? htmlspecialchars((string)$item['expiry_date']) : '---' ?> |
= number_format((float)$item['vat_rate'], 2) ?>% |
+
-
+
+
@@ -12223,6 +12233,54 @@ document.addEventListener('DOMContentLoaded', function() {
+
+
+
+
+
+
+
+
+
+
+
+
+
+ For barcode labels with P / E dates, 50 × 35 mm or larger is recommended.
+
+
+
+
+
+
diff --git a/pages/barcode_pos_script.php b/pages/barcode_pos_script.php
index 080f25f..1d2a02f 100644
--- a/pages/barcode_pos_script.php
+++ b/pages/barcode_pos_script.php
@@ -18,6 +18,19 @@
};
}
+ 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;
@@ -77,10 +90,14 @@
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.style.width = '100%';
+ svg.style.width = Math.round(scale * 100) + '%';
+ svg.style.maxWidth = '100%';
svg.style.height = 'auto';
+ svg.style.display = 'block';
+ svg.style.margin = '0 auto';
return svg.outerHTML;
}
@@ -93,6 +110,7 @@
const dimensions = getSingleBarcodePrintDimensions();
const width = dimensions.width;
const height = dimensions.height;
+ const scale = getSingleBarcodeScale(width, height);
const nameContainer = document.getElementById('barcodeLabelName');
const priceContainer = document.getElementById('barcodeLabelPrice');
const previewContainer = document.getElementById('barcodeContainer');
@@ -104,15 +122,18 @@
nameContainer.innerHTML = buildSingleBarcodeNameHtml(label.nameAr, label.nameEn);
priceContainer.textContent = label.price ? 'OMR ' + label.price : '';
- previewContainer.style.width = Math.min(Math.max(width * 4, 180), 320) + 'px';
+ previewContainer.style.width = Math.min(Math.max(width * 3.6, 170), 280) + 'px';
previewContainer.style.maxWidth = '100%';
previewContainer.style.padding = height <= 25 ? '12px' : '16px';
svg.innerHTML = '';
JsBarcode(svg, label.sku, getSingleBarcodeOptions(label.sku, width, height));
svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
- svg.style.width = '100%';
+ svg.style.width = Math.round(scale * 100) + '%';
+ svg.style.maxWidth = '100%';
svg.style.height = 'auto';
+ svg.style.display = 'block';
+ svg.style.margin = '0 auto';
}
function initSingleBarcodePreviewControls() {
@@ -272,6 +293,309 @@
}, 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.88;
+ }
+ if (labelWidthMm <= 40 || labelHeightMm <= 30) {
+ return 0.84;
+ }
+ if (labelWidthMm <= 50 || labelHeightMm <= 35) {
+ return 0.80;
+ }
+ return 0.76;
+ }
+
+ function getDatedBarcodeOptions(sku, labelWidthMm, labelHeightMm) {
+ const baseOptions = getSingleBarcodeOptions(sku, labelWidthMm, labelHeightMm);
+ const skuLength = String(sku || '').length;
+ const showValue = labelHeightMm >= 42 && labelWidthMm >= 50 && skuLength <= 16;
+
+ return {
+ ...baseOptions,
+ displayValue: showValue,
+ fontSize: showValue ? 9 : 8,
+ textMargin: showValue ? 2 : 0,
+ height: Math.max(Math.round(baseOptions.height * (showValue ? 0.78 : 0.86)), 24),
+ 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.style.width = Math.round(scale * 100) + '%';
+ svg.style.maxWidth = '100%';
+ svg.style.height = 'auto';
+ svg.style.display = 'block';
+ svg.style.margin = '0 auto';
+ 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 nameContainer = document.getElementById('datedBarcodeLabelName');
+ const datesContainer = document.getElementById('datedBarcodeLabelDates');
+ 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);
+ datesContainer.innerHTML = buildDatedBarcodeDateRowsHtml(
+ productionInput ? productionInput.value : '',
+ expiryInput ? expiryInput.value : ''
+ );
+ previewContainer.style.width = Math.min(Math.max(width * 3.8, 180), 300) + 'px';
+ previewContainer.style.maxWidth = '100%';
+ previewContainer.style.padding = height <= 30 ? '10px 12px' : '12px 14px';
+
+ svg.innerHTML = '';
+ JsBarcode(svg, label.sku, getDatedBarcodeOptions(label.sku, width, height));
+ svg.setAttribute('preserveAspectRatio', 'xMidYMid meet');
+ svg.style.width = Math.round(scale * 100) + '%';
+ svg.style.maxWidth = '100%';
+ svg.style.height = 'auto';
+ svg.style.display = 'block';
+ svg.style.margin = '0 auto';
+ }
+
+ 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, defaultExpiryDate) {
+ if (!sku) {
+ Swal.fire('Error', 'This item has no SKU/Barcode assigned.', 'error');
+ return;
+ }
+
+ window.currentDatedBarcodeLabel = {
+ sku: String(sku),
+ nameAr: nameAr || '',
+ nameEn: nameEn || ''
+ };
+
+ 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 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}
+ ${dateHtml}
+
+ `;
+ }
+
+ 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) {
|