// 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 = = json_encode(($lang ?? 'en') === 'ar' ? 'خطأ' : 'Error', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
const invoiceActionLoadError = = json_encode(($lang ?? 'en') === 'ar' ? 'تعذر تحميل بيانات الفاتورة' : 'Failed to load invoice details', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
const invoicePrintChooserTitle = = json_encode(($lang ?? 'en') === 'ar' ? 'اختر طريقة الطباعة' : 'Choose print format', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
const invoicePrintChooserText = = json_encode(($lang ?? 'en') === 'ar' ? 'هل تريد طباعة الفاتورة كإيصال أو كفاتورة عادية؟' : 'Do you want to print this sale as a receipt or a normal invoice?', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
const invoicePrintNormalLabel = = json_encode(($lang ?? 'en') === 'ar' ? 'فاتورة عادية' : 'Normal Invoice', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
const invoicePrintReceiptLabel = = json_encode(($lang ?? 'en') === 'ar' ? 'إيصال' : 'Receipt', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
const invoicePrintCancelLabel = = json_encode(($lang ?? 'en') === 'ar' ? 'إلغاء' : 'Cancel', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
const invoicePrintFallbackPrompt = = json_encode(($lang ?? 'en') === 'ar' ? 'اضغط موافق لطباعة الفاتورة العادية، أو إلغاء لطباعة الإيصال.' : 'Press OK for a normal invoice, or Cancel for a receipt.', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
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);
});
});