feat: initial foundation for collaborative canteen ordering system in vanilla PHP
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Core\Database\Database;
|
||||
use PDO;
|
||||
|
||||
class Customer
|
||||
{
|
||||
public static function all(): array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->query("SELECT * FROM customers ORDER BY name ASC");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function find(int $id): ?array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("SELECT * FROM customers WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$row = $stmt->fetch();
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
public static function count(): int
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
return (int)$db->query("SELECT COUNT(*) FROM customers")->fetchColumn();
|
||||
}
|
||||
|
||||
public static function create(array $data): int
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO customers (name, email, phone, address)
|
||||
VALUES (:name, :email, :phone, :address)
|
||||
");
|
||||
$stmt->execute([
|
||||
':name' => $data['name'],
|
||||
':email' => $data['email'],
|
||||
':phone' => $data['phone'] ?? '',
|
||||
':address' => $data['address'] ?? '',
|
||||
]);
|
||||
return (int)$db->lastInsertId();
|
||||
}
|
||||
|
||||
public static function update(int $id, array $data): bool
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("
|
||||
UPDATE customers
|
||||
SET name = :name, email = :email, phone = :phone, address = :address
|
||||
WHERE id = :id
|
||||
");
|
||||
return $stmt->execute([
|
||||
':id' => $id,
|
||||
':name' => $data['name'],
|
||||
':email' => $data['email'],
|
||||
':phone' => $data['phone'] ?? '',
|
||||
':address' => $data['address'] ?? '',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Core\Database\Database;
|
||||
use PDO;
|
||||
|
||||
class DailySession
|
||||
{
|
||||
public static function create(int $creatorId, string $title, string $canteenName, string $sessionDate, ?string $notes): int
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO daily_sessions (creator_id, session_date, title, canteen_name, status, notes)
|
||||
VALUES (?, ?, ?, ?, 'open', ?)
|
||||
");
|
||||
$stmt->execute([
|
||||
$creatorId,
|
||||
$sessionDate,
|
||||
trim($title),
|
||||
trim($canteenName),
|
||||
trim((string)$notes)
|
||||
]);
|
||||
|
||||
return (int)$db->lastInsertId();
|
||||
}
|
||||
|
||||
public static function find(int $id): ?array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("
|
||||
SELECT s.*, u.name as creator_name, u.username as creator_username
|
||||
FROM daily_sessions s
|
||||
JOIN users u ON s.creator_id = u.id
|
||||
WHERE s.id = ?
|
||||
");
|
||||
$stmt->execute([$id]);
|
||||
$session = $stmt->fetch();
|
||||
|
||||
if (!$session) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$session['menus'] = SessionMenu::getBySessionId($id);
|
||||
$session['orders'] = EmployeeOrder::getBySessionId($id);
|
||||
$session['recap'] = self::getCanteenRecap($id);
|
||||
|
||||
return $session;
|
||||
}
|
||||
|
||||
public static function all(): array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->query("
|
||||
SELECT s.*, u.name as creator_name, u.username as creator_username,
|
||||
(SELECT COUNT(*) FROM employee_orders WHERE session_id = s.id) as order_count,
|
||||
(SELECT COUNT(*) FROM session_menus WHERE session_id = s.id) as menu_count,
|
||||
(SELECT COALESCE(SUM(total_bill), 0) FROM employee_orders WHERE session_id = s.id) as total_session_amount
|
||||
FROM daily_sessions s
|
||||
JOIN users u ON s.creator_id = u.id
|
||||
ORDER BY s.id DESC
|
||||
");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function getActiveSessions(): array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->query("
|
||||
SELECT s.*, u.name as creator_name, u.username as creator_username,
|
||||
(SELECT COUNT(*) FROM employee_orders WHERE session_id = s.id) as order_count,
|
||||
(SELECT COUNT(*) FROM session_menus WHERE session_id = s.id) as menu_count,
|
||||
(SELECT COALESCE(SUM(total_bill), 0) FROM employee_orders WHERE session_id = s.id) as total_session_amount
|
||||
FROM daily_sessions s
|
||||
JOIN users u ON s.creator_id = u.id
|
||||
WHERE s.status IN ('open', 'closed', 'arrived')
|
||||
ORDER BY s.id DESC
|
||||
");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function updateStatus(int $id, string $status): bool
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("UPDATE daily_sessions SET status = ?, updated_at = datetime('now', 'localtime') WHERE id = ?");
|
||||
return $stmt->execute([$status, $id]);
|
||||
}
|
||||
|
||||
public static function updateCanteenInfo(int $id, string $canteenName, ?string $notes): bool
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("UPDATE daily_sessions SET canteen_name = ?, notes = ?, updated_at = datetime('now', 'localtime') WHERE id = ?");
|
||||
return $stmt->execute([$canteenName, $notes, $id]);
|
||||
}
|
||||
|
||||
public static function getCanteenRecap(int $sessionId): array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("
|
||||
SELECT eoi.item_name,
|
||||
SUM(eoi.quantity) as total_quantity,
|
||||
GROUP_CONCAT(DISTINCT eoi.notes) as combined_notes
|
||||
FROM employee_order_items eoi
|
||||
JOIN employee_orders eo ON eoi.employee_order_id = eo.id
|
||||
WHERE eo.session_id = ?
|
||||
GROUP BY eoi.item_name
|
||||
ORDER BY total_quantity DESC
|
||||
");
|
||||
$stmt->execute([$sessionId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Core\Database\Database;
|
||||
use PDO;
|
||||
|
||||
class Employee
|
||||
{
|
||||
public static function all(): array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->query("
|
||||
SELECT e.*,
|
||||
(SELECT COUNT(*) FROM employee_orders WHERE employee_id = e.id) as total_orders,
|
||||
(SELECT COALESCE(SUM(total_bill), 0) FROM employee_orders WHERE employee_id = e.id AND status = 'unpaid') as unpaid_balance
|
||||
FROM employees e
|
||||
ORDER BY e.name ASC
|
||||
");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function find(int $id): ?array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("SELECT * FROM employees WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$res = $stmt->fetch();
|
||||
return $res ?: null;
|
||||
}
|
||||
|
||||
public static function findOrCreateByName(string $name, ?string $mattermostUsername = null): int
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$name = trim($name);
|
||||
|
||||
$stmt = $db->prepare("SELECT id FROM employees WHERE LOWER(name) = LOWER(?)");
|
||||
$stmt->execute([$name]);
|
||||
$id = $stmt->fetchColumn();
|
||||
|
||||
if ($id) {
|
||||
return (int)$id;
|
||||
}
|
||||
|
||||
$stmt = $db->prepare("INSERT INTO employees (name, mattermost_username) VALUES (?, ?)");
|
||||
$stmt->execute([$name, $mattermostUsername ?: strtolower(preg_replace('/[^a-zA-Z0-9]/', '', $name))]);
|
||||
return (int)$db->lastInsertId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Core\Database\Database;
|
||||
use PDO;
|
||||
|
||||
class EmployeeOrder
|
||||
{
|
||||
public static function getBySessionId(int $sessionId): array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("
|
||||
SELECT eo.*, u.name as user_name, u.username as mattermost_username, u.email as user_email
|
||||
FROM employee_orders eo
|
||||
JOIN users u ON eo.user_id = u.id
|
||||
WHERE eo.session_id = ?
|
||||
ORDER BY eo.id ASC
|
||||
");
|
||||
$stmt->execute([$sessionId]);
|
||||
$orders = $stmt->fetchAll();
|
||||
|
||||
foreach ($orders as &$order) {
|
||||
$order['items'] = self::getItemsByOrderId((int)$order['id']);
|
||||
}
|
||||
|
||||
return $orders;
|
||||
}
|
||||
|
||||
public static function getItemsByOrderId(int $orderId): array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("SELECT * FROM employee_order_items WHERE employee_order_id = ? ORDER BY id ASC");
|
||||
$stmt->execute([$orderId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function findUserOrderBySession(int $sessionId, int $userId): ?array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("SELECT * FROM employee_orders WHERE session_id = ? AND user_id = ?");
|
||||
$stmt->execute([$sessionId, $userId]);
|
||||
$order = $stmt->fetch();
|
||||
|
||||
if (!$order) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$order['items'] = self::getItemsByOrderId((int)$order['id']);
|
||||
return $order;
|
||||
}
|
||||
|
||||
public static function createOrder(int $sessionId, int $userId, ?string $notes, array $items): int
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$db->beginTransaction();
|
||||
|
||||
try {
|
||||
$stmtOrder = $db->prepare("
|
||||
INSERT INTO employee_orders (session_id, user_id, status, notes, total_bill)
|
||||
VALUES (?, ?, 'unpaid', ?, 0.00)
|
||||
");
|
||||
$stmtOrder->execute([$sessionId, $userId, $notes]);
|
||||
$orderId = (int)$db->lastInsertId();
|
||||
|
||||
$stmtItem = $db->prepare("
|
||||
INSERT INTO employee_order_items (employee_order_id, session_menu_id, item_name, quantity, unit_price, total_price, notes)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||
");
|
||||
|
||||
$totalBill = 0.0;
|
||||
|
||||
foreach ($items as $item) {
|
||||
$menuId = !empty($item['session_menu_id']) ? (int)$item['session_menu_id'] : null;
|
||||
$name = trim((string)($item['item_name'] ?? ''));
|
||||
$qty = max(1, (int)($item['quantity'] ?? 1));
|
||||
$unitPrice = (float)($item['unit_price'] ?? 0.0);
|
||||
$lineTotal = $qty * $unitPrice;
|
||||
$itemNotes = trim((string)($item['notes'] ?? ''));
|
||||
|
||||
if (empty($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$stmtItem->execute([
|
||||
$orderId,
|
||||
$menuId,
|
||||
$name,
|
||||
$qty,
|
||||
$unitPrice,
|
||||
$lineTotal,
|
||||
$itemNotes
|
||||
]);
|
||||
|
||||
$totalBill += $lineTotal;
|
||||
}
|
||||
|
||||
// Update total bill
|
||||
$stmtUpdateTotal = $db->prepare("UPDATE employee_orders SET total_bill = ? WHERE id = ?");
|
||||
$stmtUpdateTotal->execute([$totalBill, $orderId]);
|
||||
|
||||
$db->commit();
|
||||
return $orderId;
|
||||
} catch (\Throwable $e) {
|
||||
$db->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public static function togglePaid(int $orderId): bool
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("
|
||||
UPDATE employee_orders
|
||||
SET status = CASE WHEN status = 'paid' THEN 'unpaid' ELSE 'paid' END,
|
||||
updated_at = datetime('now', 'localtime')
|
||||
WHERE id = ?
|
||||
");
|
||||
return $stmt->execute([$orderId]);
|
||||
}
|
||||
|
||||
public static function delete(int $orderId): bool
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("DELETE FROM employee_orders WHERE id = ?");
|
||||
return $stmt->execute([$orderId]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Core\Database\Database;
|
||||
use PDO;
|
||||
|
||||
class Order
|
||||
{
|
||||
public static function all(?string $status = null, ?string $search = null): array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$query = "
|
||||
SELECT o.*, c.name as customer_name, c.email as customer_email,
|
||||
(SELECT COUNT(*) FROM order_items WHERE order_id = o.id) as item_count
|
||||
FROM orders o
|
||||
JOIN customers c ON o.customer_id = c.id
|
||||
";
|
||||
|
||||
$conditions = [];
|
||||
$params = [];
|
||||
|
||||
if ($status && $status !== 'all') {
|
||||
$conditions[] = "o.status = :status";
|
||||
$params[':status'] = $status;
|
||||
}
|
||||
|
||||
if ($search) {
|
||||
$conditions[] = "(o.order_number LIKE :search OR c.name LIKE :search OR c.email LIKE :search)";
|
||||
$params[':search'] = "%{$search}%";
|
||||
}
|
||||
|
||||
if (!empty($conditions)) {
|
||||
$query .= " WHERE " . implode(" AND ", $conditions);
|
||||
}
|
||||
|
||||
$query .= " ORDER BY o.id DESC";
|
||||
|
||||
$stmt = $db->prepare($query);
|
||||
$stmt->execute($params);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function find(int $id): ?array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("
|
||||
SELECT o.*, c.name as customer_name, c.email as customer_email,
|
||||
c.phone as customer_phone, c.address as customer_address
|
||||
FROM orders o
|
||||
JOIN customers c ON o.customer_id = c.id
|
||||
WHERE o.id = ?
|
||||
");
|
||||
$stmt->execute([$id]);
|
||||
$order = $stmt->fetch();
|
||||
|
||||
if (!$order) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$order['items'] = OrderItem::getByOrderId($id);
|
||||
return $order;
|
||||
}
|
||||
|
||||
public static function count(?string $status = null): int
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
if ($status) {
|
||||
$stmt = $db->prepare("SELECT COUNT(*) FROM orders WHERE status = ?");
|
||||
$stmt->execute([$status]);
|
||||
return (int)$stmt->fetchColumn();
|
||||
}
|
||||
return (int)$db->query("SELECT COUNT(*) FROM orders")->fetchColumn();
|
||||
}
|
||||
|
||||
public static function totalRevenue(): float
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
return (float)$db->query("SELECT COALESCE(SUM(total), 0) FROM orders WHERE status != 'Cancelled'")->fetchColumn();
|
||||
}
|
||||
|
||||
public static function recent(int $limit = 5): array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("
|
||||
SELECT o.*, c.name as customer_name,
|
||||
(SELECT COUNT(*) FROM order_items WHERE order_id = o.id) as item_count
|
||||
FROM orders o
|
||||
JOIN customers c ON o.customer_id = c.id
|
||||
ORDER BY o.id DESC
|
||||
LIMIT :limit
|
||||
");
|
||||
$stmt->bindValue(':limit', $limit, PDO::PARAM_INT);
|
||||
$stmt->execute();
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function createOrderWithItems(array $orderData, array $items): int
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$db->beginTransaction();
|
||||
|
||||
try {
|
||||
$orderNumber = 'ORD-' . date('Ymd') . '-' . strtoupper(substr(uniqid(), -4));
|
||||
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO orders (order_number, customer_id, status, subtotal, tax, discount, total, notes)
|
||||
VALUES (:order_number, :customer_id, :status, :subtotal, :tax, :discount, :total, :notes)
|
||||
");
|
||||
|
||||
$stmt->execute([
|
||||
':order_number' => $orderNumber,
|
||||
':customer_id' => $orderData['customer_id'],
|
||||
':status' => $orderData['status'] ?? 'Pending',
|
||||
':subtotal' => $orderData['subtotal'],
|
||||
':tax' => $orderData['tax'] ?? 0.00,
|
||||
':discount' => $orderData['discount'] ?? 0.00,
|
||||
':total' => $orderData['total'],
|
||||
':notes' => $orderData['notes'] ?? '',
|
||||
]);
|
||||
|
||||
$orderId = (int)$db->lastInsertId();
|
||||
|
||||
foreach ($items as $item) {
|
||||
if (empty($item['product_id']) || empty($item['quantity'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$productId = (int)$item['product_id'];
|
||||
$quantity = (int)$item['quantity'];
|
||||
$unitPrice = (float)$item['unit_price'];
|
||||
$totalPrice = $quantity * $unitPrice;
|
||||
$productName = $item['product_name'] ?? 'Item';
|
||||
|
||||
OrderItem::create([
|
||||
'order_id' => $orderId,
|
||||
'product_id' => $productId,
|
||||
'product_name' => $productName,
|
||||
'quantity' => $quantity,
|
||||
'unit_price' => $unitPrice,
|
||||
'total_price' => $totalPrice,
|
||||
]);
|
||||
|
||||
// Deduct stock inventory
|
||||
Product::deductStock($productId, $quantity);
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
return $orderId;
|
||||
} catch (\Throwable $e) {
|
||||
$db->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public static function updateStatus(int $id, string $status): bool
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("UPDATE orders SET status = ?, updated_at = datetime('now', 'localtime') WHERE id = ?");
|
||||
return $stmt->execute([$status, $id]);
|
||||
}
|
||||
|
||||
public static function delete(int $id): bool
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("DELETE FROM orders WHERE id = ?");
|
||||
return $stmt->execute([$id]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Core\Database\Database;
|
||||
use PDO;
|
||||
|
||||
class OrderItem
|
||||
{
|
||||
public static function getByOrderId(int $orderId): array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("
|
||||
SELECT oi.*, p.sku, p.category
|
||||
FROM order_items oi
|
||||
LEFT JOIN products p ON oi.product_id = p.id
|
||||
WHERE oi.order_id = ?
|
||||
ORDER BY oi.id ASC
|
||||
");
|
||||
$stmt->execute([$orderId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function create(array $data): int
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO order_items (order_id, product_id, product_name, quantity, unit_price, total_price)
|
||||
VALUES (:order_id, :product_id, :product_name, :quantity, :unit_price, :total_price)
|
||||
");
|
||||
$stmt->execute([
|
||||
':order_id' => $data['order_id'],
|
||||
':product_id' => $data['product_id'],
|
||||
':product_name' => $data['product_name'],
|
||||
':quantity' => $data['quantity'],
|
||||
':unit_price' => $data['unit_price'],
|
||||
':total_price' => $data['total_price'],
|
||||
]);
|
||||
return (int)$db->lastInsertId();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Core\Database\Database;
|
||||
use PDO;
|
||||
|
||||
class Product
|
||||
{
|
||||
public static function all(): array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->query("SELECT * FROM products ORDER BY name ASC");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function find(int $id): ?array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("SELECT * FROM products WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$row = $stmt->fetch();
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
public static function count(): int
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
return (int)$db->query("SELECT COUNT(*) FROM products")->fetchColumn();
|
||||
}
|
||||
|
||||
public static function lowStockCount(int $threshold = 25): int
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("SELECT COUNT(*) FROM products WHERE stock <= ?");
|
||||
$stmt->execute([$threshold]);
|
||||
return (int)$stmt->fetchColumn();
|
||||
}
|
||||
|
||||
public static function create(array $data): int
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("
|
||||
INSERT INTO products (sku, name, description, price, stock, category)
|
||||
VALUES (:sku, :name, :description, :price, :stock, :category)
|
||||
");
|
||||
$stmt->execute([
|
||||
':sku' => $data['sku'],
|
||||
':name' => $data['name'],
|
||||
':description' => $data['description'] ?? '',
|
||||
':price' => $data['price'],
|
||||
':stock' => (int)($data['stock'] ?? 0),
|
||||
':category' => $data['category'] ?? 'General',
|
||||
]);
|
||||
return (int)$db->lastInsertId();
|
||||
}
|
||||
|
||||
public static function update(int $id, array $data): bool
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("
|
||||
UPDATE products
|
||||
SET sku = :sku, name = :name, description = :description,
|
||||
price = :price, stock = :stock, category = :category
|
||||
WHERE id = :id
|
||||
");
|
||||
return $stmt->execute([
|
||||
':id' => $id,
|
||||
':sku' => $data['sku'],
|
||||
':name' => $data['name'],
|
||||
':description' => $data['description'] ?? '',
|
||||
':price' => $data['price'],
|
||||
':stock' => (int)($data['stock'] ?? 0),
|
||||
':category' => $data['category'] ?? 'General',
|
||||
]);
|
||||
}
|
||||
|
||||
public static function deductStock(int $id, int $quantity): bool
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("UPDATE products SET stock = MAX(0, stock - ?) WHERE id = ?");
|
||||
return $stmt->execute([$quantity, $id]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Core\Database\Database;
|
||||
use PDO;
|
||||
|
||||
class SessionMenu
|
||||
{
|
||||
public static function getBySessionId(int $sessionId): array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("SELECT * FROM session_menus WHERE session_id = ? ORDER BY id ASC");
|
||||
$stmt->execute([$sessionId]);
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function add(int $sessionId, string $name, float $price = 0.00): int
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("INSERT INTO session_menus (session_id, name, final_price) VALUES (?, ?, ?)");
|
||||
$stmt->execute([$sessionId, trim($name), $price]);
|
||||
return (int)$db->lastInsertId();
|
||||
}
|
||||
|
||||
public static function delete(int $id): bool
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("DELETE FROM session_menus WHERE id = ?");
|
||||
return $stmt->execute([$id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update final prices from canteen bill and recalculate all employee order line items
|
||||
*/
|
||||
public static function updatePricesAndRecalculate(int $sessionId, array $menuPrices): void
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$db->beginTransaction();
|
||||
|
||||
try {
|
||||
$stmtUpdateMenu = $db->prepare("UPDATE session_menus SET final_price = ? WHERE id = ? AND session_id = ?");
|
||||
$stmtUpdateItems = $db->prepare("
|
||||
UPDATE employee_order_items
|
||||
SET unit_price = ?, total_price = quantity * ?
|
||||
WHERE session_menu_id = ?
|
||||
");
|
||||
|
||||
foreach ($menuPrices as $menuId => $price) {
|
||||
$menuId = (int)$menuId;
|
||||
$price = (float)$price;
|
||||
|
||||
$stmtUpdateMenu->execute([$price, $menuId, $sessionId]);
|
||||
$stmtUpdateItems->execute([$price, $price, $menuId]);
|
||||
}
|
||||
|
||||
// Recalculate total_bill on employee_orders
|
||||
$stmtOrders = $db->prepare("SELECT id FROM employee_orders WHERE session_id = ?");
|
||||
$stmtOrders->execute([$sessionId]);
|
||||
$orders = $stmtOrders->fetchAll();
|
||||
|
||||
$stmtUpdateOrderTotal = $db->prepare("
|
||||
UPDATE employee_orders
|
||||
SET total_bill = (SELECT COALESCE(SUM(total_price), 0) FROM employee_order_items WHERE employee_order_id = ?)
|
||||
WHERE id = ?
|
||||
");
|
||||
|
||||
foreach ($orders as $order) {
|
||||
$stmtUpdateOrderTotal->execute([$order['id'], $order['id']]);
|
||||
}
|
||||
|
||||
$db->commit();
|
||||
} catch (\Throwable $e) {
|
||||
$db->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Core\Database\Database;
|
||||
use PDO;
|
||||
|
||||
class User
|
||||
{
|
||||
public static function all(): array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->query("
|
||||
SELECT u.id, u.name, u.username, u.email, u.created_at,
|
||||
(SELECT COUNT(*) FROM employee_orders WHERE user_id = u.id) as total_orders,
|
||||
(SELECT COALESCE(SUM(total_bill), 0) FROM employee_orders WHERE user_id = u.id AND status = 'unpaid') as unpaid_balance
|
||||
FROM users u
|
||||
ORDER BY u.name ASC
|
||||
");
|
||||
return $stmt->fetchAll();
|
||||
}
|
||||
|
||||
public static function find(int $id): ?array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$stmt = $db->prepare("SELECT id, name, username, email, created_at FROM users WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$res = $stmt->fetch();
|
||||
return $res ?: null;
|
||||
}
|
||||
|
||||
public static function findByIdentifier(string $identifier): ?array
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$identifier = trim($identifier);
|
||||
$stmt = $db->prepare("SELECT * FROM users WHERE LOWER(username) = LOWER(?) OR LOWER(email) = LOWER(?)");
|
||||
$stmt->execute([$identifier, $identifier]);
|
||||
$res = $stmt->fetch();
|
||||
return $res ?: null;
|
||||
}
|
||||
|
||||
public static function create(string $name, string $username, string $email, string $password): int
|
||||
{
|
||||
$db = Database::getConnection();
|
||||
$hash = password_hash($password, PASSWORD_DEFAULT);
|
||||
|
||||
$stmt = $db->prepare("INSERT INTO users (name, username, email, password_hash) VALUES (?, ?, ?, ?)");
|
||||
$stmt->execute([
|
||||
trim($name),
|
||||
strtolower(trim($username)),
|
||||
strtolower(trim($email)),
|
||||
$hash
|
||||
]);
|
||||
|
||||
return (int)$db->lastInsertId();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user