224 lines
11 KiB
PHP
224 lines
11 KiB
PHP
// Edit Invoice Logic
|
|
document.querySelectorAll('.edit-invoice-btn').forEach(btn => {
|
|
btn.addEventListener('click', function() {
|
|
const data = JSON.parse(this.dataset.json);
|
|
document.getElementById('edit_invoice_id').value = data.id;
|
|
document.getElementById('edit_customer_id').value = data.customer_id;
|
|
document.getElementById('edit_invoice_date').value = data.invoice_date;
|
|
document.getElementById('edit_due_date').value = data.due_date || '';
|
|
document.getElementById('edit_payment_type').value = data.payment_type || 'cash';
|
|
document.getElementById('edit_status').value = data.status || 'unpaid';
|
|
document.getElementById('edit_paid_amount').value = parseFloat(data.paid_amount || 0).toFixed(3);
|
|
|
|
if (data.status === 'partially_paid') {
|
|
document.getElementById('editPaidAmountContainer').style.display = 'block';
|
|
} else {
|
|
document.getElementById('editPaidAmountContainer').style.display = 'none';
|
|
}
|
|
|
|
const tableBody = document.getElementById('editInvoiceItemsTableBody');
|
|
tableBody.innerHTML = '';
|
|
|
|
data.items.forEach(item => {
|
|
// We need more data than what's in invoice_items (like SKU and names, but we have them from the join in PHP)
|
|
// The dataset-json already contains name_en, name_ar etc because of the PHP logic at line 1093
|
|
const itemMeta = {
|
|
id: item.item_id,
|
|
name_en: item.name_en,
|
|
name_ar: item.name_ar,
|
|
sku: '', // Optional, or fetch if needed
|
|
vat_rate: 0 // Will be handled if we have it in the join
|
|
};
|
|
|
|
// Fetch current item details to get VAT rate if possible, or use stored if available
|
|
// For simplicity, let's assume we want to use the item's current VAT rate or store it.
|
|
// Looking at the join at line 1093, it doesn't fetch vat_rate. Let's fix that in PHP too.
|
|
|
|
addItemToTable({
|
|
id: item.item_id,
|
|
name_en: item.name_en,
|
|
name_ar: item.name_ar,
|
|
sku: '',
|
|
vat_rate: item.vat_rate || 0 // We'll add this to PHP join
|
|
}, tableBody, null, null,
|
|
document.getElementById('edit_grandTotal'),
|
|
document.getElementById('edit_subtotal'),
|
|
document.getElementById('edit_totalVat'),
|
|
{ quantity: item.quantity, unit_price: item.unit_price });
|
|
});
|
|
});
|
|
});
|
|
|
|
// View and Print Invoice Logic
|
|
document.addEventListener('click', function(e) {
|
|
if (e.target.closest('.view-invoice-btn')) {
|
|
const btn = e.target.closest('.view-invoice-btn');
|
|
const data = JSON.parse(btn.dataset.json);
|
|
if (window.viewAndPrintA4Invoice) {
|
|
window.viewAndPrintA4Invoice(data, false);
|
|
}
|
|
}
|
|
if (e.target.closest('.print-a4-btn')) {
|
|
const btn = e.target.closest('.print-a4-btn');
|
|
const data = JSON.parse(btn.dataset.json);
|
|
if (window.viewAndPrintA4Invoice) {
|
|
window.viewAndPrintA4Invoice(data, true);
|
|
}
|
|
}
|
|
});
|
|
|
|
// Return Logic (General for Sales and Purchase)
|
|
const setupReturnLogic = (selectId, containerId, tbodyId, totalDisplayId, submitBtnId, type = "sale") => {
|
|
const select = document.getElementById(selectId);
|
|
if (!select) return;
|
|
|
|
const calculateTotal = function() {
|
|
let total = 0;
|
|
document.querySelectorAll('#' + tbodyId + ' tr').forEach(row => {
|
|
const qtyInput = row.querySelector('.return-qty-input');
|
|
if (!qtyInput) return;
|
|
const qty = parseFloat(qtyInput.value) || 0;
|
|
const price = parseFloat(qtyInput.dataset.price) || 0;
|
|
const lineTotal = qty * price;
|
|
const lineTotalDisplay = row.querySelector('.line-total');
|
|
if (lineTotalDisplay) {
|
|
lineTotalDisplay.innerText = lineTotal.toFixed(3);
|
|
}
|
|
total += lineTotal;
|
|
});
|
|
const totalDisplay = document.getElementById(totalDisplayId);
|
|
if (totalDisplay) {
|
|
totalDisplay.innerText = 'OMR ' + total.toFixed(3);
|
|
}
|
|
const submitBtn = document.getElementById(submitBtnId);
|
|
if (submitBtn) {
|
|
submitBtn.disabled = total <= 0;
|
|
}
|
|
};
|
|
|
|
const handleInvoiceChange = async function() {
|
|
const invoiceId = select.value;
|
|
const container = document.getElementById(containerId);
|
|
const tbody = document.getElementById(tbodyId);
|
|
const submitBtn = document.getElementById(submitBtnId);
|
|
|
|
if (!invoiceId) {
|
|
if (container) container.style.display = 'none';
|
|
if (submitBtn) submitBtn.disabled = true;
|
|
return;
|
|
}
|
|
|
|
if (tbody) {
|
|
tbody.innerHTML = '<tr><td colspan="5" class="text-center"><div class="spinner-border spinner-border-sm text-primary"></div> <span data-en="Loading items..." data-ar="جاري تحميل الأصناف...">Loading items...</span></td></tr>';
|
|
}
|
|
if (container) container.style.display = 'block';
|
|
|
|
try {
|
|
const resp = await fetch(`index.php?action=get_invoice_items&invoice_id=${invoiceId}&type=${type}`);
|
|
const items = await resp.json();
|
|
|
|
if (tbody) {
|
|
tbody.innerHTML = '';
|
|
if (items.length === 0) {
|
|
tbody.innerHTML = '<tr><td colspan="5" class="text-center text-muted" data-en="No items found for this invoice." data-ar="لا توجد أصناف لهذه الفاتورة.">No items found for this invoice.</td></tr>';
|
|
} else {
|
|
let html = '';
|
|
items.forEach(item => {
|
|
html += `
|
|
<tr>
|
|
<td>${item.name_en}<br><small class="text-muted">${item.sku}</small></td>
|
|
<td>${parseFloat(item.quantity).toFixed(2)}</td>
|
|
<td>
|
|
<input type="number" name="quantities[]" class="form-control form-control-sm return-qty-input"
|
|
step="0.01" min="0" max="${item.quantity}" value="0"
|
|
data-price="${item.unit_price}">
|
|
<input type="hidden" name="item_ids[]" value="${item.item_id}">
|
|
<input type="hidden" name="prices[]" value="${item.unit_price}">
|
|
</td>
|
|
<td class="text-end">${parseFloat(item.unit_price).toFixed(3)}</td>
|
|
<td class="text-end line-total">0.000</td>
|
|
</tr>
|
|
`;
|
|
});
|
|
tbody.innerHTML = html;
|
|
}
|
|
}
|
|
|
|
if (submitBtn) submitBtn.disabled = true;
|
|
|
|
const qtyInputs = tbody.querySelectorAll('.return-qty-input');
|
|
qtyInputs.forEach(input => {
|
|
['input', 'change', 'keyup'].forEach(evt => input.addEventListener(evt, calculateTotal));
|
|
});
|
|
|
|
calculateTotal();
|
|
} catch (e) {
|
|
console.error(e);
|
|
if (window.Swal) Swal.fire('Error', 'Failed to fetch invoice items', 'error');
|
|
}
|
|
};
|
|
|
|
select.addEventListener('change', handleInvoiceChange);
|
|
if (window.jQuery && jQuery.fn.select2) {
|
|
$(select).on('select2:select change', handleInvoiceChange);
|
|
}
|
|
};
|
|
|
|
setupReturnLogic('return_invoice_select', 'return_items_container', 'return_items_tbody', 'return_total_display', 'submit_return_btn', 'sale');
|
|
setupReturnLogic('purchase_return_invoice_select', 'purchase_return_items_container', 'purchase_return_items_tbody', 'purchase_return_total_display', 'purchase_submit_return_btn', 'purchase');
|
|
|
|
// Return Invoice Button from Sales/Purchases list
|
|
document.querySelectorAll('.return-invoice-btn').forEach(btn => {
|
|
btn.addEventListener('click', function() {
|
|
const invoiceId = this.dataset.id;
|
|
const targetModal = this.dataset.bsTarget;
|
|
const selectId = targetModal === '#addSalesReturnModal' ? 'return_invoice_select' : 'purchase_return_invoice_select';
|
|
const select = document.getElementById(selectId);
|
|
if (select) {
|
|
$(select).val(invoiceId).trigger('change');
|
|
}
|
|
});
|
|
});
|
|
|
|
// View Return Logic
|
|
document.querySelectorAll('.view-return-btn').forEach(btn => {
|
|
btn.addEventListener('click', async function() {
|
|
const returnId = this.dataset.id;
|
|
const type = '<?= $page === "purchase_returns" ? "purchase" : "sale" ?>';
|
|
const modal = new bootstrap.Modal(document.getElementById('viewReturnDetailsModal'));
|
|
|
|
try {
|
|
const resp = await fetch(`index.php?action=get_return_details&return_id=${returnId}&type=${type}`);
|
|
const data = await resp.json();
|
|
|
|
if (data) {
|
|
document.getElementById('view_return_no').innerText = (type === 'purchase' ? 'PRET-' : 'SRET-') + String(data.id).padStart(5, '0');
|
|
document.getElementById('view_return_party').innerText = data.party_name;
|
|
document.getElementById('view_return_date').innerText = data.return_date;
|
|
const refId = data.purchase_id || data.invoice_id;
|
|
const refPrefix = type === 'purchase' ? 'PUR-' : 'INV-';
|
|
document.getElementById('view_return_invoice').innerText = refPrefix + String(refId).padStart(5, '0');
|
|
document.getElementById('view_return_total').innerText = 'OMR ' + parseFloat(data.total_amount).toFixed(3);
|
|
document.getElementById('view_return_notes').innerText = data.notes || 'No notes';
|
|
|
|
let itemsHtml = '';
|
|
data.items.forEach(item => {
|
|
itemsHtml += `
|
|
<tr>
|
|
<td>${item.name_en}<br><small class="text-muted">${item.sku}</small></td>
|
|
<td class="text-center">${parseFloat(item.quantity).toFixed(2)}</td>
|
|
<td class="text-end">${parseFloat(item.unit_price).toFixed(3)}</td>
|
|
<td class="text-end">${parseFloat(item.total_price).toFixed(3)}</td>
|
|
</tr>
|
|
`;
|
|
});
|
|
document.getElementById('view_return_items_tbody').innerHTML = itemsHtml;
|
|
modal.show();
|
|
}
|
|
} catch (e) {
|
|
console.error(e);
|
|
Swal.fire('Error', 'Failed to fetch return details', 'error');
|
|
}
|
|
});
|
|
});
|