diff --git a/admin/ads.php b/admin/ads.php index c4dcbb4..35c3641 100644 --- a/admin/ads.php +++ b/admin/ads.php @@ -17,6 +17,9 @@ $pdo->exec("CREATE TABLE IF NOT EXISTS ads_images ( created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP )"); +// Ensure display_layout column exists (for older installations) +$pdo->exec("ALTER TABLE ads_images ADD COLUMN IF NOT EXISTS display_layout ENUM('both', 'split', 'fullscreen') DEFAULT 'both' AFTER is_active"); + if (isset($_GET['delete'])) { if (!has_permission('ads_del')) { $message = '
Access Denied: You do not have permission to delete advertisements.
'; diff --git a/admin/customer_edit.php b/admin/customer_edit.php deleted file mode 100644 index f35f2cd..0000000 --- a/admin/customer_edit.php +++ /dev/null @@ -1,89 +0,0 @@ -prepare("SELECT * FROM customers WHERE id = ?"); -$stmt->execute([$id]); -$customer = $stmt->fetch(); - -if (!$customer) { - header("Location: customers.php"); - exit; -} - -// Handle Update -if ($_SERVER['REQUEST_METHOD'] === 'POST') { - $name = $_POST['name']; - $email = $_POST['email']; - $phone = $_POST['phone']; - $address = $_POST['address']; - - $stmt = $pdo->prepare("UPDATE customers SET name = ?, email = ?, phone = ?, address = ? WHERE id = ?"); - if ($stmt->execute([$name, $email, $phone, $address, $id])) { - $message = '
Customer updated successfully!
'; - // Refresh data - $stmt = $pdo->prepare("SELECT * FROM customers WHERE id = ?"); - $stmt->execute([$id]); - $customer = $stmt->fetch(); - } else { - $message = '
Error updating customer.
'; - } -} - -include 'includes/header.php'; -?> - -
- Back to Customers -

Edit Customer:

-
- - - -
-
-
-
-
- - -
-
- - -
-
- - -
-
- -
-
-
- -
-
-
- - -
-
-
- Cancel - -
-
-
-
- - diff --git a/admin/customers.php b/admin/customers.php index 8587496..b9baad8 100644 --- a/admin/customers.php +++ b/admin/customers.php @@ -6,21 +6,40 @@ $pdo = db(); $message = ''; -// Handle Add Customer -if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action']) && $_POST['action'] === 'add_customer') { - if (!has_permission('customers_add')) { - $message = '
Access Denied: You do not have permission to add customers.
'; - } else { - $name = $_POST['name']; - $email = $_POST['email']; - $phone = $_POST['phone']; - $address = $_POST['address']; - - $stmt = $pdo->prepare("INSERT INTO customers (name, email, phone, address) VALUES (?, ?, ?, ?)"); - if ($stmt->execute([$name, $email, $phone, $address])) { - $message = '
Customer added successfully!
'; +// Handle Add/Edit Customer +if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { + if ($_POST['action'] === 'add_customer') { + if (!has_permission('customers_add')) { + $message = '
Access Denied: You do not have permission to add customers.
'; } else { - $message = '
Error adding customer.
'; + $name = $_POST['name']; + $email = $_POST['email']; + $phone = $_POST['phone']; + $address = $_POST['address']; + + $stmt = $pdo->prepare("INSERT INTO customers (name, email, phone, address) VALUES (?, ?, ?, ?)"); + if ($stmt->execute([$name, $email, $phone, $address])) { + $message = '
Customer added successfully!
'; + } else { + $message = '
Error adding customer.
'; + } + } + } elseif ($_POST['action'] === 'edit_customer') { + if (!has_permission('customers_add')) { // Use customers_add for editing as well + $message = '
Access Denied: You do not have permission to edit customers.
'; + } else { + $id = $_POST['id']; + $name = $_POST['name']; + $email = $_POST['email']; + $phone = $_POST['phone']; + $address = $_POST['address']; + + $stmt = $pdo->prepare("UPDATE customers SET name = ?, email = ?, phone = ?, address = ? WHERE id = ?"); + if ($stmt->execute([$name, $email, $phone, $address, $id])) { + $message = '
Customer updated successfully!
'; + } else { + $message = '
Error updating customer.
'; + } } } } @@ -48,7 +67,7 @@ include 'includes/header.php';

Customers

- @@ -87,7 +106,11 @@ include 'includes/header.php';
- + @@ -112,33 +135,34 @@ include 'includes/header.php';
- + - @@ -131,8 +170,13 @@ include 'includes/header.php'; - - + + @@ -154,4 +198,94 @@ include 'includes/header.php'; + + + + + \ No newline at end of file diff --git a/admin/includes/header.php b/admin/includes/header.php index b8c3b0f..cf4234c 100644 --- a/admin/includes/header.php +++ b/admin/includes/header.php @@ -357,7 +357,7 @@ function can_view($module) { @@ -396,7 +396,7 @@ function can_view($module) { @@ -417,7 +417,7 @@ function can_view($module) { diff --git a/admin/order_view.php b/admin/order_view.php index d53a8a0..0536942 100644 --- a/admin/order_view.php +++ b/admin/order_view.php @@ -14,7 +14,7 @@ if (!$id) { // Fetch Order Details $stmt = $pdo->prepare("SELECT o.*, ot.name as outlet_name, pt.name as payment_type_name, - c.name as customer_name, c.phone as customer_phone, c.email as customer_email, + c.name as customer_name, c.phone as customer_phone, c.email as customer_email, c.address as customer_address, u.username as created_by_username FROM orders o LEFT JOIN outlets ot ON o.outlet_id = ot.id @@ -56,6 +56,7 @@ foreach ($items as $item) { Back to List + @@ -222,4 +223,246 @@ foreach ($items as $item) { - \ No newline at end of file + + + + + + `; + win.document.write(html); + win.document.close(); +} + \ No newline at end of file diff --git a/admin/purchase_edit.php b/admin/purchase_edit.php index 0020edc..a43b880 100644 --- a/admin/purchase_edit.php +++ b/admin/purchase_edit.php @@ -42,37 +42,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } if ($id) { - // Update purchase - // Before updating, if it was completed and now it's not, we might need to revert stock. - // But for simplicity, we'll only increase stock when status changes TO completed. $old_status = $purchase['status']; - $stmt = $pdo->prepare("UPDATE purchases SET supplier_id = ?, purchase_date = ?, status = ?, notes = ?, total_amount = ? WHERE id = ?"); $stmt->execute([$supplier_id, $purchase_date, $status, $notes, $total_amount, $id]); - // Re-fetch items to handle stock reversal if needed $stmt = $pdo->prepare("SELECT * FROM purchase_items WHERE purchase_id = ?"); $stmt->execute([$id]); $old_items = $stmt->fetchAll(); - // If status was completed, revert old stock if ($old_status === 'completed') { foreach ($old_items as $oi) { $pdo->prepare("UPDATE products SET stock_quantity = stock_quantity - ? WHERE id = ?") ->execute([$oi['quantity'], $oi['product_id']]); } } - - // Delete old items $pdo->prepare("DELETE FROM purchase_items WHERE purchase_id = ?")->execute([$id]); } else { - // Create purchase $stmt = $pdo->prepare("INSERT INTO purchases (supplier_id, purchase_date, status, notes, total_amount) VALUES (?, ?, ?, ?, ?)"); $stmt->execute([$supplier_id, $purchase_date, $status, $notes, $total_amount]); $id = $pdo->lastInsertId(); } - // Insert items foreach ($product_ids as $index => $pid) { $qty = $quantities[$index]; $cost = $cost_prices[$index]; @@ -81,7 +71,6 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $stmt = $pdo->prepare("INSERT INTO purchase_items (purchase_id, product_id, quantity, cost_price, total_price) VALUES (?, ?, ?, ?, ?)"); $stmt->execute([$id, $pid, $qty, $cost, $total_item_price]); - // If status is completed, increase stock if ($status === 'completed') { $pdo->prepare("UPDATE products SET stock_quantity = stock_quantity + ?, cost_price = ? WHERE id = ?") ->execute([$qty, $cost, $pid]); @@ -98,7 +87,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } $suppliers = $pdo->query("SELECT * FROM suppliers ORDER BY name")->fetchAll(); -$products = $pdo->query("SELECT * FROM products ORDER BY name")->fetchAll(); +$products = $pdo->query("SELECT id, name, cost_price FROM products ORDER BY name")->fetchAll(); +$products_json = json_encode($products); include 'includes/header.php'; ?> @@ -114,53 +104,56 @@ include 'includes/header.php';
-
-
+
+
-
General Information
-
- - -
-
- - -
-
- - -
Stock is only updated when status is set to Completed.
-
-
- - +
Purchase Details
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
-
- -
-
+ +
-
Purchase Items
- +
Products to Purchase
+
+
+ + +
+ +
- + @@ -170,40 +163,22 @@ include 'includes/header.php'; - - - - - - - - - + - + - + - + @@ -211,12 +186,17 @@ include 'includes/header.php';
PRODUCT QTY
- -
- +
+
Grand Total:
+ +
+
+
Use the search bar above to add products to this purchase.
+
-
@@ -226,9 +206,13 @@ include 'includes/header.php'; \ No newline at end of file diff --git a/admin/purchases.php b/admin/purchases.php index ff5ce63..3ad8183 100644 --- a/admin/purchases.php +++ b/admin/purchases.php @@ -1,6 +1,6 @@ Access Denied: You do not have permission to delete purchases.
'; } else { $id = $_GET['delete']; - // Logic to revert stock could be added here, but usually deletions are just deletions. $pdo->prepare("DELETE FROM purchases WHERE id = ?")->execute([$id]); header("Location: purchases.php"); exit; @@ -64,7 +63,7 @@ include 'includes/header.php';

Manage inventory restocks and supplier invoices

- + New Purchase @@ -72,17 +71,17 @@ include 'includes/header.php'; -
-
+
+
- - + +
- @@ -90,7 +89,7 @@ include 'includes/header.php';
- @@ -98,20 +97,20 @@ include 'includes/header.php';
- + - +
-
+
- - +
+ @@ -139,18 +138,18 @@ include 'includes/header.php'; - - + `).join(''); const customerHtml = data.customer ? ` -
- Customer:
- ${data.customer.name}
- ${data.customer.phone || ''} +
+
+ Customer Details + ${tr['Customer Details']} +
+
+
${data.customer.name}
+ ${data.customer.phone ? `
Tel: ${data.customer.phone}
` : ''} + ${data.customer.address ? `
${data.customer.address}
` : ''}
` : ''; - const tableHtml = data.tableNumber ? `
Table: ${data.tableNumber}
` : ''; - const paymentHtml = data.paymentMethod ? `
Payment: ${data.paymentMethod}
` : ''; - const loyaltyHtml = data.loyaltyRedeemed ? `
* Free Meal Redeemed *
` : ''; + const tableHtml = data.tableNumber ? ` +
+ Table: ${data.tableNumber} + ${tr['Table']}: ${data.tableNumber} +
` : ''; + + const paymentHtml = data.paymentMethod ? ` +
+ Payment: ${data.paymentMethod} + ${tr['Payment']}: ${data.paymentMethod} +
` : ''; + + const loyaltyHtml = data.loyaltyRedeemed ? `
* Loyalty Reward Applied *
` : ''; + + const vatRate = parseFloat(settings.vat_rate) || 0; + const subtotal = data.total + data.discount; + const vatAmount = vatRate > 0 ? (data.total * (vatRate / (100 + vatRate))) : 0; + + const logoHtml = settings.logo_url ? `` : ''; const html = ` - + Receipt #${data.orderId}
-

FLATLOGIC POS

-
123 Main St, City
-
Tel: 123-456-7890
+ ${logoHtml} +

${settings.company_name}

+
${CURRENT_OUTLET.name}
+
${settings.address}
+
Tel: ${settings.phone}
+ ${settings.vat_number ? `
VAT No / الرقم الضريبي: ${settings.vat_number}
` : ''} + ${settings.ctr_number ? `
CTR No / رقم السجل: ${settings.ctr_number}
` : ''}
-
-
Order #${data.orderId}
-
${data.date}
-
Type: ${data.orderType.toUpperCase()}
- ${tableHtml} - ${paymentHtml} - ${loyaltyHtml} +
+ +
+
+ Order: #${data.orderId} + ${tr['Order']}: #${data.orderId} +
+
+ Type: ${data.orderType.toUpperCase()} + ${tr['Type']}: ${tr[data.orderType] || data.orderType} +
+
+ Date: ${data.date} + ${tr['Date']}: ${data.date} +
+
+ Staff: ${CURRENT_USER.name} + ${tr['Staff']}: ${CURRENT_USER.name} +
+ ${tableHtml} + ${paymentHtml} + ${loyaltyHtml} + +
${customerHtml}
Date Supplier -
+
diff --git a/admin/ratings.php b/admin/ratings.php index 19eb9bd..2e32b75 100644 --- a/admin/ratings.php +++ b/admin/ratings.php @@ -8,39 +8,70 @@ if (function_exists('require_permission')) { $pdo = db(); $tab = $_GET['tab'] ?? 'staff'; +$date_from = $_GET['date_from'] ?? ''; +$date_to = $_GET['date_to'] ?? ''; + +$params = []; +$where_clauses = []; + +if ($date_from) { + $where_clauses[] = "r.created_at >= :date_from"; + $params['date_from'] = $date_from . ' 00:00:00'; +} +if ($date_to) { + $where_clauses[] = "r.created_at <= :date_to"; + $params['date_to'] = $date_to . ' 23:59:59'; +} + +$where_sql = !empty($where_clauses) ? "WHERE " . implode(" AND ", $where_clauses) : ""; // Fetch Staff summary stats -$summaryStmt = $pdo->query(" +$summaryQuery = " SELECT u.id, u.full_name, u.username, u.profile_pic, AVG(r.rating) as avg_rating, COUNT(r.id) as total_ratings FROM users u JOIN staff_ratings r ON u.id = r.user_id + $where_sql GROUP BY u.id ORDER BY avg_rating DESC -"); +"; +$summaryStmt = $pdo->prepare($summaryQuery); +$summaryStmt->execute($params); $summaries = $summaryStmt->fetchAll(PDO::FETCH_ASSOC); // Fetch Service summary stats -$serviceSummaryStmt = $pdo->query(" - SELECT AVG(rating) as avg_rating, COUNT(id) as total_ratings FROM service_ratings -"); +$serviceWhereSql = str_replace('r.created_at', 'created_at', $where_sql); +$serviceParams = $params; + +$serviceSummaryQuery = "SELECT AVG(rating) as avg_rating, COUNT(id) as total_ratings FROM service_ratings $serviceWhereSql"; +$serviceSummaryStmt = $pdo->prepare($serviceSummaryQuery); +$serviceSummaryStmt->execute($serviceParams); $serviceSummary = $serviceSummaryStmt->fetch(PDO::FETCH_ASSOC); if ($tab === 'service') { - $query = "SELECT * FROM service_ratings ORDER BY created_at DESC"; + $query = "SELECT * FROM service_ratings $serviceWhereSql ORDER BY created_at DESC"; + $list_params = $serviceParams; } else { $query = " SELECT r.*, u.full_name, u.username, u.profile_pic FROM staff_ratings r JOIN users u ON r.user_id = u.id + $where_sql ORDER BY r.created_at DESC "; + $list_params = $params; } -$pagination = paginate_query($pdo, $query); +$pagination = paginate_query($pdo, $query, $list_params); $ratings = $pagination['data']; ?> + +

Ratings & Feedback

@@ -53,14 +84,38 @@ $ratings = $pagination['data'];
+
+
+
+ +
+ + +
+
+ + +
+
+ + + Reset + +
+
+
+
+
- + @@ -113,43 +138,44 @@ include 'includes/header.php';
- + -
- ${item.name}
- ${item.variant_name ? `(${item.variant_name})` : ''} +
+
${item.name}
+ ${item.variant_name ? `
(${item.variant_name})
` : ''} +
${item.quantity} x ${formatCurrency(item.price)}
${item.quantity} x ${formatCurrency(item.price)}${formatCurrency(item.quantity * item.price)}
- ${itemsHtml} + + + + + + + + ${itemsHtml} +
+ ITEM / الصنف + + TOTAL / المجموع +
+
+
- - + + ${data.discount > 0 ? ` - + ` : ''} - - - + ${vatRate > 0 ? ` + + + + ` : ''} + + +
Subtotal${formatCurrency(data.total + data.discount)}Subtotal / ${tr['Subtotal']}${formatCurrency(subtotal)}
DiscountDiscount / ${tr['Discount']} -${formatCurrency(data.discount)}
Total${formatCurrency(data.total)}
Tax Incl. (${vatRate}%) / ${tr['Tax Included']}${formatCurrency(vatAmount)}
TOTAL / ${tr['TOTAL']}${formatCurrency(data.total)}
+
+ diff --git a/db/migrations/023_add_discount_to_orders.sql b/db/migrations/023_add_discount_to_orders.sql new file mode 100644 index 0000000..213bc6b --- /dev/null +++ b/db/migrations/023_add_discount_to_orders.sql @@ -0,0 +1,2 @@ +-- Add discount column to orders table +ALTER TABLE orders ADD COLUMN discount DECIMAL(10, 2) DEFAULT 0.00 AFTER total_amount; diff --git a/db/migrations/024_add_address_to_customers.sql b/db/migrations/024_add_address_to_customers.sql new file mode 100644 index 0000000..41c0eb2 --- /dev/null +++ b/db/migrations/024_add_address_to_customers.sql @@ -0,0 +1,18 @@ +-- Add address column to customers table +SET @dbname = DATABASE(); +SET @tablename = "customers"; +SET @columnname = "address"; +SET @preparedStatement = (SELECT IF( + ( + SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS + WHERE + (table_name = @tablename) + AND (table_schema = @dbname) + AND (column_name = @columnname) + ) > 0, + "SELECT 1", + "ALTER TABLE customers ADD COLUMN address TEXT;" +)); +PREPARE alterIfNotExists FROM @preparedStatement; +EXECUTE alterIfNotExists; +DEALLOCATE PREPARE alterIfNotExists; diff --git a/db/migrations/025_add_display_layout_to_ads.sql b/db/migrations/025_add_display_layout_to_ads.sql new file mode 100644 index 0000000..88d548f --- /dev/null +++ b/db/migrations/025_add_display_layout_to_ads.sql @@ -0,0 +1,2 @@ +-- Add display_layout column to ads_images table +ALTER TABLE ads_images ADD COLUMN IF NOT EXISTS display_layout ENUM('both', 'split', 'fullscreen') DEFAULT 'both' AFTER is_active; diff --git a/pos.php b/pos.php index 65a908f..866ad7a 100644 --- a/pos.php +++ b/pos.php @@ -485,6 +485,7 @@ if (!$loyalty_settings) { const LOYALTY_SETTINGS = ; const PRODUCT_VARIANTS = ; const PAYMENT_TYPES = ; + const CURRENT_USER = $currentUser['id'], 'name' => $currentUser['name']]) ?>; const CURRENT_OUTLET = $outlet_id, 'name' => $current_outlet_name]) ?>; const BASE_URL = '';