v1.0.0.1
StocketBase
- <?php
-
- declare(strict_types=1);
-
- if (count(get_included_files()) === 1) {
- http_response_code(403);
- exit;
- }
-
- require_once __DIR__ . '/../includes/currency.php';
-
- if (!Auth::hasRoleAtLeast(Auth::ROLE_STORE_MANAGER)) {
- echo '<p>You don\'t have access to orders.</p>';
- return;
- }
-
- $usersDb = Database::users();
-
- $orderId = (int) ($_GET['id'] ?? 0);
- $statement = $usersDb->prepare('SELECT o.*, u.email AS account_email, u.display_name FROM orders o LEFT JOIN user_accounts u ON u.id = o.user_account_id WHERE o.id = :id LIMIT 1');
- $statement->execute(['id' => $orderId]);
- $order = $statement->fetch();
-
- if (!$order) {
- echo '<p>We couldn\'t find this order.</p>';
- return;
- }
-
- $itemsStatement = $usersDb->prepare('SELECT * FROM order_items WHERE order_id = :id');
- $itemsStatement->execute(['id' => $orderId]);
- $items = $itemsStatement->fetchAll();
-
- $historyStatement = $usersDb->prepare('SELECT * FROM order_status_history WHERE order_id = :id ORDER BY created_at DESC');
- $historyStatement->execute(['id' => $orderId]);
- $history = $historyStatement->fetchAll();
-
- $allowedStatuses = ['pending', 'paid', 'processing', 'fulfilled', 'shipped', 'completed', 'canceled', 'refunded', 'failed'];
-
- ?>
- <div class="dash-header-row">
- <h1 class="dash-title"><?= Icons::icon('checklist', 'icon icon-lg') ?>Order <?= htmlspecialchars($order['order_number']) ?></h1>
- <a href="dashboard.php?view=orders" class="dash-btn"><?= Icons::icon('checklist', 'icon icon-sm') ?>Back to orders</a>
- </div>
-
- <div class="article-form-grid">
- <div class="article-form-main">
-
- <h2 class="dash-subtitle"><?= Icons::icon('grid', 'icon icon-sm') ?>What they ordered</h2>
- <table class="dash-table">
- <thead><tr><th>Product</th><th>Option</th><th>Quantity</th><th>Price each</th><th>Total</th></tr></thead>
- <tbody>
- <?php foreach ($items as $item): ?>
- <tr>
- <td><?= htmlspecialchars($item['product_title_snapshot']) ?></td>
- <td><?= htmlspecialchars((string) ($item['variant_name_snapshot'] ?? 'Standard')) ?></td>
- <td><?= (int) $item['quantity'] ?></td>
- <td><?= htmlspecialchars(Currency::format((int) $item['unit_price_cents'], $order['currency'])) ?></td>
- <td><?= htmlspecialchars(Currency::format((int) $item['total_cents'], $order['currency'])) ?></td>
- </tr>
- <?php endforeach; ?>
- </tbody>
- </table>
-
- <div class="sidebar-box" style="max-width:360px;margin-top:16px;">
- <p>Subtotal: <?= htmlspecialchars(Currency::format((int) $order['subtotal_cents'], $order['currency'])) ?></p>
- <?php if ((int) $order['discount_cents'] > 0): ?>
- <p>Discount<?= $order['discount_code'] ? ' (' . htmlspecialchars($order['discount_code']) . ')' : '' ?>: -<?= htmlspecialchars(Currency::format((int) $order['discount_cents'], $order['currency'])) ?></p>
- <?php endif; ?>
- <?php if ((int) $order['shipping_cents'] > 0): ?>
- <p>Shipping: <?= htmlspecialchars(Currency::format((int) $order['shipping_cents'], $order['currency'])) ?></p>
- <?php endif; ?>
- <?php if ((int) $order['tax_cents'] > 0): ?>
- <p>Tax: <?= htmlspecialchars(Currency::format((int) $order['tax_cents'], $order['currency'])) ?></p>
- <?php endif; ?>
- <p><strong>Total: <?= htmlspecialchars(Currency::format((int) $order['total_cents'], $order['currency'])) ?></strong></p>
- </div>
-
- <h2 class="dash-subtitle"><?= Icons::icon('map-pin', 'icon icon-sm') ?>Shipping address</h2>
- <p>
- <?= htmlspecialchars((string) $order['shipping_name']) ?><br>
- <?= htmlspecialchars((string) $order['shipping_address_line1']) ?><br>
- <?php if (!empty($order['shipping_address_line2'])): ?><?= htmlspecialchars($order['shipping_address_line2']) ?><br><?php endif; ?>
- <?= htmlspecialchars((string) $order['shipping_city']) ?>, <?= htmlspecialchars((string) $order['shipping_region']) ?> <?= htmlspecialchars((string) $order['shipping_postal_code']) ?><br>
- <?= htmlspecialchars((string) $order['shipping_country']) ?>
- </p>
-
- <?php if (!empty($order['customer_note'])): ?>
- <h2 class="dash-subtitle"><?= Icons::icon('message', 'icon icon-sm') ?>Customer note</h2>
- <p><?= nl2br(htmlspecialchars($order['customer_note'])) ?></p>
- <?php endif; ?>
-
- <h2 class="dash-subtitle"><?= Icons::icon('activity', 'icon icon-sm') ?>History</h2>
- <ul style="list-style:none;padding:0;margin:0;display:grid;gap:8px;font-size:13px;">
- <?php foreach ($history as $entry): ?>
- <li><span class="status-pill status-<?= htmlspecialchars($entry['status']) ?>"><?= htmlspecialchars(StatusLabel::get((string) $entry['status'])) ?></span> <?= htmlspecialchars($entry['created_at']) ?><?= $entry['note'] ? ', ' . htmlspecialchars($entry['note']) : '' ?></li>
- <?php endforeach; ?>
- <?php if (empty($history)): ?>
- <li>Nothing has changed yet.</li>
- <?php endif; ?>
- </ul>
-
- </div>
-
- <div class="article-form-sidebar">
-
- <div class="sidebar-box">
- <label>Customer</label>
- <p><?= htmlspecialchars((string) ($order['display_name'] ?: 'Guest')) ?><br><?= htmlspecialchars((string) ($order['account_email'] ?: $order['guest_email'] ?: 'No email')) ?></p>
- <?php if ($order['user_account_id']): ?>
- <a href="dashboard.php?view=customer-detail&id=<?= (int) $order['user_account_id'] ?>" class="dash-btn-small"><?= Icons::icon('eye', 'icon icon-sm') ?>View customer</a>
- <?php endif; ?>
- </div>
-
- <div class="sidebar-box">
- <label>Payment</label>
- <p>Paid with: <?= htmlspecialchars((string) ($order['provider'] ? ucfirst((string) $order['provider']) : 'Not paid yet')) ?><br>
- Payment reference: <?= htmlspecialchars((string) ($order['provider_payment_id'] ?: 'None yet')) ?></p>
- </div>
-
- <div class="sidebar-box">
- <label>Change status</label>
- <form method="post">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="update_order_status">
- <input type="hidden" name="order_id" value="<?= (int) $order['id'] ?>">
- <select name="status">
- <?php foreach ($allowedStatuses as $status): ?>
- <option value="<?= htmlspecialchars($status) ?>" <?= $order['status'] === $status ? 'selected' : '' ?>><?= htmlspecialchars(StatusLabel::get($status)) ?></option>
- <?php endforeach; ?>
- </select>
- <label>Tracking number (optional)</label>
- <input type="text" name="tracking_number" value="<?= htmlspecialchars((string) ($order['tracking_number'] ?? '')) ?>">
- <label>Tracking link (optional)</label>
- <input type="text" name="tracking_url" value="<?= htmlspecialchars((string) ($order['tracking_url'] ?? '')) ?>">
- <label>Note for the history (optional)</label>
- <input type="text" name="note">
- <label><input type="checkbox" name="notify_customer" checked> Let the customer know by email</label>
- <button type="submit" class="dash-btn dash-btn-primary" style="margin-top:10px;"><?= Icons::icon('check', 'icon icon-sm') ?>Save status</button>
- </form>
- </div>
-
- <?php if (in_array($order['status'], ['paid', 'processing', 'fulfilled', 'shipped', 'completed'], true) && !empty($order['provider_payment_id'])): ?>
- <div class="sidebar-box">
- <label>Refund</label>
- <form method="post" onsubmit="return confirm('Refund this order through ' + <?= json_encode($order['provider']) ?> + '? The money goes back to the customer and this can\'t be undone.');">
- <?= Csrf::field() ?>
- <input type="hidden" name="action" value="refund_order">
- <input type="hidden" name="order_id" value="<?= (int) $order['id'] ?>">
- <button type="submit" class="dash-btn dash-btn-danger"><?= Icons::icon('x', 'icon icon-sm') ?>Refund</button>
- </form>
- </div>
- <?php endif; ?>
-
- </div>
- </div>
-