38682-vm/admin/orders.php
2026-02-23 13:12:33 +00:00

351 lines
17 KiB
PHP

<?php
declare(strict_types=1);
require_once __DIR__ . '/../db/config.php';
require_once __DIR__ . '/../includes/functions.php';
$pdo = db();
require_permission('orders_view');
// Handle status updates
if (isset($_POST['action']) && $_POST['action'] === 'update_status') {
if (!has_permission('orders_add')) {
header("Location: orders.php?error=permission_denied");
exit;
}
$order_id = $_POST['order_id'];
$new_status = $_POST['status'];
$stmt = $pdo->prepare("UPDATE orders SET status = ? WHERE id = ?");
$stmt->execute([$new_status, $order_id]);
header("Location: orders.php?" . http_build_query($_GET)); // Keep filters
exit;
}
// Handle stopping all promotions
if (isset($_POST['action']) && $_POST['action'] === 'stop_promotions') {
if (!has_permission('manage_products')) {
header("Location: orders.php?error=permission_denied");
exit;
}
// Set promo_date_to to yesterday for all currently active promotions
$stmt = $pdo->prepare("UPDATE products SET promo_date_to = DATE_SUB(CURDATE(), INTERVAL 1 DAY) WHERE (promo_date_to >= CURDATE() OR promo_date_to IS NULL) AND promo_discount_percent IS NOT NULL");
$stmt->execute();
header("Location: orders.php?success=promotions_stopped");
exit;
}
// Fetch Outlets for Filter
$outlets = $pdo->query("SELECT id, name FROM outlets ORDER BY name")->fetchAll(PDO::FETCH_ASSOC);
// Build Query with Filters
$params = [];
$where = [];
// Filter: Outlet
if (!empty($_GET['outlet_id'])) {
$where[] = "o.outlet_id = :outlet_id";
$params[':outlet_id'] = $_GET['outlet_id'];
}
// Filter: Date Range
if (!empty($_GET['start_date'])) {
$where[] = "DATE(o.created_at) >= :start_date";
$params[':start_date'] = $_GET['start_date'];
}
if (!empty($_GET['end_date'])) {
$where[] = "DATE(o.created_at) <= :end_date";
$params[':end_date'] = $_GET['end_date'];
}
// Filter: Search (Order No)
if (!empty($_GET['search'])) {
if (is_numeric($_GET['search'])) {
$where[] = "o.id = :search";
$params[':search'] = $_GET['search'];
}
}
$where_clause = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
// Calculate Total Sum for filtered orders
$sum_query = "SELECT SUM(total_amount) as total_sum FROM orders o $where_clause";
$stmt_sum = $pdo->prepare($sum_query);
$stmt_sum->execute($params);
$total_sum = $stmt_sum->fetchColumn() ?: 0;
// Main Query
$query = "SELECT o.*, ot.name as outlet_name, pt.name as payment_type_name,
(SELECT GROUP_CONCAT(CONCAT(p.name, ' x', oi.quantity) SEPARATOR ', ') FROM order_items oi JOIN products p ON oi.product_id = p.id WHERE oi.order_id = o.id) as items_summary
FROM orders o
LEFT JOIN outlets ot ON o.outlet_id = ot.id
LEFT JOIN payment_types pt ON o.payment_type_id = pt.id
$where_clause
ORDER BY o.created_at DESC";
$orders_pagination = paginate_query($pdo, $query, $params);
$orders = $orders_pagination['data'];
// Add total sum to pagination object for rendering
$orders_pagination['total_amount_sum'] = $total_sum;
include 'includes/header.php';
?>
<div class="d-flex justify-content-between align-items-center mb-4">
<h2 class="fw-bold mb-0">Order Management</h2>
<div class="d-flex gap-2">
<?php if (has_permission('manage_products')): ?>
<form method="POST" onsubmit="return confirm('Are you sure you want to stop all running promotions? This will end all active promotions by setting their end date to yesterday.');">
<input type="hidden" name="action" value="stop_promotions">
<button type="submit" class="btn btn-danger shadow-sm">
<i class="bi bi-stop-circle me-1"></i> Stop All Promotions
</button>
</form>
<?php endif; ?>
<span class="badge bg-success bg-opacity-10 text-success border border-success px-3 py-2 rounded-pill d-flex align-items-center">
<i class="bi bi-circle-fill small me-1"></i> Live
</span>
</div>
</div>
<?php if (isset($_GET['error']) && $_GET['error'] === 'permission_denied'): ?>
<div class="alert alert-danger border-0 shadow-sm rounded-3">Access Denied: You do not have permission to perform this action.</div>
<?php endif; ?>
<?php if (isset($_GET['success']) && $_GET['success'] === 'promotions_stopped'): ?>
<div class="alert alert-success border-0 shadow-sm rounded-3">
<i class="bi bi-check-circle-fill me-2"></i> All running promotions have been stopped successfully.
</div>
<?php endif; ?>
<!-- Summary Stats -->
<div class="row mb-4">
<div class="col-md-4">
<div class="card border-0 shadow-sm">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-shrink-0 bg-primary bg-opacity-10 text-primary p-3 rounded">
<i class="bi bi-currency-dollar fs-4"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted mb-0 small text-uppercase fw-bold">Total Revenue</h6>
<div class="fs-4 fw-bold text-primary"><?= format_currency($total_sum) ?></div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-0 shadow-sm">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-shrink-0 bg-success bg-opacity-10 text-success p-3 rounded">
<i class="bi bi-receipt fs-4"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted mb-0 small text-uppercase fw-bold">Total Orders</h6>
<div class="fs-4 fw-bold text-success"><?= $orders_pagination['total_rows'] ?></div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card border-0 shadow-sm">
<div class="card-body">
<div class="d-flex align-items-center">
<div class="flex-shrink-0 bg-info bg-opacity-10 text-info p-3 rounded">
<i class="bi bi-calendar-event fs-4"></i>
</div>
<div class="flex-grow-1 ms-3">
<h6 class="text-muted mb-0 small text-uppercase fw-bold">Date Range</h6>
<div class="small fw-bold">
<?= !empty($_GET['start_date']) ? date('M d, Y', strtotime($_GET['start_date'])) : 'Start' ?>
-
<?= !empty($_GET['end_date']) ? date('M d, Y', strtotime($_GET['end_date'])) : 'Today' ?>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Filters -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body bg-light">
<form method="GET" class="row g-3 align-items-end">
<div class="col-md-3">
<label class="form-label small fw-bold text-muted">Outlet</label>
<select name="outlet_id" class="form-select">
<option value="">All Outlets</option>
<?php foreach ($outlets as $outlet): ?>
<option value="<?= $outlet['id'] ?>" <?= (isset($_GET['outlet_id']) && $_GET['outlet_id'] == $outlet['id']) ? 'selected' : '' ?>>
<?= htmlspecialchars($outlet['name']) ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="col-md-3">
<label class="form-label small fw-bold text-muted">Date Range</label>
<div class="input-group">
<input type="date" name="start_date" class="form-control" value="<?= $_GET['start_date'] ?? '' ?>" placeholder="Start">
<span class="input-group-text bg-white border-start-0 border-end-0">-</span>
<input type="date" name="end_date" class="form-control" value="<?= $_GET['end_date'] ?? '' ?>" placeholder="End">
</div>
</div>
<div class="col-md-3">
<label class="form-label small fw-bold text-muted">Search</label>
<input type="text" name="search" class="form-control" placeholder="Order No (ID)" value="<?= htmlspecialchars($_GET['search'] ?? '') ?>">
</div>
<div class="col-md-3">
<div class="d-flex gap-2">
<button type="submit" class="btn btn-primary w-100">
<i class="bi bi-filter"></i> Filter
</button>
<a href="orders.php" class="btn btn-outline-secondary">
<i class="bi bi-x-lg"></i>
</a>
</div>
</div>
</form>
</div>
</div>
<div class="card border-0 shadow-sm">
<div class="card-body p-0">
<!-- Pagination Controls -->
<div class="p-3 border-bottom bg-light">
<?php render_pagination_controls($orders_pagination); ?>
</div>
<div class="table-responsive">
<table class="table table-hover align-middle mb-0">
<thead class="bg-light">
<tr>
<th class="ps-4">ID</th>
<th>Outlet</th>
<th>Customer</th>
<th>Type</th>
<th>Source</th>
<th>Items</th>
<th>Total</th>
<th>Payment</th>
<th>Status</th>
<th>Time</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<?php foreach ($orders as $order): ?>
<tr>
<td class="ps-4">#<?= $order['id'] ?></td>
<td>
<span class="badge bg-white text-dark border">
<i class="bi bi-shop me-1"></i>
<?= htmlspecialchars($order['outlet_name'] ?? 'Unknown') ?>
</span>
</td>
<td>
<?php if (!empty($order['customer_name'])): ?>
<div><?= htmlspecialchars((string)($order['customer_name'] ?? '')) ?></div>
<?php if (!empty($order['customer_phone'])): ?>
<small class="text-muted"><i class="bi bi-telephone me-1"></i><?= htmlspecialchars((string)($order['customer_phone'] ?? '')) ?></small>
<?php endif; ?>
<?php else: ?>
<span class="text-muted small">Guest</span>
<?php endif; ?>
</td>
<td>
<?php
$badge = match($order['order_type']) {
'dine-in' => 'bg-info',
'takeaway' => 'bg-success',
'delivery' => 'bg-warning',
'drive-thru' => 'bg-primary',
default => 'bg-secondary'
};
?>
<span class="badge <?= $badge ?> text-dark bg-opacity-25 border border-<?= str_replace('bg-', '', $badge) ?>"><?= ucfirst($order['order_type']) ?></span>
</td>
<td>
<?php if ($order['order_type'] === 'dine-in' && $order['table_number']): ?>
<span class="badge bg-secondary">Table <?= htmlspecialchars((string)($order['table_number'] ?? '')) ?></span>
<?php else: ?>
<span class="badge bg-light text-dark border"><?= ucfirst($order['order_type']) ?></span>
<?php endif; ?>
</td>
<td><small class="text-muted"><?= htmlspecialchars((string)($order['items_summary'] ?? '')) ?></small></td>
<td><?= format_currency($order['total_amount']) ?></td>
<td>
<?php
$payment_name = $order['payment_type_name'] ?? 'Unpaid';
$payment_badge = match(strtolower($payment_name)) {
'cash' => 'bg-success',
'credit card' => 'bg-primary',
'loyalty redeem' => 'bg-warning',
'unpaid' => 'bg-secondary',
default => 'bg-secondary'
};
?>
<span class="badge <?= $payment_badge ?> text-dark bg-opacity-25 border border-<?= str_replace('bg-', '', $payment_badge) ?>">
<?= htmlspecialchars((string)($payment_name ?? '')) ?>
</span>
</td>
<td>
<span class="badge rounded-pill status-<?= $order['status'] ?>">
<?= ucfirst($order['status']) ?>
</span>
</td>
<td class="text-muted small">
<div><?= date('M d', strtotime($order['created_at'])) ?></div>
<div><?= date('H:i', strtotime($order['created_at'])) ?></div>
</td>
<td>
<?php if (has_permission('orders_add')): ?>
<form method="POST" class="d-flex gap-2">
<input type="hidden" name="order_id" value="<?= $order['id'] ?>">
<input type="hidden" name="action" value="update_status">
<?php if ($order['status'] === 'pending'): ?>
<button type="submit" name="status" value="preparing" class="btn btn-sm btn-primary">
<i class="bi bi-play-fill"></i> Start
</button>
<button type="submit" name="status" value="cancelled" class="btn btn-sm btn-outline-danger">
<i class="bi bi-x"></i>
</button>
<?php elseif ($order['status'] === 'preparing'): ?>
<button type="submit" name="status" value="ready" class="btn btn-sm btn-warning text-dark">
<i class="bi bi-check-circle"></i> Ready
</button>
<?php elseif ($order['status'] === 'ready'): ?>
<button type="submit" name="status" value="completed" class="btn btn-sm btn-success">
<i class="bi bi-check-all"></i> Complete
</button>
<?php else: ?>
<span class="text-muted small">-</span>
<?php endif; ?>
</form>
<?php else: ?>
<span class="text-muted small">View Only</span>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
<?php if (empty($orders)): ?>
<tr>
<td colspan="11" class="text-center py-5 text-muted">
<i class="bi bi-inbox fs-1 d-block mb-2"></i>
No active orders found matching your criteria.
</td>
</tr>
<?php endif; ?>
</tbody>
</table>
</div>
<!-- Bottom Pagination -->
<div class="p-3 border-top bg-light">
<?php render_pagination_controls($orders_pagination); ?>
</div>
</div>
</div>
<?php include 'includes/footer.php'; ?>