diff --git a/api/place_order.php b/api/place_order.php
new file mode 100644
index 0000000..d12316f
--- /dev/null
+++ b/api/place_order.php
@@ -0,0 +1,97 @@
+ 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']);
+}
diff --git a/includes/app.php b/includes/app.php
index 25c4104..84a6e83 100644
--- a/includes/app.php
+++ b/includes/app.php
@@ -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),
];
}
diff --git a/includes/footer.php b/includes/footer.php
index 69a9e57..84fd362 100644
--- a/includes/footer.php
+++ b/includes/footer.php
@@ -6,6 +6,20 @@ $isPublic = !isset($user) || !$user;
+
+
+
+
+
diff --git a/includes/header.php b/includes/header.php
index a860e12..ff9dd6e 100644
--- a/includes/header.php
+++ b/includes/header.php
@@ -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;
?>
@@ -138,6 +138,9 @@ $isPublic = !isset($user) || !$user;
= h(tr('الموردون', 'Suppliers')) ?>
+
+ = h(tr('طلبات المتجر', 'Online Orders')) ?>
+
= h(tr('العملاء', 'Customers')) ?>
@@ -153,16 +156,29 @@ $isPublic = !isset($user) || !$user;
-
- = h(tr('إعدادات الشركة', 'Company Settings')) ?>
-
+
+