index again
This commit is contained in:
parent
4034cc3c7f
commit
c23bc66b9c
61
index.php
61
index.php
@ -2106,20 +2106,73 @@ if (isset($_GET['action']) || isset($_POST['action'])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'get_invoice_details') {
|
||||
header('Content-Type: application/json');
|
||||
$invoice_id = (int)($_GET['invoice_id'] ?? 0);
|
||||
$type = (($_GET['type'] ?? 'sale') === 'purchase') ? 'purchase' : 'sale';
|
||||
|
||||
if ($invoice_id < 1) {
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid invoice id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$table = ($type === 'purchase') ? 'purchases' : 'invoices';
|
||||
$itemTable = ($type === 'purchase') ? 'purchase_items' : 'invoice_items';
|
||||
$fkColumn = ($type === 'purchase') ? 'purchase_id' : 'invoice_id';
|
||||
$partyColumn = ($type === 'purchase') ? 'supplier_id' : 'customer_id';
|
||||
$partyTable = ($type === 'purchase') ? 'suppliers' : 'customers';
|
||||
$partyAlias = ($type === 'purchase') ? 'supplier_name' : 'customer_name';
|
||||
|
||||
$where = ['v.id = ?'];
|
||||
$params = [$invoice_id];
|
||||
if (db_column_exists($table, 'outlet_id')) {
|
||||
$oid = current_outlet_id();
|
||||
if ($oid !== -1) {
|
||||
$where[] = '(v.outlet_id = ? OR v.outlet_id IS NULL)';
|
||||
$params[] = $oid;
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = db()->prepare("SELECT v.*, c.name AS {$partyAlias}, c.phone AS party_phone FROM $table v LEFT JOIN $partyTable c ON v.$partyColumn = c.id WHERE " . implode(' AND ', $where) . " LIMIT 1");
|
||||
$stmt->execute($params);
|
||||
$invoice = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$invoice) {
|
||||
echo json_encode(['success' => false, 'error' => 'Invoice not found']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$partyName = trim((string)($invoice[$partyAlias] ?? ''));
|
||||
if ($partyName === '' && $type === 'sale' && !empty($invoice['is_pos'])) {
|
||||
$partyName = 'Walk-in Customer';
|
||||
}
|
||||
|
||||
$invoice['type'] = $type;
|
||||
$invoice['party_name'] = $partyName !== '' ? $partyName : '---';
|
||||
$invoice['paid_amount'] = (float)($invoice['paid_amount'] ?? 0);
|
||||
|
||||
$stmtItems = db()->prepare("SELECT li.*, i.name_en, i.name_ar, i.sku, i.vat_rate, i.stock_quantity FROM $itemTable li LEFT JOIN stock_items i ON li.item_id = i.id WHERE li.$fkColumn = ?");
|
||||
$stmtItems->execute([$invoice_id]);
|
||||
$invoice['items'] = $stmtItems->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode($invoice, JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($action === 'get_invoice_items') {
|
||||
header('Content-Type: application/json');
|
||||
$invoice_id = (int)$_GET['invoice_id'];
|
||||
$type = $_GET['type'] ?? 'sale';
|
||||
|
||||
if ($type === 'purchase') {
|
||||
$stmt = db()->prepare("SELECT pi.*, i.name_en, i.name_ar, i.sku
|
||||
$stmt = db()->prepare("SELECT pi.*, i.name_en, i.name_ar, i.sku, i.vat_rate, i.stock_quantity
|
||||
FROM purchase_items pi
|
||||
JOIN stock_items i ON pi.item_id = i.id
|
||||
LEFT JOIN stock_items i ON pi.item_id = i.id
|
||||
WHERE pi.purchase_id = ?");
|
||||
} else {
|
||||
$stmt = db()->prepare("SELECT ii.*, i.name_en, i.name_ar, i.sku
|
||||
$stmt = db()->prepare("SELECT ii.*, i.name_en, i.name_ar, i.sku, i.vat_rate, i.stock_quantity
|
||||
FROM invoice_items ii
|
||||
JOIN stock_items i ON ii.item_id = i.id
|
||||
LEFT JOIN stock_items i ON ii.item_id = i.id
|
||||
WHERE ii.invoice_id = ?");
|
||||
}
|
||||
$stmt->execute([$invoice_id]);
|
||||
|
||||
@ -1,223 +1,148 @@
|
||||
// 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
|
||||
};
|
||||
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 {};
|
||||
}
|
||||
};
|
||||
|
||||
// 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 });
|
||||
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 = '<tr><td colspan="6" class="text-center"><div class="spinner-border spinner-border-sm text-primary"></div> <span data-en="Loading invoice..." data-ar="جاري تحميل الفاتورة...">Loading invoice...</span></td></tr>';
|
||||
}
|
||||
|
||||
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
|
||||
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');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -130,11 +130,11 @@
|
||||
$inv['total_in_words'] = numberToWordsOMR($inv['total_with_vat']);
|
||||
|
||||
if ($type === 'sale') {
|
||||
$item_stmt = db()->prepare("SELECT ii.*, i.name_en, i.name_ar, i.vat_rate FROM invoice_items ii LEFT JOIN stock_items i ON ii.item_id = i.id WHERE ii.invoice_id = ?");
|
||||
$item_stmt = db()->prepare("SELECT ii.*, i.name_en, i.name_ar, i.sku, i.vat_rate, i.stock_quantity FROM invoice_items ii LEFT JOIN stock_items i ON ii.item_id = i.id WHERE ii.invoice_id = ?");
|
||||
$item_stmt->execute([$inv['id']]);
|
||||
$inv['items'] = $item_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
} else {
|
||||
$item_stmt = db()->prepare("SELECT pi.*, i.name_en, i.name_ar, i.vat_rate FROM purchase_items pi LEFT JOIN stock_items i ON pi.item_id = i.id WHERE pi.purchase_id = ?");
|
||||
$item_stmt = db()->prepare("SELECT pi.*, i.name_en, i.name_ar, i.sku, i.vat_rate, i.stock_quantity FROM purchase_items pi LEFT JOIN stock_items i ON pi.item_id = i.id WHERE pi.purchase_id = ?");
|
||||
$item_stmt->execute([$inv['id']]);
|
||||
$inv['items'] = $item_stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
@ -519,7 +519,7 @@
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label fw-bold" data-en="<?= $page === 'sales' ? 'Customer' : 'Supplier' ?>" data-ar="<?= $page === 'sales' ? 'العميل' : 'المورد' ?>"><?= $page === 'sales' ? 'Customer' : 'Supplier' ?></label>
|
||||
<select name="customer_id" id="edit_customer_id" class="form-select select2" required>
|
||||
<select name="customer_id" id="edit_customer_id" class="form-select select2" <?= $page === 'sales' ? '' : 'required' ?>>
|
||||
<option value="">---</option>
|
||||
<?php foreach ($data['customers_list'] as $c): ?>
|
||||
<option value="<?= $c['id'] ?>"><?= htmlspecialchars($c['name']) ?></option>
|
||||
|
||||
@ -12,7 +12,8 @@
|
||||
$cust_supplier_col = ($type === 'purchase') ? 'supplier_id' : 'customer_id';
|
||||
$fk_col = ($type === 'purchase') ? 'purchase_id' : 'invoice_id';
|
||||
|
||||
$cust_id = (int)$_POST['customer_id'];
|
||||
$rawCustomerId = $_POST['customer_id'] ?? '';
|
||||
$cust_id = ($type === 'sale' && ($rawCustomerId === '' || $rawCustomerId === null)) ? null : (int)$rawCustomerId;
|
||||
$inv_date = $_POST['invoice_date'] ?: date('Y-m-d');
|
||||
$due_date = $_POST['due_date'] ?: null;
|
||||
$status = $_POST['status'] ?? 'pending';
|
||||
@ -111,7 +112,8 @@
|
||||
$cust_supplier_col = ($type === 'purchase') ? 'supplier_id' : 'customer_id';
|
||||
$fk_col = ($type === 'purchase') ? 'purchase_id' : 'invoice_id';
|
||||
|
||||
$cust_id = (int)$_POST['customer_id'];
|
||||
$rawCustomerId = $_POST['customer_id'] ?? '';
|
||||
$cust_id = ($type === 'sale' && ($rawCustomerId === '' || $rawCustomerId === null)) ? null : (int)$rawCustomerId;
|
||||
$date = $_POST['invoice_date'] ?: date('Y-m-d');
|
||||
$due_date = $_POST['due_date'] ?: null;
|
||||
$status = $_POST['status'] ?? 'pending';
|
||||
|
||||
@ -164,14 +164,15 @@
|
||||
<td class="text-end text-success">OMR <?= number_format((float)$inv['paid_amount'], 3) ?></td>
|
||||
<td class="text-end text-danger fw-bold">OMR <?= number_format((float)$inv['balance_amount'], 3) ?></td>
|
||||
<td class="text-end d-print-none">
|
||||
<?php $invoiceJson = (string)(json_encode($inv, JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE) ?: '{}'); ?>
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button class="btn btn-outline-info view-invoice-btn" data-json="<?= htmlspecialchars(json_encode($inv)) ?>" title="View"><i class="bi bi-eye"></i></button>
|
||||
<button class="btn btn-outline-info view-invoice-btn" data-json="<?= htmlspecialchars($invoiceJson) ?>" title="View"><i class="bi bi-eye"></i></button>
|
||||
<button class="btn btn-outline-warning return-invoice-btn" data-id="<?= $inv['id'] ?>" data-bs-toggle="modal" data-bs-target="<?= $page === 'sales' ? '#addSalesReturnModal' : '#addPurchaseReturnModal' ?>" title="Return"><i class="bi bi-arrow-return-left"></i></button>
|
||||
<button class="btn btn-outline-primary edit-invoice-btn" data-json="<?= htmlspecialchars(json_encode($inv)) ?>" data-bs-toggle="modal" data-bs-target="#editInvoiceModal" title="Edit"><i class="bi bi-pencil"></i></button>
|
||||
<button class="btn btn-outline-primary edit-invoice-btn" data-id="<?= $inv['id'] ?>" data-type="<?= htmlspecialchars($inv['type']) ?>" data-json="<?= htmlspecialchars($invoiceJson) ?>" data-bs-toggle="modal" data-bs-target="#editInvoiceModal" title="Edit"><i class="bi bi-pencil"></i></button>
|
||||
<?php if ($inv['status'] !== 'paid'): ?>
|
||||
<button class="btn btn-outline-success pay-invoice-btn" data-id="<?= $inv['id'] ?>" data-total="<?= $inv['total_with_vat'] ?>" data-paid="<?= $inv['paid_amount'] ?>" data-bs-toggle="modal" data-bs-target="#payInvoiceModal" title="Payment"><i class="bi bi-cash-coin"></i></button>
|
||||
<?php endif; ?>
|
||||
<button class="btn btn-outline-secondary print-a4-btn" data-json="<?= htmlspecialchars(json_encode($inv)) ?>" title="Print A4 Invoice"><i class="bi bi-printer"></i></button>
|
||||
<button class="btn btn-outline-secondary print-a4-btn" data-json="<?= htmlspecialchars($invoiceJson) ?>" title="Print A4 Invoice"><i class="bi bi-printer"></i></button>
|
||||
<form method="POST" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this invoice?')">
|
||||
<input type="hidden" name="id" value="<?= $inv['id'] ?>">
|
||||
<button type="submit" name="delete_invoice" class="btn btn-outline-danger" title="Delete"><i class="bi bi-trash"></i></button>
|
||||
|
||||
1066
tmp_inline_script.js
Normal file
1066
tmp_inline_script.js
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user