diff --git a/admin.php b/admin.php
index 1a077b3..9ab7a86 100644
--- a/admin.php
+++ b/admin.php
@@ -51,7 +51,7 @@ $recent_orders = $stmt->fetchAll();
| = $order['id'] ?> |
= $lang === 'ar' ? ($order['customer_name_ar'] ?: $order['customer_name_en']) : $order['customer_name_en'] ?> |
- = number_format($order['total_price'], 2) ?> |
+ = format_amount($order['total_price']) ?> |
= __($order['status']) ?> |
= __($order['payment_status']) ?> |
@@ -167,4 +167,3 @@ function getPaymentStatusColor($status) {
][$status] ?? 'info';
}
require_once __DIR__ . '/includes/footer.php';
-?>
\ No newline at end of file
diff --git a/api/checkout.php b/api/checkout.php
index 541565a..0911d2b 100644
--- a/api/checkout.php
+++ b/api/checkout.php
@@ -9,6 +9,7 @@ if (!isset($_SESSION['user_id'])) {
}
$input = json_decode(file_get_contents('php://input'), true);
+$order_id = $input['order_id'] ?? null;
$customer_id = $input['customer_id'] ?: null;
$items = $input['items'] ?? [];
$vat_total = (float)($input['vat_total'] ?? 0);
@@ -25,15 +26,35 @@ try {
$pdo = db();
$pdo->beginTransaction();
- // Recalculate total if not provided correctly, but for now we trust the client-side breakdown
- // If we wanted to be more secure, we'd fetch prices from DB here.
+ if ($order_id) {
+ // Update existing order
+ $stmt = $pdo->prepare("UPDATE orders SET customer_id = ?, total_price = ?, vat_total = ? WHERE id = ? AND branch_id = ?");
+ $stmt->execute([$customer_id, $total_price, $vat_total, $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', 'unpaid')");
+ $stmt->execute([$branch_id, $customer_id, $user_id, $total_price, $vat_total]);
+ $order_id = $pdo->lastInsertId();
- $order_number = 'ORD-' . time() . '-' . rand(100, 999);
-
- $stmt = $pdo->prepare("INSERT INTO orders (branch_id, customer_id, user_id, order_number, total_price, vat_total, status, payment_status)
- VALUES (?, ?, ?, ?, ?, ?, 'received', 'unpaid')");
- $stmt->execute([$branch_id, $customer_id, $user_id, $order_number, $total_price, $vat_total]);
- $order_id = $pdo->lastInsertId();
+ // Get branch prefix
+ $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 = str_pad($prefix, 3, 'X'); // Just in case it's shorter than 3
+
+ // Format order_number as XXX#####1 (5 digits for #####)
+ $order_number = strtoupper($prefix) . str_pad($order_id, 5, '0', STR_PAD_LEFT) . '1';
+
+ // 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]);
+ }
$stmt_item = $pdo->prepare("INSERT INTO order_items (order_id, item_id, variant_id, service_id, quantity, unit_price, vat_amount, subtotal)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
diff --git a/api/delete_order.php b/api/delete_order.php
new file mode 100644
index 0000000..e0e8bba
--- /dev/null
+++ b/api/delete_order.php
@@ -0,0 +1,31 @@
+ false, 'error' => 'Unauthorized']);
+ exit;
+}
+
+$input = json_decode(file_get_contents('php://input'), true);
+$id = $input['id'] ?? null;
+
+if (!$id) {
+ echo json_encode(['success' => false, 'error' => 'Missing order ID']);
+ exit;
+}
+
+try {
+ $pdo = db();
+ $stmt = $pdo->prepare("DELETE FROM orders WHERE id = ?");
+ $stmt->execute([$id]);
+
+ if ($stmt->rowCount() > 0) {
+ echo json_encode(['success' => true]);
+ } else {
+ echo json_encode(['success' => false, 'error' => 'Order not found or already deleted']);
+ }
+} catch (Exception $e) {
+ echo json_encode(['success' => false, 'error' => $e->getMessage()]);
+}
diff --git a/api/get_order_items.php b/api/get_order_items.php
new file mode 100644
index 0000000..f26fe80
--- /dev/null
+++ b/api/get_order_items.php
@@ -0,0 +1,35 @@
+ false, 'error' => 'Order ID is required']);
+ exit;
+}
+
+try {
+ $stmt = db()->prepare("SELECT oi.quantity, i.name_en as item_en, i.name_ar as item_ar,
+ s.name_en as service_en, s.name_ar as service_ar
+ FROM order_items oi
+ JOIN items i ON oi.item_id = i.id
+ JOIN services s ON oi.service_id = s.id
+ WHERE oi.order_id = ?");
+ $stmt->execute([$order_id]);
+ $items = $stmt->fetchAll(PDO::FETCH_ASSOC);
+
+ // Fetch order number for the modal title
+ $stmt = db()->prepare("SELECT order_number FROM orders WHERE id = ?");
+ $stmt->execute([$order_id]);
+ $order = $stmt->fetch(PDO::FETCH_ASSOC);
+
+ echo json_encode([
+ 'success' => true,
+ 'order_number' => $order['order_number'] ?? '',
+ 'items' => $items
+ ]);
+} catch (Exception $e) {
+ echo json_encode(['success' => false, 'error' => $e->getMessage()]);
+}
diff --git a/api/update_order_status.php b/api/update_order_status.php
new file mode 100644
index 0000000..892d6ba
--- /dev/null
+++ b/api/update_order_status.php
@@ -0,0 +1,38 @@
+ false, 'error' => 'Unauthorized']);
+ exit;
+}
+
+$input = json_decode(file_get_contents('php://input'), true);
+$id = $input['id'] ?? null;
+$status = $input['status'] ?? null;
+
+if (!$id || !$status) {
+ echo json_encode(['success' => false, 'error' => 'Missing order ID or status']);
+ exit;
+}
+
+$valid_statuses = ['received', 'processing', 'ready', 'delivered', 'cancelled'];
+if (!in_array($status, $valid_statuses)) {
+ echo json_encode(['success' => false, 'error' => 'Invalid status']);
+ exit;
+}
+
+try {
+ $pdo = db();
+ $stmt = $pdo->prepare("UPDATE orders SET status = ? WHERE id = ?");
+ $stmt->execute([$status, $id]);
+
+ if ($stmt->rowCount() > 0) {
+ echo json_encode(['success' => true]);
+ } else {
+ echo json_encode(['success' => false, 'error' => 'Order not found or status already same']);
+ }
+} catch (Exception $e) {
+ echo json_encode(['success' => false, 'error' => $e->getMessage()]);
+}
diff --git a/branches.php b/branches.php
index a3c63b9..79048e1 100644
--- a/branches.php
+++ b/branches.php
@@ -20,11 +20,33 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$name_ar = $_POST['name_ar'];
$company_id = $_POST['company_id'];
$phone = $_POST['phone'];
- $stmt = db()->prepare("INSERT INTO branches (name_en, name_ar, company_id, phone) VALUES (?, ?, ?, ?)");
- $stmt->execute([$name_en, $name_ar, $company_id, $phone]);
+ $prefix = strtoupper(substr($_POST['prefix'] ?? '', 0, 3));
+ $stmt = db()->prepare("INSERT INTO branches (name_en, name_ar, company_id, phone, prefix) VALUES (?, ?, ?, ?, ?)");
+ $stmt->execute([$name_en, $name_ar, $company_id, $phone, $prefix]);
header('Location: branches.php');
exit;
}
+
+ if ($_POST['action'] === 'edit_branch') {
+ $id = $_POST['id'];
+ $name_en = $_POST['name_en'];
+ $name_ar = $_POST['name_ar'];
+ $company_id = $_POST['company_id'];
+ $phone = $_POST['phone'];
+ $prefix = strtoupper(substr($_POST['prefix'] ?? '', 0, 3));
+ $stmt = db()->prepare("UPDATE branches SET name_en = ?, name_ar = ?, company_id = ?, phone = ?, prefix = ? WHERE id = ?");
+ $stmt->execute([$name_en, $name_ar, $company_id, $phone, $prefix, $id]);
+ header('Location: branches.php');
+ exit;
+ }
+}
+
+if (isset($_GET['delete'])) {
+ $id = $_GET['delete'];
+ $stmt = db()->prepare("DELETE FROM branches WHERE id = ?");
+ $stmt->execute([$id]);
+ header('Location: branches.php');
+ exit;
}
// NOW Include header
@@ -57,6 +79,11 @@ $companies = db()->query("SELECT * FROM companies")->fetchAll();
+
+
+
+ = __('exactly_3_letters') ?? 'Exactly 3 letters' ?>
+
+
+
+
+
+
\ No newline at end of file
diff --git a/company_profile.php b/company_profile.php
new file mode 100644
index 0000000..420f5c7
--- /dev/null
+++ b/company_profile.php
@@ -0,0 +1,138 @@
+' . __('Access Denied') . '';
+ require_once __DIR__ . '/includes/footer.php';
+ exit;
+}
+
+$success = '';
+$error = '';
+
+// Get company data
+$stmt = db()->query("SELECT * FROM companies LIMIT 1");
+$company = $stmt->fetch();
+
+if ($_SERVER['REQUEST_METHOD'] === 'POST') {
+ $name_en = $_POST['name_en'] ?? '';
+ $name_ar = $_POST['name_ar'] ?? '';
+ $email = $_POST['email'] ?? '';
+ $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'] ?? '';
+
+ // 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;
+ }
+ }
+
+ // 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;
+ }
+ }
+
+ 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']]);
+ $success = __('success_update');
+ // Refresh data
+ $stmt = db()->query("SELECT * FROM companies LIMIT 1");
+ $company = $stmt->fetch();
+ } catch (Exception $e) {
+ $error = __('error_update') . ' ' . $e->getMessage();
+ }
+}
+
+?>
+
+
+ = __('company_profile') ?>
+
+
+
+ = $success ?>
+
+
+
+ = $error ?>
+
+
+
+
+
\ No newline at end of file
diff --git a/db/migrations/06_profile_additions.sql b/db/migrations/06_profile_additions.sql
new file mode 100644
index 0000000..c1b49a3
--- /dev/null
+++ b/db/migrations/06_profile_additions.sql
@@ -0,0 +1,13 @@
+-- Add logo, favicon and other details to companies table
+ALTER TABLE companies
+ADD COLUMN logo VARCHAR(255) DEFAULT NULL AFTER name_ar,
+ADD COLUMN favicon VARCHAR(255) DEFAULT NULL AFTER logo,
+ADD COLUMN email VARCHAR(100) DEFAULT NULL AFTER favicon,
+ADD COLUMN phone VARCHAR(20) DEFAULT NULL AFTER email,
+ADD COLUMN address_en TEXT DEFAULT NULL AFTER phone,
+ADD COLUMN address_ar TEXT DEFAULT NULL AFTER address_en,
+ADD COLUMN vat_number VARCHAR(50) DEFAULT NULL AFTER address_ar;
+
+-- Add profile_picture to users table
+ALTER TABLE users
+ADD COLUMN profile_picture VARCHAR(255) DEFAULT NULL AFTER email;
diff --git a/db/migrations/07_update_currency_precision.sql b/db/migrations/07_update_currency_precision.sql
new file mode 100644
index 0000000..d73a331
--- /dev/null
+++ b/db/migrations/07_update_currency_precision.sql
@@ -0,0 +1,8 @@
+-- Update decimal precision for all monetary columns to 3 decimals
+ALTER TABLE prices MODIFY COLUMN price DECIMAL(12, 3) NOT NULL DEFAULT 0.000;
+ALTER TABLE orders MODIFY COLUMN total_price DECIMAL(12, 3) NOT NULL DEFAULT 0.000;
+ALTER TABLE orders MODIFY COLUMN vat_total DECIMAL(12, 3) DEFAULT 0.000;
+ALTER TABLE order_items MODIFY COLUMN unit_price DECIMAL(12, 3) NOT NULL;
+ALTER TABLE order_items MODIFY COLUMN vat_amount DECIMAL(12, 3) DEFAULT 0.000;
+ALTER TABLE order_items MODIFY COLUMN subtotal DECIMAL(12, 3) NOT NULL;
+ALTER TABLE payments MODIFY COLUMN amount DECIMAL(12, 3) NOT NULL;
diff --git a/db/migrations/08_add_ctr_vat_no.sql b/db/migrations/08_add_ctr_vat_no.sql
new file mode 100644
index 0000000..fca8fe7
--- /dev/null
+++ b/db/migrations/08_add_ctr_vat_no.sql
@@ -0,0 +1,6 @@
+-- Add CTR No and VAT No to companies table
+ALTER TABLE companies ADD COLUMN ctr_no VARCHAR(50) DEFAULT NULL AFTER vat_number;
+ALTER TABLE companies ADD COLUMN vat_no VARCHAR(50) DEFAULT NULL AFTER ctr_no;
+
+-- Migrate existing vat_number to vat_no if any
+UPDATE companies SET vat_no = vat_number WHERE vat_no IS NULL AND vat_number IS NOT NULL;
diff --git a/db/migrations/09_add_branch_prefix.sql b/db/migrations/09_add_branch_prefix.sql
new file mode 100644
index 0000000..7718501
--- /dev/null
+++ b/db/migrations/09_add_branch_prefix.sql
@@ -0,0 +1,2 @@
+-- Migration 09: Add prefix column to branches table
+ALTER TABLE branches ADD COLUMN prefix VARCHAR(3) DEFAULT NULL;
diff --git a/includes/header.php b/includes/header.php
index c0333eb..935870d 100644
--- a/includes/header.php
+++ b/includes/header.php
@@ -8,18 +8,34 @@ if (!isset($_SESSION['user_id']) && basename($_SERVER['PHP_SELF']) !== 'login.ph
exit;
}
-$current_user = $_SESSION['user_id'] ?? null;
+$current_user_id = $_SESSION['user_id'] ?? null;
$current_branch = $_SESSION['branch_id'] ?? null;
$current_role = $_SESSION['role'] ?? 'cashier';
+// Fetch Global Company Info
+$stmt = db()->query("SELECT * FROM companies LIMIT 1");
+$company_info = $stmt->fetch();
+
+// Fetch Current User Info (for profile picture)
+$current_user_data = null;
+if ($current_user_id) {
+ $stmt = db()->prepare("SELECT * FROM users WHERE id = ?");
+ $stmt->execute([$current_user_id]);
+ $current_user_data = $stmt->fetch();
+}
+
?>
- Laundry System - = __($title ?? 'dashboard') ?>
+ = htmlspecialchars(is_arabic() ? $company_info['name_ar'] : $company_info['name_en']) ?> - = __($title ?? 'dashboard') ?>
+
+
+
+
@@ -69,6 +85,16 @@ $current_role = $_SESSION['role'] ?? 'cashier';
.lang-switch {
font-size: 0.9rem;
}
+ .user-avatar-sm {
+ width: 32px;
+ height: 32px;
+ object-fit: cover;
+ border-radius: 50%;
+ }
+ .dropdown-item i {
+ margin-right: 0.5rem;
+ = is_rtl() ? 'margin-left: 0.5rem; margin-right: 0;' : '' ?>
+ }
@@ -78,8 +104,12 @@ $current_role = $_SESSION['role'] ?? 'cashier';
|