39728-vm/edit_online_order.php
Flatlogic Bot 5f4a0b383c update pos
2026-04-20 15:36:59 +00:00

572 lines
21 KiB
PHP

<?php
require_once __DIR__ . '/includes/app.php';
$user = require_permission('sales', 'show'); // Same permission as online_orders.php
$editOrderId = (int)($_GET['id'] ?? 0);
$editOrder = null;
if ($editOrderId > 0) {
$stmt = db()->prepare('SELECT * FROM online_orders WHERE id = :id');
$stmt->execute([':id' => $editOrderId]);
$editOrder = $stmt->fetch();
}
if (!$editOrder) {
die(tr('الطلب غير موجود.', 'Order not found.'));
}
$pageTitle = tr('تعديل طلب', 'Edit Order') . ' #' . $editOrderId;
$activeNav = 'online_orders';
$error = '';
$catalog = catalog();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$customerName = trim((string) ($_POST['customer_name'] ?? ''));
$customerPhone = trim((string) ($_POST['customer_phone'] ?? ''));
$customerAddress = trim((string) ($_POST['customer_address'] ?? ''));
$saleStatus = trim((string) ($_POST['sale_status'] ?? 'pending'));
$cartJson = (string) ($_POST['cart_json'] ?? '[]');
$items = json_decode($cartJson, true);
if ($customerName === '' || $customerPhone === '' || $customerAddress === '') {
$error = tr('الرجاء تعبئة بيانات العميل الأساسية.', 'Please fill the main customer details.');
} elseif (!is_array($items) || $items === []) {
$error = tr('أضف صنفاً واحداً على الأقل إلى الطلب.', 'Add at least one item to the order.');
} else {
$normalized = [];
$subtotal = 0.0;
$totalVat = 0.0;
foreach ($items as $item) {
$sku = (string) ($item['sku'] ?? '');
$qty = (int) ($item['qty'] ?? 0);
if (!isset($catalog[$sku]) || $qty < 1) {
continue; // if sku doesn't exist in catalog or qty invalid
}
$product = $catalog[$sku];
$price = (float) $product['price'];
$lineTotal = $price * $qty;
$vatPercent = (float) ($product['vat'] ?? 0);
$itemVat = $lineTotal * ($vatPercent / 100);
$totalVat += $itemVat;
$normalized[] = [
'id' => $product['id'] ?? 0,
'sku' => $sku,
'name' => current_lang() === 'ar' ? $product['name_ar'] : $product['name_en'],
'name_ar' => $product['name_ar'],
'name_en' => $product['name_en'],
'qty' => $qty,
'price' => $price,
'line_total' => $lineTotal,
'vat_percent' => $vatPercent,
'vat_amount' => $itemVat
];
$subtotal += $lineTotal;
}
if ($normalized === []) {
$error = tr('الطلب غير صالح بعد التحقق من الأصناف.', 'The order is invalid after product validation.');
} else {
$stmt = db()->prepare('UPDATE online_orders SET
customer_name = :customer_name,
customer_phone = :customer_phone,
customer_address = :customer_address,
items_json = :items_json,
subtotal = :subtotal,
vat_amount = :vat_amount,
total_amount = :total_amount,
status = :status
WHERE id = :id');
$stmt->execute([
':customer_name' => $customerName,
':customer_phone' => $customerPhone,
':customer_address' => $customerAddress,
':items_json' => json_encode($normalized, JSON_UNESCAPED_UNICODE),
':subtotal' => $subtotal,
':vat_amount' => $totalVat,
':total_amount' => $subtotal + $totalVat,
':status' => $saleStatus,
':id' => $editOrderId,
]);
set_flash('success', tr('تم تحديث الطلب بنجاح.', 'Order updated successfully.'));
redirect_to('online_orders.php');
}
}
}
require __DIR__ . '/includes/header.php';
?>
<style>
.smart-form-card {
background: #fff;
border-radius: 12px;
box-shadow: 0 4px 15px rgba(0,0,0,0.05);
border: 1px solid #edf2f9;
margin-bottom: 2rem;
}
.smart-form-header {
padding: 1.5rem 2rem;
border-bottom: 1px solid #edf2f9;
background-color: #fcfdfd;
border-radius: 12px 12px 0 0;
}
.smart-form-body {
padding: 2rem;
}
.section-title {
font-size: 1.1rem;
font-weight: 600;
color: #2c3e50;
margin-bottom: 1.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
}
.form-label {
font-weight: 500;
color: #495057;
margin-bottom: 0.4rem;
}
.custom-input {
border: 1px solid #ced4da;
border-radius: 8px;
padding: 0.6rem 1rem;
font-size: 0.95rem;
transition: all 0.2s ease-in-out;
}
.custom-input:focus {
border-color: #3b82f6;
box-shadow: 0 0 0 0.25rem rgba(59, 130, 246, 0.25);
}
.search-wrapper {
position: relative;
max-width: 600px;
margin-bottom: 2rem;
}
.search-icon {
position: absolute;
top: 50%;
left: 1rem;
transform: translateY(-50%);
color: #6c757d;
}
[dir="rtl"] .search-icon {
left: auto;
right: 1rem;
}
.search-input {
padding-left: 2.5rem;
}
[dir="rtl"] .search-input {
padding-left: 1rem;
padding-right: 2.5rem;
}
.item-search-dropdown {
position: absolute;
top: 100%;
left: 0;
right: 0;
background: #fff;
border-radius: 8px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
z-index: 1000;
max-height: 300px;
overflow-y: auto;
display: none;
border: 1px solid #edf2f9;
margin-top: 0.5rem;
}
.item-search-dropdown.show { display: block; }
.search-item-row {
padding: 0.75rem 1rem;
cursor: pointer;
border-bottom: 1px solid #edf2f9;
transition: background 0.15s;
}
.search-item-row:hover { background: #f8f9fa; }
.search-item-row:last-child { border-bottom: none; }
.table-modern {
width: 100%;
border-collapse: separate;
border-spacing: 0;
border: 1px solid #edf2f9;
border-radius: 8px;
overflow: hidden;
}
.table-modern th {
background: #f8f9fa;
padding: 1rem;
font-weight: 600;
color: #495057;
border-bottom: 1px solid #edf2f9;
font-size: 0.9rem;
}
.table-modern td {
padding: 1rem;
vertical-align: middle;
border-bottom: 1px solid #edf2f9;
}
.table-modern tr:last-child td {
border-bottom: none;
}
.qty-control {
width: 80px;
text-align: center;
border: 1px solid #ced4da;
border-radius: 6px;
padding: 0.4rem;
}
.btn-remove {
color: #dc3545;
background: rgba(220, 53, 69, 0.1);
border: none;
width: 32px;
height: 32px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.btn-remove:hover {
background: #dc3545;
color: #fff;
}
.totals-box {
background: #f8f9fa;
border-radius: 8px;
padding: 1.5rem;
border: 1px solid #edf2f9;
}
.totals-row {
display: flex;
justify-content: space-between;
margin-bottom: 0.75rem;
color: #495057;
}
.totals-row.grand-total {
margin-top: 1rem;
padding-top: 1rem;
border-top: 1px solid #dee2e6;
font-size: 1.25rem;
font-weight: 700;
color: #212529;
margin-bottom: 0;
}
</style>
<div class="container-fluid py-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h3 class="fw-bold mb-0 text-dark"><?= h($pageTitle) ?></h3>
<a href="online_orders.php" class="btn btn-outline-secondary btn-sm">
<i class="bi bi-arrow-left"></i> <?= h(tr('عودة للطلبات', 'Back to Orders')) ?>
</a>
</div>
<?php if ($error !== ''): ?>
<div class="alert alert-danger rounded-3 shadow-sm mb-4"><i class="bi bi-exclamation-triangle-fill me-2"></i><?= h($error) ?></div>
<?php endif; ?>
<form method="post" id="smart-sale-form">
<input type="hidden" name="cart_json" id="cart_json" value="[]">
<div class="row">
<div class="col-lg-8">
<!-- Items Section -->
<div class="smart-form-card">
<div class="smart-form-header">
<div class="section-title mb-0">
<i class="bi bi-cart-plus text-primary"></i> <?= h(tr('عناصر الطلب', 'Order Items')) ?>
</div>
</div>
<div class="smart-form-body">
<!-- Search Bar -->
<div class="search-wrapper">
<i class="bi bi-search search-icon"></i>
<input type="text" id="itemSearchInput" class="form-control custom-input search-input form-control-lg" placeholder="<?= h(tr('ابحث بالاسم أو الباركود...', 'Search by name or barcode...')) ?>" autocomplete="off">
<div id="itemDropdown" class="item-search-dropdown"></div>
</div>
<!-- Table -->
<div class="table-responsive">
<table class="table-modern" id="invoiceTable">
<thead>
<tr>
<th width="45%"><?= h(tr('المنتج', 'Product')) ?></th>
<th width="15%" class="text-center"><?= h(tr('السعر', 'Price')) ?></th>
<th width="15%" class="text-center"><?= h(tr('الكمية', 'Qty')) ?></th>
<th width="20%" class="text-center"><?= h(tr('الإجمالي', 'Total')) ?></th>
<th width="5%"></th>
</tr>
</thead>
<tbody id="invoiceLines">
<tr id="emptyInvoiceRow">
<td colspan="5" class="text-center py-5 text-muted">
<i class="bi bi-inbox fs-1 d-block mb-2 text-light"></i>
<?= h(tr('لم يتم إضافة أي منتجات بعد.', 'No products added yet.')) ?>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-lg-4">
<!-- Settings Section -->
<div class="smart-form-card">
<div class="smart-form-header">
<div class="section-title mb-0">
<i class="bi bi-person text-primary"></i> <?= h(tr('بيانات العميل', 'Customer Details')) ?>
</div>
</div>
<div class="smart-form-body">
<div class="mb-3">
<label class="form-label"><?= h(tr('اسم العميل', 'Customer Name')) ?></label>
<input type="text" name="customer_name" class="form-control custom-input" required value="<?= h($editOrder['customer_name'] ?? '' ) ?>">
</div>
<div class="mb-3">
<label class="form-label"><?= h(tr('رقم الهاتف', 'Phone Number')) ?></label>
<input type="text" name="customer_phone" class="form-control custom-input" required value="<?= h($editOrder['customer_phone'] ?? '' ) ?>">
</div>
<div class="mb-3">
<label class="form-label"><?= h(tr('العنوان', 'Address')) ?></label>
<textarea name="customer_address" class="form-control custom-input" rows="3" required><?= h($editOrder['customer_address'] ?? '' ) ?></textarea>
</div>
<div class="mb-4">
<label class="form-label"><?= h(tr('حالة الطلب', 'Order Status')) ?></label>
<select class="form-select custom-input" name="sale_status">
<option value="pending" <?= $editOrder['status'] === 'pending' ? 'selected' : '' ?>><?= h(tr('قيد الانتظار', 'Pending')) ?></option>
<option value="accepted" <?= $editOrder['status'] === 'accepted' ? 'selected' : '' ?>><?= h(tr('مقبول', 'Accepted')) ?></option>
<option value="completed" <?= $editOrder['status'] === 'completed' ? 'selected' : '' ?>><?= h(tr('مكتمل', 'Completed')) ?></option>
<option value="rejected" <?= $editOrder['status'] === 'rejected' ? 'selected' : '' ?>><?= h(tr('مرفوض', 'Rejected')) ?></option>
</select>
</div>
<!-- Summary -->
<div class="totals-box mb-4">
<div class="totals-row">
<span><?= h(tr('المجموع الفرعي', 'Subtotal')) ?></span>
<span id="displaySubtotal" class="fw-medium">0.000</span>
</div>
<div class="totals-row">
<span><?= h(tr('الضريبة (مضافة)', 'VAT (Added)')) ?></span>
<span id="displayVat" class="text-muted">0.000</span>
</div>
<div class="totals-row grand-total">
<span><?= h(tr('الإجمالي', 'Total')) ?></span>
<span id="displayTotal" class="text-primary">0.000 <?= h(tr('ر.ع', 'OMR')) ?></span>
</div>
</div>
<button type="submit" class="btn btn-primary w-100 py-2 fs-5 rounded-3 shadow-sm">
<i class="bi bi-check-circle me-1"></i> <?= h(tr('حفظ التعديلات', 'Save Changes')) ?>
</button>
</div>
</div>
</div>
</div>
</form>
</div>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
const catalogData = <?= json_encode($catalog, JSON_UNESCAPED_UNICODE) ?>;
const catalogArray = Object.values(catalogData);
let invoiceItems = {};
// Prepopulate from editOrder
const initialItemsJson = <?= empty($editOrder['items_json']) ? '[]' : $editOrder['items_json'] ?>;
initialItemsJson.forEach(item => {
// Merge existing item with catalog data to keep pricing accurate based on current catalog if needed
// or respect saved price depending on logic. Here we respect the saved price, but fall back to catalog.
const product = catalogData[item.sku];
invoiceItems[item.sku] = {
sku: item.sku,
name: item.name || ('<?= current_lang() ?>' === 'ar' ? (product ? product.name_ar : '') : (product ? product.name_en : '')),
price: parseFloat(item.price || (product ? product.price : 0)),
qty: parseInt(item.qty),
vat: parseFloat(item.vat_percent || (product ? product.vat : 0))
};
});
const searchInput = document.getElementById('itemSearchInput');
const dropdown = document.getElementById('itemDropdown');
const tbody = document.getElementById('invoiceLines');
const emptyRow = document.getElementById('emptyInvoiceRow');
const cartJson = document.getElementById('cart_json');
const currencySuffix = ' <?= h(tr('ر.ع', 'OMR')) ?>';
// Search logic
searchInput.addEventListener('input', function() {
const q = this.value.toLowerCase().trim();
dropdown.innerHTML = '';
if (q === '') {
dropdown.classList.remove('show');
return;
}
const matches = catalogArray.filter(item => {
const nameAr = (item.name_ar || '').toLowerCase();
const nameEn = (item.name_en || '').toLowerCase();
const sku = (item.sku || '').toLowerCase();
return nameAr.includes(q) || nameEn.includes(q) || sku.includes(q);
}).slice(0, 6);
if (matches.length > 0) {
matches.forEach(item => {
const div = document.createElement('div');
div.className = 'search-item-row d-flex justify-content-between align-items-center';
const name = '<?= current_lang() ?>' === 'ar' ? item.name_ar : item.name_en;
div.innerHTML = `
<div>
<div class="fw-medium text-dark">${name}</div>
<div class="text-muted small">SKU: ${item.sku}</div>
</div>
<div class="fw-semibold text-primary">${parseFloat(item.price).toFixed(3)}</div>
`;
div.onclick = () => {
addItemToInvoice(item.sku);
searchInput.value = '';
dropdown.classList.remove('show');
searchInput.focus();
};
dropdown.appendChild(div);
});
dropdown.classList.add('show');
} else {
dropdown.classList.remove('show');
}
});
// Barcode scanner integration on enter
searchInput.addEventListener('keypress', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
const q = this.value.trim();
if(q === '') return;
const match = catalogArray.find(item => item.sku === q);
if (match) {
addItemToInvoice(match.sku);
searchInput.value = '';
dropdown.classList.remove('show');
}
}
});
document.addEventListener('click', function(e) {
if (!searchInput.contains(e.target) && !dropdown.contains(e.target)) {
dropdown.classList.remove('show');
}
});
function addItemToInvoice(sku) {
if (invoiceItems[sku]) {
invoiceItems[sku].qty += 1;
} else {
const item = catalogData[sku];
invoiceItems[sku] = {
sku: sku,
name: '<?= current_lang() ?>' === 'ar' ? item.name_ar : item.name_en,
price: parseFloat(item.price),
qty: 1,
vat: parseFloat(item.vat || 0)
};
}
renderInvoice();
}
function changeQty(sku, newQty) {
const qty = parseInt(newQty);
if (isNaN(qty) || qty < 1) {
delete invoiceItems[sku];
} else {
invoiceItems[sku].qty = qty;
}
renderInvoice();
}
function removeItem(sku) {
delete invoiceItems[sku];
renderInvoice();
}
function renderInvoice() {
const skus = Object.keys(invoiceItems);
if (skus.length === 0) {
tbody.innerHTML = '';
tbody.appendChild(emptyRow);
updateTotals(0, 0);
cartJson.value = '[]';
return;
}
tbody.innerHTML = '';
let totalAmount = 0;
let totalVat = 0;
const cartData = [];
skus.forEach(sku => {
const item = invoiceItems[sku];
const lineTotal = item.qty * item.price;
const vatPercent = item.vat || 0;
const itemVat = lineTotal * (vatPercent / 100);
totalVat += itemVat;
totalAmount += lineTotal;
cartData.push({ sku: item.sku, qty: item.qty });
const tr = document.createElement('tr');
tr.innerHTML = `
<td>
<div class="fw-medium text-dark">${item.name}</div>
<div class="text-muted small">SKU: ${item.sku}</div>
</td>
<td class="text-center text-muted align-middle">${item.price.toFixed(3)}</td>
<td class="text-center align-middle">
<input type="number" class="qty-control mx-auto fw-medium" min="1" value="${item.qty}" onchange="changeQty('${sku}', this.value)" onkeyup="if(event.key==='Enter') changeQty('${sku}', this.value)">
</td>
<td class="text-center fw-semibold text-dark align-middle">${lineTotal.toFixed(3)}</td>
<td class="text-center align-middle">
<button type="button" class="btn-remove mx-auto" onclick="removeItem('${sku}')" title="<?= h(tr('إزالة', 'Remove')) ?>">
<i class="bi bi-trash"></i>
</button>
</td>
`;
tbody.appendChild(tr);
});
updateTotals(totalAmount, totalVat);
cartJson.value = JSON.stringify(cartData);
}
function updateTotals(total, vat) {
const subtotal = total;
const finalTotal = subtotal + vat;
document.getElementById('displaySubtotal').innerText = subtotal.toFixed(3);
document.getElementById('displayVat').innerText = vat.toFixed(3);
document.getElementById('displayTotal').innerText = finalTotal.toFixed(3) + currencySuffix;
}
renderInvoice();
// Intercept form submission to check if items exist
document.getElementById('smart-sale-form').addEventListener('submit', function(e) {
if (Object.keys(invoiceItems).length === 0) {
e.preventDefault();
Swal.fire({icon: 'warning', text: '<?= h(tr('الرجاء إضافة أصناف للفاتورة أولاً.', 'Please add items to the invoice first.')) ?>'});
}
});
</script>
<?php require __DIR__ . '/includes/footer.php'; ?>