From 1ac0f55ef85a9cc7f35840a1824db6da94570546 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Sun, 19 Apr 2026 09:52:55 +0000 Subject: [PATCH] update sales id --- assets/css/custom.css | 36 ++++- cookies.txt | 2 +- edit_sale.php | 29 +++- includes/app.php | 48 ++---- includes/sale_form.php | 32 +++- login.php | 21 +-- print_receipt.php | 4 +- reports.php | 331 ++++++++++++++++++++++++++++++++++++----- sale.php | 4 +- sales.php | 5 +- users.php | 256 ++++++++++++++++++++----------- 11 files changed, 561 insertions(+), 207 deletions(-) diff --git a/assets/css/custom.css b/assets/css/custom.css index 110658f..0fdc9a2 100644 --- a/assets/css/custom.css +++ b/assets/css/custom.css @@ -166,4 +166,38 @@ body.auth-body { [dir="rtl"] .form-select-lg { padding-right: 1rem; padding-left: 3rem; -} \ No newline at end of file +} + +/* Print specific styles */ +@media print { + #sidebar-wrapper, .top-navbar, .d-print-none { + display: none !important; + } + #page-content-wrapper { + margin: 0 !important; + width: 100% !important; + min-width: 100% !important; + padding: 0 !important; + } + body { + font-size: 11pt; + background-color: #fff; + } + .table { + font-size: 10pt; + margin-bottom: 1rem; + } + .table th, .table td { + padding: 0.3rem; + } + .fs-5 { + font-size: 1rem !important; + } + h2 { + font-size: 1.5rem; + } + .surface-card { + box-shadow: none !important; + border: none !important; + } +} diff --git a/cookies.txt b/cookies.txt index 24dbe0b..0f3bb80 100644 --- a/cookies.txt +++ b/cookies.txt @@ -2,4 +2,4 @@ # https://curl.se/docs/http-cookies.html # This file was generated by libcurl! Edit at your own risk. -127.0.0.1 FALSE / FALSE 0 PHPSESSID 6qft39lctsp4e64kmen99qtqfo +127.0.0.1 FALSE / FALSE 0 PHPSESSID une8u6o0qtfojm7tp3ppv39knh diff --git a/edit_sale.php b/edit_sale.php index 4e6c672..7f07565 100644 --- a/edit_sale.php +++ b/edit_sale.php @@ -46,6 +46,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } else { $normalized = []; $subtotal = 0.0; + $totalVat = 0.0; $itemCount = 0; foreach ($items as $item) { $sku = (string) ($item['sku'] ?? ''); @@ -56,6 +57,11 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $product = $catalog[$sku]; $price = (float) $product['price']; $lineTotal = $price * $qty; + + $vatPercent = (float) ($product['vat'] ?? 0); + $itemVat = $lineTotal - ($lineTotal / (1 + ($vatPercent / 100))); + $totalVat += $itemVat; + $normalized[] = [ 'sku' => $sku, 'name_ar' => $product['name_ar'], @@ -63,6 +69,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { 'qty' => $qty, 'price' => $price, 'line_total' => $lineTotal, + 'vat_percent' => $vatPercent, + 'vat_amount' => $itemVat ]; $subtotal += $lineTotal; $itemCount += $qty; @@ -79,6 +87,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { items_json = :items_json, item_count = :item_count, subtotal = :subtotal, + vat_amount = :vat_amount, total_amount = :total_amount, status = :status, notes = :notes @@ -380,8 +389,8 @@ require __DIR__ . '/includes/header.php'; 0.000
- - + + 0.000
@@ -633,18 +642,24 @@ function renderInvoice() { if (skus.length === 0) { tbody.innerHTML = ''; tbody.appendChild(emptyRow); - updateTotals(0); + updateTotals(0, 0); cartJson.value = '[]'; return; } tbody.innerHTML = ''; let totalAmount = 0; + let totalVat = 0; const cartData = []; skus.forEach(sku => { const item = invoiceItems[sku]; const lineTotal = item.qty * item.price; + + const vatPercent = parseFloat(catalogData[sku].vat) || 0; + const itemVat = lineTotal - (lineTotal / (1 + (vatPercent / 100))); + totalVat += itemVat; + totalAmount += lineTotal; cartData.push({ sku: item.sku, qty: item.qty }); @@ -668,12 +683,14 @@ function renderInvoice() { tbody.appendChild(tr); }); - updateTotals(totalAmount); + updateTotals(totalAmount, totalVat); cartJson.value = JSON.stringify(cartData); } -function updateTotals(total) { - document.getElementById('displaySubtotal').innerText = total.toFixed(3); +function updateTotals(total, vat) { + const subtotal = total - vat; + document.getElementById('displaySubtotal').innerText = subtotal.toFixed(3); + document.getElementById('displayVat').innerText = vat.toFixed(3); document.getElementById('displayTotal').innerText = total.toFixed(3) + currencySuffix; } diff --git a/includes/app.php b/includes/app.php index 6a430e3..e3a935f 100644 --- a/includes/app.php +++ b/includes/app.php @@ -115,35 +115,6 @@ function branch_label(string $code): string return current_lang() === 'ar' ? $branch['name_ar'] : $branch['name_en']; } -function demo_users(): array -{ - return [ - 'owner' => [ - 'username' => 'owner', - 'password' => 'owner123', - 'role' => 'owner', - 'branch_code' => 'muscat', - 'name_ar' => 'مالك النظام', - 'name_en' => 'System Owner', - ], - 'manager_muscat' => [ - 'username' => 'manager_muscat', - 'password' => 'manager123', - 'role' => 'manager', - 'branch_code' => 'muscat', - 'name_ar' => 'مدير فرع مسقط', - 'name_en' => 'Muscat Branch Manager', - ], - 'cashier_sohar' => [ - 'username' => 'cashier_sohar', - 'password' => 'cashier123', - 'role' => 'cashier', - 'branch_code' => 'sohar', - 'name_ar' => 'كاشير فرع صحار', - 'name_en' => 'Sohar Cashier', - ], - ]; -} function role_label(string $role): string { @@ -162,18 +133,21 @@ function current_user(): ?array function login_attempt(string $username, string $password): bool { - $users = demo_users(); - if (!isset($users[$username])) { + require_once __DIR__ . "/../db/config.php"; + $stmt = db()->prepare("SELECT * FROM users WHERE username = ?"); + $stmt->execute([$username]); + $user = $stmt->fetch(); + + if (!$user) { return false; } - $user = $users[$username]; - if ($user['password'] !== $password) { - return false; + if (password_verify($password, $user["password"])) { + $_SESSION["auth_user"] = $user; + return true; } - $_SESSION['auth_user'] = $user; - return true; + return false; } function logout_user(): void @@ -525,7 +499,7 @@ function purchase_pipeline(): array function receipt_code(): string { - return 'AR-' . date('ymd-His') . '-' . random_int(100, 999); + return (string) random_int(100000, 999999); } function create_purchase(array $data): int diff --git a/includes/sale_form.php b/includes/sale_form.php index ebf70ab..860bb8b 100644 --- a/includes/sale_form.php +++ b/includes/sale_form.php @@ -31,6 +31,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { } else { $normalized = []; $subtotal = 0.0; + $totalVat = 0.0; $itemCount = 0; foreach ($items as $item) { $sku = (string) ($item['sku'] ?? ''); @@ -41,6 +42,12 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $product = $catalog[$sku]; $price = (float) $product['price']; $lineTotal = $price * $qty; + + $vatPercent = (float) ($product['vat'] ?? 0); + // Assuming price is inclusive of VAT: + $itemVat = $lineTotal - ($lineTotal / (1 + ($vatPercent / 100))); + $totalVat += $itemVat; + $normalized[] = [ 'sku' => $sku, 'name_ar' => $product['name_ar'], @@ -48,6 +55,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { 'qty' => $qty, 'price' => $price, 'line_total' => $lineTotal, + 'vat_percent' => $vatPercent, + 'vat_amount' => $itemVat ]; $subtotal += $lineTotal; $itemCount += $qty; @@ -68,7 +77,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { 'payment_method' => $paymentMethod, 'items' => $normalized, 'item_count' => $itemCount, - 'subtotal' => $subtotal, + 'subtotal' => $subtotal - $totalVat, + 'vat_amount' => $totalVat, 'total_amount' => $subtotal, 'status' => $saleStatus, 'notes' => $notes !== '' ? $notes : null, @@ -360,8 +370,8 @@ require __DIR__ . '/header.php'; 0.000
- - + + 0.000
@@ -601,18 +611,24 @@ function renderInvoice() { if (skus.length === 0) { tbody.innerHTML = ''; tbody.appendChild(emptyRow); - updateTotals(0); + updateTotals(0, 0); cartJson.value = '[]'; return; } tbody.innerHTML = ''; let totalAmount = 0; + let totalVat = 0; const cartData = []; skus.forEach(sku => { const item = invoiceItems[sku]; const lineTotal = item.qty * item.price; + + const vatPercent = parseFloat(catalogData[sku].vat) || 0; + const itemVat = lineTotal - (lineTotal / (1 + (vatPercent / 100))); + totalVat += itemVat; + totalAmount += lineTotal; cartData.push({ sku: item.sku, qty: item.qty }); @@ -636,12 +652,14 @@ function renderInvoice() { tbody.appendChild(tr); }); - updateTotals(totalAmount); + updateTotals(totalAmount, totalVat); cartJson.value = JSON.stringify(cartData); } -function updateTotals(total) { - document.getElementById('displaySubtotal').innerText = total.toFixed(3); +function updateTotals(total, vat) { + const subtotal = total - vat; + document.getElementById('displaySubtotal').innerText = subtotal.toFixed(3); + document.getElementById('displayVat').innerText = vat.toFixed(3); document.getElementById('displayTotal').innerText = total.toFixed(3) + currencySuffix; } diff --git a/login.php b/login.php index 81fadc9..4f9690e 100644 --- a/login.php +++ b/login.php @@ -35,7 +35,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? ''; $projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; $projectName = $_SERVER['PROJECT_NAME'] ?? app_name(); $assetVersion = date('YmdHi'); -$accounts = demo_users(); +$accounts = []; ?> @@ -175,24 +175,7 @@ $accounts = demo_users(); -
-
- -
- -
- -
- -
- -
+
diff --git a/print_receipt.php b/print_receipt.php index 803e26d..acd5442 100644 --- a/print_receipt.php +++ b/print_receipt.php @@ -248,8 +248,8 @@ $registerNo = 'REG-01';
- - + +
diff --git a/reports.php b/reports.php index f728ed6..5bcb0ec 100644 --- a/reports.php +++ b/reports.php @@ -3,50 +3,301 @@ require_once __DIR__ . '/includes/app.php'; $user = require_roles(['owner', 'manager']); $pageTitle = tr('التقارير', 'Reports'); $activeNav = 'reports'; + +$tab = $_GET['tab'] ?? 'summary'; $dbError = null; -$report = ['gross' => 0.0, 'branch_totals' => [], 'payment_totals' => [], 'product_totals' => [], 'sales_count' => 0]; -try { - $report = report_metrics(); -} catch (Throwable $e) { - $dbError = $e->getMessage(); + +if ($tab === 'sales') { + $dateFrom = $_GET['date_from'] ?? date('Y-m-01'); + $dateTo = $_GET['date_to'] ?? date('Y-m-t'); + $branchFilter = $_GET['branch'] ?? ''; + + $params = []; + $where = base_sales_query_filters($params, null, $branchFilter ?: null); + $where .= ' AND DATE(sale_date) >= :date_from AND DATE(sale_date) <= :date_to'; + $params[':date_from'] = $dateFrom; + $params[':date_to'] = $dateTo; + + $sql = 'SELECT * FROM sales_orders' . $where . ' ORDER BY sale_date DESC'; + try { + $stmt = db()->prepare($sql); + foreach ($params as $k => $v) { + $stmt->bindValue($k, $v); + } + $stmt->execute(); + $salesReport = $stmt->fetchAll(); + } catch(Throwable $e) { + $dbError = $e->getMessage(); + $salesReport = []; + } +} elseif ($tab === 'orders') { + $branchFilter = $_GET['branch'] ?? ''; + $params = []; + $where = base_sales_query_filters($params, null, $branchFilter ?: null); + $where .= " AND status = 'order'"; + + $sql = 'SELECT * FROM sales_orders' . $where . ' ORDER BY sale_date ASC'; + try { + $stmt = db()->prepare($sql); + foreach ($params as $k => $v) { + $stmt->bindValue($k, $v); + } + $stmt->execute(); + $followUpOrders = $stmt->fetchAll(); + } catch(Throwable $e) { + $dbError = $e->getMessage(); + $followUpOrders = []; + } +} else { + $report = ['gross' => 0.0, 'branch_totals' => [], 'payment_totals' => [], 'product_totals' => [], 'sales_count' => 0]; + try { + $report = report_metrics(); + } catch (Throwable $e) { + $dbError = $e->getMessage(); + } } + require __DIR__ . '/includes/header.php'; ?> -
-
-
-
-
-
-
-
-

- -
- -

- -
- $amount): ?> -
- -
- -
+ +
+
+

-
-
-

- -

- -
- $amount): ?> -
- +
+ + + + +
+ + + + +
+
+
+ +
+ + +
+
+ + +
+
+ + +
+
+ +
+
-
-
-
- + +
+

+

+ +

+ +
+ +
+
+

+ +
+ + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+
+ +
+ + + +
+
+
+ +
+ + +
+
+
+
+ +
+

+

+ +

+ +
+ +
+
+

+ +
+ + +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+
+ + + +
+ + + +
+
+
+
+
+
+
+
+

+ +

+ +
+ $amount): ?> +
+ +
+ +
+
+
+
+

+ +

+ +
+ $amount): ?> +
+ +
+ +
+
+
+ + + + \ No newline at end of file diff --git a/sale.php b/sale.php index 4b1791c..b1d806b 100644 --- a/sale.php +++ b/sale.php @@ -424,8 +424,8 @@ require __DIR__ . '/includes/header.php'; - - + + diff --git a/sales.php b/sales.php index 2a0c8cd..1628cd2 100644 --- a/sales.php +++ b/sales.php @@ -14,7 +14,8 @@ if (isset($_GET['mark_paid']) && is_numeric($_GET['mark_paid'])) { $id = (int)$_GET['mark_paid']; db()->prepare("UPDATE sales_orders SET status = 'completed' WHERE id = ?")->execute([$id]); } catch(Throwable $e) {} - header("Location: sales.php"); + $redirect = $_GET["redirect"] ?? "sales.php"; + header("Location: " . $redirect); exit; } @@ -99,7 +100,7 @@ require __DIR__ . '/includes/header.php';
- diff --git a/users.php b/users.php index fd0b024..afd8284 100644 --- a/users.php +++ b/users.php @@ -4,25 +4,86 @@ $user = require_roles(['owner']); $pageTitle = tr('المستخدمون والأدوار', 'Users & Roles'); $activeNav = 'users'; -$allAccounts = demo_users(); +$flash = pull_flash(); + +// Handle POST actions (Create, Update, Delete) +if ($_SERVER['REQUEST_METHOD'] === 'POST') { + $action = $_POST['action'] ?? ''; + + if ($action === 'add') { + $username = trim($_POST['username'] ?? ''); + $password = $_POST['password'] ?? ''; + $name_ar = trim($_POST['name_ar'] ?? ''); + $name_en = trim($_POST['name_en'] ?? ''); + $role = $_POST['role'] ?? 'cashier'; + $branch_code = $_POST['branch_code'] ?? 'muscat'; + + if ($username && $password && $name_ar) { + $hash = password_hash($password, PASSWORD_DEFAULT); + try { + $stmt = db()->prepare("INSERT INTO users (username, password, role, branch_code, name_ar, name_en) VALUES (?, ?, ?, ?, ?, ?)"); + $stmt->execute([$username, $hash, $role, $branch_code, $name_ar, $name_en]); + set_flash('success', tr('تمت إضافة المستخدم بنجاح.', 'User added successfully.')); + } catch (PDOException $e) { + set_flash('error', tr('حدث خطأ، قد يكون اسم المستخدم موجوداً مسبقاً.', 'Error occurred, username might already exist.')); + } + } else { + set_flash('error', tr('يرجى تعبئة الحقول المطلوبة.', 'Please fill required fields.')); + } + redirect_to('users.php'); + } + + if ($action === 'edit') { + $id = (int)($_POST['id'] ?? 0); + $username = trim($_POST['username'] ?? ''); + $name_ar = trim($_POST['name_ar'] ?? ''); + $name_en = trim($_POST['name_en'] ?? ''); + $role = $_POST['role'] ?? 'cashier'; + $branch_code = $_POST['branch_code'] ?? 'muscat'; + $password = $_POST['password'] ?? ''; + + if ($id && $username && $name_ar) { + try { + if ($password) { + $hash = password_hash($password, PASSWORD_DEFAULT); + $stmt = db()->prepare("UPDATE users SET username=?, password=?, role=?, branch_code=?, name_ar=?, name_en=? WHERE id=?"); + $stmt->execute([$username, $hash, $role, $branch_code, $name_ar, $name_en, $id]); + } else { + $stmt = db()->prepare("UPDATE users SET username=?, role=?, branch_code=?, name_ar=?, name_en=? WHERE id=?"); + $stmt->execute([$username, $role, $branch_code, $name_ar, $name_en, $id]); + } + set_flash('success', tr('تم تعديل المستخدم بنجاح.', 'User updated successfully.')); + } catch (PDOException $e) { + set_flash('error', tr('حدث خطأ أثناء التعديل.', 'Error occurred during update.')); + } + } + redirect_to('users.php'); + } + + if ($action === 'delete') { + $id = (int)($_POST['id'] ?? 0); + if ($id && $id !== $user['id']) { + $stmt = db()->prepare("DELETE FROM users WHERE id=?"); + $stmt->execute([$id]); + set_flash('success', tr('تم حذف المستخدم بنجاح.', 'User deleted successfully.')); + } else { + set_flash('error', tr('لا يمكن حذف حسابك الحالي.', 'Cannot delete your own account.')); + } + redirect_to('users.php'); + } +} // Search logic $search = $_GET['q'] ?? ''; -$filteredAccounts = []; +$searchQuery = "%{$search}%"; + if ($search) { - $lowerSearch = strtolower($search); - foreach ($allAccounts as $key => $acc) { - if ( - str_contains(strtolower((string)$acc['name_ar']), $lowerSearch) || - str_contains(strtolower((string)$acc['name_en']), $lowerSearch) || - str_contains(strtolower((string)$acc['username']), $lowerSearch) - ) { - $filteredAccounts[$key] = $acc; - } - } + $stmt = db()->prepare("SELECT * FROM users WHERE name_ar LIKE ? OR name_en LIKE ? OR username LIKE ? ORDER BY id DESC"); + $stmt->execute([$searchQuery, $searchQuery, $searchQuery]); } else { - $filteredAccounts = $allAccounts; + $stmt = db()->query("SELECT * FROM users ORDER BY id DESC"); } +$filteredAccounts = $stmt->fetchAll(); // Pagination logic $page = max(1, (int)($_GET['p'] ?? 1)); @@ -32,15 +93,17 @@ $totalPages = max(1, ceil($total / $limit)); $offset = ($page - 1) * $limit; $accounts = array_slice($filteredAccounts, $offset, $limit, true); +$availableBranches = branches(); + require __DIR__ . '/includes/header.php'; ?>

-

+

-
@@ -51,6 +114,13 @@ require __DIR__ . '/includes/header.php';
+ + + +
@@ -71,7 +141,7 @@ require __DIR__ . '/includes/header.php'; - $account): ?> +
@@ -83,12 +153,19 @@ require __DIR__ . '/includes/header.php'; - - + +
+ + + +
+ @@ -109,35 +186,52 @@ require __DIR__ . '/includes/header.php';
- -