// Edit Invoice Logic const parseInvoiceButtonPayload = (btn) => { if (!btn || !btn.dataset || !btn.dataset.json) return {}; try { return JSON.parse(btn.dataset.json); } catch (error) { console.warn('Failed to parse invoice payload from button data.', error); return {}; } }; const normalizeEditPaymentType = (paymentType) => { let normalized = String(paymentType || 'cash').toLowerCase().replace(/[\s-]+/g, '_'); if (normalized === 'pos') normalized = 'cash'; if (!['cash', 'card', 'bank_transfer', 'credit'].includes(normalized)) { normalized = 'cash'; } return normalized; }; const renderEditInvoiceItems = (items) => { const tableBody = document.getElementById('editInvoiceItemsTableBody'); const grandTotalEl = document.getElementById('edit_grandTotal'); const subtotalEl = document.getElementById('edit_subtotal'); const totalVatEl = document.getElementById('edit_totalVat'); if (!tableBody) return; tableBody.innerHTML = ''; if (!Array.isArray(items) || items.length === 0) { if (typeof recalculate === 'function') { recalculate(tableBody, grandTotalEl, subtotalEl, totalVatEl); } return; } items.forEach(item => { addItemToTable({ id: item.item_id || item.id, name_en: item.name_en || item.item_name_en || 'Item', name_ar: item.name_ar || item.item_name_ar || '', sku: item.sku || '', vat_rate: item.vat_rate || 0, purchase_price: item.purchase_price || 0, last_sale_price: item.last_sale_price || 0, stock_quantity: item.stock_quantity || 0 }, tableBody, null, null, grandTotalEl, subtotalEl, totalVatEl, { quantity: item.quantity, unit_price: item.unit_price, purchase_price: item.purchase_price || 0, last_sale_price: item.last_sale_price || 0 }); }); }; const populateEditInvoiceModal = (data) => { if (!data) return; const invoiceIdInput = document.getElementById('edit_invoice_id'); const partySelect = document.getElementById('edit_customer_id'); const invoiceDateInput = document.getElementById('edit_invoice_date'); const dueDateInput = document.getElementById('edit_due_date'); const paymentTypeSelect = document.getElementById('edit_payment_type'); const statusSelect = document.getElementById('edit_status'); const paidAmountInput = document.getElementById('edit_paid_amount'); const paidAmountContainer = document.getElementById('editPaidAmountContainer'); const discountAmountInput = document.getElementById('edit_discount_amount'); const partyId = data.customer_id ?? data.supplier_id ?? ''; const partyLabel = data.party_name || data.customer_name || data.supplier_name || ''; invoiceEnsureSelectOption('edit_customer_id', partyId, partyLabel); if (invoiceIdInput) invoiceIdInput.value = data.id || ''; if (partySelect) { invoiceSetBlankSelectOptionLabel(partySelect, (partyId === '' && partyLabel) ? partyLabel : '---'); partySelect.value = partyId; invoiceSyncSelect2Value(partySelect); } if (invoiceDateInput) invoiceDateInput.value = data.invoice_date || ''; if (dueDateInput) dueDateInput.value = data.due_date || ''; if (paymentTypeSelect) paymentTypeSelect.value = normalizeEditPaymentType(data.payment_type); if (statusSelect) statusSelect.value = data.status || 'unpaid'; if (paidAmountInput) paidAmountInput.value = parseFloat(data.paid_amount || 0).toFixed(3); if (discountAmountInput) discountAmountInput.value = parseFloat(data.discount_amount || 0).toFixed(3); if (paidAmountContainer) { paidAmountContainer.style.display = data.status === 'partially_paid' ? 'block' : 'none'; } renderEditInvoiceItems(data.items || []); const tableBody = document.getElementById('editInvoiceItemsTableBody'); const grandTotalEl = document.getElementById('edit_grandTotal'); const subtotalEl = document.getElementById('edit_subtotal'); const totalVatEl = document.getElementById('edit_totalVat'); if (tableBody && typeof recalculate === 'function') { recalculate(tableBody, grandTotalEl, subtotalEl, totalVatEl); } }; document.querySelectorAll('.edit-invoice-btn').forEach(btn => { btn.addEventListener('click', async function() { const fallbackData = parseInvoiceButtonPayload(this); const invoiceId = this.dataset.id || fallbackData.id || ''; const type = this.dataset.type || fallbackData.type || invoiceType || 'sale'; const tableBody = document.getElementById('editInvoiceItemsTableBody'); if (Object.keys(fallbackData).length > 0) { populateEditInvoiceModal(fallbackData); } else if (tableBody) { tableBody.innerHTML = '
Loading invoice...'; } if (!invoiceId) return; try { const resp = await fetch(`index.php?action=get_invoice_details&invoice_id=${encodeURIComponent(invoiceId)}&type=${encodeURIComponent(type)}`); const data = await resp.json(); if (data && data.id) { populateEditInvoiceModal(data); } else if (data && data.error) { throw new Error(data.error); } } catch (error) { console.error('Failed to load invoice details for edit modal.', error); if (window.Swal) { Swal.fire('Error', 'Failed to load invoice details', 'error'); } } }); }); // View and Print Invoice Logic const invoiceActionErrorTitle = ; const invoiceActionLoadError = ; const invoicePrintChooserTitle = ; const invoicePrintChooserText = ; const invoicePrintNormalLabel = ; const invoicePrintReceiptLabel = ; const invoicePrintCancelLabel = ; const invoicePrintFallbackPrompt = ; const buildInvoiceDocumentNo = (data) => { const prefix = (data && data.type === 'purchase') ? 'PUR-' : 'INV-'; const numericId = parseInt(data && data.id ? data.id : 0, 10); return prefix + String(Number.isFinite(numericId) && numericId > 0 ? numericId : 0).padStart(5, '0'); }; const normalizeInvoiceActionData = (payload, fallback = {}) => { const merged = { ...(fallback || {}), ...(payload || {}) }; merged.type = String(merged.type || invoiceType || 'sale').toLowerCase() === 'purchase' ? 'purchase' : 'sale'; merged.party_name = merged.party_name || merged.customer_name || merged.supplier_name || (merged.type === 'sale' && merged.is_pos ? 'Walk-in Customer' : '---'); merged.customer_name = merged.customer_name || merged.supplier_name || merged.party_name || '---'; merged.customer_phone = merged.customer_phone || merged.party_phone || ''; merged.customer_tax_id = merged.customer_tax_id || merged.party_tax_id || ''; merged.outlet_name = merged.outlet_name || ''; merged.items = Array.isArray(merged.items) ? merged.items : (Array.isArray(fallback.items) ? fallback.items : []); merged.document_no = merged.document_no || merged.transaction_no || (merged.id ? buildInvoiceDocumentNo(merged) : ''); merged.total_in_words = merged.total_in_words || fallback.total_in_words || ''; return merged; }; const showInvoiceActionError = (message = invoiceActionLoadError) => { if (window.Swal) { Swal.fire(invoiceActionErrorTitle, message, 'error'); return; } window.alert(message); }; const loadInvoiceActionData = async (btn) => { const fallbackData = normalizeInvoiceActionData(parseInvoiceButtonPayload(btn)); const invoiceId = btn?.dataset?.id || fallbackData.id || ''; const type = btn?.dataset?.type || fallbackData.type || invoiceType || 'sale'; const baseData = normalizeInvoiceActionData({ ...fallbackData, id: invoiceId || fallbackData.id, type }, fallbackData); if (!invoiceId) { return baseData; } try { const response = await fetch(`index.php?action=get_invoice_details&invoice_id=${encodeURIComponent(invoiceId)}&type=${encodeURIComponent(type)}`); const data = await response.json(); if (data && data.id) { return normalizeInvoiceActionData(data, baseData); } if (data && data.error) { throw new Error(data.error); } } catch (error) { console.error('Failed to load invoice details for view/print.', error); } return baseData; }; const printInvoiceAsReceipt = (data) => { if (typeof window.printPosReceiptFromInvoice !== 'function') { if (typeof window.viewAndPrintA4Invoice === 'function') { window.viewAndPrintA4Invoice(data, true); } return; } window.printPosReceiptFromInvoice(data); setTimeout(() => { const receiptContent = document.getElementById('posReceiptContent'); if (!receiptContent) { return; } if (typeof window.printReceiptHtml === 'function') { window.printReceiptHtml(receiptContent.innerHTML, { title: data.document_no || data.transaction_no || 'Sale Receipt' }); } else { const printArea = document.getElementById('posPrintArea'); if (printArea) { if (typeof window.prepareReceiptPrintArea === 'function') { window.prepareReceiptPrintArea(receiptContent.innerHTML, printArea); } else { printArea.innerHTML = receiptContent.innerHTML; const receipt = printArea.querySelector('.thermal-receipt'); if (receipt) { receipt.classList.add('thermal-receipt-print'); } } document.body.classList.add('printing-receipt'); window.print(); document.body.classList.remove('printing-receipt'); printArea.innerHTML = ''; } } const receiptModalEl = document.getElementById('posReceiptModal'); const receiptModal = receiptModalEl ? bootstrap.Modal.getInstance(receiptModalEl) : null; if (receiptModal) { receiptModal.hide(); } }, 250); }; const promptInvoicePrintMode = async (data) => { const isSaleInvoice = String(data?.type || invoiceType || 'sale').toLowerCase() === 'sale'; if (!isSaleInvoice || typeof window.printPosReceiptFromInvoice !== 'function') { if (typeof window.viewAndPrintA4Invoice === 'function') { window.viewAndPrintA4Invoice(data, true); } return; } if (window.Swal) { const result = await Swal.fire({ title: invoicePrintChooserTitle, text: invoicePrintChooserText, icon: 'question', showCancelButton: true, showDenyButton: true, confirmButtonText: invoicePrintNormalLabel, denyButtonText: invoicePrintReceiptLabel, cancelButtonText: invoicePrintCancelLabel, reverseButtons: true }); if (result.isConfirmed) { if (typeof window.viewAndPrintA4Invoice === 'function') { setTimeout(() => window.viewAndPrintA4Invoice(data, true), 120); } } else if (result.isDenied) { setTimeout(() => printInvoiceAsReceipt(data), 120); } return; } if (window.confirm(invoicePrintFallbackPrompt)) { if (typeof window.viewAndPrintA4Invoice === 'function') { window.viewAndPrintA4Invoice(data, true); } } else { printInvoiceAsReceipt(data); } }; document.querySelectorAll('.view-invoice-btn').forEach(btn => { btn.addEventListener('click', async function(event) { event.preventDefault(); const data = await loadInvoiceActionData(this); if (!data || !data.id) { showInvoiceActionError(); return; } if (typeof window.viewAndPrintA4Invoice === 'function') { window.viewAndPrintA4Invoice(data, false); } }); }); document.querySelectorAll('.print-a4-btn').forEach(btn => { btn.addEventListener('click', async function(event) { event.preventDefault(); const data = await loadInvoiceActionData(this); if (!data || !data.id) { showInvoiceActionError(); return; } await promptInvoicePrintMode(data); }); });