339 lines
16 KiB
PHP
339 lines
16 KiB
PHP
<?php
|
|
$title = 'orders';
|
|
require_once __DIR__ . '/includes/header.php';
|
|
|
|
$current_role = $_SESSION['role'] ?? 'cashier';
|
|
$session_branch_id = $_SESSION['branch_id'] ?? 'all';
|
|
|
|
// Filters
|
|
$status_filter = $_GET['status'] ?? '';
|
|
$payment_filter = $_GET['payment_status'] ?? '';
|
|
// Default to session branch if no explicit GET filter
|
|
$branch_filter = $_GET['branch_id'] ?? $session_branch_id;
|
|
$from_date = $_GET['from_date'] ?? '';
|
|
$to_date = $_GET['to_date'] ?? '';
|
|
$search = $_GET['search'] ?? '';
|
|
|
|
// Pagination
|
|
$page = isset($_GET['page']) ? (int)$_GET['page'] : 1;
|
|
if ($page < 1) $page = 1;
|
|
$limit = 10;
|
|
$offset = ($page - 1) * $limit;
|
|
|
|
$sql_base = " FROM orders o
|
|
LEFT JOIN customers c ON o.customer_id = c.id
|
|
LEFT JOIN branches b ON o.branch_id = b.id
|
|
WHERE 1=1";
|
|
$params = [];
|
|
|
|
// Branch restriction
|
|
if ($branch_filter !== 'all') {
|
|
$sql_base .= " AND o.branch_id = ?";
|
|
$params[] = $branch_filter;
|
|
}
|
|
|
|
if ($status_filter) {
|
|
$sql_base .= " AND o.status = ?";
|
|
$params[] = $status_filter;
|
|
}
|
|
if ($payment_filter) {
|
|
$sql_base .= " AND o.payment_status = ?";
|
|
$params[] = $payment_filter;
|
|
}
|
|
if ($from_date) {
|
|
$sql_base .= " AND DATE(o.created_at) >= ?";
|
|
$params[] = $from_date;
|
|
}
|
|
if ($to_date) {
|
|
$sql_base .= " AND DATE(o.created_at) <= ?";
|
|
$params[] = $to_date;
|
|
}
|
|
if ($search) {
|
|
$sql_base .= " AND (o.order_number LIKE ? OR c.phone LIKE ? OR c.name_en LIKE ? OR c.name_ar LIKE ?)";
|
|
$search_param = "%$search%";
|
|
$params[] = $search_param;
|
|
$params[] = $search_param;
|
|
$params[] = $search_param;
|
|
$params[] = $search_param;
|
|
}
|
|
|
|
// Total count for pagination
|
|
$count_sql = "SELECT COUNT(*) " . $sql_base;
|
|
$stmt_count = db()->prepare($count_sql);
|
|
$stmt_count->execute($params);
|
|
$total_items = $stmt_count->fetchColumn();
|
|
$total_pages = ceil($total_items / $limit);
|
|
|
|
// Final data query
|
|
$sql = "SELECT o.*, c.name_en as customer_name_en, c.name_ar as customer_name_ar, c.phone as customer_phone,
|
|
b.name_en as branch_name_en, b.name_ar as branch_name_ar " . $sql_base . " ORDER BY o.created_at DESC LIMIT $limit OFFSET $offset";
|
|
$stmt = db()->prepare($sql);
|
|
$stmt->execute($params);
|
|
$orders = $stmt->fetchAll();
|
|
|
|
$branches = [];
|
|
if ($current_role === 'super_admin') {
|
|
$branches = db()->query("SELECT id, name_en, name_ar FROM branches")->fetchAll();
|
|
}
|
|
|
|
?>
|
|
|
|
<div class="card p-4 border-0 shadow-sm" style="border-radius: 20px;">
|
|
<div class="d-flex flex-wrap justify-content-between align-items-center mb-4">
|
|
<h5 class="fw-bold mb-3 mb-md-0"><?= __('orders_list') ?? 'Orders List' ?></h5>
|
|
<?php if (has_permission('add')): ?>
|
|
<a href="pos.php" class="btn btn-primary px-4" style="border-radius: 12px;">
|
|
<i class="bi bi-plus-lg me-1"></i> <?= __('new_order') ?>
|
|
</a>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<!-- Filters -->
|
|
<form action="" method="GET" class="row g-3 mb-4">
|
|
<div class="col-md-3">
|
|
<input type="text" name="search" class="form-control" placeholder="<?= __('search_placeholder') ?>" value="<?= htmlspecialchars($search) ?>" style="border-radius: 12px;">
|
|
</div>
|
|
<?php if ($current_role === 'super_admin'): ?>
|
|
<div class="col-md-2">
|
|
<select name="branch_id" class="form-select" style="border-radius: 12px;" onchange="this.form.submit()">
|
|
<option value="all"><?= __('all_branches') ?></option>
|
|
<?php foreach($branches as $b): ?>
|
|
<option value="<?= $b['id'] ?>" <?= $branch_filter == $b['id'] ? 'selected' : '' ?>>
|
|
<?= $lang === 'ar' ? ($b['name_ar'] ?: $b['name_en']) : $b['name_en'] ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<?php endif; ?>
|
|
<div class="col-md-2">
|
|
<select name="status" class="form-select" style="border-radius: 12px;" onchange="this.form.submit()">
|
|
<option value=""><?= __('all_status') ?></option>
|
|
<option value="received" <?= $status_filter === 'received' ? 'selected' : '' ?>><?= __('received') ?></option>
|
|
<option value="processing" <?= $status_filter === 'processing' ? 'selected' : '' ?>><?= __('processing') ?></option>
|
|
<option value="ready" <?= $status_filter === 'ready' ? 'selected' : '' ?>><?= __('ready') ?></option>
|
|
<option value="delivered" <?= $status_filter === 'delivered' ? 'selected' : '' ?>><?= __('delivered') ?></option>
|
|
<option value="cancelled" <?= $status_filter === 'cancelled' ? 'selected' : '' ?>><?= __('cancelled') ?></option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-2">
|
|
<select name="payment_status" class="form-select" style="border-radius: 12px;" onchange="this.form.submit()">
|
|
<option value=""><?= __('all_payments') ?></option>
|
|
<option value="unpaid" <?= $payment_filter === 'unpaid' ? 'selected' : '' ?>><?= __('unpaid') ?></option>
|
|
<option value="partially_paid" <?= $payment_filter === 'partially_paid' ? 'selected' : '' ?>><?= __('partially_paid') ?></option>
|
|
<option value="paid" <?= $payment_filter === 'paid' ? 'selected' : '' ?>><?= __('paid') ?></option>
|
|
</select>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="input-group">
|
|
<input type="date" name="from_date" class="form-control" value="<?= $from_date ?>" style="border-radius: 12px 0 0 12px;">
|
|
<input type="date" name="to_date" class="form-control" value="<?= $to_date ?>" style="border-radius: 0 12px 12px 0;">
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>ID</th>
|
|
<th><?= __('order_number') ?></th>
|
|
<?php if ($current_role === 'super_admin'): ?>
|
|
<th><?= __('branch') ?></th>
|
|
<?php endif; ?>
|
|
<th><?= __('customer') ?></th>
|
|
<th><?= __('subtotal') ?></th>
|
|
<th><?= __('loyalty_discount') ?></th>
|
|
<th><?= __('total') ?></th>
|
|
<th><?= __('status') ?></th>
|
|
<th><?= __('payment_status') ?></th>
|
|
<th><?= __('date') ?></th>
|
|
<th class="text-end"><?= __('actions') ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach($orders as $order): ?>
|
|
<tr>
|
|
<td><?= $order['id'] ?></td>
|
|
<td class="fw-bold"><?= $order['order_number'] ?></td>
|
|
<?php if ($current_role === 'super_admin'): ?>
|
|
<td><span class="small text-muted"><?= $lang === 'ar' ? ($order['branch_name_ar'] ?: $order['branch_name_en']) : $order['branch_name_en'] ?></span></td>
|
|
<?php endif; ?>
|
|
<td>
|
|
<div class="fw-bold"><?= $lang === 'ar' ? ($order['customer_name_ar'] ?: $order['customer_name_en']) : $order['customer_name_en'] ?></div>
|
|
<div class="small text-muted"><?= $order['customer_phone'] ?></div>
|
|
</td>
|
|
<td class="small text-muted"><?= format_amount($order['total_price']) ?></td>
|
|
<td class="text-success small fw-bold"><?= $order['loyalty_discount'] > 0 ? '-' . format_amount($order['loyalty_discount']) : '-' ?></td>
|
|
<td class="fw-bold text-primary"><?= format_amount($order['total_price'] - $order['loyalty_discount']) ?></td>
|
|
<td><span class="badge bg-<?= getStatusColor($order['status']) ?> rounded-pill px-3 py-2"><?= __($order['status']) ?></span></td>
|
|
<td><span class="badge bg-<?= getPaymentStatusColor($order['payment_status']) ?> rounded-pill px-3 py-2 text-white"><?= __($order['payment_status']) ?></span></td>
|
|
<td class="small"><?= date('d/m/Y H:i', strtotime($order['created_at'])) ?></td>
|
|
<td class="text-end">
|
|
<div class="d-flex gap-1 justify-content-end">
|
|
<a href="order_details.php?id=<?= $order['id'] ?>" class="btn btn-sm btn-light border-0 p-2 text-primary" title="<?= __('view') ?? 'View' ?>" style="border-radius: 8px;">
|
|
<i class="bi bi-eye-fill"></i>
|
|
</a>
|
|
<?php if (has_permission('edit')): ?>
|
|
<button class="btn btn-sm btn-light border-0 p-2 text-info status-change-btn" data-id="<?= $order['id'] ?>" data-status="<?= $order['status'] ?>" title="<?= __('update_status') ?? 'Update Status' ?>" style="border-radius: 8px;">
|
|
<i class="bi bi-arrow-repeat"></i>
|
|
</button>
|
|
<a href="pos.php?edit_order_id=<?= $order['id'] ?>" class="btn btn-sm btn-light border-0 p-2 text-warning" title="<?= __('edit') ?>" style="border-radius: 8px;">
|
|
<i class="bi bi-pencil-fill"></i>
|
|
</a>
|
|
<?php endif; ?>
|
|
<?php if (has_permission('delete')): ?>
|
|
<button class="btn btn-sm btn-light border-0 p-2 text-danger delete-order-btn" data-id="<?= $order['id'] ?>" title="<?= __('delete') ?>" style="border-radius: 8px;">
|
|
<i class="bi bi-trash-fill"></i>
|
|
</button>
|
|
<?php endif; ?>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
<?php if (empty($orders)): ?>
|
|
<tr>
|
|
<td colspan="<?= $current_role === 'super_admin' ? 11 : 10 ?>" class="text-center py-5 text-muted">
|
|
<i class="bi bi-inbox fs-1 d-block mb-3 opacity-25"></i>
|
|
<?= __('no_orders_found') ?? 'No orders found' ?>
|
|
</td>
|
|
</tr>
|
|
<?php endif; ?>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- Pagination -->
|
|
<?php if ($total_pages > 1): ?>
|
|
<nav class="mt-4">
|
|
<ul class="pagination justify-content-center">
|
|
<li class="page-item <?= $page <= 1 ? 'disabled' : '' ?>">
|
|
<a class="page-link" href="?<?= http_build_query(array_merge($_GET, ['page' => $page - 1])) ?>" style="border-radius: 8px 0 0 8px;"><?= __('previous') ?></a>
|
|
</li>
|
|
<?php for ($i = 1; $i <= $total_pages; $i++): ?>
|
|
<li class="page-item <?= $page == $i ? 'active' : '' ?>">
|
|
<a class="page-link" href="?<?= http_build_query(array_merge($_GET, ['page' => $i])) ?>"><?= $i ?></a>
|
|
</li>
|
|
<?php endfor; ?>
|
|
<li class="page-item <?= $page >= $total_pages ? 'disabled' : '' ?>">
|
|
<a class="page-link" href="?<?= http_build_query(array_merge($_GET, ['page' => $page + 1])) ?>" style="border-radius: 0 8px 8px 0;"><?= __('next') ?></a>
|
|
</li>
|
|
</ul>
|
|
</nav>
|
|
<?php endif; ?>
|
|
</div>
|
|
|
|
<!-- Status Update Modal -->
|
|
<div class="modal fade" id="statusModal" tabindex="-1">
|
|
<div class="modal-dialog modal-dialog-centered">
|
|
<div class="modal-content border-0 rounded-4 shadow-lg">
|
|
<div class="modal-header border-0 pb-0">
|
|
<h5 class="modal-title fw-bold"><?= __('update_status') ?></h5>
|
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
|
</div>
|
|
<div class="modal-body p-4">
|
|
<input type="hidden" id="modalOrderId">
|
|
<div class="row g-2">
|
|
<div class="col-6">
|
|
<button class="btn btn-outline-secondary w-100 py-3 rounded-4 status-opt" data-status="received"><?= __('received') ?></button>
|
|
</div>
|
|
<div class="col-6">
|
|
<button class="btn btn-outline-primary w-100 py-3 rounded-4 status-opt" data-status="processing"><?= __('processing') ?></button>
|
|
</div>
|
|
<div class="col-6">
|
|
<button class="btn btn-outline-success w-100 py-3 rounded-4 status-opt" data-status="ready"><?= __('ready') ?></button>
|
|
</div>
|
|
<div class="col-6">
|
|
<button class="btn btn-outline-dark w-100 py-3 rounded-4 status-opt" data-status="delivered"><?= __('delivered') ?></button>
|
|
</div>
|
|
<div class="col-12 mt-2">
|
|
<button class="btn btn-outline-danger w-100 py-3 rounded-4 status-opt" data-status="cancelled"><?= __('cancelled') ?></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const statusModal = new bootstrap.Modal(document.getElementById('statusModal'));
|
|
|
|
// Status Change
|
|
document.querySelectorAll('.status-change-btn').forEach(btn => {
|
|
btn.onclick = () => {
|
|
const id = btn.dataset.id;
|
|
const currentStatus = btn.dataset.status;
|
|
document.getElementById('modalOrderId').value = id;
|
|
|
|
// Highlight current status
|
|
document.querySelectorAll('.status-opt').forEach(opt => {
|
|
opt.classList.remove('active');
|
|
if (opt.dataset.status === currentStatus) opt.classList.add('active');
|
|
});
|
|
|
|
statusModal.show();
|
|
};
|
|
});
|
|
|
|
document.querySelectorAll('.status-opt').forEach(btn => {
|
|
btn.onclick = async () => {
|
|
const id = document.getElementById('modalOrderId').value;
|
|
const status = btn.dataset.status;
|
|
|
|
try {
|
|
const res = await fetch('api/update_order_status.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ id, status })
|
|
});
|
|
const data = await res.json();
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert(data.error);
|
|
}
|
|
} catch (e) {
|
|
alert('Error updating status');
|
|
}
|
|
};
|
|
});
|
|
|
|
// Delete Order
|
|
document.querySelectorAll('.delete-order-btn').forEach(btn => {
|
|
btn.onclick = async () => {
|
|
if (confirm('<?= __('are_you_sure') ?>')) {
|
|
const id = btn.dataset.id;
|
|
try {
|
|
const res = await fetch('api/delete_order.php', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ id })
|
|
});
|
|
const data = await res.json();
|
|
if (data.success) {
|
|
location.reload();
|
|
} else {
|
|
alert(data.error);
|
|
}
|
|
} catch (e) {
|
|
alert('Error deleting order');
|
|
}
|
|
}
|
|
};
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
.status-opt.active {
|
|
background-color: var(--bs-primary);
|
|
color: white;
|
|
border-color: var(--bs-primary);
|
|
}
|
|
.status-opt:hover {
|
|
transform: translateY(-2px);
|
|
transition: all 0.2s;
|
|
}
|
|
</style>
|
|
|
|
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|