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 ? `

` : ''}
${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()}
| # |
Description |
Qty |
Unit Price |
VAT |
Total |
${itemsHtml}
| 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, '
')}
` : ''}
`;
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 = `
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 = `
`;
}
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); }
});