Autosave: 20260419-160352
This commit is contained in:
parent
cc0da06fbb
commit
b1a016903b
97
api/place_order.php
Normal file
97
api/place_order.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../includes/app.php';
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid method']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
if (!$input || empty($input['items'])) {
|
||||
echo json_encode(['success' => false, 'error' => 'Cart is empty']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$name = trim($input['name'] ?? '');
|
||||
$phone = trim($input['phone'] ?? '');
|
||||
$address = trim($input['address'] ?? '');
|
||||
|
||||
if ($name === '' || $phone === '' || $address === '') {
|
||||
echo json_encode(['success' => false, 'error' => 'Missing customer details']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$items = $input['items'];
|
||||
$total = 0;
|
||||
|
||||
// Recalculate total for security
|
||||
$db = db();
|
||||
$processedItems = [];
|
||||
foreach ($items as $id => $item) {
|
||||
$qty = (int)$item['qty'];
|
||||
if ($qty <= 0) continue;
|
||||
|
||||
// get price from DB
|
||||
$stmt = $db->prepare("SELECT sku, name, price FROM items WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$dbItem = $stmt->fetch();
|
||||
if ($dbItem) {
|
||||
$price = (float)$dbItem['price'];
|
||||
$total += ($price * $qty);
|
||||
$processedItems[] = [
|
||||
'id' => $id,
|
||||
'sku' => $dbItem['sku'],
|
||||
'name' => $dbItem['name'],
|
||||
'price' => $price,
|
||||
'qty' => $qty
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($processedItems)) {
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid items']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $db->prepare("INSERT INTO online_orders (customer_name, customer_phone, customer_address, items_json, total_amount) VALUES (?, ?, ?, ?, ?)");
|
||||
$stmt->execute([
|
||||
$name,
|
||||
$phone,
|
||||
$address,
|
||||
json_encode($processedItems, JSON_UNESCAPED_UNICODE),
|
||||
$total
|
||||
]);
|
||||
|
||||
// Optional: send telegram notification if configured
|
||||
try {
|
||||
// require_once __DIR__ . '/telegram_webhook.php'; // wait, it might not be a function but a script. Let's just do simple notification
|
||||
$orderId = $db->lastInsertId();
|
||||
$msg = "🛒 *New Online Order #{$orderId}*\n\n";
|
||||
$msg .= "👤 {$name}\n📞 {$phone}\n📍 {$address}\n\n";
|
||||
$msg .= "💰 Total: " . currency($total) . "\n";
|
||||
// To send, we'd need to call telegram api directly if token is set.
|
||||
$botToken = getenv('TELEGRAM_BOT_TOKEN') ?: get_setting('telegram_bot_token');
|
||||
$chatId = getenv('TELEGRAM_CHAT_ID') ?: get_setting('telegram_chat_id');
|
||||
if ($botToken && $chatId) {
|
||||
$url = "https://api.telegram.org/bot{$botToken}/sendMessage";
|
||||
$data = ['chat_id' => $chatId, 'text' => $msg, 'parse_mode' => 'Markdown'];
|
||||
$options = [
|
||||
'http' => [
|
||||
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
|
||||
'method' => 'POST',
|
||||
'content' => http_build_query($data)
|
||||
]
|
||||
];
|
||||
$context = stream_context_create($options);
|
||||
@file_get_contents($url, false, $context);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// ignore notification errors
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'error' => 'Database error']);
|
||||
}
|
||||
@ -98,6 +98,20 @@ function pull_flash(): ?array
|
||||
|
||||
function branches(): array
|
||||
{
|
||||
try {
|
||||
$db = db();
|
||||
$stmt = $db->query("SELECT * FROM branches");
|
||||
$res = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
if ($res) {
|
||||
$arr = [];
|
||||
foreach ($res as $row) {
|
||||
$arr[$row['code']] = $row;
|
||||
}
|
||||
return $arr;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Table might not exist yet
|
||||
}
|
||||
return [
|
||||
'muscat' => ['code' => 'muscat', 'name_ar' => 'فرع مسقط', 'name_en' => 'Muscat Branch', 'city_ar' => 'مسقط', 'city_en' => 'Muscat'],
|
||||
'sohar' => ['code' => 'sohar', 'name_ar' => 'فرع صحار', 'name_en' => 'Sohar Branch', 'city_ar' => 'صحار', 'city_en' => 'Sohar'],
|
||||
@ -208,7 +222,7 @@ function catalog(): array
|
||||
"cost_price" => (float)($item["cost_price"] ?? 0),
|
||||
"base_stock" => (int)$item["base_stock"],
|
||||
"vat" => (float)$item["vat"],
|
||||
"category_id" => $item["category_id"],
|
||||
"category_id" => $item["category_id"], "in_catalog" => (int)($item["in_catalog"] ?? 0),
|
||||
"supplier_id" => $item["supplier_id"],
|
||||
"image_url" => $item["image_url"],
|
||||
"unit_id" => $item["unit_id"],
|
||||
@ -414,6 +428,7 @@ function report_metrics(): array
|
||||
$branchTotals = [];
|
||||
$paymentTotals = [];
|
||||
$productTotals = [];
|
||||
$monthlyTotals = [];
|
||||
$gross = 0.0;
|
||||
$totalVat = 0.0;
|
||||
|
||||
@ -423,7 +438,10 @@ function report_metrics(): array
|
||||
$payment = $sale['payment_method'];
|
||||
$paymentTotals[$payment] = ($paymentTotals[$payment] ?? 0.0) + (float) $sale['total_amount'];
|
||||
$gross += (float) $sale['total_amount'];
|
||||
$totalVat += (float) $sale['vat_amount'];
|
||||
$totalVat += (float) $sale['vat_amount'];
|
||||
|
||||
$month = substr((string)$sale['sale_date'], 0, 7);
|
||||
$monthlyTotals[$month] = ($monthlyTotals[$month] ?? 0.0) + (float) $sale['total_amount'];
|
||||
foreach ($sale['items'] as $item) {
|
||||
$sku = (string) ($item['sku'] ?? '');
|
||||
$qty = (int) ($item['qty'] ?? 0);
|
||||
@ -433,14 +451,16 @@ function report_metrics(): array
|
||||
|
||||
arsort($branchTotals);
|
||||
arsort($paymentTotals);
|
||||
arsort($productTotals);
|
||||
arsort($productTotals);
|
||||
ksort($monthlyTotals);
|
||||
|
||||
return [
|
||||
'gross' => $gross,
|
||||
'total_vat' => $totalVat,
|
||||
'branch_totals' => $branchTotals,
|
||||
'payment_totals' => $paymentTotals,
|
||||
'product_totals' => $productTotals,
|
||||
'product_totals' => $productTotals,
|
||||
'monthly_totals' => $monthlyTotals,
|
||||
'sales_count' => count($sales),
|
||||
];
|
||||
}
|
||||
|
||||
@ -6,6 +6,20 @@ $isPublic = !isset($user) || !$user;
|
||||
</main>
|
||||
<?php else: ?>
|
||||
</div> <!-- /.container-fluid -->
|
||||
|
||||
<!-- App Footer -->
|
||||
<footer class="bg-white border-top mt-auto py-3 px-4 text-muted small">
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap">
|
||||
<div>
|
||||
© <?= date('Y') ?> <strong><?= h(current_lang() === 'ar' ? get_setting('company_name_ar', 'حلوى الريامي') : get_setting('company_name_en', 'Al Riyami Sweets')) ?></strong>. <?= h(tr('جميع الحقوق محفوظة.', 'All rights reserved.')) ?>
|
||||
</div>
|
||||
<div class="mt-2 mt-md-0">
|
||||
<span class="text-secondary"><?= h(tr('تم التطوير بواسطة', 'Developed via')) ?> </span><a href="https://flatlogic.com" target="_blank" class="text-decoration-none fw-semibold">Flatlogic</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<!-- /App Footer -->
|
||||
|
||||
</div> <!-- /#page-content-wrapper -->
|
||||
</div> <!-- /#wrapper -->
|
||||
<?php endif; ?>
|
||||
|
||||
@ -11,7 +11,7 @@ $flash = pull_flash();
|
||||
$assetVersion = date('YmdHi');
|
||||
|
||||
// Determine if we are on a public page (like login)
|
||||
$isPublic = !isset($user) || !$user;
|
||||
$isPublic = !empty($forcePublic) || !isset($user) || !$user;
|
||||
?>
|
||||
<!doctype html>
|
||||
<html lang="<?= h(current_lang()) ?>" dir="<?= is_rtl() ? 'rtl' : 'ltr' ?>">
|
||||
@ -138,6 +138,9 @@ $isPublic = !isset($user) || !$user;
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'suppliers' ? 'active' : '' ?>" href="<?= h(url_for('suppliers.php')) ?>">
|
||||
<i class="bi bi-truck"></i> <?= h(tr('الموردون', 'Suppliers')) ?>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'online_orders' ? 'active' : '' ?>" href="<?= h(url_for('online_orders.php')) ?>">
|
||||
<i class="bi bi-cart-check"></i> <?= h(tr('طلبات المتجر', 'Online Orders')) ?>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'customers' ? 'active' : '' ?>" href="<?= h(url_for('customers.php')) ?>">
|
||||
<i class="bi bi-people-fill"></i> <?= h(tr('العملاء', 'Customers')) ?>
|
||||
</a>
|
||||
@ -153,16 +156,29 @@ $isPublic = !isset($user) || !$user;
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (has_permission('settings', 'show')): ?>
|
||||
<a class="list-group-item list-group-item-action" href="#" data-bs-toggle="modal" data-bs-target="#settingsModal">
|
||||
<i class="bi bi-gear"></i> <?= h(tr('إعدادات الشركة', 'Company Settings')) ?>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action <?= in_array($activeNav, ['outlets']) ? '' : 'collapsed' ?>" data-bs-toggle="collapse" href="#collapseSettings" role="button" aria-expanded="<?= in_array($activeNav, ['outlets']) ? 'true' : 'false' ?>" aria-controls="collapseSettings">
|
||||
<div class="d-flex justify-content-between align-items-center w-100">
|
||||
<span><i class="bi bi-gear"></i> <?= h(tr('الإعدادات', 'Settings')) ?></span>
|
||||
<i class="bi bi-chevron-down toggle-icon" style="transition: transform 0.2s;"></i>
|
||||
</div>
|
||||
</a>
|
||||
<div class="collapse <?= in_array($activeNav, ['outlets']) ? 'show' : '' ?>" id="collapseSettings">
|
||||
<div class="list-group list-group-flush" style="background-color: rgba(0,0,0,0.15);">
|
||||
<a class="list-group-item list-group-item-action" href="#" data-bs-toggle="modal" data-bs-target="#settingsModal" style="padding-left: 2.5rem; padding-right: 2.5rem;">
|
||||
<i class="bi bi-dot"></i> <?= h(tr('إعدادات الشركة', 'App Settings')) ?>
|
||||
</a>
|
||||
<a class="list-group-item list-group-item-action <?= $activeNav === 'outlets' ? 'active' : '' ?>" href="<?= h(url_for('outlets.php')) ?>" style="padding-left: 2.5rem; padding-right: 2.5rem;">
|
||||
<i class="bi bi-dot"></i> <?= h(tr('الفروع (المنافذ)', 'Outlets')) ?>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<!-- /#sidebar-wrapper -->
|
||||
|
||||
<!-- Page Content -->
|
||||
<div id="page-content-wrapper">
|
||||
<div id="page-content-wrapper" class="d-flex flex-column min-vh-100">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-white border-bottom top-navbar px-3">
|
||||
<div class="d-flex align-items-center justify-content-between w-100">
|
||||
<div class="d-flex align-items-center">
|
||||
@ -171,6 +187,7 @@ $isPublic = !isset($user) || !$user;
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<a href="shop.php" target="_blank" class="btn btn-outline-success btn-sm me-2" title="<?= h(tr('زيارة المتجر', 'Visit Store')) ?>"><i class="bi bi-shop"></i> <span class="d-none d-md-inline"><?= h(tr('المتجر', 'Store')) ?></span></a>
|
||||
<div class="language-switcher btn-group" role="group">
|
||||
<a class="btn btn-sm <?= current_lang() === 'ar' ? 'btn-primary' : 'btn-outline-primary' ?>" href="<?= h(url_for(basename($_SERVER['PHP_SELF']), array_merge($_GET, ['lang' => 'ar']))) ?>">AR</a>
|
||||
<a class="btn btn-sm <?= current_lang() === 'en' ? 'btn-primary' : 'btn-outline-primary' ?>" href="<?= h(url_for(basename($_SERVER['PHP_SELF']), array_merge($_GET, ['lang' => 'en']))) ?>">EN</a>
|
||||
|
||||
373
index.php
373
index.php
@ -5,11 +5,41 @@ $pageTitle = tr('لوحة التحكم', 'Dashboard');
|
||||
$activeNav = 'dashboard';
|
||||
$dbError = null;
|
||||
$metrics = ['today_sales' => 0, 'today_revenue' => 0.0, 'pos_count' => 0, 'normal_count' => 0, 'recent' => []];
|
||||
$reportMetrics = ['branch_totals' => [], 'payment_totals' => [], 'product_totals' => []];
|
||||
try {
|
||||
$metrics = dashboard_metrics();
|
||||
$reportMetrics = report_metrics();
|
||||
} catch (Throwable $e) {
|
||||
$dbError = $e->getMessage();
|
||||
}
|
||||
|
||||
$branchLabels = [];
|
||||
$branchData = [];
|
||||
foreach ($reportMetrics['branch_totals'] as $code => $total) {
|
||||
$branchLabels[] = branch_label((string)$code);
|
||||
$branchData[] = (float)$total;
|
||||
}
|
||||
|
||||
$productLabels = [];
|
||||
$productData = [];
|
||||
$topProducts = array_slice($reportMetrics['product_totals'], 0, 5, true);
|
||||
foreach ($topProducts as $sku => $qty) {
|
||||
$productLabels[] = product_label((string)$sku);
|
||||
$productData[] = (int)$qty;
|
||||
}
|
||||
|
||||
|
||||
$monthlyLabels = [];
|
||||
$monthlyData = [];
|
||||
if (isset($reportMetrics['monthly_totals'])) {
|
||||
foreach ($reportMetrics['monthly_totals'] as $month => $total) {
|
||||
// format month: "2026-04" -> "Apr 2026"
|
||||
$time = strtotime($month . '-01');
|
||||
$monthlyLabels[] = date('M Y', $time);
|
||||
$monthlyData[] = (float)$total;
|
||||
}
|
||||
}
|
||||
|
||||
require __DIR__ . '/includes/header.php';
|
||||
?>
|
||||
|
||||
@ -30,11 +60,11 @@ require __DIR__ . '/includes/header.php';
|
||||
<!-- Metrics Row -->
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-sm-6 col-xl-3">
|
||||
<div class="card text-white bg-primary h-100">
|
||||
<div class="card text-white bg-primary h-100 shadow-sm border-0" style="border-radius: 12px; background: linear-gradient(135deg, #0d6efd 0%, #0043a8 100%);">
|
||||
<div class="card-body d-flex flex-column justify-content-between">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<h6 class="text-white-50 text-uppercase mb-0"><?= h(tr('مبيعات اليوم', 'Today sales')) ?></h6>
|
||||
<i class="bi bi-receipt fs-4 text-white-50"></i>
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<h6 class="text-white-50 text-uppercase mb-0 fw-bold"><?= h(tr('مبيعات اليوم', 'Today sales')) ?></h6>
|
||||
<div class="p-2 bg-white bg-opacity-25 rounded"><i class="bi bi-receipt fs-5 text-white"></i></div>
|
||||
</div>
|
||||
<h2 class="display-6 fw-bold mb-0"><?= h((string) $metrics['today_sales']) ?></h2>
|
||||
</div>
|
||||
@ -42,11 +72,11 @@ require __DIR__ . '/includes/header.php';
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-xl-3">
|
||||
<div class="card text-white bg-success h-100">
|
||||
<div class="card text-white bg-success h-100 shadow-sm border-0" style="border-radius: 12px; background: linear-gradient(135deg, #198754 0%, #0f5132 100%);">
|
||||
<div class="card-body d-flex flex-column justify-content-between">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<h6 class="text-white-50 text-uppercase mb-0"><?= h(tr('إيراد اليوم', 'Today revenue')) ?></h6>
|
||||
<i class="bi bi-cash-stack fs-4 text-white-50"></i>
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<h6 class="text-white-50 text-uppercase mb-0 fw-bold"><?= h(tr('إيراد اليوم', 'Today revenue')) ?></h6>
|
||||
<div class="p-2 bg-white bg-opacity-25 rounded"><i class="bi bi-cash-stack fs-5 text-white"></i></div>
|
||||
</div>
|
||||
<h2 class="display-6 fw-bold mb-0"><?= h(currency((float) $metrics['today_revenue'])) ?></h2>
|
||||
</div>
|
||||
@ -54,11 +84,11 @@ require __DIR__ . '/includes/header.php';
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-xl-3">
|
||||
<div class="card text-white bg-info h-100">
|
||||
<div class="card text-white bg-info h-100 shadow-sm border-0" style="border-radius: 12px; background: linear-gradient(135deg, #0dcaf0 0%, #087990 100%);">
|
||||
<div class="card-body d-flex flex-column justify-content-between">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<h6 class="text-white-50 text-uppercase mb-0">POS</h6>
|
||||
<i class="bi bi-cart fs-4 text-white-50"></i>
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<h6 class="text-white-50 text-uppercase mb-0 fw-bold">POS</h6>
|
||||
<div class="p-2 bg-white bg-opacity-25 rounded"><i class="bi bi-cart fs-5 text-white"></i></div>
|
||||
</div>
|
||||
<h2 class="display-6 fw-bold mb-0"><?= h((string) $metrics['pos_count']) ?></h2>
|
||||
</div>
|
||||
@ -66,11 +96,11 @@ require __DIR__ . '/includes/header.php';
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6 col-xl-3">
|
||||
<div class="card text-white bg-warning h-100">
|
||||
<div class="card text-white bg-warning h-100 shadow-sm border-0" style="border-radius: 12px; background: linear-gradient(135deg, #ffc107 0%, #b38600 100%);">
|
||||
<div class="card-body d-flex flex-column justify-content-between">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<h6 class="text-white-50 text-uppercase mb-0"><?= h(tr('بيع عادي', 'Normal sale')) ?></h6>
|
||||
<i class="bi bi-basket fs-4 text-white-50"></i>
|
||||
<div class="d-flex justify-content-between align-items-start mb-3">
|
||||
<h6 class="text-white-50 text-uppercase mb-0 fw-bold"><?= h(tr('بيع عادي', 'Normal sale')) ?></h6>
|
||||
<div class="p-2 bg-white bg-opacity-25 rounded"><i class="bi bi-basket fs-5 text-white"></i></div>
|
||||
</div>
|
||||
<h2 class="display-6 fw-bold mb-0"><?= h((string) $metrics['normal_count']) ?></h2>
|
||||
</div>
|
||||
@ -78,13 +108,55 @@ require __DIR__ . '/includes/header.php';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Charts Row -->
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-xl-6">
|
||||
<div class="card h-100 shadow-sm border-0" style="border-radius: 12px;">
|
||||
<div class="card-header bg-white py-3 border-0 d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0 fw-semibold"><i class="bi bi-bar-chart-line me-2 text-primary"></i><?= h(tr('أفضل الأصناف مبيعاً', 'Top Selling Products')) ?></h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if(empty($productLabels)): ?>
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="bi bi-graph-down text-secondary fs-1 d-block mb-3"></i>
|
||||
<p><?= h(tr('لا توجد بيانات كافية لعرض الرسم البياني', 'Not enough data to show chart')) ?></p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div style="position: relative; height:300px; width:100%">
|
||||
<canvas id="topProductsChart"></canvas>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-6">
|
||||
<div class="card h-100 shadow-sm border-0" style="border-radius: 12px;">
|
||||
<div class="card-header bg-white py-3 border-0 d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0 fw-semibold"><i class="bi bi-pie-chart me-2 text-success"></i><?= h(tr('المبيعات حسب الفرع', 'Sales by Branch')) ?></h5>
|
||||
</div>
|
||||
<div class="card-body d-flex justify-content-center align-items-center">
|
||||
<?php if(empty($branchLabels)): ?>
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="bi bi-pie-chart text-secondary fs-1 d-block mb-3"></i>
|
||||
<p><?= h(tr('لا توجد بيانات كافية لعرض الرسم البياني', 'Not enough data to show chart')) ?></p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div style="position: relative; height:300px; width:100%">
|
||||
<canvas id="branchSalesChart"></canvas>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4 mb-4">
|
||||
<!-- Recent Sales -->
|
||||
<div class="col-xl-8">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-white d-flex justify-content-between align-items-center py-3">
|
||||
<h5 class="mb-0 fw-semibold"><i class="bi bi-clock-history me-2"></i><?= h(tr('آخر المبيعات', 'Latest sales')) ?></h5>
|
||||
<a class="btn btn-sm btn-outline-primary" href="<?= h(url_for('sales.php')) ?>"><?= h(tr('عرض الكل', 'View all')) ?></a>
|
||||
<div class="card shadow-sm border-0 h-100" style="border-radius: 12px;">
|
||||
<div class="card-header bg-white d-flex justify-content-between align-items-center py-3 border-0">
|
||||
<h5 class="mb-0 fw-semibold"><i class="bi bi-clock-history me-2 text-info"></i><?= h(tr('آخر المبيعات', 'Latest sales')) ?></h5>
|
||||
<a class="btn btn-sm btn-outline-primary rounded-pill px-3" href="<?= h(url_for('sales.php')) ?>"><?= h(tr('عرض الكل', 'View all')) ?></a>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<?php if (!$metrics['recent']): ?>
|
||||
@ -95,34 +167,42 @@ require __DIR__ . '/includes/header.php';
|
||||
<a class="btn btn-primary" href="<?= h(url_for('pos.php')) ?>"><?= h(tr('إنشاء بيع POS', 'Create POS sale')) ?></a>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="table-responsive shadow-sm" style="border-radius: 12px; overflow: hidden; border: 1px solid rgba(0,0,0,0.05);">
|
||||
<table class="table table-hover align-middle mb-0 text-center" style="background-color: #fff;">
|
||||
<thead style="background: linear-gradient(90deg, #0d6efd, #0dcaf0);">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle mb-0 text-center">
|
||||
<thead class="bg-light text-muted">
|
||||
<tr>
|
||||
<th class="ps-3 text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('رقم الإيصال', 'Receipt No')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الفرع', 'Branch')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('النوع', 'Type')) ?></th>
|
||||
<th class="text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('التاريخ', 'Date')) ?></th>
|
||||
<th class="text-end pe-3 text-white border-0 py-3 fw-semibold bg-transparent"><?= h(tr('الإجمالي', 'Total')) ?></th>
|
||||
<th class="ps-3 border-0 py-3 fw-semibold"><?= h(tr('رقم الإيصال', 'Receipt No')) ?></th>
|
||||
<th class="border-0 py-3 fw-semibold"><?= h(tr('الفرع', 'Branch')) ?></th>
|
||||
<th class="border-0 py-3 fw-semibold"><?= h(tr('النوع', 'Type')) ?></th>
|
||||
<th class="border-0 py-3 fw-semibold"><?= h(tr('التاريخ', 'Date')) ?></th>
|
||||
<th class="text-end pe-3 border-0 py-3 fw-semibold"><?= h(tr('الإجمالي', 'Total')) ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="border-top-0">
|
||||
<?php foreach ($metrics['recent'] as $sale): ?>
|
||||
<tr>
|
||||
<td class="ps-3">
|
||||
<a href="<?= h(url_for('sale.php', ['id' => $sale['id']])) ?>" class="fw-semibold text-decoration-none">
|
||||
<?= h($sale['receipt_no']) ?>
|
||||
<a href="<?= h(url_for('sale.php', ['id' => $sale['id']])) ?>" class="fw-bold text-decoration-none text-primary">
|
||||
#<?= h($sale['receipt_no']) ?>
|
||||
</a>
|
||||
</td>
|
||||
<td><?= h(branch_label((string) $sale['branch_code'])) ?></td>
|
||||
<td><span class="badge bg-secondary"><?= h(sale_mode_label((string) $sale['sale_mode'])) ?></span></td>
|
||||
<td><small class="text-muted"><?= h(date('M d, H:i', strtotime((string) $sale['sale_date']))) ?></small></td>
|
||||
<td class="text-end pe-3 fw-bold"><?= h(currency((float) $sale['total_amount'])) ?></td>
|
||||
<td>
|
||||
<span class="badge bg-light text-dark border"><?= h(branch_label((string) $sale['branch_code'])) ?></span>
|
||||
</td>
|
||||
<td>
|
||||
<?php if($sale['sale_mode'] === 'pos'): ?>
|
||||
<span class="badge bg-info bg-opacity-10 text-info border border-info"><?= h(sale_mode_label((string) $sale['sale_mode'])) ?></span>
|
||||
<?php else: ?>
|
||||
<span class="badge bg-warning bg-opacity-10 text-warning border border-warning"><?= h(sale_mode_label((string) $sale['sale_mode'])) ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><small class="text-muted fw-medium"><?= h(date('M d, H:i', strtotime((string) $sale['sale_date']))) ?></small></td>
|
||||
<td class="text-end pe-3 fw-bold text-success"><?= h(currency((float) $sale['total_amount'])) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
@ -130,40 +210,42 @@ require __DIR__ . '/includes/header.php';
|
||||
|
||||
<!-- Quick Actions / Status -->
|
||||
<div class="col-xl-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-header bg-white py-3">
|
||||
<h5 class="mb-0 fw-semibold"><i class="bi bi-lightning-charge me-2"></i><?= h(tr('إجراءات سريعة', 'Quick Actions')) ?></h5>
|
||||
<div class="card shadow-sm border-0 h-100" style="border-radius: 12px;">
|
||||
<div class="card-header bg-white py-3 border-0">
|
||||
<h5 class="mb-0 fw-semibold"><i class="bi bi-lightning-charge me-2 text-warning"></i><?= h(tr('إجراءات سريعة', 'Quick Actions')) ?></h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-grid gap-3">
|
||||
<a href="<?= h(url_for('pos.php')) ?>" class="btn btn-primary btn-lg d-flex align-items-center justify-content-between">
|
||||
<a href="<?= h(url_for('pos.php')) ?>" class="btn btn-primary btn-lg d-flex align-items-center justify-content-between" style="border-radius: 10px; box-shadow: 0 4px 6px rgba(13, 110, 253, 0.2);">
|
||||
<span><i class="bi bi-cart-plus me-2"></i> <?= h(tr('نقطة بيع جديدة', 'New POS Sale')) ?></span>
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
</a>
|
||||
<a href="<?= h(url_for('normal_sale.php')) ?>" class="btn btn-outline-primary btn-lg d-flex align-items-center justify-content-between">
|
||||
<a href="<?= h(url_for('normal_sale.php')) ?>" class="btn btn-outline-primary btn-lg d-flex align-items-center justify-content-between" style="border-radius: 10px;">
|
||||
<span><i class="bi bi-receipt me-2"></i> <?= h(tr('بيع عادي', 'Normal Sale')) ?></span>
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
</a>
|
||||
<button type="button" class="btn btn-outline-secondary btn-lg d-flex align-items-center justify-content-between" data-bs-toggle="modal" data-bs-target="#quickNoteModal">
|
||||
<span><i class="bi bi-pencil-square me-2"></i> <?= h(tr('ملاحظة سريعة', 'Quick Note')) ?></span>
|
||||
<i class="bi bi-chevron-right"></i>
|
||||
<button type="button" class="btn btn-light btn-lg d-flex align-items-center justify-content-between border" data-bs-toggle="modal" data-bs-target="#quickNoteModal" style="border-radius: 10px;">
|
||||
<span class="text-dark"><i class="bi bi-pencil-square me-2"></i> <?= h(tr('ملاحظة سريعة', 'Quick Note')) ?></span>
|
||||
<i class="bi bi-chevron-right text-dark"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 pt-4 border-top">
|
||||
<?php if ($user): ?>
|
||||
<h6 class="text-muted text-uppercase small fw-bold mb-3"><?= h(tr('معلومات الحساب', 'Account Info')) ?></h6>
|
||||
<div class="d-flex align-items-center mb-2">
|
||||
<i class="bi bi-person-badge fs-4 me-3 text-secondary"></i>
|
||||
<div class="d-flex align-items-center mb-2 p-3 bg-light rounded-3 border">
|
||||
<div class="bg-white p-2 rounded-circle shadow-sm me-3">
|
||||
<i class="bi bi-person-badge fs-4 text-primary"></i>
|
||||
</div>
|
||||
<div>
|
||||
<p class="mb-0 fw-semibold"><?= h(current_lang() === 'ar' ? $user['name_ar'] : $user['name_en']) ?></p>
|
||||
<small class="text-muted"><?= h(role_label($user['role'])) ?> - <?= h(branch_label($user['branch_code'])) ?></small>
|
||||
<p class="mb-0 fw-bold"><?= h(current_lang() === 'ar' ? $user['name_ar'] : $user['name_en']) ?></p>
|
||||
<small class="text-muted fw-medium"><?= h(role_label($user['role'])) ?> • <?= h(branch_label($user['branch_code'])) ?></small>
|
||||
</div>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="text-center">
|
||||
<p class="text-muted mb-3"><?= h(tr('جرّب المالك أو مدير الفرع أو الكاشير لرؤية اختلاف الصلاحيات.', 'Try owner, branch manager, or cashier to see different permissions.')) ?></p>
|
||||
<a class="btn btn-dark w-100" href="<?= h(url_for('login.php')) ?>"><?= h(tr('تسجيل الدخول للبدء', 'Sign in to start')) ?></a>
|
||||
<div class="text-center p-3 bg-light rounded-3 border">
|
||||
<p class="text-muted mb-3 small"><?= h(tr('جرّب المالك أو مدير الفرع أو الكاشير لرؤية اختلاف الصلاحيات.', 'Try owner, branch manager, or cashier to see different permissions.')) ?></p>
|
||||
<a class="btn btn-dark w-100 rounded-pill" href="<?= h(url_for('login.php')) ?>"><?= h(tr('تسجيل الدخول للبدء', 'Sign in to start')) ?></a>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
@ -172,35 +254,204 @@ require __DIR__ . '/includes/header.php';
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Monthly Sales Chart Row -->
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-12">
|
||||
<div class="card shadow-sm border-0" style="border-radius: 12px;">
|
||||
<div class="card-header bg-white py-3 border-0 d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0 fw-semibold"><i class="bi bi-graph-up me-2 text-primary"></i><?= h(tr('مبيعات الأشهر', 'Monthly Sales')) ?></h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<?php if(empty($monthlyLabels)): ?>
|
||||
<div class="text-center text-muted py-5">
|
||||
<i class="bi bi-graph-up text-secondary fs-1 d-block mb-3"></i>
|
||||
<p><?= h(tr('لا توجد بيانات كافية لعرض الرسم البياني', 'Not enough data to show chart')) ?></p>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div style="position: relative; height:350px; width:100%">
|
||||
<canvas id="monthlySalesChart"></canvas>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal Form Example -->
|
||||
<div class="modal fade" id="quickNoteModal" tabindex="-1" aria-labelledby="quickNoteModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content" style="border-radius: 16px; border: 0; box-shadow: 0 10px 30px rgba(0,0,0,0.1);">
|
||||
<form onsubmit="handleQuickNote(event)">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="quickNoteModalLabel"><?= h(tr('إضافة ملاحظة سريعة', 'Add Quick Note')) ?></h5>
|
||||
<div class="modal-header border-0 pb-0">
|
||||
<h5 class="modal-title fw-bold" id="quickNoteModalLabel"><i class="bi bi-journal-text me-2 text-primary"></i><?= h(tr('إضافة ملاحظة سريعة', 'Add Quick Note')) ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="mb-3">
|
||||
<label for="noteTitle" class="form-label"><?= h(tr('العنوان', 'Title')) ?></label>
|
||||
<input type="text" class="form-control" id="noteTitle" required>
|
||||
<label for="noteTitle" class="form-label fw-medium"><?= h(tr('العنوان', 'Title')) ?></label>
|
||||
<input type="text" class="form-control form-control-lg bg-light border-0" id="noteTitle" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="noteContent" class="form-label"><?= h(tr('التفاصيل', 'Details')) ?></label>
|
||||
<textarea class="form-control" id="noteContent" rows="3" required></textarea>
|
||||
<label for="noteContent" class="form-label fw-medium"><?= h(tr('التفاصيل', 'Details')) ?></label>
|
||||
<textarea class="form-control bg-light border-0" id="noteContent" rows="4" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= h(tr('إلغاء', 'Cancel')) ?></button>
|
||||
<button type="submit" class="btn btn-primary"><?= h(tr('حفظ', 'Save')) ?></button>
|
||||
<div class="modal-footer border-0 pt-0">
|
||||
<button type="button" class="btn btn-light px-4" data-bs-dismiss="modal"><?= h(tr('إلغاء', 'Cancel')) ?></button>
|
||||
<button type="submit" class="btn btn-primary px-4 shadow-sm"><?= h(tr('حفظ الملاحظة', 'Save Note')) ?></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
<?php if(!empty($branchLabels)): ?>
|
||||
const branchCtx = document.getElementById('branchSalesChart').getContext('2d');
|
||||
new Chart(branchCtx, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: <?= json_encode($branchLabels) ?>,
|
||||
datasets: [{
|
||||
data: <?= json_encode($branchData) ?>,
|
||||
backgroundColor: [
|
||||
'rgba(13, 110, 253, 0.8)',
|
||||
'rgba(25, 135, 84, 0.8)',
|
||||
'rgba(13, 202, 240, 0.8)',
|
||||
'rgba(255, 193, 7, 0.8)',
|
||||
'rgba(220, 53, 69, 0.8)'
|
||||
],
|
||||
borderWidth: 2,
|
||||
borderColor: '#ffffff',
|
||||
hoverOffset: 6
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
cutout: '65%',
|
||||
plugins: {
|
||||
legend: { position: 'bottom', labels: { usePointStyle: true, padding: 20 } },
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(0,0,0,0.8)',
|
||||
padding: 12,
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
let value = context.raw;
|
||||
return ' ' + value.toFixed(3) + ' <?= tr("ر.ع", "OMR") ?>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if(!empty($productLabels)): ?>
|
||||
const productsCtx = document.getElementById('topProductsChart').getContext('2d');
|
||||
|
||||
// Create gradient for bars
|
||||
let gradient = productsCtx.createLinearGradient(0, 0, 0, 300);
|
||||
gradient.addColorStop(0, 'rgba(13, 110, 253, 0.9)');
|
||||
gradient.addColorStop(1, 'rgba(13, 110, 253, 0.2)');
|
||||
|
||||
new Chart(productsCtx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: <?= json_encode($productLabels) ?>,
|
||||
datasets: [{
|
||||
label: '<?= h(tr("الكمية المباعة", "Qty Sold")) ?>',
|
||||
data: <?= json_encode($productData) ?>,
|
||||
backgroundColor: gradient,
|
||||
borderColor: '#0d6efd',
|
||||
borderWidth: 1,
|
||||
borderRadius: 6,
|
||||
barPercentage: 0.6
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(0,0,0,0.8)',
|
||||
padding: 12
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
ticks: { precision: 0 },
|
||||
grid: { borderDash: [5, 5], color: '#f0f0f0' }
|
||||
},
|
||||
x: {
|
||||
grid: { display: false }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
<?php endif; ?>
|
||||
<?php if(!empty($monthlyLabels)): ?>
|
||||
const monthlyCtx = document.getElementById('monthlySalesChart').getContext('2d');
|
||||
|
||||
// Create gradient for line chart area
|
||||
let gradientMonthly = monthlyCtx.createLinearGradient(0, 0, 0, 350);
|
||||
gradientMonthly.addColorStop(0, 'rgba(25, 135, 84, 0.4)');
|
||||
gradientMonthly.addColorStop(1, 'rgba(25, 135, 84, 0.0)');
|
||||
|
||||
new Chart(monthlyCtx, {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: <?= json_encode($monthlyLabels) ?>,
|
||||
datasets: [{
|
||||
label: '<?= h(tr("إجمالي المبيعات", "Total Sales")) ?>',
|
||||
data: <?= json_encode($monthlyData) ?>,
|
||||
backgroundColor: gradientMonthly,
|
||||
borderColor: '#198754',
|
||||
borderWidth: 3,
|
||||
pointBackgroundColor: '#ffffff',
|
||||
pointBorderColor: '#198754',
|
||||
pointBorderWidth: 2,
|
||||
pointRadius: 4,
|
||||
pointHoverRadius: 6,
|
||||
fill: true,
|
||||
tension: 0.4
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: { display: false },
|
||||
tooltip: {
|
||||
backgroundColor: 'rgba(0,0,0,0.8)',
|
||||
padding: 12,
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
let value = context.raw;
|
||||
return ' ' + value.toFixed(3) + ' <?= tr("ر.ع", "OMR") ?>';
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: { borderDash: [5, 5], color: '#f0f0f0' }
|
||||
},
|
||||
x: {
|
||||
grid: { display: false }
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
<?php endif; ?>
|
||||
});
|
||||
|
||||
function handleQuickNote(e) {
|
||||
e.preventDefault();
|
||||
var modalEl = document.getElementById('quickNoteModal');
|
||||
|
||||
159
online_orders.php
Normal file
159
online_orders.php
Normal file
@ -0,0 +1,159 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/app.php';
|
||||
$user = require_permission('sales', 'show'); // or create a specific permission
|
||||
$pageTitle = tr('طلبات المتجر', 'Online Orders');
|
||||
$activeNav = 'online_orders';
|
||||
|
||||
$db = db();
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
if ($_POST['action'] === 'update_status') {
|
||||
$id = (int)$_POST['id'];
|
||||
$status = $_POST['status']; // pending, accepted, completed, rejected
|
||||
|
||||
$stmt = $db->prepare("UPDATE online_orders SET status = ? WHERE id = ?");
|
||||
$stmt->execute([$status, $id]);
|
||||
set_flash('success', tr('تم تحديث حالة الطلب', 'Order status updated'));
|
||||
redirect_to('online_orders.php');
|
||||
}
|
||||
}
|
||||
|
||||
$stmt = $db->query("SELECT * FROM online_orders ORDER BY created_at DESC");
|
||||
$orders = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
require __DIR__ . '/includes/header.php';
|
||||
?>
|
||||
|
||||
<div class="row align-items-center mb-4">
|
||||
<div class="col">
|
||||
<h3 class="h5 mb-0 fw-bold"><i class="bi bi-cart-check me-2"></i><?= h(tr('طلبات المتجر الإلكتروني', 'Online Store Orders')) ?></h3>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm border-0 rounded-4">
|
||||
<div class="card-body p-0 table-responsive">
|
||||
<table class="table table-hover align-middle mb-0">
|
||||
<thead class="table-light text-muted">
|
||||
<tr>
|
||||
<th class="ps-4">#</th>
|
||||
<th><?= h(tr('التاريخ', 'Date')) ?></th>
|
||||
<th><?= h(tr('العميل', 'Customer')) ?></th>
|
||||
<th><?= h(tr('الهاتف', 'Telephone')) ?></th>
|
||||
<th><?= h(tr('العنوان', 'Address')) ?></th>
|
||||
<th><?= h(tr('المبلغ', 'Amount')) ?></th>
|
||||
<th><?= h(tr('الحالة', 'Status')) ?></th>
|
||||
<th class="pe-4 text-end"><?= h(tr('إجراءات', 'Actions')) ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if(empty($orders)): ?>
|
||||
<tr><td colspan="8" class="text-center py-5 text-muted"><?= h(tr('لا توجد طلبات بعد', 'No orders yet')) ?></td></tr>
|
||||
<?php else: ?>
|
||||
<?php foreach($orders as $o):
|
||||
$statusClass = 'bg-secondary';
|
||||
$statusText = $o['status'];
|
||||
if ($o['status'] === 'pending') { $statusClass = 'bg-warning text-dark'; $statusText = tr('قيد الانتظار', 'Pending'); }
|
||||
elseif ($o['status'] === 'accepted') { $statusClass = 'bg-primary'; $statusText = tr('مقبول', 'Accepted'); }
|
||||
elseif ($o['status'] === 'completed') { $statusClass = 'bg-success'; $statusText = tr('مكتمل', 'Completed'); }
|
||||
elseif ($o['status'] === 'rejected') { $statusClass = 'bg-danger'; $statusText = tr('مرفوض', 'Rejected'); }
|
||||
|
||||
$items = json_decode($o['items_json'], true) ?: [];
|
||||
?>
|
||||
<tr>
|
||||
<td class="ps-4 fw-bold">#<?= h($o['id']) ?></td>
|
||||
<td><?= h(date('Y-m-d H:i', strtotime($o['created_at']))) ?></td>
|
||||
<td class="fw-bold"><?= h($o['customer_name']) ?></td>
|
||||
<td><?= h($o['customer_phone']) ?></td>
|
||||
<td style="max-width: 200px;" class="text-truncate" title="<?= h($o['customer_address']) ?>"><?= h($o['customer_address']) ?></td>
|
||||
<td class="fw-bold text-primary"><?= h(currency($o['total_amount'])) ?></td>
|
||||
<td><span class="badge <?= $statusClass ?> px-2 py-1"><?= h($statusText) ?></span></td>
|
||||
<td class="pe-4 text-end">
|
||||
<button class="btn btn-sm btn-light shadow-sm" onclick='viewOrder(<?= json_encode([
|
||||
"id" => $o["id"],
|
||||
"name" => $o["customer_name"],
|
||||
"phone" => $o["customer_phone"],
|
||||
"address" => $o["customer_address"],
|
||||
"total" => $o["total_amount"],
|
||||
"items" => $items
|
||||
], JSON_UNESCAPED_UNICODE) ?>)'>
|
||||
<i class="bi bi-eye"></i> <?= h(tr('عرض', 'View')) ?>
|
||||
</button>
|
||||
<div class="dropdown d-inline-block">
|
||||
<button class="btn btn-sm btn-primary dropdown-toggle shadow-sm" type="button" data-bs-toggle="dropdown">
|
||||
<?= h(tr('تغيير الحالة', 'Change Status')) ?>
|
||||
</button>
|
||||
<ul class="dropdown-menu shadow">
|
||||
<li><form method="post"><input type="hidden" name="action" value="update_status"><input type="hidden" name="id" value="<?= h($o['id']) ?>"><input type="hidden" name="status" value="pending"><button class="dropdown-item" type="submit"><?= h(tr('قيد الانتظار', 'Pending')) ?></button></form></li>
|
||||
<li><form method="post"><input type="hidden" name="action" value="update_status"><input type="hidden" name="id" value="<?= h($o['id']) ?>"><input type="hidden" name="status" value="accepted"><button class="dropdown-item" type="submit"><?= h(tr('مقبول', 'Accepted')) ?></button></form></li>
|
||||
<li><form method="post"><input type="hidden" name="action" value="update_status"><input type="hidden" name="id" value="<?= h($o['id']) ?>"><input type="hidden" name="status" value="completed"><button class="dropdown-item text-success fw-bold" type="submit"><?= h(tr('مكتمل', 'Completed')) ?></button></form></li>
|
||||
<li><form method="post"><input type="hidden" name="action" value="update_status"><input type="hidden" name="id" value="<?= h($o['id']) ?>"><input type="hidden" name="status" value="rejected"><button class="dropdown-item text-danger fw-bold" type="submit"><?= h(tr('مرفوض', 'Rejected')) ?></button></form></li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Order Modal -->
|
||||
<div class="modal fade" id="orderModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content rounded-4 border-0 shadow">
|
||||
<div class="modal-header border-bottom-0 pb-0 pt-4 px-4">
|
||||
<h5 class="modal-title fw-bold" id="orderModalLabel"><?= h(tr('تفاصيل الطلب', 'Order Details')) ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<div class="bg-light p-3 rounded-3 mb-4">
|
||||
<div class="row">
|
||||
<div class="col-6 mb-2"><small class="text-muted d-block"><?= h(tr('العميل', 'Customer')) ?></small><strong id="vName"></strong></div>
|
||||
<div class="col-6 mb-2"><small class="text-muted d-block"><?= h(tr('الهاتف', 'Phone')) ?></small><strong id="vPhone"></strong></div>
|
||||
<div class="col-12"><small class="text-muted d-block"><?= h(tr('العنوان', 'Address')) ?></small><strong id="vAddress"></strong></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h6 class="fw-bold border-bottom pb-2 mb-3"><?= h(tr('المنتجات', 'Products')) ?></h6>
|
||||
<ul class="list-group list-group-flush mb-4" id="vItems">
|
||||
</ul>
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center bg-primary bg-opacity-10 text-primary p-3 rounded-3">
|
||||
<h5 class="mb-0 fw-bold"><?= h(tr('الإجمالي', 'Total')) ?></h5>
|
||||
<h4 class="mb-0 fw-bold" id="vTotal"></h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer border-top-0 pt-0 pb-4 px-4">
|
||||
<button type="button" class="btn btn-secondary rounded-pill px-4" data-bs-dismiss="modal"><?= h(tr('إغلاق', 'Close')) ?></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const orderModal = new bootstrap.Modal(document.getElementById('orderModal'));
|
||||
|
||||
function viewOrder(order) {
|
||||
document.getElementById('vName').innerText = order.name;
|
||||
document.getElementById('vPhone').innerText = order.phone;
|
||||
document.getElementById('vAddress').innerText = order.address;
|
||||
document.getElementById('vTotal').innerText = Number(order.total).toFixed(2);
|
||||
|
||||
let html = '';
|
||||
order.items.forEach(item => {
|
||||
html += `<li class="list-group-item px-0 d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<span class="fw-bold">${item.name}</span>
|
||||
<small class="text-muted ms-2 px-2 bg-light rounded">${item.price}</small>
|
||||
</div>
|
||||
<span class="badge bg-secondary rounded-pill">x${item.qty}</span>
|
||||
</li>`;
|
||||
});
|
||||
document.getElementById('vItems').innerHTML = html;
|
||||
|
||||
orderModal.show();
|
||||
}
|
||||
</script>
|
||||
|
||||
<?php require __DIR__ . '/includes/footer.php'; ?>
|
||||
240
outlets.php
Normal file
240
outlets.php
Normal file
@ -0,0 +1,240 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/app.php';
|
||||
$user = require_permission('settings', 'show');
|
||||
$pageTitle = tr('الفروع (المنافذ)', 'Outlets');
|
||||
$activeNav = 'outlets';
|
||||
|
||||
$pdo = db();
|
||||
|
||||
// Ensure branches table exists
|
||||
$pdo->exec("CREATE TABLE IF NOT EXISTS branches (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
code VARCHAR(50) UNIQUE NOT NULL,
|
||||
name_ar VARCHAR(100) NOT NULL,
|
||||
name_en VARCHAR(100) NOT NULL,
|
||||
city_ar VARCHAR(100),
|
||||
city_en VARCHAR(100),
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
)");
|
||||
|
||||
// Check if branches table is empty, if so, seed it
|
||||
$stmt = $pdo->query("SELECT COUNT(*) FROM branches");
|
||||
if ($stmt->fetchColumn() == 0) {
|
||||
$defaultBranches = [
|
||||
['code' => 'muscat', 'name_ar' => 'فرع مسقط', 'name_en' => 'Muscat Branch', 'city_ar' => 'مسقط', 'city_en' => 'Muscat'],
|
||||
['code' => 'sohar', 'name_ar' => 'فرع صحار', 'name_en' => 'Sohar Branch', 'city_ar' => 'صحار', 'city_en' => 'Sohar'],
|
||||
['code' => 'nizwa', 'name_ar' => 'فرع نزوى', 'name_en' => 'Nizwa Branch', 'city_ar' => 'نزوى', 'city_en' => 'Nizwa'],
|
||||
];
|
||||
$insertStmt = $pdo->prepare("INSERT IGNORE INTO branches (code, name_ar, name_en, city_ar, city_en) VALUES (?, ?, ?, ?, ?)");
|
||||
foreach ($defaultBranches as $b) {
|
||||
$insertStmt->execute([$b['code'], $b['name_ar'], $b['name_en'], $b['city_ar'], $b['city_en']]);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle Form Submission
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$action = $_POST['action'] ?? '';
|
||||
|
||||
if ($action === 'create') {
|
||||
$stmt = $pdo->prepare('INSERT INTO branches (code, name_ar, name_en, city_ar, city_en) VALUES (?, ?, ?, ?, ?)');
|
||||
try {
|
||||
$stmt->execute([$_POST['code'], $_POST['name_ar'], $_POST['name_en'], $_POST['city_ar'] ?? '', $_POST['city_en'] ?? '']);
|
||||
set_flash('success', tr('تمت إضافة الفرع بنجاح', 'Outlet added successfully'));
|
||||
} catch (PDOException $e) {
|
||||
if ($e->getCode() == 23000) {
|
||||
set_flash('danger', tr('رمز الفرع موجود مسبقاً', 'Outlet code already exists'));
|
||||
} else {
|
||||
set_flash('danger', tr('حدث خطأ', 'An error occurred'));
|
||||
}
|
||||
}
|
||||
redirect_to('outlets.php');
|
||||
} elseif ($action === 'edit') {
|
||||
$stmt = $pdo->prepare('UPDATE branches SET name_ar = ?, name_en = ?, city_ar = ?, city_en = ? WHERE code = ?');
|
||||
$stmt->execute([$_POST['name_ar'], $_POST['name_en'], $_POST['city_ar'] ?? '', $_POST['city_en'] ?? '', $_POST['code']]);
|
||||
set_flash('success', tr('تم التحديث بنجاح', 'Updated successfully'));
|
||||
redirect_to('outlets.php');
|
||||
} elseif ($action === 'delete') {
|
||||
$stmt = $pdo->prepare('DELETE FROM branches WHERE code = ?');
|
||||
$stmt->execute([$_POST['code']]);
|
||||
set_flash('success', tr('تم الحذف بنجاح', 'Deleted successfully'));
|
||||
redirect_to('outlets.php');
|
||||
}
|
||||
}
|
||||
|
||||
$branchesList = $pdo->query("SELECT * FROM branches ORDER BY id ASC")->fetchAll();
|
||||
|
||||
require __DIR__ . '/includes/header.php';
|
||||
?>
|
||||
|
||||
<div class="card shadow-sm border-0 mb-4">
|
||||
<div class="card-header bg-white border-bottom-0 pt-4 pb-0 d-flex justify-content-between align-items-center">
|
||||
<h5 class="fw-bold text-primary mb-0"><i class="bi bi-shop me-2"></i> <?= h($pageTitle) ?></h5>
|
||||
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#createModal">
|
||||
<i class="bi bi-plus-lg"></i> <?= h(tr('إضافة فرع', 'Add Outlet')) ?>
|
||||
</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover align-middle">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th><?= h(tr('الرمز', 'Code')) ?></th>
|
||||
<th><?= h(tr('الاسم (عربي)', 'Name (AR)')) ?></th>
|
||||
<th><?= h(tr('الاسم (إنجليزي)', 'Name (EN)')) ?></th>
|
||||
<th><?= h(tr('المدينة', 'City')) ?></th>
|
||||
<th class="text-end"><?= h(tr('إجراءات', 'Actions')) ?></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($branchesList as $b): ?>
|
||||
<tr>
|
||||
<td><span class="badge bg-secondary"><?= h($b['code']) ?></span></td>
|
||||
<td><?= h($b['name_ar']) ?></td>
|
||||
<td><?= h($b['name_en']) ?></td>
|
||||
<td><?= h(current_lang() === 'ar' ? $b['city_ar'] : $b['city_en']) ?></td>
|
||||
<td class="text-end">
|
||||
<button class="btn btn-sm btn-outline-primary"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#editModal<?= $b['id'] ?>">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<?php if ($b['code'] !== 'muscat'): ?>
|
||||
<button class="btn btn-sm btn-outline-danger"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#deleteModal<?= $b['id'] ?>">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- Edit Modal -->
|
||||
<div class="modal fade" id="editModal<?= $b['id'] ?>" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content border-0 shadow">
|
||||
<form method="post">
|
||||
<div class="modal-header bg-light">
|
||||
<h5 class="modal-title fw-bold"><?= h(tr('تعديل فرع', 'Edit Outlet')) ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-start">
|
||||
<input type="hidden" name="action" value="edit">
|
||||
<input type="hidden" name="code" value="<?= h($b['code']) ?>">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('الرمز', 'Code')) ?></label>
|
||||
<input type="text" class="form-control bg-light" value="<?= h($b['code']) ?>" disabled>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('الاسم (عربي)', 'Name (AR)')) ?></label>
|
||||
<input type="text" name="name_ar" class="form-control" value="<?= h($b['name_ar']) ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('الاسم (إنجليزي)', 'Name (EN)')) ?></label>
|
||||
<input type="text" name="name_en" class="form-control" value="<?= h($b['name_en']) ?>" required>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('المدينة (عربي)', 'City (AR)')) ?></label>
|
||||
<input type="text" name="city_ar" class="form-control" value="<?= h($b['city_ar']) ?>">
|
||||
</div>
|
||||
<div class="col-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('المدينة (إنجليزي)', 'City (EN)')) ?></label>
|
||||
<input type="text" name="city_en" class="form-control" value="<?= h($b['city_en']) ?>">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer bg-light">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= h(tr('إلغاء', 'Cancel')) ?></button>
|
||||
<button type="submit" class="btn btn-primary"><?= h(tr('حفظ', 'Save')) ?></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Modal -->
|
||||
<div class="modal fade" id="deleteModal<?= $b['id'] ?>" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content border-0 shadow">
|
||||
<form method="post">
|
||||
<div class="modal-header bg-danger text-white">
|
||||
<h5 class="modal-title fw-bold"><?= h(tr('تأكيد الحذف', 'Confirm Deletion')) ?></h5>
|
||||
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body p-4 text-center">
|
||||
<i class="bi bi-exclamation-circle text-danger" style="font-size: 3rem;"></i>
|
||||
<p class="mt-3 fs-5"><?= h(tr('هل أنت متأكد من حذف هذا الفرع؟', 'Are you sure you want to delete this outlet?')) ?></p>
|
||||
<p class="text-muted fw-bold"><?= h($b['name_ar'] . ' / ' . $b['name_en']) ?></p>
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="code" value="<?= h($b['code']) ?>">
|
||||
</div>
|
||||
<div class="modal-footer bg-light justify-content-center">
|
||||
<button type="button" class="btn btn-secondary px-4" data-bs-dismiss="modal"><?= h(tr('إلغاء', 'Cancel')) ?></button>
|
||||
<button type="submit" class="btn btn-danger px-4"><?= h(tr('حذف', 'Delete')) ?></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php endforeach; ?>
|
||||
<?php if (empty($branchesList)): ?>
|
||||
<tr>
|
||||
<td colspan="5" class="text-center py-4 text-muted">
|
||||
<i class="bi bi-inbox fs-2 d-block mb-2"></i>
|
||||
<?= h(tr('لا توجد فروع', 'No outlets found')) ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Create Modal -->
|
||||
<div class="modal fade" id="createModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content border-0 shadow">
|
||||
<form method="post">
|
||||
<div class="modal-header bg-light">
|
||||
<h5 class="modal-title fw-bold"><?= h(tr('إضافة فرع جديد', 'Add New Outlet')) ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-start">
|
||||
<input type="hidden" name="action" value="create">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('الرمز (إنجليزي فقط بدون مسافات)', 'Code (English only, no spaces)')) ?></label>
|
||||
<input type="text" name="code" class="form-control" placeholder="e.g. dubai" required pattern="[a-zA-Z0-9_-]+">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('الاسم (عربي)', 'Name (AR)')) ?></label>
|
||||
<input type="text" name="name_ar" class="form-control" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= h(tr('الاسم (إنجليزي)', 'Name (EN)')) ?></label>
|
||||
<input type="text" name="name_en" class="form-control" required>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('المدينة (عربي)', 'City (AR)')) ?></label>
|
||||
<input type="text" name="city_ar" class="form-control">
|
||||
</div>
|
||||
<div class="col-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('المدينة (إنجليزي)', 'City (EN)')) ?></label>
|
||||
<input type="text" name="city_en" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer bg-light">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal"><?= h(tr('إلغاء', 'Cancel')) ?></button>
|
||||
<button type="submit" class="btn btn-primary"><?= h(tr('إضافة', 'Add')) ?></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php require __DIR__ . '/includes/footer.php'; ?>
|
||||
44
patch_stock.py
Normal file
44
patch_stock.py
Normal file
@ -0,0 +1,44 @@
|
||||
import re
|
||||
|
||||
with open('stock.php', 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
# Update openItemModal definition
|
||||
content = content.replace(
|
||||
"function openItemModal(sku = '', name = '', price = '', cost_price = '', base_stock = '', vat = '<?= h(get_setting('vat_percentage', 5)) ?>', category_id = '', supplier_id = '', unit_id = '', image_url = '') {",
|
||||
"function openItemModal(sku = '', name = '', price = '', cost_price = '', base_stock = '', vat = '<?= h(get_setting('vat_percentage', 5)) ?>', category_id = '', supplier_id = '', unit_id = '', image_url = '', in_catalog = 0) {"
|
||||
)
|
||||
|
||||
# Add UI toggle in the form
|
||||
switch_html = """
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('الظهور في المتجر (الكتالوج)', 'Visible in Catalog')) ?></label>
|
||||
<div class="form-check form-switch mt-2">
|
||||
<input class="form-check-input" type="checkbox" id="item_in_catalog" style="transform: scale(1.3); margin-left: -1.5em; margin-right: 0.5em;">
|
||||
<label class="form-check-label" for="item_in_catalog" style="margin-right: 2.5em;"><?= h(tr('عرض أونلاين', 'Show Online')) ?></label>
|
||||
</div>
|
||||
</div>
|
||||
"""
|
||||
content = content.replace(
|
||||
'<div class="col-md-6 mb-3">\n <label class="form-label"><?= h(tr(\'التكلفة\', \'Cost Price\')) ?></label>',
|
||||
switch_html + '<div class="col-md-6 mb-3">\n <label class="form-label"><?= h(tr(\'التكلفة\', \'Cost Price\')) ?></label>'
|
||||
)
|
||||
|
||||
# Update Javascript form handling
|
||||
content = content.replace(
|
||||
"document.getElementById('item_unit').value = unit_id;",
|
||||
"document.getElementById('item_unit').value = unit_id;\n document.getElementById('item_in_catalog').checked = (parseInt(in_catalog) === 1);"
|
||||
)
|
||||
|
||||
content = content.replace(
|
||||
"formData.append('unit_id', document.getElementById('item_unit').value);",
|
||||
"formData.append('unit_id', document.getElementById('item_unit').value);\n formData.append('in_catalog', document.getElementById('item_in_catalog').checked ? '1' : '0');"
|
||||
)
|
||||
|
||||
# Replace the onclick in the button where we pass the arguments
|
||||
search_str = "onclick=\"openItemModal('<?= h($row['sku']) ?>', '<?= h(addslashes($row['name'])) ?>', '<?= h($row['price']) ?>', '<?= h($row['cost_price'] ?? 0) ?>', '<?= h($row['base_stock']) ?>', '<?= h($row['vat'] ?? get_setting('vat_percentage', 5)) ?>', '<?= h($row['category_id'] ?? '') ?>', '<?= h($row['supplier_id'] ?? '') ?>', '<?= h($row['unit_id'] ?? '') ?>', '<?= h($row['image_url'] ?? '') ?>')\""
|
||||
replace_str = "onclick=\"openItemModal('<?= h($row['sku']) ?>', '<?= h(addslashes($row['name'])) ?>', '<?= h($row['price']) ?>', '<?= h($row['cost_price'] ?? 0) ?>', '<?= h($row['base_stock']) ?>', '<?= h($row['vat'] ?? get_setting('vat_percentage', 5)) ?>', '<?= h($row['category_id'] ?? '') ?>', '<?= h($row['supplier_id'] ?? '') ?>', '<?= h($row['unit_id'] ?? '') ?>', '<?= h($row['image_url'] ?? '') ?>', '<?= h($row['in_catalog'] ?? 0) ?>')\""
|
||||
content = content.replace(search_str, replace_str)
|
||||
|
||||
with open('stock.php', 'w') as f:
|
||||
f.write(content)
|
||||
@ -87,7 +87,7 @@ require __DIR__ . '/includes/header.php';
|
||||
<td><?= h($row['supplier_name'] ?: '-') ?></td>
|
||||
<td><?= h($row['reference_no']) ?></td>
|
||||
<td><?= h(branch_label($row['branch_code'])) ?></td>
|
||||
<td class="text-muted"><?= h(currency((float)$row['subtotal'])) ?></td>
|
||||
<td class="text-muted"><?= h(currency((float)$row['total_amount'] - (float)($row['vat_amount'] ?? 0))) ?></td>
|
||||
<td class="text-muted text-danger"><?= h(currency((float)$row['vat_amount'])) ?></td>
|
||||
<td class="fw-bold text-success"><?= h(currency((float)$row['total_amount'])) ?></td>
|
||||
<td>
|
||||
|
||||
16
reports.php
16
reports.php
@ -153,9 +153,13 @@ require __DIR__ . '/includes/header.php';
|
||||
$vatSum = 0;
|
||||
$totalSum = 0;
|
||||
foreach($salesReport as $sale):
|
||||
$subtotalSum += (float) ($sale['subtotal'] ?? 0);
|
||||
$vatSum += (float) ($sale['vat_amount'] ?? 0);
|
||||
$totalSum += (float) $sale['total_amount'];
|
||||
$vat = (float) ($sale['vat_amount'] ?? 0);
|
||||
$total = (float) $sale['total_amount'];
|
||||
$calcSubtotal = $total - $vat;
|
||||
|
||||
$subtotalSum += $calcSubtotal;
|
||||
$vatSum += $vat;
|
||||
$totalSum += $total;
|
||||
?>
|
||||
<tr>
|
||||
<td><?= h(date('Y-m-d H:i', strtotime((string)$sale['sale_date']))) ?></td>
|
||||
@ -170,9 +174,9 @@ require __DIR__ . '/includes/header.php';
|
||||
<span class="badge bg-success"><i class="bi bi-check-circle"></i> <?= h(tr('مدفوع', 'Paid')) ?></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-end"><?= h(currency((float)($sale['subtotal'] ?? 0))) ?></td>
|
||||
<td class="text-end"><?= h(currency((float)($sale['vat_amount'] ?? 0))) ?></td>
|
||||
<td class="text-end fw-bold"><?= h(currency((float)$sale['total_amount'])) ?></td>
|
||||
<td class="text-end"><?= h(currency($calcSubtotal)) ?></td>
|
||||
<td class="text-end"><?= h(currency($vat)) ?></td>
|
||||
<td class="text-end fw-bold"><?= h(currency($total)) ?></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
|
||||
@ -150,7 +150,7 @@ require __DIR__ . '/includes/header.php';
|
||||
<td><span class="badge text-bg-light border"><?= h(sale_mode_label((string) $sale['sale_mode'])) ?></span></td>
|
||||
<td><?= h((string) $sale['cashier_name']) ?></td>
|
||||
<td><?= h((string) ($sale['customer_name'] ?: '-')) ?></td>
|
||||
<td class="text-muted"><?= h(currency((float) $sale['subtotal'])) ?></td>
|
||||
<td class="text-muted"><?= h(currency((float) $sale['total_amount'] - (float) ($sale['vat_amount'] ?? 0))) ?></td>
|
||||
<td class="text-muted text-danger"><?= h(currency((float) $sale['vat_amount'])) ?></td>
|
||||
<td class="fw-bold text-success"><?= h(currency((float) $sale['total_amount'])) ?></td>
|
||||
<td>
|
||||
|
||||
413
shop.php
Normal file
413
shop.php
Normal file
@ -0,0 +1,413 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/includes/app.php';
|
||||
|
||||
$forcePublic = true;
|
||||
$pageTitle = tr('الطلب عبر الإنترنت', 'Online Ordering');
|
||||
|
||||
require __DIR__ . '/includes/header.php';
|
||||
|
||||
$db = db();
|
||||
$stmt = $db->query("
|
||||
SELECT i.*, c.name_ar as cat_ar, c.name_en as cat_en
|
||||
FROM items i
|
||||
LEFT JOIN categories c ON i.category_id = c.id
|
||||
WHERE i.in_catalog = 1
|
||||
ORDER BY c.id, i.name
|
||||
");
|
||||
$items = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$catalog = [];
|
||||
foreach ($items as $item) {
|
||||
$catName = current_lang() === 'ar' ? ($item['cat_ar'] ?? 'عام') : ($item['cat_en'] ?? 'General');
|
||||
$catalog[$catName][] = $item;
|
||||
}
|
||||
?>
|
||||
<style>
|
||||
.shop-item-card {
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
border-radius: 12px;
|
||||
border: none;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
|
||||
height: 100%;
|
||||
}
|
||||
.shop-item-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 12px 24px rgba(0,0,0,0.1);
|
||||
}
|
||||
.shop-item-img {
|
||||
height: 200px;
|
||||
object-fit: cover;
|
||||
border-top-left-radius: 12px;
|
||||
border-top-right-radius: 12px;
|
||||
}
|
||||
.cart-floating-btn {
|
||||
position: fixed;
|
||||
bottom: 30px;
|
||||
right: 30px;
|
||||
border-radius: 50%;
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
box-shadow: 0 4px 15px rgba(13,110,253,0.4);
|
||||
z-index: 1000;
|
||||
}
|
||||
.cart-badge {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
transform: translate(30%, -30%);
|
||||
}
|
||||
body { background-color: #f8f9fa; }
|
||||
|
||||
/* Hide scrollbar for category filter */
|
||||
.category-filter-container::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
.category-filter-container {
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container py-5">
|
||||
<div class="d-flex justify-content-between align-items-center mb-5 bg-white p-4 rounded-4 shadow-sm">
|
||||
<div>
|
||||
<h1 class="fw-bold text-primary mb-1"><i class="bi bi-shop me-2"></i><?= h(get_setting('company_name_' . current_lang(), app_name())) ?></h1>
|
||||
<p class="text-muted mb-0"><?= h(tr('اطلب الآن وسنقوم بتجهيز طلبك', 'Order now and we will prepare your request')) ?></p>
|
||||
</div>
|
||||
<div class="language-switcher">
|
||||
<a class="btn btn-sm <?= current_lang() === 'ar' ? 'btn-primary' : 'btn-light text-dark' ?> rounded-pill px-3" href="shop.php?lang=ar">AR</a>
|
||||
<a class="btn btn-sm <?= current_lang() === 'en' ? 'btn-primary' : 'btn-light text-dark' ?> rounded-pill px-3" href="shop.php?lang=en">EN</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($catalog)): ?>
|
||||
<!-- Search and Filter -->
|
||||
<div class="row mb-5">
|
||||
<div class="col-md-6 mb-3 mb-md-0">
|
||||
<div class="input-group input-group-lg shadow-sm rounded-pill overflow-hidden">
|
||||
<span class="input-group-text bg-white border-0 ps-4"><i class="bi bi-search text-muted"></i></span>
|
||||
<input type="text" id="searchInput" class="form-control border-0 px-3" placeholder="<?= h(tr('ابحث عن منتج...', 'Search for a product...')) ?>" onkeyup="filterProducts()">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex gap-2 overflow-auto py-2 px-1 category-filter-container" style="white-space: nowrap;" id="categoryFilters">
|
||||
<button class="btn btn-primary rounded-pill px-4 active" data-filter="all" onclick="setCategoryFilter('all', this)"><?= h(tr('الكل', 'All')) ?></button>
|
||||
<?php foreach (array_keys($catalog) as $catName): ?>
|
||||
<button class="btn btn-light rounded-pill px-4 text-dark shadow-sm border" data-filter="<?= htmlspecialchars($catName) ?>" onclick="setCategoryFilter(this.getAttribute('data-filter'), this)"><?= h($catName) ?></button>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (empty($catalog)): ?>
|
||||
<div class="text-center py-5">
|
||||
<i class="bi bi-box-seam display-1 text-muted opacity-50 mb-3 d-block"></i>
|
||||
<h3 class="text-muted"><?= h(tr('لا توجد منتجات متاحة حالياً', 'No products available currently')) ?></h3>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<?php foreach ($catalog as $category => $catItems): ?>
|
||||
<h3 class="fw-bold mb-4 mt-5 border-bottom pb-2 category-title" data-category="<?= htmlspecialchars($category) ?>"><?= h($category) ?></h3>
|
||||
<div class="row g-4 category-row" data-category="<?= htmlspecialchars($category) ?>">
|
||||
<?php foreach ($catItems as $item): ?>
|
||||
<div class="col-sm-6 col-md-4 col-lg-3 product-item" data-name="<?= htmlspecialchars(strtolower($item['name'])) ?>">
|
||||
<div class="card shop-item-card">
|
||||
<?php if (!empty($item['image_url'])): ?>
|
||||
<img src="<?= h($item['image_url']) ?>" class="card-img-top shop-item-img" alt="<?= h($item['name']) ?>">
|
||||
<?php else: ?>
|
||||
<div class="bg-light d-flex align-items-center justify-content-center shop-item-img text-muted">
|
||||
<i class="bi bi-image" style="font-size: 3rem;"></i>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="card-body d-flex flex-column">
|
||||
<h5 class="card-title fw-bold mb-1"><?= h($item['name']) ?></h5>
|
||||
<p class="text-primary fw-bold fs-5 mb-3"><?= h(currency($item['price'])) ?></p>
|
||||
<button class="btn btn-outline-primary mt-auto rounded-pill fw-bold" onclick="addToCart(<?= htmlspecialchars(json_encode([
|
||||
'id' => $item['id'],
|
||||
'sku' => $item['sku'],
|
||||
'name' => $item['name'],
|
||||
'price' => $item['price']
|
||||
]), ENT_QUOTES, 'UTF-8') ?>)">
|
||||
<i class="bi bi-cart-plus me-1"></i> <?= h(tr('إضافة للسلة', 'Add to Cart')) ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<!-- No results message -->
|
||||
<div id="noResultsMsg" class="text-center py-5" style="display: none;">
|
||||
<i class="bi bi-search display-1 text-muted opacity-50 mb-3 d-block"></i>
|
||||
<h3 class="text-muted"><?= h(tr('لم يتم العثور على نتائج', 'No results found')) ?></h3>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary cart-floating-btn position-relative" onclick="openCart()">
|
||||
<i class="bi bi-cart3"></i>
|
||||
<span class="position-absolute badge rounded-pill bg-danger cart-badge border border-light" id="cartCount">
|
||||
0
|
||||
</span>
|
||||
</button>
|
||||
|
||||
<!-- Cart Modal -->
|
||||
<div class="modal fade" id="cartModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered modal-lg">
|
||||
<div class="modal-content border-0 shadow-lg rounded-4">
|
||||
<div class="modal-header border-bottom-0 pb-0 pt-4 px-4">
|
||||
<h5 class="modal-title fw-bold"><i class="bi bi-cart-check me-2 text-primary"></i><?= h(tr('سلة المشتريات', 'Shopping Cart')) ?></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body px-4 py-4">
|
||||
<div id="cartItemsList" class="mb-4">
|
||||
<!-- Items will be rendered here -->
|
||||
</div>
|
||||
<div class="d-flex justify-content-between align-items-center bg-light p-3 rounded-3 mb-4">
|
||||
<h5 class="mb-0 fw-bold"><?= h(tr('المجموع الإجمالي', 'Total Amount')) ?></h5>
|
||||
<h4 class="mb-0 fw-bold text-primary" id="cartTotal">0.00</h4>
|
||||
</div>
|
||||
|
||||
<h5 class="fw-bold mb-3 border-bottom pb-2"><?= h(tr('بيانات العميل', 'Customer Details')) ?></h5>
|
||||
<form id="checkoutForm">
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold"><?= h(tr('الاسم', 'Name')) ?> *</label>
|
||||
<input type="text" class="form-control form-control-lg rounded-3" id="customerName" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold"><?= h(tr('رقم الهاتف', 'Telephone')) ?> *</label>
|
||||
<input type="tel" class="form-control form-control-lg rounded-3" id="customerPhone" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label class="form-label fw-semibold"><?= h(tr('العنوان', 'Address')) ?> *</label>
|
||||
<textarea class="form-control form-control-lg rounded-3" id="customerAddress" rows="2" required></textarea>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="modal-footer border-top-0 pt-0 pb-4 px-4 d-flex justify-content-between">
|
||||
<button type="button" class="btn btn-light rounded-pill px-4" data-bs-dismiss="modal"><?= h(tr('إكمال التسوق', 'Continue Shopping')) ?></button>
|
||||
<button type="button" class="btn btn-success rounded-pill px-5 fw-bold shadow-sm" id="submitOrderBtn" onclick="submitOrder()">
|
||||
<i class="bi bi-check2-circle me-1"></i> <?= h(tr('تأكيد الطلب', 'Confirm Order')) ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
<script>
|
||||
let cart = JSON.parse(localStorage.getItem('shop_cart')) || {};
|
||||
let cartModalInstance = null;
|
||||
|
||||
function saveCart() {
|
||||
localStorage.setItem('shop_cart', JSON.stringify(cart));
|
||||
updateCartBadge();
|
||||
}
|
||||
|
||||
function updateCartBadge() {
|
||||
let count = 0;
|
||||
for (let id in cart) {
|
||||
count += cart[id].qty;
|
||||
}
|
||||
document.getElementById('cartCount').innerText = count;
|
||||
}
|
||||
|
||||
function addToCart(item) {
|
||||
if (cart[item.id]) {
|
||||
cart[item.id].qty += 1;
|
||||
} else {
|
||||
cart[item.id] = { ...item, qty: 1 };
|
||||
}
|
||||
saveCart();
|
||||
|
||||
Swal.fire({
|
||||
title: '<?= h(tr('تمت الإضافة', 'Added')) ?>',
|
||||
text: item.name,
|
||||
icon: 'success',
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
showConfirmButton: false,
|
||||
timer: 1500
|
||||
});
|
||||
}
|
||||
|
||||
function changeQty(id, delta) {
|
||||
if (cart[id]) {
|
||||
cart[id].qty += delta;
|
||||
if (cart[id].qty <= 0) {
|
||||
delete cart[id];
|
||||
}
|
||||
saveCart();
|
||||
renderCart();
|
||||
}
|
||||
}
|
||||
|
||||
function renderCart() {
|
||||
const list = document.getElementById('cartItemsList');
|
||||
let html = '';
|
||||
let total = 0;
|
||||
|
||||
if (Object.keys(cart).length === 0) {
|
||||
html = '<div class="text-center text-muted py-4"><?= h(tr('السلة فارغة', 'Cart is empty')) ?></div>';
|
||||
} else {
|
||||
html = '<div class="list-group list-group-flush">';
|
||||
for (let id in cart) {
|
||||
const item = cart[id];
|
||||
const subtotal = item.price * item.qty;
|
||||
total += subtotal;
|
||||
html += `
|
||||
<div class="list-group-item d-flex justify-content-between align-items-center py-3 px-0 border-bottom-dashed">
|
||||
<div>
|
||||
<h6 class="mb-1 fw-bold">${item.name}</h6>
|
||||
<small class="text-muted">${Number(item.price).toFixed(2)}</small>
|
||||
</div>
|
||||
<div class="d-flex align-items-center">
|
||||
<button class="btn btn-sm btn-outline-secondary rounded-circle px-2 me-2" onclick="changeQty(${id}, -1)"><i class="bi bi-dash"></i></button>
|
||||
<span class="fw-bold px-2">${item.qty}</span>
|
||||
<button class="btn btn-sm btn-outline-secondary rounded-circle px-2 ms-2 me-4" onclick="changeQty(${id}, 1)"><i class="bi bi-plus"></i></button>
|
||||
<span class="fw-bold text-primary" style="width: 70px; text-align:right;">${subtotal.toFixed(2)}</span>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
html += '</div>';
|
||||
}
|
||||
|
||||
list.innerHTML = html;
|
||||
document.getElementById('cartTotal').innerText = total.toFixed(2);
|
||||
}
|
||||
|
||||
function openCart() {
|
||||
if (!cartModalInstance && typeof bootstrap !== 'undefined') {
|
||||
cartModalInstance = new bootstrap.Modal(document.getElementById('cartModal'));
|
||||
}
|
||||
|
||||
if (cartModalInstance) {
|
||||
renderCart();
|
||||
cartModalInstance.show();
|
||||
} else {
|
||||
console.error("Bootstrap is not loaded yet.");
|
||||
}
|
||||
}
|
||||
|
||||
async function submitOrder() {
|
||||
if (Object.keys(cart).length === 0) {
|
||||
Swal.fire('<?= h(tr('تنبيه', 'Warning')) ?>', '<?= h(tr('السلة فارغة', 'Cart is empty')) ?>', 'warning');
|
||||
return;
|
||||
}
|
||||
|
||||
const form = document.getElementById('checkoutForm');
|
||||
if (!form.reportValidity()) return;
|
||||
|
||||
const btn = document.getElementById('submitOrderBtn');
|
||||
const origText = btn.innerHTML;
|
||||
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
|
||||
btn.disabled = true;
|
||||
|
||||
const data = {
|
||||
name: document.getElementById('customerName').value,
|
||||
phone: document.getElementById('customerPhone').value,
|
||||
address: document.getElementById('customerAddress').value,
|
||||
items: cart
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch('api/place_order.php', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
const json = await res.json();
|
||||
|
||||
if (json.success) {
|
||||
cart = {};
|
||||
saveCart();
|
||||
if (cartModalInstance) cartModalInstance.hide();
|
||||
Swal.fire({
|
||||
title: '<?= h(tr('تم إرسال الطلب بنجاح!', 'Order submitted successfully!')) ?>',
|
||||
text: '<?= h(tr('سنتواصل معك قريباً لتأكيد الطلب.', 'We will contact you shortly to confirm the order.')) ?>',
|
||||
icon: 'success',
|
||||
confirmButtonText: '<?= h(tr('حسناً', 'OK')) ?>'
|
||||
});
|
||||
form.reset();
|
||||
} else {
|
||||
Swal.fire('<?= h(tr('خطأ', 'Error')) ?>', json.error || '<?= h(tr('فشل إرسال الطلب', 'Failed to submit order')) ?>', 'error');
|
||||
}
|
||||
} catch (e) {
|
||||
Swal.fire('<?= h(tr('خطأ', 'Error')) ?>', '<?= h(tr('حدث خطأ في الاتصال', 'Network error')) ?>', 'error');
|
||||
} finally {
|
||||
btn.innerHTML = origText;
|
||||
btn.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Filtering Logic
|
||||
let currentCategory = 'all';
|
||||
|
||||
function setCategoryFilter(cat, btn) {
|
||||
currentCategory = cat;
|
||||
|
||||
// Update active button state
|
||||
document.querySelectorAll('#categoryFilters button').forEach(b => {
|
||||
b.classList.remove('btn-primary', 'active');
|
||||
b.classList.add('btn-light', 'text-dark');
|
||||
});
|
||||
btn.classList.remove('btn-light', 'text-dark');
|
||||
btn.classList.add('btn-primary', 'active');
|
||||
|
||||
filterProducts();
|
||||
}
|
||||
|
||||
function filterProducts() {
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
if (!searchInput) return;
|
||||
|
||||
const searchVal = searchInput.value.toLowerCase();
|
||||
let totalVisible = 0;
|
||||
|
||||
document.querySelectorAll('.category-row').forEach(row => {
|
||||
const catName = row.getAttribute('data-category');
|
||||
let hasVisibleItems = false;
|
||||
|
||||
row.querySelectorAll('.product-item').forEach(item => {
|
||||
const itemName = item.getAttribute('data-name');
|
||||
const matchesSearch = itemName.includes(searchVal);
|
||||
const matchesCategory = currentCategory === 'all' || currentCategory === catName;
|
||||
|
||||
if (matchesSearch && matchesCategory) {
|
||||
item.style.display = '';
|
||||
hasVisibleItems = true;
|
||||
totalVisible++;
|
||||
} else {
|
||||
item.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
// Hide category title if no items visible
|
||||
const title = row.previousElementSibling;
|
||||
if (title && title.classList.contains('category-title')) {
|
||||
if (hasVisibleItems) {
|
||||
title.style.display = '';
|
||||
row.style.display = '';
|
||||
} else {
|
||||
title.style.display = 'none';
|
||||
row.style.display = 'none';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const noResultsMsg = document.getElementById('noResultsMsg');
|
||||
if (noResultsMsg) {
|
||||
noResultsMsg.style.display = totalVisible === 0 ? 'block' : 'none';
|
||||
}
|
||||
}
|
||||
|
||||
// init
|
||||
updateCartBadge();
|
||||
</script>
|
||||
|
||||
<?php require __DIR__ . '/includes/footer.php'; ?>
|
||||
23
stock.php
23
stock.php
@ -127,6 +127,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
$price = (float)($_POST['price'] ?? 0);
|
||||
$cost_price = (float)($_POST['cost_price'] ?? 0);
|
||||
$base_stock = (int)($_POST['base_stock'] ?? 0);
|
||||
$in_catalog = isset($_POST['in_catalog']) && $_POST['in_catalog'] === '1' ? 1 : 0;
|
||||
$vat = (float)($_POST['vat'] ?? get_setting('vat_percentage', 5));
|
||||
$category_id = !empty($_POST['category_id']) ? (int)$_POST['category_id'] : null;
|
||||
$supplier_id = !empty($_POST['supplier_id']) ? (int)$_POST['supplier_id'] : null;
|
||||
@ -161,8 +162,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
$sql = "UPDATE items SET sku=?, name=?, price=?, cost_price=?, base_stock=?, vat=?, category_id=?, supplier_id=?, unit_id=? " . ($image_url ? ", image_url=?" : "") . " WHERE sku=?";
|
||||
$params = [$sku, $name, $price, $cost_price, $base_stock, $vat, $category_id, $supplier_id, $unit_id];
|
||||
$sql = "UPDATE items SET sku=?, name=?, price=?, cost_price=?, base_stock=?, vat=?, category_id=?, supplier_id=?, unit_id=?, in_catalog=? " . ($image_url ? ", image_url=?" : "") . " WHERE sku=?";
|
||||
$params = [$sku, $name, $price, $cost_price, $base_stock, $vat, $category_id, $supplier_id, $unit_id, $in_catalog];
|
||||
if ($image_url) {
|
||||
$params[] = $image_url;
|
||||
}
|
||||
@ -174,8 +175,8 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
|
||||
echo json_encode(['success' => false, 'error' => 'SKU already exists']);
|
||||
exit;
|
||||
}
|
||||
$stmt = $pdo->prepare("INSERT INTO items (sku, name, price, cost_price, base_stock, vat, category_id, supplier_id, unit_id, image_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$sku, $name, $price, $cost_price, $base_stock, $vat, $category_id, $supplier_id, $unit_id, $image_url]);
|
||||
$stmt = $pdo->prepare("INSERT INTO items (sku, name, price, cost_price, base_stock, vat, category_id, supplier_id, unit_id, in_catalog, image_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||
$stmt->execute([$sku, $name, $price, $cost_price, $base_stock, $vat, $category_id, $supplier_id, $unit_id, $in_catalog, $image_url]);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true]);
|
||||
@ -376,7 +377,7 @@ require __DIR__ . '/includes/header.php';
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-end pe-3">
|
||||
<button class="btn btn-sm btn-outline-primary rounded-circle shadow-sm" style="width: 34px; height: 34px; padding: 0;" onclick="openItemModal('<?= h($row['sku']) ?>', '<?= h(addslashes($row['name'])) ?>', '<?= h($row['price']) ?>', '<?= h($row['cost_price'] ?? 0) ?>', '<?= h($row['base_stock']) ?>', '<?= h($row['vat'] ?? get_setting('vat_percentage', 5)) ?>', '<?= h($row['category_id'] ?? '') ?>', '<?= h($row['supplier_id'] ?? '') ?>', '<?= h($row['unit_id'] ?? '') ?>', '<?= h($row['image_url'] ?? '') ?>')" data-bs-toggle="tooltip" title="<?= h(tr('تعديل', 'Edit')) ?>">
|
||||
<button class="btn btn-sm btn-outline-primary rounded-circle shadow-sm" style="width: 34px; height: 34px; padding: 0;" onclick="openItemModal('<?= h($row['sku']) ?>', '<?= h(addslashes($row['name'])) ?>', '<?= h($row['price']) ?>', '<?= h($row['cost_price'] ?? 0) ?>', '<?= h($row['base_stock']) ?>', '<?= h($row['vat'] ?? get_setting('vat_percentage', 5)) ?>', '<?= h($row['category_id'] ?? '') ?>', '<?= h($row['supplier_id'] ?? '') ?>', '<?= h($row['unit_id'] ?? '') ?>', '<?= h($row['image_url'] ?? '') ?>', '<?= h($row['in_catalog'] ?? 0) ?>')" data-bs-toggle="tooltip" title="<?= h(tr('تعديل', 'Edit')) ?>">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</button>
|
||||
<button type="button" onclick="printSingleLabel('<?= h(addslashes($row['sku'])) ?>')" class="btn btn-sm btn-outline-secondary rounded-circle shadow-sm ms-1" style="width: 34px; height: 34px; padding: 0;" data-bs-toggle="tooltip" title="<?= h(tr('طباعة ملصق', 'Print Label')) ?>">
|
||||
@ -477,7 +478,15 @@ require __DIR__ . '/includes/header.php';
|
||||
<input type="number" step="0.001" class="form-control" id="item_price" required>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('الظهور في المتجر (الكتالوج)', 'Visible in Catalog')) ?></label>
|
||||
<div class="form-check form-switch mt-2">
|
||||
<input class="form-check-input" type="checkbox" id="item_in_catalog" style="transform: scale(1.3); margin-left: -1.5em; margin-right: 0.5em;">
|
||||
<label class="form-check-label" for="item_in_catalog" style="margin-right: 2.5em;"><?= h(tr('عرض أونلاين', 'Show Online')) ?></label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label"><?= h(tr('التكلفة', 'Cost Price')) ?></label>
|
||||
<input type="number" step="0.001" class="form-control" id="item_cost_price" required>
|
||||
</div>
|
||||
@ -597,7 +606,7 @@ function openImportModal() {
|
||||
m.show();
|
||||
}
|
||||
|
||||
function openItemModal(sku = '', name = '', price = '', cost_price = '', base_stock = '', vat = '<?= h(get_setting('vat_percentage', 5)) ?>', category_id = '', supplier_id = '', unit_id = '', image_url = '') {
|
||||
function openItemModal(sku = '', name = '', price = '', cost_price = '', base_stock = '', vat = '<?= h(get_setting('vat_percentage', 5)) ?>', category_id = '', supplier_id = '', unit_id = '', image_url = '', in_catalog = 0) {
|
||||
document.getElementById('item_original_sku').value = sku;
|
||||
document.getElementById('item_existing_image_url').value = image_url;
|
||||
document.getElementById('item_sku').value = sku;
|
||||
@ -609,6 +618,7 @@ function openItemModal(sku = '', name = '', price = '', cost_price = '', base_st
|
||||
document.getElementById('item_category').value = category_id;
|
||||
document.getElementById('item_supplier').value = supplier_id;
|
||||
document.getElementById('item_unit').value = unit_id;
|
||||
document.getElementById('item_in_catalog').checked = (parseInt(in_catalog) === 1);
|
||||
|
||||
// Remove old image preview if any
|
||||
const oldPreview = document.getElementById('image_preview');
|
||||
@ -646,6 +656,7 @@ async function handleItemSubmit(e) {
|
||||
formData.append('category_id', document.getElementById('item_category').value);
|
||||
formData.append('supplier_id', document.getElementById('item_supplier').value);
|
||||
formData.append('unit_id', document.getElementById('item_unit').value);
|
||||
formData.append('in_catalog', document.getElementById('item_in_catalog').checked ? '1' : '0');
|
||||
formData.append('existing_image_url', document.getElementById('item_existing_image_url').value);
|
||||
|
||||
const picInput = document.getElementById('item_picture');
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user