From 370ceb510e65d2a3bfb1d8d742d7ba1ea1029966 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Mon, 23 Feb 2026 12:56:09 +0000 Subject: [PATCH] modifying loyalty --- admin/ads.php | 2 + admin/areas.php | 2 + admin/categories.php | 2 + admin/company.php | 2 + admin/customers.php | 2 + admin/expense_categories.php | 84 +++++++ admin/expense_category_edit.php | 88 +++++++ admin/expense_edit.php | 133 +++++++++++ admin/expenses.php | 157 ++++++++++++ admin/includes/header.php | 137 ++++++++++- admin/index.php | 11 +- admin/integrations.php | 2 + admin/loyalty.php | 29 ++- admin/orders.php | 16 +- admin/outlets.php | 2 + admin/payment_types.php | 2 + admin/product_edit.php | 33 ++- admin/product_variants.php | 12 +- admin/products.php | 191 ++++++++++----- admin/reports.php | 162 +++++++++++-- admin/suppliers.php | 2 + admin/tables.php | 2 + admin/user_group_edit.php | 208 ++++++++++++++++ admin/user_groups.php | 177 +++++++------- admin/users.php | 290 ++++++++--------------- api/order.php | 155 ++++++------ assets/js/main.js | 6 +- db/migrations/012_expenses_schema.sql | 18 ++ db/migrations/013_product_promotions.sql | 4 + db/migrations/014_loyalty_toggle.sql | 18 ++ includes/functions.php | 34 ++- kitchen.php | 19 +- pos.php | 46 +++- qorder.php | 21 +- 34 files changed, 1595 insertions(+), 474 deletions(-) create mode 100644 admin/expense_categories.php create mode 100644 admin/expense_category_edit.php create mode 100644 admin/expense_edit.php create mode 100644 admin/expenses.php create mode 100644 admin/user_group_edit.php create mode 100644 db/migrations/012_expenses_schema.sql create mode 100644 db/migrations/013_product_promotions.sql create mode 100644 db/migrations/014_loyalty_toggle.sql diff --git a/admin/ads.php b/admin/ads.php index 49a61e1..d85cd67 100644 --- a/admin/ads.php +++ b/admin/ads.php @@ -1,4 +1,6 @@ prepare("SELECT COUNT(*) FROM expenses WHERE category_id = ?"); + $stmt->execute([$id]); + if ($stmt->fetchColumn() > 0) { + $_SESSION['error'] = "Cannot delete category as it has linked expenses."; + } else { + $pdo->prepare("DELETE FROM expense_categories WHERE id = ?")->execute([$id]); + $_SESSION['success'] = "Category deleted successfully."; + } + header("Location: expense_categories.php"); + exit; +} + +$query = "SELECT * FROM expense_categories ORDER BY name ASC"; +$categories_pagination = paginate_query($pdo, $query); +$categories = $categories_pagination['data']; + +include 'includes/header.php'; +?> + +
+

Expense Categories

+ + Add Category + +
+ + +
+ + +
+ + +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
IDNameDescriptionActions
# + + +
No categories found.
+
+
+ +
+
+
+ + diff --git a/admin/expense_category_edit.php b/admin/expense_category_edit.php new file mode 100644 index 0000000..702a662 --- /dev/null +++ b/admin/expense_category_edit.php @@ -0,0 +1,88 @@ +prepare("SELECT * FROM expense_categories WHERE id = ?"); + $stmt->execute([$id]); + $category = $stmt->fetch(); + if ($category) { + $isEdit = true; + } else { + header("Location: expense_categories.php"); + exit; + } +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $name = trim($_POST['name']); + $description = trim($_POST['description']); + + if (empty($name)) { + $message = '
Category name is required.
'; + } else { + try { + if ($isEdit) { + $stmt = $pdo->prepare("UPDATE expense_categories SET name = ?, description = ? WHERE id = ?"); + $stmt->execute([$name, $description, $id]); + $message = '
Category updated successfully!
'; + $stmt = $pdo->prepare("SELECT * FROM expense_categories WHERE id = ?"); + $stmt->execute([$id]); + $category = $stmt->fetch(); + } else { + $stmt = $pdo->prepare("INSERT INTO expense_categories (name, description) VALUES (?, ?)"); + $stmt->execute([$name, $description]); + header("Location: expense_categories.php?success=created"); + exit; + } + } catch (PDOException $e) { + $message = '
Database error: ' . $e->getMessage() . '
'; + } + } +} + +if (!$isEdit) { + $category = [ + 'name' => $_POST['name'] ?? '', + 'description' => $_POST['description'] ?? '' + ]; +} + +include 'includes/header.php'; +?> + +
+ Back to Categories +

+
+ + + +
+
+
+
+ + +
+
+ + +
+
+
+ Cancel + +
+
+
+
+ + diff --git a/admin/expense_edit.php b/admin/expense_edit.php new file mode 100644 index 0000000..4b3c8e6 --- /dev/null +++ b/admin/expense_edit.php @@ -0,0 +1,133 @@ +prepare("SELECT * FROM expenses WHERE id = ?"); + $stmt->execute([$id]); + $expense = $stmt->fetch(); + if ($expense) { + $isEdit = true; + } else { + header("Location: expenses.php"); + exit; + } +} + +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $category_id = $_POST['category_id']; + $outlet_id = $_POST['outlet_id']; + $amount = $_POST['amount']; + $description = trim($_POST['description']); + $expense_date = $_POST['expense_date']; + + if (empty($category_id) || empty($amount) || empty($expense_date)) { + $message = '
Category, amount, and date are required.
'; + } else { + try { + if ($isEdit) { + $stmt = $pdo->prepare("UPDATE expenses SET category_id = ?, outlet_id = ?, amount = ?, description = ?, expense_date = ? WHERE id = ?"); + $stmt->execute([$category_id, $outlet_id, $amount, $description, $expense_date, $id]); + $message = '
Expense updated successfully!
'; + $stmt = $pdo->prepare("SELECT * FROM expenses WHERE id = ?"); + $stmt->execute([$id]); + $expense = $stmt->fetch(); + } else { + $stmt = $pdo->prepare("INSERT INTO expenses (category_id, outlet_id, amount, description, expense_date) VALUES (?, ?, ?, ?, ?)"); + $stmt->execute([$category_id, $outlet_id, $amount, $description, $expense_date]); + header("Location: expenses.php?success=created"); + exit; + } + } catch (PDOException $e) { + $message = '
Database error: ' . $e->getMessage() . '
'; + } + } +} + +if (!$isEdit) { + $expense = [ + 'category_id' => $_POST['category_id'] ?? '', + 'outlet_id' => $_POST['outlet_id'] ?? '', + 'amount' => $_POST['amount'] ?? '', + 'description' => $_POST['description'] ?? '', + 'expense_date' => $_POST['expense_date'] ?? date('Y-m-d') + ]; +} + +$expense_categories = $pdo->query("SELECT * FROM expense_categories ORDER BY name ASC")->fetchAll(); +$outlets = $pdo->query("SELECT * FROM outlets ORDER BY name ASC")->fetchAll(); + +include 'includes/header.php'; +?> + +
+ Back to Expenses +

+
+ + + +
+
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+
+
+
+ +
+ $ + +
+
+
+
+
+ + +
+
+
+
+ + +
+
+
+ Cancel + +
+
+
+
+ + diff --git a/admin/expenses.php b/admin/expenses.php new file mode 100644 index 0000000..f376a5e --- /dev/null +++ b/admin/expenses.php @@ -0,0 +1,157 @@ +Access Denied: You do not have permission to delete expenses.'; + } else { + $id = $_GET['delete']; + $pdo->prepare("DELETE FROM expenses WHERE id = ?")->execute([$id]); + header("Location: expenses.php"); + exit; + } +} + +$expense_categories = $pdo->query("SELECT * FROM expense_categories ORDER BY name")->fetchAll(); +$outlets = $pdo->query("SELECT * FROM outlets ORDER BY name")->fetchAll(); + +$search = $_GET['search'] ?? ''; +$category_filter = $_GET['category_filter'] ?? ''; +$outlet_filter = $_GET['outlet_filter'] ?? ''; + +$params = []; +$where = []; + +$query = "SELECT e.*, ec.name as category_name, o.name as outlet_name + FROM expenses e + LEFT JOIN expense_categories ec ON e.category_id = ec.id + LEFT JOIN outlets o ON e.outlet_id = o.id"; + +if ($search) { + $where[] = "e.description LIKE ?"; + $params[] = "%$search%"; +} + +if ($category_filter) { + $where[] = "e.category_id = ?"; + $params[] = $category_filter; +} + +if ($outlet_filter) { + $where[] = "e.outlet_id = ?"; + $params[] = $outlet_filter; +} + +if (!empty($where)) { + $query .= " WHERE " . implode(" AND ", $where); +} + +$query .= " ORDER BY e.expense_date DESC, e.id DESC"; + +$expenses_pagination = paginate_query($pdo, $query, $params); +$expenses = $expenses_pagination['data']; + +include 'includes/header.php'; +?> + +
+
+

Expenses

+

Track and manage business expenditures

+
+ + + Add Expense + + +
+ + + +
+
+
+
+ +
+
+ +
+
+ +
+
+ + + + +
+
+
+
+ +
+
+
+ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
DateCategoryOutletDescriptionAmountActions
+ + + + + + +
No expenses found.
+
+
+ +
+
+
+ + diff --git a/admin/includes/header.php b/admin/includes/header.php index 5e720eb..5362e72 100644 --- a/admin/includes/header.php +++ b/admin/includes/header.php @@ -43,6 +43,12 @@ function isGroupExpanded($pages) { function getGroupToggleClass($pages) { return in_array(basename($_SERVER['PHP_SELF']), $pages) ? '' : 'collapsed'; } + +// Permission helper for sidebar +function can_view($module) { + if (!function_exists('has_permission')) return true; + return has_permission($module . '_view'); +} ?> @@ -223,14 +229,20 @@ function getGroupToggleClass($pages) { diff --git a/admin/index.php b/admin/index.php index c328e74..1209cf7 100644 --- a/admin/index.php +++ b/admin/index.php @@ -3,6 +3,7 @@ require_once __DIR__ . '/../db/config.php'; require_once __DIR__ . '/../includes/functions.php'; $pdo = db(); +require_permission('dashboard_view'); // Fetch Dashboard Stats $today = date('Y-m-d'); @@ -36,9 +37,11 @@ include 'includes/header.php';

Dashboard

Welcome back, !

+
- New Order + New Order
+
@@ -51,7 +54,7 @@ include 'includes/header.php';
Today's Revenue
-

$

+

@@ -144,7 +147,7 @@ include 'includes/header.php'; - $ + @@ -160,9 +163,11 @@ include 'includes/header.php'; + + diff --git a/admin/integrations.php b/admin/integrations.php index 98319d5..759cac9 100644 --- a/admin/integrations.php +++ b/admin/integrations.php @@ -1,4 +1,6 @@ prepare("UPDATE loyalty_settings SET points_per_order = ?, points_for_free_meal = ? WHERE id = 1"); - $stmt->execute([$points_per_order, $points_for_free_meal]); + $stmt = $pdo->prepare("UPDATE loyalty_settings SET points_per_order = ?, points_for_free_meal = ?, is_enabled = ? WHERE id = 1"); + $stmt->execute([$points_per_order, $points_for_free_meal, $is_enabled]); $success_msg = "Loyalty settings updated successfully!"; } @@ -19,7 +22,7 @@ $settings = $stmt->fetch(PDO::FETCH_ASSOC); if (!$settings) { // Default fallback if migration failed or empty - $settings = ['points_per_order' => 10, 'points_for_free_meal' => 70]; + $settings = ['points_per_order' => 10, 'points_for_free_meal' => 70, 'is_enabled' => 1]; } // Fetch Customers with Points @@ -39,7 +42,14 @@ include 'includes/header.php'; ?>
-

Loyalty Program

+
+

Loyalty Program

+ + Active + + Disabled + +
@@ -166,6 +176,15 @@ include 'includes/header.php';
- + \ No newline at end of file diff --git a/admin/orders.php b/admin/orders.php index 858597b..8278aab 100644 --- a/admin/orders.php +++ b/admin/orders.php @@ -1,10 +1,16 @@ prepare("UPDATE orders SET status = ? WHERE id = ?"); @@ -77,6 +83,10 @@ include 'includes/header.php';
+ +
Access Denied: You do not have permission to perform this action.
+ +
@@ -260,6 +270,7 @@ include 'includes/header.php';
+
@@ -283,6 +294,9 @@ include 'includes/header.php'; -
+ + View Only + @@ -304,4 +318,4 @@ include 'includes/header.php';
- \ No newline at end of file + diff --git a/admin/outlets.php b/admin/outlets.php index e2bd32a..f6cec63 100644 --- a/admin/outlets.php +++ b/admin/outlets.php @@ -1,4 +1,6 @@ prepare("UPDATE products SET name = ?, category_id = ?, price = ?, description = ?, image_url = ? WHERE id = ?"); - if ($stmt->execute([$name, $category_id, $price, $description, $image_url, $id])) { + $stmt = $pdo->prepare("UPDATE products SET name = ?, category_id = ?, price = ?, description = ?, image_url = ?, promo_discount_percent = ?, promo_date_from = ?, promo_date_to = ? WHERE id = ?"); + if ($stmt->execute([$name, $category_id, $price, $description, $image_url, $promo_discount_percent, $promo_date_from, $promo_date_to, $id])) { $message = '
Product updated successfully!
'; // Refresh product data $stmt = $pdo->prepare("SELECT * FROM products WHERE id = ?"); @@ -105,6 +110,28 @@ include 'includes/header.php'; + +
+
+
Promotion Settings
+
+
+ + +
+
+ + +
+
+ + +
+
+
If active, this discount will be automatically applied to the regular price.
+
+
+
@@ -133,4 +160,4 @@ include 'includes/header.php';
- \ No newline at end of file + diff --git a/admin/product_variants.php b/admin/product_variants.php index bb0f9f8..3fe296d 100644 --- a/admin/product_variants.php +++ b/admin/product_variants.php @@ -1,5 +1,6 @@
@@ -98,8 +101,11 @@ include 'includes/header.php'; No change - - + +
+ + +
'; + } else { + $name = $_POST['name']; + $category_id = $_POST['category_id']; + $price = $_POST['price']; + $description = $_POST['description']; - $fileInfo = pathinfo($_FILES['image']['name']); - $fileExt = strtolower($fileInfo['extension']); - $allowedExts = ['jpg', 'jpeg', 'png', 'gif', 'webp']; + $promo_discount_percent = $_POST['promo_discount_percent'] !== '' ? $_POST['promo_discount_percent'] : null; + $promo_date_from = $_POST['promo_date_from'] !== '' ? $_POST['promo_date_from'] : null; + $promo_date_to = $_POST['promo_date_to'] !== '' ? $_POST['promo_date_to'] : null; + + // Image handling + $image_url = 'https://placehold.co/400x300?text=' . urlencode($name); - if (in_array($fileExt, $allowedExts)) { - $fileName = uniqid('prod_') . '.' . $fileExt; - $targetFile = $uploadDir . $fileName; + if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) { + $uploadDir = __DIR__ . '/../assets/images/products/'; + if (!is_dir($uploadDir)) { + mkdir($uploadDir, 0755, true); + } - if (move_uploaded_file($_FILES['image']['tmp_name'], $targetFile)) { - $image_url = 'assets/images/products/' . $fileName; + $fileInfo = pathinfo($_FILES['image']['name']); + $fileExt = strtolower($fileInfo['extension']); + $allowedExts = ['jpg', 'jpeg', 'png', 'gif', 'webp']; + + if (in_array($fileExt, $allowedExts)) { + $fileName = uniqid('prod_') . '.' . $fileExt; + $targetFile = $uploadDir . $fileName; + + if (move_uploaded_file($_FILES['image']['tmp_name'], $targetFile)) { + $image_url = 'assets/images/products/' . $fileName; + } } } - } - $stmt = $pdo->prepare("INSERT INTO products (name, category_id, price, description, image_url) VALUES (?, ?, ?, ?, ?)"); - if ($stmt->execute([$name, $category_id, $price, $description, $image_url])) { - $message = '
Product added successfully!
'; - } else { - $message = '
Error adding product.
'; + $stmt = $pdo->prepare("INSERT INTO products (name, category_id, price, description, image_url, promo_discount_percent, promo_date_from, promo_date_to) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); + if ($stmt->execute([$name, $category_id, $price, $description, $image_url, $promo_discount_percent, $promo_date_from, $promo_date_to])) { + $message = '
Product added successfully!
'; + } else { + $message = '
Error adding product.
'; + } } } // Handle Delete if (isset($_GET['delete'])) { - $id = $_GET['delete']; - $pdo->prepare("DELETE FROM products WHERE id = ?")->execute([$id]); - header("Location: products.php"); - exit; + if (!has_permission('products_del')) { + $message = '
Access Denied: You do not have permission to delete products.
'; + } else { + $id = $_GET['delete']; + $pdo->prepare("DELETE FROM products WHERE id = ?")->execute([$id]); + header("Location: products.php"); + exit; + } } // Fetch Categories for Dropdown (moved up for filter usage) @@ -92,9 +107,11 @@ include 'includes/header.php';

Products

Manage your catalog

+ + @@ -142,11 +159,20 @@ include 'includes/header.php'; Product Category Price + Promotion Actions - + = $product['promo_date_from'] && + $today <= $product['promo_date_to']; + ?>
@@ -161,19 +187,47 @@ include 'includes/header.php'; - + + +
+
+ + + + + + + Active (-%) +
Ends
+ + + Upcoming +
Starts
+ + Expired + + + - +
+ + + + +
@@ -189,8 +243,9 @@ include 'includes/header.php'; +