const escapeHtml = (value) => { const stringValue = value == null ? '' : String(value); return stringValue.replace(/[&<>"']/g, (character) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[character] || character)); }; const humanizeInvoiceText = (value) => { const raw = String(value ?? '').trim(); if (!raw) { return '---'; } return raw .replace(/[_-]+/g, ' ') .replace(/\s+/g, ' ') .toLowerCase() .replace(/\b\w/g, (letter) => letter.toUpperCase()); }; const formatInvoiceCurrency = (amount, decimals = 3) => { const numericAmount = Number.isFinite(parseFloat(amount)) ? parseFloat(amount) : 0; return `= __('currency') ?>${numericAmount.toFixed(decimals)}`; }; const setInvoiceText = (id, value) => { const element = document.getElementById(id); if (element) { element.textContent = value == null || value === '' ? '---' : String(value); } return element; }; const toggleInvoiceField = (containerId, valueId, value) => { const container = document.getElementById(containerId); const valueElement = document.getElementById(valueId); if (!container || !valueElement) { return; } if (value) { valueElement.textContent = value; container.style.display = ''; } else { valueElement.textContent = ''; container.style.display = 'none'; } }; let invoicePrintOriginalParent = null; let invoicePrintOriginalNextSibling = null; let invoicePrintRestoreTimer = null; const prepareInvoiceForPrint = () => { const modal = document.getElementById('viewInvoiceModal'); if (!modal || !document.body) { return; } if (!invoicePrintOriginalParent) { invoicePrintOriginalParent = modal.parentNode; invoicePrintOriginalNextSibling = modal.nextSibling; } if (modal.parentNode !== document.body) { document.body.appendChild(modal); } document.body.classList.add('printing-invoice'); }; const restoreInvoiceAfterPrint = () => { const modal = document.getElementById('viewInvoiceModal'); if (!document.body) { return; } document.body.classList.remove('printing-invoice'); if (!modal || !invoicePrintOriginalParent || modal.parentNode === invoicePrintOriginalParent) { return; } invoicePrintOriginalParent.insertBefore(modal, invoicePrintOriginalNextSibling); }; window.printInvoiceDocument = function() { prepareInvoiceForPrint(); window.print(); if (invoicePrintRestoreTimer) { window.clearTimeout(invoicePrintRestoreTimer); } invoicePrintRestoreTimer = window.setTimeout(() => { restoreInvoiceAfterPrint(); }, 1200); }; window.addEventListener('beforeprint', prepareInvoiceForPrint); window.addEventListener('afterprint', restoreInvoiceAfterPrint); window.viewAndPrintA4Invoice = function(data, autoPrint = true) { if (!data) return; const invoiceType = String(data.type || 'sale').toLowerCase() === 'purchase' ? 'purchase' : 'sale'; const invoiceDisplayNo = data.document_no || data.transaction_no || ((invoiceType === 'purchase' ? 'PUR-' : 'INV-') + data.id.toString().padStart(5, '0')); const paymentText = humanizeInvoiceText(data.payment_type || 'cash'); const statusKey = String(data.status || '').toLowerCase(); const statusText = statusKey === 'partially_paid' ? 'Partially Paid' : humanizeInvoiceText(statusKey); const typeText = invoiceType === 'purchase' ? 'Purchase Invoice' : 'Sales Invoice'; const documentTitle = invoiceType === 'purchase' ? 'Purchase Invoice / فاتورة شراء' : 'Tax Invoice / فاتورة ضريبية'; const documentSubtitle = invoiceType === 'purchase' ? 'Official purchase record / مستند شراء رسمي' : 'Official tax document / مستند ضريبي رسمي'; const partyLabelText = invoiceType === 'purchase' ? 'Supplier Details / بيانات المورد' : 'Bill To / بيانات العميل'; const partyLabelEn = invoiceType === 'purchase' ? 'Supplier Details' : 'Bill To'; const partyLabelAr = invoiceType === 'purchase' ? 'بيانات المورد' : 'بيانات العميل'; const formatQty = typeof window.formatQuantity === 'function' ? window.formatQuantity : (value) => { const numericValue = Number.isFinite(parseFloat(value)) ? parseFloat(value) : 0; return Number.isInteger(numericValue) ? String(numericValue) : numericValue.toFixed(3).replace(/\.?0+$/, ''); }; setInvoiceText('invNumber', invoiceDisplayNo); setInvoiceText('invDate', data.invoice_date || '---'); setInvoiceText('invPaymentType', paymentText); setInvoiceText('invPaymentTypeSummary', paymentText); setInvoiceText('invCustomerName', data.customer_name || '---'); setInvoiceText('invStatusText', statusText); setInvoiceText('invDocumentTitle', documentTitle); setInvoiceText('invDocumentSubtitle', documentSubtitle); setInvoiceText('invAmountInWords', data.total_in_words || '---'); toggleInvoiceField('invCustomerPhoneContainer', 'invCustomerPhone', data.customer_phone || ''); toggleInvoiceField('invCustomerTaxIdContainer', 'invCustomerTaxId', data.customer_tax_id || ''); toggleInvoiceField('invOutletRow', 'invOutletName', data.outlet_name || ''); const partyLabel = document.getElementById('invPartyLabel'); if (partyLabel) { partyLabel.textContent = partyLabelText; partyLabel.setAttribute('data-en', partyLabelEn); partyLabel.setAttribute('data-ar', partyLabelAr); } const invoiceTypeLabel = document.getElementById('invoiceTypeLabel'); if (invoiceTypeLabel) { invoiceTypeLabel.textContent = typeText; invoiceTypeLabel.className = 'invoice-pill ' + (invoiceType === 'purchase' ? 'invoice-pill--purchase' : 'invoice-pill--sale'); } const statusLabel = document.getElementById('invoiceStatusLabel'); let statusClass = 'invoice-pill--neutral'; if (statusKey === 'paid') { statusClass = 'invoice-pill--paid'; } else if (statusKey === 'unpaid') { statusClass = 'invoice-pill--unpaid'; } else if (statusKey === 'partially_paid') { statusClass = 'invoice-pill--partial'; } if (statusLabel) { statusLabel.textContent = statusText; statusLabel.className = 'invoice-pill ' + statusClass; } const body = document.getElementById('invItemsBody'); if (body) { body.innerHTML = ''; if (Array.isArray(data.items) && data.items.length > 0) { data.items.forEach((item, index) => { const tr = document.createElement('tr'); const englishName = escapeHtml(item.name_en || item.name || 'Item'); const arabicName = escapeHtml(item.name_ar || ''); const vatRateText = `${(Number.isFinite(parseFloat(item.vat_rate)) ? parseFloat(item.vat_rate) : 0).toFixed(2)}%`; const itemDetails = []; if (arabicName) { itemDetails.push(arabicName); } itemDetails.push(`VAT ${vatRateText}`); const itemNameHtml = `