index no end changes
This commit is contained in:
parent
c23bc66b9c
commit
0b7a05f3a8
22
index.php
22
index.php
@ -8827,11 +8827,15 @@ runtime_debug_mark('page:rendering', ['page' => (string)$page]);
|
||||
<div class="btn-group btn-group-sm">
|
||||
<button class="btn btn-outline-info view-quotation-btn" data-json="<?= htmlspecialchars(json_encode($q)) ?>" title="View"><i class="bi bi-eye"></i></button>
|
||||
<button class="btn btn-outline-secondary" onclick="window.viewAndPrintQuotation(<?= htmlspecialchars(json_encode($q)) ?>)" title="Print"><i class="bi bi-printer"></i></button>
|
||||
<button class="btn btn-outline-primary edit-quotation-btn" data-json="<?= htmlspecialchars(json_encode($q)) ?>" data-bs-toggle="modal" data-bs-target="#editQuotationModal" title="Edit"><i class="bi bi-pencil-square"></i></button>
|
||||
<button class="btn btn-outline-primary edit-quotation-btn" data-id="<?= $q['id'] ?>" data-json="<?= htmlspecialchars(json_encode($q)) ?>" data-bs-toggle="modal" data-bs-target="#editQuotationModal" title="Edit"><i class="bi bi-pencil-square"></i></button>
|
||||
<?php if ($q['status'] === 'pending'): ?>
|
||||
<button class="btn btn-outline-success convert-quotation-btn" data-id="<?= $q['id'] ?>" title="Convert to Invoice"><i class="bi bi-receipt"></i></button>
|
||||
<?php endif; ?>
|
||||
<button class="btn btn-outline-danger" onclick="if(confirm('Delete this quotation?')) { const f = document.createElement('form'); f.method='POST'; f.innerHTML='<input type=hidden name=delete_quotation><input type=hidden name=id value=<?= $q['id'] ?>>'; document.body.appendChild(f); f.submit(); }" title="Delete"><i class="bi bi-trash"></i></button>
|
||||
<form method="POST" class="d-inline js-swal-confirm-form" data-confirm-title="Delete this quotation?" data-confirm-text="This quotation will be permanently removed." data-confirm-button="Yes, delete it" data-cancel-button="Keep it">
|
||||
<input type="hidden" name="delete_quotation" value="1">
|
||||
<input type="hidden" name="id" value="<?= $q['id'] ?>">
|
||||
<button type="submit" class="btn btn-outline-danger" title="Delete"><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -8950,9 +8954,13 @@ runtime_debug_mark('page:rendering', ['page' => (string)$page]);
|
||||
<button class="btn btn-outline-info view-lpo-btn" data-json="<?= htmlspecialchars(json_encode($q)) ?>" title="View"><i class="bi bi-eye"></i></button>
|
||||
<?php if ($q['status'] !== 'converted'): ?>
|
||||
<button class="btn btn-outline-success" onclick="if(confirm('Convert this LPO to Purchase Invoice?')) { const f = document.createElement('form'); f.method='POST'; f.innerHTML='<input type=hidden name=convert_lpo_to_purchase><input type=hidden name=lpo_id value=<?= $q['id'] ?>>'; document.body.appendChild(f); f.submit(); }" title="Convert to Purchase"><i class="bi bi-arrow-repeat"></i></button>
|
||||
<button class="btn btn-outline-primary edit-lpo-btn" data-json="<?= htmlspecialchars(json_encode($q)) ?>" data-bs-toggle="modal" data-bs-target="#editLpoModal" title="Edit"><i class="bi bi-pencil-square"></i></button>
|
||||
<button class="btn btn-outline-primary edit-lpo-btn" data-id="<?= $q['id'] ?>" data-json="<?= htmlspecialchars(json_encode($q)) ?>" data-bs-toggle="modal" data-bs-target="#editLpoModal" title="Edit"><i class="bi bi-pencil-square"></i></button>
|
||||
<?php endif; ?>
|
||||
<button class="btn btn-outline-danger" onclick="if(confirm('Delete this LPO?')) { const f = document.createElement('form'); f.method='POST'; f.innerHTML='<input type=hidden name=delete_lpo><input type=hidden name=id value=<?= $q['id'] ?>>'; document.body.appendChild(f); f.submit(); }" title="Delete"><i class="bi bi-trash"></i></button>
|
||||
<form method="POST" class="d-inline js-swal-confirm-form" data-confirm-title="Delete this LPO?" data-confirm-text="This LPO will be permanently removed." data-confirm-button="Yes, delete it" data-cancel-button="Keep it">
|
||||
<input type="hidden" name="delete_lpo" value="1">
|
||||
<input type="hidden" name="id" value="<?= $q['id'] ?>">
|
||||
<button type="submit" class="btn btn-outline-danger" title="Delete"><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -12000,7 +12008,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
});
|
||||
|
||||
<?php if (in_array($page, ['sales', 'purchases', 'lpos', 'quotations'], true)): ?>
|
||||
<?php runtime_debug_require('pages/sales_purchases_invoice_form_helpers.php', ['phase' => 'script', 'page' => (string)$page]); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($page === 'lpos' || $page === 'quotations'): ?>
|
||||
<?php runtime_debug_require('pages/lpo_quotation_script.php', ['phase' => 'script', 'page' => (string)$page]); ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($page === 'sales' || $page === 'purchases'): ?>
|
||||
<?php runtime_debug_require('pages/sales_purchases_page_script.php', ['phase' => 'script', 'page' => (string)$page]); ?>
|
||||
|
||||
@ -2,36 +2,77 @@
|
||||
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');
|
||||
|
||||
|
||||
const parseLpoQuotationButtonPayload = (btn) => {
|
||||
if (!btn || !btn.dataset || !btn.dataset.json) return {};
|
||||
try {
|
||||
return JSON.parse(btn.dataset.json);
|
||||
} catch (error) {
|
||||
console.warn('Failed to parse LPO/Quotation payload from button data.', error);
|
||||
return {};
|
||||
}
|
||||
};
|
||||
|
||||
const renderExistingDocumentItems = (items, tableBodyId, grandTotalId, subtotalId, totalVatId) => {
|
||||
const tableBody = document.getElementById(tableBodyId);
|
||||
const grandTotalEl = document.getElementById(grandTotalId);
|
||||
const subtotalEl = document.getElementById(subtotalId);
|
||||
const totalVatEl = document.getElementById(totalVatId);
|
||||
|
||||
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
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
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 data = parseLpoQuotationButtonPayload(this);
|
||||
if (Object.keys(data).length === 0) return;
|
||||
|
||||
const supplierSelect = document.getElementById('edit_lpo_supplier_id');
|
||||
supplierSelect.value = data.supplier_id;
|
||||
if (window.jQuery && $(supplierSelect).data('select2')) {
|
||||
$(supplierSelect).trigger('change');
|
||||
const supplierId = data.supplier_id ?? '';
|
||||
const supplierLabel = data.supplier_name || '';
|
||||
|
||||
invoiceEnsureSelectOption('edit_lpo_supplier_id', supplierId, supplierLabel);
|
||||
|
||||
const lpoIdInput = document.getElementById('edit_lpo_id');
|
||||
const lpoDateInput = document.getElementById('edit_lpo_date');
|
||||
const deliveryDateInput = document.getElementById('edit_lpo_delivery_date');
|
||||
const statusSelect = document.getElementById('edit_lpo_status');
|
||||
const termsInput = document.getElementById('edit_lpo_terms');
|
||||
|
||||
if (lpoIdInput) lpoIdInput.value = data.id || '';
|
||||
if (supplierSelect) {
|
||||
invoiceSetBlankSelectOptionLabel(supplierSelect, (supplierId === '' && supplierLabel) ? supplierLabel : '---');
|
||||
supplierSelect.value = supplierId;
|
||||
invoiceSyncSelect2Value(supplierSelect);
|
||||
}
|
||||
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 });
|
||||
});
|
||||
if (lpoDateInput) lpoDateInput.value = data.lpo_date || '';
|
||||
if (deliveryDateInput) deliveryDateInput.value = data.delivery_date || '';
|
||||
if (statusSelect) statusSelect.value = data.status || 'pending';
|
||||
if (termsInput) termsInput.value = data.terms_conditions || '';
|
||||
|
||||
renderExistingDocumentItems(data.items || [], 'editLpoItemsTableBody', 'edit_lpo_grand_display', 'edit_lpo_subtotal_display', 'edit_lpo_vat_display');
|
||||
});
|
||||
});
|
||||
|
||||
@ -182,29 +223,31 @@
|
||||
|
||||
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 });
|
||||
});
|
||||
const data = parseLpoQuotationButtonPayload(this);
|
||||
if (Object.keys(data).length === 0) return;
|
||||
|
||||
const customerSelect = document.getElementById('edit_quot_customer_id');
|
||||
const customerId = data.customer_id ?? '';
|
||||
const customerLabel = data.customer_name || data.party_name || '';
|
||||
|
||||
invoiceEnsureSelectOption('edit_quot_customer_id', customerId, customerLabel);
|
||||
|
||||
const quotationIdInput = document.getElementById('edit_quotation_id');
|
||||
const quotationDateInput = document.getElementById('edit_quot_date');
|
||||
const validUntilInput = document.getElementById('edit_quot_valid');
|
||||
const statusSelect = document.getElementById('edit_quot_status');
|
||||
|
||||
if (quotationIdInput) quotationIdInput.value = data.id || '';
|
||||
if (customerSelect) {
|
||||
invoiceSetBlankSelectOptionLabel(customerSelect, (customerId === '' && customerLabel) ? customerLabel : '---');
|
||||
customerSelect.value = customerId;
|
||||
invoiceSyncSelect2Value(customerSelect);
|
||||
}
|
||||
if (quotationDateInput) quotationDateInput.value = data.quotation_date || '';
|
||||
if (validUntilInput) validUntilInput.value = data.valid_until || '';
|
||||
if (statusSelect) statusSelect.value = data.status || 'pending';
|
||||
|
||||
renderExistingDocumentItems(data.items || [], 'editQuotItemsTableBody', 'edit_quot_grand_display', 'edit_quot_subtotal_display', 'edit_quot_vat_display');
|
||||
});
|
||||
});
|
||||
|
||||
@ -355,10 +398,8 @@
|
||||
editBtn.className = 'btn btn-primary';
|
||||
editBtn.innerHTML = '<i class="bi bi-pencil-square"></i> <span data-en="Edit" data-ar="تعديل">Edit</span>';
|
||||
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}']`);
|
||||
const originalEditBtn = document.querySelector(`.edit-quotation-btn[data-id="${data.id}"]`);
|
||||
if (originalEditBtn) originalEditBtn.click();
|
||||
};
|
||||
actionButtons.appendChild(editBtn);
|
||||
|
||||
@ -9,34 +9,6 @@
|
||||
}
|
||||
};
|
||||
|
||||
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';
|
||||
@ -92,13 +64,13 @@
|
||||
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);
|
||||
invoiceEnsureSelectOption('edit_customer_id', partyId, partyLabel);
|
||||
|
||||
if (invoiceIdInput) invoiceIdInput.value = data.id || '';
|
||||
if (partySelect) {
|
||||
setBlankSelectOptionLabel(partySelect, (partyId === '' && partyLabel) ? partyLabel : '---');
|
||||
invoiceSetBlankSelectOptionLabel(partySelect, (partyId === '' && partyLabel) ? partyLabel : '---');
|
||||
partySelect.value = partyId;
|
||||
syncSelect2Value(partySelect);
|
||||
invoiceSyncSelect2Value(partySelect);
|
||||
}
|
||||
if (invoiceDateInput) invoiceDateInput.value = data.invoice_date || '';
|
||||
if (dueDateInput) dueDateInput.value = data.due_date || '';
|
||||
|
||||
@ -53,6 +53,100 @@
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
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 = '';
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
<?php
|
||||
// Sales/Purchases page-specific JavaScript bundle extracted from index.php.
|
||||
require __DIR__ . '/sales_purchases_payment_receipt_script.php';
|
||||
require __DIR__ . '/sales_purchases_invoice_form_helpers.php';
|
||||
require __DIR__ . '/sales_purchases_print_script.php';
|
||||
require __DIR__ . '/sales_purchases_invoice_actions_script.php';
|
||||
|
||||
@ -173,9 +173,10 @@
|
||||
<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($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?')">
|
||||
<form method="POST" class="d-inline js-swal-confirm-form" data-confirm-title="<?= htmlspecialchars(($lang ?? 'en') === 'ar' ? 'هل تريد حذف هذه الفاتورة؟' : 'Delete this invoice?', ENT_QUOTES) ?>" data-confirm-text="<?= htmlspecialchars(($lang ?? 'en') === 'ar' ? 'سيتم حذف هذه الفاتورة نهائياً.' : 'This invoice will be permanently removed.', ENT_QUOTES) ?>" data-confirm-button="<?= htmlspecialchars(($lang ?? 'en') === 'ar' ? 'نعم، احذفها' : 'Yes, delete it', ENT_QUOTES) ?>" data-cancel-button="<?= htmlspecialchars(($lang ?? 'en') === 'ar' ? 'إلغاء' : 'Keep it', ENT_QUOTES) ?>">
|
||||
<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>
|
||||
<input type="hidden" name="delete_invoice" value="1">
|
||||
<button type="submit" class="btn btn-outline-danger" title="Delete"><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user