From e135bdd9e063eee5963d63dbb7b6a4ab73e435b8 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Tue, 3 Mar 2026 13:15:17 +0000 Subject: [PATCH] Autosave: 20260303-131517 --- api/checkout.php | 153 +++---- api/search_customers.php | 4 +- company_profile.php | 248 +++++------ customer_statement.php | 384 ++++++------------ customers.php | 202 +++++---- db/migrations/16_add_loyalty_system.sql | 22 + .../17_add_loyalty_discount_to_orders.sql | 2 + includes/lang.php | 38 +- order_details.php | 22 +- orders.php | 10 +- pos.php | 204 +++++++--- receipt.php | 14 +- 12 files changed, 658 insertions(+), 645 deletions(-) create mode 100644 db/migrations/16_add_loyalty_system.sql create mode 100644 db/migrations/17_add_loyalty_discount_to_orders.sql diff --git a/api/checkout.php b/api/checkout.php index 7a4bcdf..bb939a8 100644 --- a/api/checkout.php +++ b/api/checkout.php @@ -16,36 +16,26 @@ $items = $input['items'] ?? []; $vat_total = (float)($input['vat_total'] ?? 0); $total_price = (float)($input['total_price'] ?? 0); $payment_method = $input['payment_method'] ?? null; +$points_to_redeem = (float)($input['points_to_redeem'] ?? 0); $branch_id = $_SESSION['branch_id']; $user_id = $_SESSION['user_id']; -// Check if branch_id is 'all' (happens when super_admin views all branches) +// Check if branch_id is 'all' if ($branch_id === 'all') { - // We need a specific branch_id to save an order. - // If we're in "all" mode, we'll try to get the user's primary branch or just the first available branch. try { $pdo = db(); $stmt_u = $pdo->prepare("SELECT branch_id FROM users WHERE id = ?"); $stmt_u->execute([$user_id]); $u_branch = $stmt_u->fetchColumn(); - if ($u_branch && $u_branch != 'all') { $branch_id = $u_branch; } else { - // Fallback to first branch in user_branches $stmt_ub = $pdo->prepare("SELECT branch_id FROM user_branches WHERE user_id = ? LIMIT 1"); $stmt_ub->execute([$user_id]); $ub_branch = $stmt_ub->fetchColumn(); - if ($ub_branch) { - $branch_id = $ub_branch; - } else { - // Last resort: first branch in system - $branch_id = $pdo->query("SELECT id FROM branches LIMIT 1")->fetchColumn(); - } + $branch_id = $ub_branch ?: $pdo->query("SELECT id FROM branches LIMIT 1")->fetchColumn(); } - } catch (Exception $e) { - // Silently continue, might fail at insert - } + } catch (Exception $e) {} } if (empty($items)) { @@ -60,72 +50,92 @@ try { $payment_status = ($payment_method && $payment_method !== 'pay_later') ? 'paid' : 'unpaid'; if ($order_id) { - // Update existing order $stmt = $pdo->prepare("UPDATE orders SET customer_id = ?, total_price = ?, vat_total = ?, payment_status = ? WHERE id = ? AND branch_id = ?"); $stmt->execute([$customer_id, $total_price, $vat_total, $payment_status, $order_id, $branch_id]); - - // Remove existing items $stmt = $pdo->prepare("DELETE FROM order_items WHERE order_id = ?"); $stmt->execute([$order_id]); } else { - // Create new order $stmt = $pdo->prepare("INSERT INTO orders (branch_id, customer_id, user_id, order_number, total_price, vat_total, status, payment_status) VALUES (?, ?, ?, NULL, ?, ?, 'received', ?)"); $stmt->execute([$branch_id, $customer_id, $user_id, $total_price, $vat_total, $payment_status]); $order_id = $pdo->lastInsertId(); - // Get branch prefix + // Order Number Generation $stmt_prefix = $pdo->prepare("SELECT prefix FROM branches WHERE id = ?"); $stmt_prefix->execute([$branch_id]); - $prefix = $stmt_prefix->fetchColumn() ?: 'ORD'; - if (strlen($prefix) > 3) $prefix = substr($prefix, 0, 3); - $prefix = strtoupper(str_pad($prefix, 3, 'X')); + $prefix = strtoupper(str_pad(substr($stmt_prefix->fetchColumn() ?: 'ORD', 0, 3), 3, 'X')); - // Determine the next serial number for this branch $stmt_max = $pdo->prepare("SELECT order_number FROM orders WHERE branch_id = ? AND order_number LIKE ? ORDER BY id DESC LIMIT 100"); $stmt_max->execute([$branch_id, "$prefix-%"]); - $existing_orders = $stmt_max->fetchAll(PDO::FETCH_COLUMN); - + $existing = $stmt_max->fetchAll(PDO::FETCH_COLUMN); $max_serial = 0; - foreach ($existing_orders as $onum) { + foreach ($existing as $onum) { if (preg_match('/' . preg_quote($prefix) . '-(\d{6})/', $onum, $matches)) { - $serial = (int)$matches[1]; - if ($serial > $max_serial) $max_serial = $serial; + if ((int)$matches[1] > $max_serial) $max_serial = (int)$matches[1]; } } - $next_serial = $max_serial + 1; - - // Format order_number as XXX-###### (6 digits) - $order_number = $prefix . '-' . str_pad($next_serial, 6, '0', STR_PAD_LEFT); - - // Update the order with the generated order_number - $stmt_update = $pdo->prepare("UPDATE orders SET order_number = ? WHERE id = ?"); - $stmt_update->execute([$order_number, $order_id]); + $order_number = $prefix . '-' . str_pad($max_serial + 1, 6, '0', STR_PAD_LEFT); + $pdo->prepare("UPDATE orders SET order_number = ? WHERE id = ?")->execute([$order_number, $order_id]); } - $stmt_item = $pdo->prepare("INSERT INTO order_items (order_id, item_id, variant_id, service_id, quantity, unit_price, vat_amount, subtotal) - VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); + $stmt_item = $pdo->prepare("INSERT INTO order_items (order_id, item_id, variant_id, service_id, quantity, unit_price, vat_amount, subtotal) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"); foreach ($items as $item) { - $subtotal = ($item['price'] * $item['quantity']); - $stmt_item->execute([ - $order_id, - $item['itemId'], - $item['variantId'] ?: null, - $item['serviceId'], - $item['quantity'], - $item['price'], - $item['vatAmount'] ?: 0, - $subtotal - ]); + $stmt_item->execute([$order_id, $item['itemId'], $item['variantId'] ?: null, $item['serviceId'], $item['quantity'], $item['price'], $item['vatAmount'] ?: 0, ($item['price'] * $item['quantity'])]); } - // Handle Payment recording - if ($payment_method && $payment_method !== 'pay_later') { - $stmt_payment = $pdo->prepare("INSERT INTO payments (order_id, amount, payment_method) VALUES (?, ?, ?)"); - $stmt_payment->execute([$order_id, $total_price, $payment_method]); + // Loyalty Redemption Logic + $redeem_amount = 0; + if ($customer_id && $points_to_redeem > 0 && get_setting('loyalty_enabled') === '1') { + $stmt_c = $pdo->prepare("SELECT loyalty_points FROM customers WHERE id = ? FOR UPDATE"); + $stmt_c->execute([$customer_id]); + $curr_points = (float)$stmt_c->fetchColumn(); + + if ($curr_points >= $points_to_redeem) { + $point_val = (float)get_setting('loyalty_currency_per_point', 0.05); + $redeem_amount = $points_to_redeem * $point_val; + + // Cap redemption at order total + if ($redeem_amount > $total_price) { + $redeem_amount = $total_price; + $points_to_redeem = $redeem_amount / $point_val; + } + + // Deduct points + $pdo->prepare("UPDATE customers SET loyalty_points = loyalty_points - ? WHERE id = ?")->execute([$points_to_redeem, $customer_id]); + + // Record loyalty transaction + $pdo->prepare("INSERT INTO loyalty_transactions (customer_id, order_id, points, type, description) VALUES (?, ?, ?, 'redeemed', ?)") + ->execute([$customer_id, $order_id, -$points_to_redeem, "Redeemed for order $order_number"]); + + // Record loyalty payment + $pdo->prepare("INSERT INTO payments (order_id, amount, payment_method) VALUES (?, ?, 'loyalty')") + ->execute([$order_id, $redeem_amount]); + + // Save loyalty discount to order + $pdo->prepare("UPDATE orders SET loyalty_discount = ? WHERE id = ?") + ->execute([$redeem_amount, $order_id]); + } + } + + // Handle Remaining Payment + $remaining_to_pay = $total_price - $redeem_amount; + if ($payment_method && $payment_method !== 'pay_later' && $remaining_to_pay > 0) { + $pdo->prepare("INSERT INTO payments (order_id, amount, payment_method) VALUES (?, ?, ?)") + ->execute([$order_id, $remaining_to_pay, $payment_method]); + } + + // Loyalty Earning Logic + if ($customer_id && get_setting('loyalty_enabled') === '1' && $payment_status === 'paid') { + $pts_per_curr = (float)get_setting('loyalty_points_per_currency', 1); + $points_earned = $remaining_to_pay * $pts_per_curr; + + if ($points_earned > 0) { + $pdo->prepare("UPDATE customers SET loyalty_points = loyalty_points + ? WHERE id = ?")->execute([$points_earned, $customer_id]); + $pdo->prepare("INSERT INTO loyalty_transactions (customer_id, order_id, points, type, description) VALUES (?, ?, ?, 'earned', ?)") + ->execute([$customer_id, $order_id, $points_earned, "Earned from order $order_number"]); + } } - // Fetch customer details for notifications $customer = null; if ($customer_id) { $stmt_cust = $pdo->prepare('SELECT name_ar, phone FROM customers WHERE id = ?'); @@ -135,10 +145,9 @@ try { $pdo->commit(); - // WhatsApp Notifications + // WhatsApp try { if (get_setting('whatsapp_enabled') === '1' && $customer && !empty($customer['phone'])) { - // 1. Order Created Notification $template = get_setting('msg_order_created_ar'); if (!empty($template)) { $details_parts = []; @@ -148,39 +157,11 @@ try { $names = $stmt_names->fetch(); $details_parts[] = ($names['item_name'] ?? 'صنف') . ' (' . ($names['service_name'] ?? 'خدمة') . ') x' . $item['quantity']; } - $order_details = implode(', ', $details_parts); - - // Get order number if not already available - if (!isset($order_number)) { - $stmt_onum = $pdo->prepare('SELECT order_number FROM orders WHERE id = ?'); - $stmt_onum->execute([$order_id]); - $order_number = $stmt_onum->fetchColumn(); - } - - $message = str_replace( - ['{customer_name}', '{order_number}', '{order_details}', '{total_price}'], - [$customer['name_ar'], $order_number, $order_details, $total_price], - $template - ); + $message = str_replace(['{customer_name}', '{order_number}', '{order_details}', '{total_price}'], [$customer['name_ar'], $order_number, implode(', ', $details_parts), $total_price], $template); send_whatsapp_message($customer['phone'], $message); } - - // 2. Payment Received Notification (if paid during checkout) - if ($payment_method && $payment_method !== 'pay_later') { - $template_pay = get_setting('msg_payment_ar'); - if (!empty($template_pay)) { - $message_pay = str_replace( - ['{customer_name}', '{order_number}', '{amount}', '{remaining_balance}'], - [$customer['name_ar'], $order_number, $total_price, 0], - $template_pay - ); - send_whatsapp_message($customer['phone'], $message_pay); - } - } } - } catch (Exception $e) { - // Silently fail for notifications - } + } catch (Exception $e) {} echo json_encode(['success' => true, 'order_id' => $order_id]); } catch (Exception $e) { diff --git a/api/search_customers.php b/api/search_customers.php index 54b949d..416c3cd 100644 --- a/api/search_customers.php +++ b/api/search_customers.php @@ -16,10 +16,10 @@ if (strlen($query) < 2) { } try { - $stmt = db()->prepare("SELECT id, name_en, name_ar, phone FROM customers WHERE name_en LIKE ? OR name_ar LIKE ? OR phone LIKE ? LIMIT 10"); + $stmt = db()->prepare("SELECT id, name_en, name_ar, phone, loyalty_points FROM customers WHERE name_en LIKE ? OR name_ar LIKE ? OR phone LIKE ? LIMIT 10"); $stmt->execute(["%$query%", "%$query%", "%$query%"]); $customers = $stmt->fetchAll(); echo json_encode($customers); } catch (Exception $e) { echo json_encode([]); -} +} \ No newline at end of file diff --git a/company_profile.php b/company_profile.php index dd357a0..7147313 100644 --- a/company_profile.php +++ b/company_profile.php @@ -3,7 +3,6 @@ $title = 'company_profile'; require_once __DIR__ . '/includes/header.php'; if ($current_role === 'limited_viewer') { header('Location: admin.php'); exit; } -// Only super_admin can access company profile if ($current_role !== 'super_admin') { echo '
' . __('Access Denied') . '
'; require_once __DIR__ . '/includes/footer.php'; @@ -14,7 +13,6 @@ $success = ''; $error = ''; $is_super = ($current_role === 'super_admin'); -// Get company data $stmt = db()->query("SELECT * FROM companies LIMIT 1"); $company = $stmt->fetch(); @@ -26,45 +24,34 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $phone = $_POST['phone'] ?? ''; $address_en = $_POST['address_en'] ?? ''; $address_ar = $_POST['address_ar'] ?? ''; - $vat_number = $_POST['vat_no'] ?? ''; // Keep for compatibility - $ctr_no = $_POST['ctr_no'] ?? ''; $vat_no = $_POST['vat_no'] ?? ''; + $ctr_no = $_POST['ctr_no'] ?? ''; - // Handle Logo Upload $logo = $company['logo']; if (isset($_FILES['logo']) && $_FILES['logo']['error'] === UPLOAD_ERR_OK) { $ext = pathinfo($_FILES['logo']['name'], PATHINFO_EXTENSION); $filename = 'logo_' . time() . '.' . $ext; $target = 'assets/images/company/' . $filename; - if (move_uploaded_file($_FILES['logo']['tmp_name'], $target)) { - $logo = $target; - } + if (move_uploaded_file($_FILES['logo']['tmp_name'], $target)) $logo = $target; } - // Handle Favicon Upload $favicon = $company['favicon']; if (isset($_FILES['favicon']) && $_FILES['favicon']['error'] === UPLOAD_ERR_OK) { $ext = pathinfo($_FILES['favicon']['name'], PATHINFO_EXTENSION); $filename = 'favicon_' . time() . '.' . $ext; $target = 'assets/images/company/' . $filename; - if (move_uploaded_file($_FILES['favicon']['tmp_name'], $target)) { - $favicon = $target; - } + if (move_uploaded_file($_FILES['favicon']['tmp_name'], $target)) $favicon = $target; } try { $stmt = db()->prepare("UPDATE companies SET name_en = ?, name_ar = ?, logo = ?, favicon = ?, email = ?, phone = ?, address_en = ?, address_ar = ?, vat_number = ?, ctr_no = ?, vat_no = ? WHERE id = ?"); - $stmt->execute([$name_en, $name_ar, $logo, $favicon, $email, $phone, $address_en, $address_ar, $vat_number, $ctr_no, $vat_no, $company['id']]); + $stmt->execute([$name_en, $name_ar, $logo, $favicon, $email, $phone, $address_en, $address_ar, $vat_no, $ctr_no, $vat_no, $company['id']]); $success = __('success_update'); - // Refresh data $stmt = db()->query("SELECT * FROM companies LIMIT 1"); $company = $stmt->fetch(); - } catch (Exception $e) { - $error = __('error_update') . ' ' . $e->getMessage(); - } + } catch (Exception $e) { $error = __('error_update') . ' ' . $e->getMessage(); } } - // Handle WhatsApp Settings Save if ($is_super && isset($_POST['save_whatsapp_settings'])) { set_setting('whatsapp_enabled', $_POST['whatsapp_enabled'] ?? '0'); set_setting('wablas_token', $_POST['wablas_token'] ?? ''); @@ -73,10 +60,16 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { set_setting('msg_order_created_ar', $_POST['msg_order_created_ar'] ?? ''); set_setting('msg_order_ready_ar', $_POST['msg_order_ready_ar'] ?? ''); set_setting('msg_payment_ar', $_POST['msg_payment_ar'] ?? ''); - $success = is_arabic() ? 'تم حفظ إعدادات الواتساب بنجاح' : 'WhatsApp settings saved successfully'; + $success = __('success_update'); + } + + if ($is_super && isset($_POST['save_loyalty_settings'])) { + set_setting('loyalty_enabled', $_POST['loyalty_enabled'] ?? '0'); + set_setting('loyalty_points_per_currency', $_POST['loyalty_points_per_currency'] ?? '1'); + set_setting('loyalty_currency_per_point', $_POST['loyalty_currency_per_point'] ?? '0.05'); + $success = __('success_update'); } } - ?>
@@ -84,122 +77,105 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
-
- - - -
- - -
-
-
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
-
- - - - Logo - -
-
- - - - Favicon - -
-
-
-
- - -
-
- - -
-
-
- -
-
-
- - -
-
-
-
-
-
- > - -
-
-
- - -
-
- - -
-
- - -
-
- - - Tags: {customer_name}, {order_number}, {order_details}, {total_price} -
-
- - - Tags: {customer_name}, {order_number} -
-
- - - Tags: {customer_name}, {order_number}, {amount}, {remaining_balance} -
-
- -
-
-
+ - \ No newline at end of file + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
Logo
+
Favicon
+
+
+
+
+
+
+
+
+ +
+ +
+
+
+
+
+ > + +
+
+
+
+
+
+
+
+ +
+
+
+
+
+ > + +
+
+ + +
+
+ + +
+
+
+
+
+ +
+ + +
+
+
+
+ + + Tags: {customer_name}, {order_number}, {order_details}, {total_price} +
+
+ + + Tags: {customer_name}, {order_number} +
+
+ + + Tags: {customer_name}, {order_number}, {amount}, {remaining_balance} +
+
+
+
+ + + diff --git a/customer_statement.php b/customer_statement.php index d9cb4b3..18037b3 100644 --- a/customer_statement.php +++ b/customer_statement.php @@ -3,45 +3,24 @@ $title = 'customer_statement'; require_once __DIR__ . '/includes/header.php'; $customer_id = $_GET['id'] ?? null; -if (!$customer_id) { - header('Location: customers.php'); - exit; -} +if (!$customer_id) { header('Location: customers.php'); exit; } -// Fetch customer details $stmt = db()->prepare("SELECT * FROM customers WHERE id = ?"); $stmt->execute([$customer_id]); $customer = $stmt->fetch(); - -if (!$customer) { - header('Location: customers.php'); - exit; -} +if (!$customer) { header('Location: customers.php'); exit; } $from_date = $_GET['from_date'] ?? ''; $to_date = $_GET['to_date'] ?? ''; +$active_tab = $_GET['tab'] ?? 'financial'; -// Base queries +// Financial Logic $orders_sql = "SELECT id, order_number, total_price, created_at FROM orders WHERE customer_id = ?"; -$payments_sql = "SELECT p.id, p.amount, p.payment_method, p.created_at, o.order_number - FROM payments p - JOIN orders o ON p.order_id = o.id - WHERE o.customer_id = ?"; -$params = [$customer_id]; - -if ($from_date) { - $orders_sql .= " AND DATE(created_at) >= ?"; - $payments_sql .= " AND DATE(p.created_at) >= ?"; - $params[] = $from_date; -} -if ($to_date) { - $orders_sql .= " AND DATE(created_at) <= ?"; - $payments_sql .= " AND DATE(p.created_at) <= ?"; - $params[] = $to_date; -} +$payments_sql = "SELECT p.id, p.amount, p.payment_method, p.created_at, o.order_number FROM payments p JOIN orders o ON p.order_id = o.id WHERE o.customer_id = ?"; +if ($from_date) { $orders_sql .= " AND DATE(created_at) >= ?"; $payments_sql .= " AND DATE(p.created_at) >= ?"; } +if ($to_date) { $orders_sql .= " AND DATE(created_at) <= ?"; $payments_sql .= " AND DATE(p.created_at) <= ?"; } $stmt_orders = db()->prepare($orders_sql); -// We need to be careful with params if both filters are set $orders_params = [$customer_id]; if ($from_date) $orders_params[] = $from_date; if ($to_date) $orders_params[] = $to_date; @@ -55,292 +34,161 @@ if ($to_date) $payments_params[] = $to_date; $stmt_payments->execute($payments_params); $payments = $stmt_payments->fetchAll(); -// Combine and sort $transactions = []; -foreach ($orders as $o) { - $transactions[] = [ - 'date' => $o['created_at'], - 'type' => 'order', - 'ref' => $o['order_number'], - 'debit' => $o['total_price'], - 'credit' => 0, - 'description' => __('order') . ' #' . $o['order_number'] - ]; -} -foreach ($payments as $p) { - $transactions[] = [ - 'date' => $p['created_at'], - 'type' => 'payment', - 'ref' => $p['order_number'], - 'debit' => 0, - 'credit' => $p['amount'], - 'description' => __('payment') . ' (' . __($p['payment_method']) . ') - ' . __('order') . ' #' . $p['order_number'] - ]; +foreach ($orders as $o) { $transactions[] = ['date' => $o['created_at'], 'type' => 'order', 'ref' => $o['order_number'], 'debit' => $o['total_price'], 'credit' => 0, 'description' => __('order') . ' #' . $o['order_number']]; } +foreach ($payments as $p) { $transactions[] = ['date' => $p['created_at'], 'type' => 'payment', 'ref' => $p['order_number'], 'debit' => 0, 'credit' => $p['amount'], 'description' => __('payment') . ' (' . __($p['payment_method']) . ') - ' . __('order') . ' #' . $p['order_number']]; } +usort($transactions, function($a, $b) { return strtotime($a['date']) - strtotime($b['date']); }); +$total_debit = 0; $total_credit = 0; $balance = 0; +foreach ($transactions as &$t) { $total_debit += $t['debit']; $total_credit += $t['credit']; $balance += ($t['debit'] - $t['credit']); $t['running_balance'] = $balance; } + +// Loyalty Logic +$loyalty_sql = "SELECT lt.*, o.order_number FROM loyalty_transactions lt LEFT JOIN orders o ON lt.order_id = o.id WHERE lt.customer_id = ?"; +if ($from_date) $loyalty_sql .= " AND DATE(lt.created_at) >= ?"; +if ($to_date) $loyalty_sql .= " AND DATE(lt.created_at) <= ?"; +$loyalty_sql .= " ORDER BY lt.created_at ASC"; +$stmt_loyalty = db()->prepare($loyalty_sql); +$loyalty_params = [$customer_id]; +if ($from_date) $loyalty_params[] = $from_date; +if ($to_date) $loyalty_params[] = $to_date; +$stmt_loyalty->execute($loyalty_params); +$loyalty_transactions = $stmt_loyalty->fetchAll(); + +$loyalty_balance = 0; +foreach ($loyalty_transactions as &$lt) { + $loyalty_balance += $lt['points']; + $lt['running_balance'] = $loyalty_balance; } -usort($transactions, function($a, $b) { - return strtotime($a['date']) - strtotime($b['date']); -}); - -$total_debit = 0; -$total_credit = 0; -$balance = 0; -foreach ($transactions as &$t) { - $total_debit += $t['debit']; - $total_credit += $t['credit']; - $balance += ($t['debit'] - $t['credit']); - $t['running_balance'] = $balance; -} - -// Fallback values if database is empty $display_company_name = ($lang === 'ar' ? ($company_info['name_ar'] ?: $company_info['name_en']) : $company_info['name_en']) ?: 'Laundry Brand'; $display_address = ($lang === 'ar' ? ($company_info['address_ar'] ?: $company_info['address_en']) : $company_info['address_en']); -$display_phone = $company_info['phone']; -$display_email = $company_info['email']; -$display_vat = $company_info['vat_no'] ?: $company_info['vat_number']; -$display_ctr = $company_info['ctr_no']; - ?>
-
+
-
- - -
-
- - -
+ +
+
- - + +
-
- + + + +
+
- - Logo - -
- - Laundry Management -
- + Logo

-
- -
- - - - : - - - - :
- -
- - - - : - - - - : - -
+

:
-

-
+

+

:

-

:

- -

- - -

- +

-
-
-
-

-

-
+

-

+

-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- -
0 ? format_amount($t['debit']) : '-' ?> 0 ? format_amount($t['credit']) : '-' ?>
-
- -
-
-
-
-
-
- : - -
-
- : - -
-
- : - -
-
-
+ +
+ + + + + + + + + + + +
0 ? format_amount($t['debit']) : '-' ?> 0 ? format_amount($t['credit']) : '-' ?>
-
- - -
-
-
-
_______________________
-
-
-
-
-
-
-
-
_______________________
-
+ +
+ + + + + + + + + + + + + + + + + + +
'.$lt['order_number'].'' : '-' ?> 0 ? '+'.number_format($lt['points'], 2) : '-' ?>
+
+ + + +
+
+
+
+ +
:
+
:
+
:
+ +
:
+
- \ No newline at end of file + diff --git a/customers.php b/customers.php index 2ea97e6..7e07674 100644 --- a/customers.php +++ b/customers.php @@ -1,5 +1,4 @@ prepare("SELECT COUNT(*) FROM orders WHERE customer_id = ?"); $stmt->execute([$id]); if ($stmt->fetchColumn() > 0) { header('Location: customers.php?error=customer_has_orders'); exit; } - $stmt = db()->prepare("DELETE FROM customers WHERE id = ?"); $stmt->execute([$id]); header('Location: customers.php?success=customer_deleted'); exit; + } elseif ($action === 'adjust_points') { + $id = $_POST['id']; + $points = (float)$_POST['points']; + $type = $_POST['type']; // 'add' or 'subtract' + $desc = $_POST['description'] ?? 'Manual adjustment'; + + $pdo = db(); + $pdo->beginTransaction(); + try { + $val = ($type === 'add') ? $points : -$points; + $pdo->prepare("UPDATE customers SET loyalty_points = loyalty_points + ? WHERE id = ?")->execute([$val, $id]); + $pdo->prepare("INSERT INTO loyalty_transactions (customer_id, points, type, description) VALUES (?, ?, 'adjusted', ?)") + ->execute([$id, $val, $desc]); + $pdo->commit(); + header('Location: customers.php?success=points_adjusted'); + exit; + } catch (Exception $e) { + $pdo->rollBack(); + header('Location: customers.php?error=' . urlencode($e->getMessage())); + exit; + } } } $title = 'customers'; require_once __DIR__ . '/includes/header.php'; - $search = $_GET['search'] ?? ''; $sql = "SELECT * FROM customers WHERE 1=1"; $params = []; - if ($search) { $sql .= " AND (name_en LIKE ? OR name_ar LIKE ? OR phone LIKE ?)"; - $params[] = "%$search%"; - $params[] = "%$search%"; - $params[] = "%$search%"; + $params[] = "%$search%"; $params[] = "%$search%"; $params[] = "%$search%"; } - $sql .= " ORDER BY created_at DESC"; $stmt = db()->prepare($sql); $stmt->execute($params); $customers = $stmt->fetchAll(); - ?>
@@ -104,31 +114,27 @@ $customers = $stmt->fetchAll();
- - + +
-
-