document.addEventListener('DOMContentLoaded', function() { console.log("DOM Content Loaded - Accounting System"); try { // Initialize Select2 for all searchable dropdowns $('.select2').each(function() { $(this).select2({ width: '100%', dropdownParent: $(this).closest('.modal').length ? $(this).closest('.modal') : $(document.body) }); }); const hasExpiryToggle = document.getElementById('hasExpiryToggle'); const expiryDateContainer = document.getElementById('expiryDateContainer'); const suggestSkuBtn = document.getElementById('suggestSkuBtn'); const skuInput = document.getElementById('skuInput'); if (suggestSkuBtn && skuInput) { suggestSkuBtn.addEventListener('click', function() { const sku = Math.floor(100000000000 + Math.random() * 900000000000).toString(); skuInput.value = sku; }); skuInput.addEventListener('keydown', function(e) { if (e.key === 'Enter') { e.preventDefault(); console.log("Barcode scan detected in SKU field, preventing form submission"); } }); } // Toggle Expiry Date visibility if (hasExpiryToggle && expiryDateContainer) { hasExpiryToggle.addEventListener('change', function() { expiryDateContainer.style.display = this.checked ? 'block' : 'none'; }); } const isPromotionToggle = document.getElementById('isPromotionToggle'); const promotionFieldsContainer = document.getElementById('promotionFieldsContainer'); if (isPromotionToggle && promotionFieldsContainer) { isPromotionToggle.addEventListener('change', function() { promotionFieldsContainer.style.display = this.checked ? 'flex' : 'none'; }); } // Translation Logic document.querySelectorAll('.btn-translate').forEach(btn => { btn.addEventListener('click', function() { const sourceId = this.getAttribute('data-source'); const targetId = this.getAttribute('data-target'); const targetLang = this.getAttribute('data-to'); const sourceText = document.getElementById(sourceId).value; if (!sourceText) { alert(targetLang === 'ar' ? 'يرجى إدخال النص أولاً' : 'Please enter text first'); return; } const originalHtml = this.innerHTML; this.disabled = true; this.innerHTML = ''; const formData = new FormData(); formData.append('action', 'translate'); formData.append('text', sourceText); formData.append('target', targetLang); fetch('index.php', { method: 'POST', body: formData }) .then(res => res.json()) .then(data => { if (data.success) { document.getElementById(targetId).value = data.translated; } else { alert('Translation error: ' + (data.error || 'Unknown error')); } }) .catch(err => { console.error(err); alert('Connection error'); }) .finally(() => { this.disabled = false; this.innerHTML = originalHtml; }); }); }); document.querySelectorAll('.isPromotionToggleEdit').forEach(toggle => { toggle.addEventListener('change', function() { const id = this.getAttribute('data-id'); const container = document.getElementById('promotionFieldsContainerEdit' + id); if (container) { container.style.display = this.checked ? 'flex' : 'none'; } }); }); // Handle Expiry toggle in Edit Modals document.querySelectorAll('.hasExpiryToggleEdit').forEach(toggle => { toggle.addEventListener('change', function() { const container = this.closest('.row').querySelector('.expiryDateContainerEdit'); if (container) { container.style.display = this.checked ? 'block' : 'none'; if (!this.checked) { container.querySelector('input').value = ''; } } }); }); // LPO Form Logic initInvoiceForm('lpoProductSearchInput', 'lpoSearchSuggestions', 'lpoItemsTableBody', 'lpo_grand_display', 'lpo_subtotal_display', 'lpo_vat_display'); initInvoiceForm('editLpoProductSearchInput', 'editLpoSearchSuggestions', 'editLpoItemsTableBody', 'edit_lpo_grand_display', 'edit_lpo_subtotal_display', 'edit_lpo_vat_display'); document.querySelectorAll('.edit-lpo-btn').forEach(btn => { btn.addEventListener('click', function() { const data = JSON.parse(this.dataset.json); document.getElementById('edit_lpo_id').value = data.id; const supplierSelect = document.getElementById('edit_lpo_supplier_id'); supplierSelect.value = data.supplier_id; if (window.jQuery && $(supplierSelect).data('select2')) { $(supplierSelect).trigger('change'); } document.getElementById('edit_lpo_date').value = data.lpo_date; document.getElementById('edit_lpo_delivery_date').value = data.delivery_date || ''; document.getElementById('edit_lpo_status').value = data.status || 'pending'; document.getElementById('edit_lpo_terms').value = data.terms_conditions || ''; const tableBody = document.getElementById('editLpoItemsTableBody'); tableBody.innerHTML = ''; data.items.forEach(item => { addItemToTable({ id: item.item_id, name_en: item.name_en, name_ar: item.name_ar, sku: '', vat_rate: item.vat_rate || 0 }, tableBody, null, null, document.getElementById('edit_lpo_grand_display'), document.getElementById('edit_lpo_subtotal_display'), document.getElementById('edit_lpo_vat_display'), { quantity: item.quantity, unit_price: item.unit_price }); }); }); }); document.querySelectorAll('.view-lpo-btn').forEach(btn => { btn.addEventListener('click', function() { const data = JSON.parse(this.dataset.json); window.viewAndPrintLPO(data); }); }); window.viewAndPrintLPO = function(data) { const modal = new bootstrap.Modal(document.getElementById('viewLpoModal')); const content = document.getElementById('lpoDetailsContent'); const logoUrl = companySettings.company_logo || ''; const companyHeader = `
${logoUrl ? `Logo` : ''}

${companySettings.company_name || 'Your Company'}

${companySettings.company_address || ''}
Phone: ${companySettings.company_phone || ''} | Email: ${companySettings.company_email || ''} ${companySettings.tax_number ? `
TRN: ${companySettings.tax_number}` : ''}

LOCAL PURCHASE ORDER

LPO-${data.id.toString().padStart(5, '0')}

`; let itemsHtml = ''; data.items.forEach((item, index) => { itemsHtml += ` ${index + 1} ${item.name_en}
${item.name_ar} ${item.quantity} ${parseFloat(item.unit_price).toFixed(3)} ${parseFloat(item.vat_rate || 0).toFixed(2)}% ${parseFloat(item.total_amount).toFixed(3)} `; }); content.innerHTML = ` ${companyHeader}
Supplier

${data.supplier_name}

${data.supplier_phone ? `Phone: ${data.supplier_phone}` : ''}

Details
Date: ${data.lpo_date}
Delivery: ${data.delivery_date || '---'}
Status: ${data.status.toUpperCase()}
${itemsHtml}
# Description Qty Unit Price VAT Total
Subtotal OMR ${parseFloat(data.total_amount).toFixed(3)}
VAT Amount OMR ${parseFloat(data.vat_amount).toFixed(2)}
Grand Total OMR ${parseFloat(data.total_with_vat).toFixed(3)}
${data.terms_conditions ? `
Terms & Conditions

${data.terms_conditions.replace(/\n/g, '
')}

` : ''}

Prepared By

Authorized Signature

`; window.printLPO = function() { const printWindow = window.open('', '_blank'); printWindow.document.write('LPO-' + data.id + ''); printWindow.document.write(''); printWindow.document.write(''); printWindow.document.write(''); printWindow.document.write(content.innerHTML); printWindow.document.write(''); printWindow.document.close(); setTimeout(() => { printWindow.print(); printWindow.close(); }, 500); }; modal.show(); }; // Quotation Form Logic initInvoiceForm('quotProductSearchInput', 'quotSearchSuggestions', 'quotItemsTableBody', 'quot_grand_display', 'quot_subtotal_display', 'quot_vat_display'); initInvoiceForm('editQuotProductSearchInput', 'editQuotSearchSuggestions', 'editQuotItemsTableBody', 'edit_quot_grand_display', 'edit_quot_subtotal_display', 'edit_quot_vat_display'); document.querySelectorAll('.edit-quotation-btn').forEach(btn => { btn.addEventListener('click', function() { const data = JSON.parse(this.dataset.json); document.getElementById('edit_quotation_id').value = data.id; document.getElementById('edit_quot_customer_id').value = data.customer_id; document.getElementById('edit_quot_date').value = data.quotation_date; document.getElementById('edit_quot_valid').value = data.valid_until || ''; document.getElementById('edit_quot_status').value = data.status || 'pending'; const tableBody = document.getElementById('editQuotItemsTableBody'); tableBody.innerHTML = ''; data.items.forEach(item => { addItemToTable({ id: item.item_id, name_en: item.name_en, name_ar: item.name_ar, sku: '', vat_rate: item.vat_rate || 0 }, tableBody, null, null, document.getElementById('edit_quot_grand_display'), document.getElementById('edit_quot_subtotal_display'), document.getElementById('edit_quot_vat_display'), { quantity: item.quantity, unit_price: item.unit_price }); }); }); }); document.querySelectorAll('.convert-quotation-btn').forEach(btn => { btn.addEventListener('click', function() { if (confirm('Convert this quotation to an invoice? This will reduce stock.')) { const f = document.createElement('form'); f.method = 'POST'; f.innerHTML = ``; document.body.appendChild(f); f.submit(); } }); }); // View Quotation Logic window.viewAndPrintQuotation = function(data, autoPrint = false) { const modal = new bootstrap.Modal(document.getElementById('viewQuotationModal')); const content = document.getElementById('quotationPrintableArea'); let itemsHtml = ''; data.items.forEach((item, index) => { itemsHtml += ` ${index + 1} ${item.name_en}
${item.name_ar} ${item.quantity} ${parseFloat(item.unit_price).toFixed(3)} ${parseFloat(item.vat_rate || 0).toFixed(2)}% ${parseFloat(item.total_price).toFixed(3)} `; }); // Company Logo and Header Construction const logoUrl = companySettings.company_logo || ''; const logoImg = logoUrl ? `` : ''; const companyName = companySettings.company_name || 'Accounting System'; const companyAddress = (companySettings.company_address || '').replace(/\n/g, '
'); const companyVat = companySettings.vat_number ? `

VAT: ${companySettings.vat_number}

` : ''; const companyPhone = companySettings.company_phone ? `

Tel: ${companySettings.company_phone}

` : ''; // Quotation Header Construction const quotDate = data.quotation_date; const quotValid = data.valid_until || 'N/A'; const quotNo = 'QUO-' + data.id.toString().padStart(5, '0'); const customerName = data.customer_name || 'Walk-in Customer'; const statusBadge = `${data.status.toUpperCase()}`; content.innerHTML = `
${logoImg}

${companyName}

${companyAddress}

${companyVat} ${companyPhone}

Quotation / عرض سعر

${statusBadge}

No / رقم: ${quotNo}

Date / التاريخ: ${quotDate}

Valid Until / صالح لغاية: ${quotValid}

To / إلى

${customerName}
${itemsHtml}
# Item Description / وصف الصنف Qty / الكمية Unit Price / سعر الوحدة VAT / الضريبة Total / الإجمالي
Subtotal / المجموع الفرعي ${parseFloat(data.total_amount).toFixed(3)}
VAT Amount / مبلغ الضريبة ${parseFloat(data.vat_amount).toFixed(2)}
Grand Total (OMR) / المجموع الكلي (رع) ${parseFloat(data.total_with_vat).toFixed(3)}

Terms & Conditions / الشروط والأحكام:

  • Quotation is valid until the date mentioned above. / عرض السعر صالح لغاية التاريخ المذكور أعلاه.
  • Prices are inclusive of VAT where applicable. / الأسعار تشمل ضريبة القيمة المضافة حيثما ينطبق ذلك.
Authorized Signature / التوقيع المعتمد

Generated by / تم إنشاؤه بواسطة ${companyName}

`; const actionButtons = document.getElementById('quotationActionButtons'); actionButtons.innerHTML = ''; if (data.status === 'pending') { const convertBtn = document.createElement('button'); convertBtn.className = 'btn btn-success me-2'; convertBtn.innerHTML = ' Convert to Invoice'; convertBtn.onclick = function() { if (confirm('Convert this quotation to an invoice?')) { const f = document.createElement('form'); f.method = 'POST'; f.innerHTML = ``; document.body.appendChild(f); f.submit(); } }; actionButtons.appendChild(convertBtn); const editBtn = document.createElement('button'); editBtn.className = 'btn btn-primary'; editBtn.innerHTML = ' Edit'; editBtn.onclick = function() { const editModal = new bootstrap.Modal(document.getElementById('editQuotationModal')); modal.hide(); const originalEditBtn = document.querySelector(`.edit-quotation-btn[data-json*='"id":${data.id},']`) || document.querySelector(`.edit-quotation-btn[data-json*='"id":${data.id}']`); if (originalEditBtn) originalEditBtn.click(); }; actionButtons.appendChild(editBtn); } modal.show(); if (autoPrint) { setTimeout(() => { window.print(); }, 500); } }; document.querySelectorAll('.view-quotation-btn').forEach(btn => { btn.addEventListener('click', function() { const data = JSON.parse(this.dataset.json); window.viewAndPrintQuotation(data, false); }); }); // Status change logic for Paid Amount field const togglePaidAmount = (statusId, containerId) => { const statusEl = document.getElementById(statusId); const containerEl = document.getElementById(containerId); if (statusEl && containerEl) { statusEl.addEventListener('change', function() { if (this.value === 'partially_paid') { containerEl.style.display = 'block'; } else { containerEl.style.display = 'none'; } }); } }; togglePaidAmount('add_status', 'addPaidAmountContainer'); togglePaidAmount('edit_status', 'editPaidAmountContainer'); // Pay Invoice Logic document.querySelectorAll('.pay-invoice-btn').forEach(btn => { btn.addEventListener('click', function() { const id = this.getAttribute('data-id'); const total = parseFloat(this.getAttribute('data-total')); const paid = parseFloat(this.getAttribute('data-paid') || 0); const remaining = total - paid; document.getElementById('pay_invoice_id').value = id; document.getElementById('pay_invoice_total').value = total.toFixed(3); document.getElementById('pay_remaining_amount').value = remaining.toFixed(3); document.getElementById('pay_amount').value = remaining.toFixed(3); document.getElementById('pay_amount').max = remaining.toFixed(3); }); }); // Show receipt modal if needed function showReceipt(paymentId) { fetch(`index.php?action=get_payment_details&payment_id=${paymentId}`) .then(res => res.json()) .then(data => { if (!data) return; document.getElementById('receiptNo').textContent = 'RCP-' + data.id.toString().padStart(5, '0'); document.getElementById('receiptDate').textContent = data.payment_date; document.getElementById('receiptCustomer').textContent = data.customer_name || '---'; document.getElementById('receiptInvNo').textContent = (data.inv_type === 'purchase' ? 'PUR-' : 'INV-') + data.inv_id.toString().padStart(5, '0'); document.getElementById('receiptMethod').textContent = data.payment_method; document.getElementById('receiptAmount').textContent = parseFloat(data.amount).toFixed(3); document.getElementById('receiptAmountWords').textContent = data.amount_words; const outletEl = document.getElementById('receiptOutletName'); if (outletEl) { outletEl.textContent = data.outlet_name ? (data.outlet_name) : ''; outletEl.style.display = data.outlet_name ? 'block' : 'none'; } // Update labels for Purchase vs Sale const partyLabel = document.getElementById('receiptPartyLabel'); const againstLabel = document.getElementById('receiptAgainstLabel'); const receiptTitle = document.querySelector('#receiptModal .modal-title'); const receiptH4 = document.querySelector('.receipt-container h4.letter-spacing-2'); if (data.inv_type === 'purchase') { partyLabel.textContent = 'Paid To'; partyLabel.setAttribute('data-en', 'Paid To'); partyLabel.setAttribute('data-ar', 'صرف إلى'); againstLabel.textContent = 'Against Purchase'; againstLabel.setAttribute('data-en', 'Against Purchase'); againstLabel.setAttribute('data-ar', 'مقابل شراء'); receiptTitle.textContent = 'Payment Voucher'; receiptTitle.setAttribute('data-en', 'Payment Voucher'); receiptTitle.setAttribute('data-ar', 'سند صرف'); receiptH4.textContent = 'PAYMENT VOUCHER'; receiptH4.setAttribute('data-en', 'PAYMENT VOUCHER'); receiptH4.setAttribute('data-ar', 'سند صرف'); } else { partyLabel.textContent = 'Received From'; partyLabel.setAttribute('data-en', 'Received From'); partyLabel.setAttribute('data-ar', 'وصلنا من'); againstLabel.textContent = 'Against Invoice'; againstLabel.setAttribute('data-en', 'Against Invoice'); againstLabel.setAttribute('data-ar', 'مقابل فاتورة'); receiptTitle.textContent = 'Payment Receipt'; receiptTitle.setAttribute('data-en', 'Payment Receipt'); receiptTitle.setAttribute('data-ar', 'سند قبض'); receiptH4.textContent = 'PAYMENT RECEIPT'; receiptH4.setAttribute('data-en', 'PAYMENT RECEIPT'); receiptH4.setAttribute('data-ar', 'سند قبض'); } const notesContainer = document.getElementById('receiptNotesContainer'); if (data.notes) { document.getElementById('receiptNotes').textContent = data.notes; notesContainer.style.display = 'block'; } else { notesContainer.style.display = 'none'; } const receiptModal = new bootstrap.Modal(document.getElementById('receiptModal')); receiptModal.show(); }); }; document.querySelectorAll('.view-payments-btn').forEach(btn => { btn.addEventListener('click', function() { const invoiceId = this.getAttribute('data-id'); const tbody = document.getElementById('paymentsTableBody'); tbody.innerHTML = 'Loading...'; fetch(`index.php?action=get_payments&invoice_id=${invoiceId}`) .then(res => res.json()) .then(data => { tbody.innerHTML = ''; if (data.length === 0) { tbody.innerHTML = 'No payments found.'; return; } data.forEach(p => { const tr = document.createElement('tr'); tr.innerHTML = ` RCP-${p.id.toString().padStart(5, '0')} ${p.payment_date} ${p.payment_method} OMR ${parseFloat(p.amount).toFixed(3)} `; tbody.appendChild(tr); }); }); }); }); window.printReceipt = function() { const content = document.getElementById('printableReceipt').innerHTML; const originalContent = document.body.innerHTML; document.body.innerHTML = content; window.print(); document.body.innerHTML = originalContent; window.location.reload(); }; // Invoice Form Logic const initInvoiceForm = (searchInputId, suggestionsId, tableBodyId, grandTotalId, subtotalId, totalVatId) => { const searchInput = document.getElementById(searchInputId); const suggestions = document.getElementById(suggestionsId); const tableBody = document.getElementById(tableBodyId); const grandTotalEl = document.getElementById(grandTotalId); const subtotalEl = document.getElementById(subtotalId); const totalVatEl = document.getElementById(totalVatId); if (!searchInput || !tableBody) return; let timeout = null; searchInput.addEventListener('input', function() { clearTimeout(timeout); const q = this.value.trim(); if (q.length < 2) { suggestions.style.display = 'none'; return; } timeout = setTimeout(() => { fetch(`index.php?action=search_items&q=${encodeURIComponent(q)}`) .then(res => res.ok ? res.json() : []) .then(data => { suggestions.innerHTML = ''; if (data.length > 0) { data.forEach(item => { const btn = document.createElement('button'); btn.type = 'button'; btn.className = 'list-group-item list-group-item-action'; btn.innerHTML = `
${item.sku} - ${item.name_en} / ${item.name_ar} Stock: ${item.stock_quantity}
`; btn.onclick = () => addItemToTable(item, tableBody, searchInput, suggestions, grandTotalEl, subtotalEl, totalVatEl); suggestions.appendChild(btn); }); suggestions.style.display = 'block'; } else { suggestions.style.display = 'none'; } }); }, 300); }); // Close suggestions when clicking outside document.addEventListener('click', function(e) { if (!searchInput.contains(e.target) && !suggestions.contains(e.target)) { suggestions.style.display = 'none'; } }); }; function addItemToTable(item, tableBody, searchInput, suggestions, grandTotalEl, subtotalEl, totalVatEl, customData = null) { if (suggestions) suggestions.style.display = 'none'; if (searchInput) searchInput.value = ''; const allowZeroStock = (typeof companySettings !== 'undefined' && String(companySettings.allow_zero_stock_sell) === '1'); const currentStock = parseFloat(item.stock_quantity) || 0; if (invoiceType === 'sale' && !allowZeroStock && !customData) { const existingInTable = Array.from(tableBody.querySelectorAll('.item-row')).find(row => row.querySelector('.item-id-input').value == item.id); let currentQtyInTable = 0; if (existingInTable) { currentQtyInTable = parseFloat(existingInTable.querySelector('.item-qty').value) || 0; } if (currentQtyInTable + 1 > currentStock) { alert('Insufficient stock! Available: ' + currentStock); return; } } const existingRow = Array.from(tableBody.querySelectorAll('.item-id-input')).find(input => input.value == item.id); if (existingRow && !customData) { const row = existingRow.closest('tr'); const qtyInput = row.querySelector('.item-qty'); qtyInput.value = parseFloat(qtyInput.value) + 1; recalculate(tableBody, grandTotalEl, subtotalEl, totalVatEl); return; } const row = document.createElement('tr'); row.className = 'item-row'; const price = customData ? customData.unit_price : (invoiceType === 'sale' ? item.sale_price : item.purchase_price); const qty = customData ? customData.quantity : 1; const vatRate = item.vat_rate || 0; row.innerHTML = `
${item.name_en}
${item.name_ar} (${item.sku})
`; tableBody.appendChild(row); attachRowListeners(row, tableBody, grandTotalEl, subtotalEl, totalVatEl); recalculate(tableBody, grandTotalEl, subtotalEl, totalVatEl); } function recalculate(tableBody, grandTotalEl, subtotalEl, totalVatEl) { let subtotal = 0; let totalVat = 0; tableBody.querySelectorAll('.item-row').forEach(row => { const qty = parseFloat(row.querySelector('.item-qty').value) || 0; const price = parseFloat(row.querySelector('.item-price').value) || 0; const vatRate = parseFloat(row.querySelector('.item-vat-rate').value) || 0; const total = qty * price; const vatAmount = total * (vatRate / 100); row.querySelector('.item-total').value = total.toFixed(3); subtotal += total; totalVat += vatAmount; }); const grandTotal = subtotal + totalVat; if (subtotalEl) subtotalEl.textContent = 'OMR ' + subtotal.toFixed(3); if (totalVatEl) totalVatEl.textContent = 'OMR ' + totalVat.toFixed(2); if (grandTotalEl) grandTotalEl.textContent = 'OMR ' + grandTotal.toFixed(3); } function attachRowListeners(row, tableBody, grandTotalEl, subtotalEl, totalVatEl) { row.querySelector('.item-qty').addEventListener('input', function() { const allowZeroStock = (typeof companySettings !== 'undefined' && String(companySettings.allow_zero_stock_sell) === '1'); if (invoiceType === 'sale' && !allowZeroStock) { const stock = parseFloat(row.querySelector('.item-row-stock').value) || 0; const qty = parseFloat(this.value) || 0; if (qty > stock) { alert('Insufficient stock! Available: ' + stock); this.value = stock; } } recalculate(tableBody, grandTotalEl, subtotalEl, totalVatEl); }); row.querySelector('.item-price').addEventListener('input', () => recalculate(tableBody, grandTotalEl, subtotalEl, totalVatEl)); row.querySelector('.remove-row').addEventListener('click', function() { row.remove(); recalculate(tableBody, grandTotalEl, subtotalEl, totalVatEl); }); } const invoiceType = 'sale'; initInvoiceForm('productSearchInput', 'searchSuggestions', 'invoiceItemsTableBody', 'grandTotal', 'subtotal', 'totalVat'); initInvoiceForm('editProductSearchInput', 'editSearchSuggestions', 'editInvoiceItemsTableBody', 'edit_grandTotal', 'edit_subtotal', 'edit_totalVat'); window.viewAndPrintA4Invoice = function(data, autoPrint = true) { if (!data) return; // Reuse view logic const invoiceDisplayNo = data.document_no || data.transaction_no || ((data.type === 'purchase' ? 'PUR-' : 'INV-') + data.id.toString().padStart(5, '0')); document.getElementById('invNumber').textContent = invoiceDisplayNo; document.getElementById('invDate').textContent = data.invoice_date; document.getElementById('invPaymentType').textContent = data.payment_type ? data.payment_type.toUpperCase() : 'CASH'; document.getElementById('invCustomerName').textContent = data.customer_name || '---'; const phoneEl = document.getElementById('invCustomerPhone'); const phoneContainer = document.getElementById('invCustomerPhoneContainer'); if (data.customer_phone) { phoneEl.textContent = data.customer_phone; phoneContainer.style.display = 'block'; } else { phoneContainer.style.display = 'none'; } const taxIdEl = document.getElementById('invCustomerTaxId'); const taxIdContainer = document.getElementById('invCustomerTaxIdContainer'); if (data.customer_tax_id) { taxIdEl.textContent = data.customer_tax_id; taxIdContainer.style.display = 'block'; } else { taxIdContainer.style.display = 'none'; } document.getElementById('invAmountInWords').textContent = data.total_in_words || ''; const invOutletEl = document.getElementById('invOutletName'); if (invOutletEl) { invOutletEl.textContent = data.outlet_name ? (data.outlet_name) : ''; invOutletEl.style.display = data.outlet_name ? 'block' : 'none'; } document.getElementById('invPartyLabel').textContent = data.type === 'sale' ? 'Bill To / فاتورة إلى' : 'Bill From / فاتورة من'; document.getElementById('invPartyLabel').setAttribute('data-en', data.type === 'sale' ? 'Bill To' : 'Bill From'); document.getElementById('invPartyLabel').setAttribute('data-ar', data.type === 'sale' ? 'فاتورة إلى' : 'فاتورة من'); document.getElementById('invoiceTypeLabel').textContent = data.type; document.getElementById('invoiceTypeLabel').className = 'badge text-uppercase ' + (data.type === 'sale' ? 'bg-success' : 'bg-warning'); const statusLabel = document.getElementById('invoiceStatusLabel'); let statusClass = 'bg-secondary'; let statusEn = data.status ? (data.status.charAt(0).toUpperCase() + data.status.slice(1)) : '---'; if (data.status === 'paid') statusClass = 'bg-success'; else if (data.status === 'unpaid') statusClass = 'bg-danger'; else if (data.status === 'partially_paid') { statusClass = 'bg-warning text-dark'; statusEn = 'Partially Paid'; } statusLabel.textContent = statusEn; statusLabel.className = 'badge text-uppercase ' + statusClass; const body = document.getElementById('invItemsBody'); body.innerHTML = ''; if (data.items) { data.items.forEach(item => { const tr = document.createElement('tr'); tr.innerHTML = ` ${item.name_en} / ${item.name_ar} ${item.quantity} ر.ع / OMR ${parseFloat(item.unit_price).toFixed(3)} ${parseFloat(item.vat_rate || 0).toFixed(2)}% ر.ع / OMR ${parseFloat(item.total_price).toFixed(3)} `; body.appendChild(tr); }); } const vatVal = parseFloat(data.vat_amount || 0); const totalVal = parseFloat(data.total_amount || 0); const grandTotalValue = (parseFloat(data.total_with_vat) || (totalVal + vatVal)); if (document.getElementById('invSubtotal')) document.getElementById('invSubtotal').innerHTML = 'ر.ع / OMR ' + (grandTotalValue - vatVal).toFixed(3); if (document.getElementById('invVatAmount')) document.getElementById('invVatAmount').innerHTML = 'ر.ع / OMR ' + vatVal.toFixed(2); if (document.getElementById('invGrandTotal')) document.getElementById('invGrandTotal').innerHTML = 'ر.ع / OMR ' + grandTotalValue.toFixed(3); if (document.getElementById('invPaidInfo')) document.getElementById('invPaidInfo').innerHTML = 'ر.ع / OMR ' + parseFloat(data.paid_amount || 0).toFixed(3); const balance = grandTotalValue - parseFloat(data.paid_amount || 0); if (document.getElementById('invBalanceInfo')) document.getElementById('invBalanceInfo').innerHTML = 'ر.ع / OMR ' + balance.toFixed(3); // Generate QR Code for Zakat, Tax and Customs Authority (ZATCA) style or simple formal const companyName = "Bahjet Al-Safa Trading"; const vatNo = "OM25418"; const qrData = `Seller: ${companyName}\nVAT: ${vatNo}\nInvoice: ${invoiceDisplayNo}\nDate: ${data.invoice_date}\nTotal: ${grandTotalValue.toFixed(3)}`; const qrUrl = `https://api.qrserver.com/v1/create-qr-code/?size=100x100&data=${encodeURIComponent(qrData)}`; if (document.getElementById('invQrCode')) { document.getElementById('invQrCode').innerHTML = `QR Code`; } const viewModal = bootstrap.Modal.getOrCreateInstance(document.getElementById('viewInvoiceModal')); viewModal.show(); if (autoPrint) { setTimeout(() => { window.print(); }, 1000); } fetch(`index.php?action=get_payments&invoice_id=${data.id}`) .then(res => res.json()) .then(payments => { const paymentsBody = document.getElementById('invPaymentsBody'); const paymentsSection = document.getElementById('invPaymentsSection'); if (paymentsBody) paymentsBody.innerHTML = ''; if (payments && payments.length > 0) { if (paymentsBody) { payments.forEach(p => { const tr = document.createElement('tr'); tr.innerHTML = `${p.payment_date}${p.payment_method}OMR ${parseFloat(p.amount).toFixed(3)}`; paymentsBody.appendChild(tr); }); } if (paymentsSection) paymentsSection.style.display = 'block'; } else { if (paymentsSection) paymentsSection.style.display = 'none'; } }).catch(err => console.error('Error fetching payments:', err)); }; // 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 syncSelect2Value = (select) => { if (!select) return; if (select.classList.contains('select2') && window.jQuery && jQuery.fn && jQuery.fn.select2) { jQuery(select).trigger('change'); } }; const ensureSelectOption = (selectId, value, label = '') => { const select = document.getElementById(selectId); if (!select || value === null || value === undefined || String(value) === '') return; const alreadyExists = Array.from(select.options || []).some(option => option.value == String(value)); if (!alreadyExists) { const option = document.createElement('option'); option.value = String(value); option.textContent = label || String(value); select.appendChild(option); } }; const setBlankSelectOptionLabel = (select, label = '---') => { if (!select) return; const emptyOption = Array.from(select.options || []).find(option => option.value === ''); if (emptyOption) { emptyOption.textContent = label; } }; 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, stock_quantity: item.stock_quantity || 0 }, tableBody, null, null, grandTotalEl, subtotalEl, totalVatEl, { quantity: item.quantity, unit_price: item.unit_price }); }); }; 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 partyId = data.customer_id ?? data.supplier_id ?? ''; const partyLabel = data.party_name || data.customer_name || data.supplier_name || ''; ensureSelectOption('edit_customer_id', partyId, partyLabel); if (invoiceIdInput) invoiceIdInput.value = data.id || ''; if (partySelect) { setBlankSelectOptionLabel(partySelect, (partyId === '' && partyLabel) ? partyLabel : '---'); partySelect.value = partyId; syncSelect2Value(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 (paidAmountContainer) { paidAmountContainer.style.display = data.status === 'partially_paid' ? 'block' : 'none'; } renderEditInvoiceItems(data.items || []); }; 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 } catch (e) { console.error("JS Error in DOMContentLoaded:", e); } });