// 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'; } }); }; const invoiceSyncSelect2Value = (select) => { if (!select) return; if (select.classList.contains('select2') && window.jQuery && window.jQuery.fn && window.jQuery.fn.select2) { window.jQuery(select).trigger('change'); } }; const invoiceEnsureSelectOption = (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 invoiceSetBlankSelectOptionLabel = (select, label = '---') => { if (!select) return; const emptyOption = Array.from(select.options || []).find(option => option.value === ''); if (emptyOption) { emptyOption.textContent = label; } }; const invoiceShowConfirmDialog = async ({ title = 'Are you sure?', text = '', confirmButtonText = 'Yes, continue', cancelButtonText = 'Cancel', icon = 'warning' } = {}) => { if (window.Swal) { const result = await Swal.fire({ title, text, icon, showCancelButton: true, confirmButtonText, cancelButtonText, reverseButtons: true, focusCancel: true, confirmButtonColor: '#dc3545', cancelButtonColor: '#6c757d' }); return !!result.isConfirmed; } return window.confirm(text ? `${title} ${text}` : title); }; const bindInvoiceSweetConfirmForms = () => { document.querySelectorAll('.js-swal-confirm-form').forEach(form => { if (form.dataset.confirmBound === '1') return; form.dataset.confirmBound = '1'; form.addEventListener('submit', async function(event) { if (form.dataset.skipConfirm === '1') { delete form.dataset.skipConfirm; return; } event.preventDefault(); const confirmed = await invoiceShowConfirmDialog({ title: form.dataset.confirmTitle || 'Are you sure?', text: form.dataset.confirmText || '', confirmButtonText: form.dataset.confirmButton || 'Yes, continue', cancelButtonText: form.dataset.cancelButton || 'Cancel', icon: form.dataset.confirmIcon || 'warning' }); if (!confirmed) return; const submitter = event.submitter || null; if (submitter && typeof form.requestSubmit === 'function') { form.dataset.skipConfirm = '1'; form.requestSubmit(submitter); return; } HTMLFormElement.prototype.submit.call(form); }); }); }; bindInvoiceSweetConfirmForms(); 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 = ''; initInvoiceForm('productSearchInput', 'searchSuggestions', 'invoiceItemsTableBody', 'grandTotal', 'subtotal', 'totalVat'); initInvoiceForm('editProductSearchInput', 'editSearchSuggestions', 'editInvoiceItemsTableBody', 'edit_grandTotal', 'edit_subtotal', 'edit_totalVat');