diff --git a/api/customers.php b/api/customers.php index c1255d2..4d5371f 100644 --- a/api/customers.php +++ b/api/customers.php @@ -5,12 +5,17 @@ $user = require_permission('customers', 'add'); if ($_SERVER['REQUEST_METHOD'] === 'POST') { header('Content-Type: application/json'); $name = trim($_POST['name'] ?? ''); - $phone = trim($_POST['phone'] ?? ''); + $phoneInput = trim($_POST['phone'] ?? ''); + $phone = $phoneInput === '' ? '' : normalize_oman_phone($phoneInput); if (!$name) { echo json_encode(['success' => false, 'error' => tr('الاسم مطلوب', 'Name is required')]); exit; } + if ($phoneInput !== '' && $phone === '') { + echo json_encode(['success' => false, 'error' => tr('رقم الهاتف يجب أن يكون عمانياً من 8 خانات.', 'Phone must be an 8-digit Oman number.')]); + exit; + } try { $pdo = db(); diff --git a/api/place_order.php b/api/place_order.php index 7a56c3d..a7ed2a8 100644 --- a/api/place_order.php +++ b/api/place_order.php @@ -14,14 +14,20 @@ if (!$input || empty($input['items'])) { } $name = trim($input['name'] ?? ''); -$phone = trim($input['phone'] ?? ''); +$phoneInput = trim($input['phone'] ?? ''); +$phone = normalize_oman_phone($phoneInput); $address = trim($input['address'] ?? ''); -if ($name === '' || $phone === '' || $address === '') { +if ($name === '' || $phoneInput === '' || $address === '') { echo json_encode(['success' => false, 'error' => 'Missing customer details']); exit; } +if ($phone === '') { + echo json_encode(['success' => false, 'error' => 'Phone must be an 8-digit Oman number']); + exit; +} + $items = $input['items']; $subtotal = 0; $totalVat = 0; @@ -78,15 +84,37 @@ try { $totalAmount ]); - // Optional: send telegram notification if configured + // Optional: send telegram and WhatsApp notifications if configured try { - $orderId = $db->lastInsertId(); - $msg = "🛒 *New Online Order #{$orderId}*\n\n"; - $msg .= "👤 {$name}\n📞 {$phone}\n📍 {$address}\n\n"; - $msg .= "💰 Subtotal: " . currency($subtotal) . "\n"; - $msg .= "🧾 VAT: " . currency($totalVat) . "\n"; - $msg .= "💵 Total: " . currency($totalAmount) . "\n"; - + $orderId = (int) $db->lastInsertId(); + $orderData = [ + 'id' => $orderId, + 'customer_name' => $name, + 'customer_phone' => $phone, + 'customer_address' => $address, + 'items' => $processedItems, + 'subtotal' => $subtotal, + 'vat_amount' => $totalVat, + 'total_amount' => $totalAmount, + 'status' => 'pending', + 'created_at' => date('Y-m-d H:i:s'), + ]; + + $msg = "🛒 *New Online Order #{$orderId}* + +"; + $msg .= "👤 {$name} +📞 " . phone_display($phone) . " +📍 {$address} + +"; + $msg .= "💰 Subtotal: " . currency($subtotal) . " +"; + $msg .= "🧾 VAT: " . currency($totalVat) . " +"; + $msg .= "💵 Total: " . currency($totalAmount) . " +"; + $botToken = getenv('TELEGRAM_BOT_TOKEN') ?: get_setting('telegram_bot_token'); $chatId = getenv('TELEGRAM_CHAT_ID') ?: get_setting('telegram_chat_id'); if ($botToken && $chatId) { @@ -94,7 +122,8 @@ try { $data = ['chat_id' => $chatId, 'text' => $msg, 'parse_mode' => 'Markdown']; $options = [ 'http' => [ - 'header' => "Content-type: application/x-www-form-urlencoded\r\n", + 'header' => "Content-type: application/x-www-form-urlencoded +", 'method' => 'POST', 'content' => http_build_query($data) ] @@ -102,10 +131,14 @@ try { $context = stream_context_create($options); @file_get_contents($url, false, $context); } + + if (wablas_is_configured()) { + wablas_notify_online_order($orderData, 'created'); + } } catch (Exception $e) { // ignore notification errors } - + echo json_encode(['success' => true]); } catch (Exception $e) { echo json_encode(['success' => false, 'error' => 'Database error: ' . $e->getMessage()]); diff --git a/api/sales_payment.php b/api/sales_payment.php new file mode 100644 index 0000000..f3f6ba4 --- /dev/null +++ b/api/sales_payment.php @@ -0,0 +1,46 @@ + false, 'error' => tr('طريقة الطلب غير مدعومة.', 'Method not allowed.')]); + exit; +} + +$saleId = (int) ($_POST['sale_id'] ?? 0); +$paymentAmount = (string) ($_POST['payment_amount'] ?? ''); +$completeOrder = !empty($_POST['complete_order']); + +if ($saleId <= 0) { + echo json_encode(['success' => false, 'error' => tr('الفاتورة غير صالحة.', 'Invalid invoice.')]); + exit; +} +if ($paymentAmount === '' || !is_numeric($paymentAmount) || (float) $paymentAmount <= 0) { + echo json_encode(['success' => false, 'error' => tr('أدخل مبلغ دفعة صحيحاً.', 'Enter a valid payment amount.')]); + exit; +} + +try { + $sale = fetch_sale($saleId); + if (!$sale) { + echo json_encode(['success' => false, 'error' => tr('الفاتورة غير موجودة أو غير مصرح بها.', 'Invoice not found or not allowed.')]); + exit; + } + + $result = apply_sale_payment($saleId, (float) $paymentAmount, $completeOrder); + + echo json_encode([ + 'success' => true, + 'message' => $result['due_amount'] <= 0.0005 + ? tr('تم سداد الفاتورة بالكامل.', 'Invoice paid in full.') + : tr('تم تسجيل الدفعة الجزئية بنجاح.', 'Partial payment recorded successfully.'), + 'payment_status' => $result['payment_status'], + 'paid_amount' => $result['paid_amount'], + 'due_amount' => $result['due_amount'], + 'applied_amount' => $result['applied_amount'], + ]); +} catch (Throwable $e) { + echo json_encode(['success' => false, 'error' => tr('تعذر تسجيل الدفعة.', 'Could not record the payment.')]); +} diff --git a/api/settings.php b/api/settings.php index e3c5c9a..45ead73 100644 --- a/api/settings.php +++ b/api/settings.php @@ -14,14 +14,29 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $keys = [ 'timezone', 'company_name_ar', 'company_name_en', 'vat_percentage', 'company_vat_number', 'company_phone', 'company_email', 'company_address', + 'wablas_enabled', 'wablas_token', 'wablas_secret_key', + 'wablas_template_created', 'wablas_template_pending', 'wablas_template_accepted', 'wablas_template_completed', 'wablas_template_rejected', 'smtp_host', 'smtp_port', 'smtp_user', 'smtp_pass', 'smtp_secure', 'mail_from', 'mail_from_name' ]; $stmt = $pdo->prepare("INSERT INTO settings (setting_key, setting_value) VALUES (?, ?) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)"); + $companyPhone = trim((string) ($_POST['company_phone'] ?? '')); + if ($companyPhone !== '') { + $companyPhone = normalize_oman_phone($companyPhone); + if ($companyPhone === '') { + set_flash('danger', tr('رقم هاتف الشركة يجب أن يكون عمانياً من 8 خانات.', 'Company phone must be an 8-digit Oman number.')); + $referer = $_SERVER['HTTP_REFERER'] ?? '../index.php'; + header('Location: ' . $referer); + exit; + } + $_POST['company_phone'] = $companyPhone; + } + foreach ($keys as $key) { if (isset($_POST[$key])) { - $stmt->execute([$key, $_POST[$key]]); + $value = is_string($_POST[$key]) ? trim($_POST[$key]) : $_POST[$key]; + $stmt->execute([$key, $value]); } } diff --git a/api/wablas_test.php b/api/wablas_test.php new file mode 100644 index 0000000..23eaf4d --- /dev/null +++ b/api/wablas_test.php @@ -0,0 +1,59 @@ + $token, + 'secret_key' => $secretKey, + 'ignore_enabled' => true, +]); + +if (!empty($result['success'])) { + set_flash('success', tr('تم إرسال رسالة الاختبار إلى 968 ', 'Test message sent to 968 ') . $phone . '.'); +} else { + $status = isset($result['status']) ? (' (' . (int) $result['status'] . ')') : ''; + set_flash('danger', tr('فشل إرسال رسالة الاختبار.', 'Failed to send the test message.') . ' ' . (string) ($result['error'] ?? ('Wablas error' . $status))); +} + +header('Location: ' . ($_SERVER['HTTP_REFERER'] ?? '../index.php')); +exit; diff --git a/cookies_wablas_test.txt b/cookies_wablas_test.txt new file mode 100644 index 0000000..efe55ff --- /dev/null +++ b/cookies_wablas_test.txt @@ -0,0 +1,5 @@ +# Netscape HTTP Cookie File +# 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 1snhg6de9qetq9ilb98ihn8qnv diff --git a/customers.php b/customers.php index ebf9950..c488862 100644 --- a/customers.php +++ b/customers.php @@ -11,13 +11,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { $action = $_POST['action'] ?? ''; if ($action === 'create') { + $phoneInput = trim((string) ($_POST['phone'] ?? '')); + $phone = $phoneInput === '' ? '' : normalize_oman_phone($phoneInput); + if ($phoneInput !== '' && $phone === '') { + set_flash('danger', tr('رقم الهاتف يجب أن يكون عمانياً من 8 خانات.', 'Phone must be an 8-digit Oman number.')); + redirect_to('customers.php'); + } + $stmt = $pdo->prepare('INSERT INTO customers (name, phone, email, address) VALUES (?, ?, ?, ?)'); - $stmt->execute([$_POST['name'], $_POST['phone'] ?? '', $_POST['email'] ?? '', $_POST['address'] ?? '']); + $stmt->execute([$_POST['name'], $phone, $_POST['email'] ?? '', $_POST['address'] ?? '']); set_flash('success', tr('تمت إضافة العميل بنجاح', 'Customer added successfully')); redirect_to('customers.php'); } elseif ($action === 'edit') { + $phoneInput = trim((string) ($_POST['phone'] ?? '')); + $phone = $phoneInput === '' ? '' : normalize_oman_phone($phoneInput); + if ($phoneInput !== '' && $phone === '') { + set_flash('danger', tr('رقم الهاتف يجب أن يكون عمانياً من 8 خانات.', 'Phone must be an 8-digit Oman number.')); + redirect_to('customers.php'); + } + $stmt = $pdo->prepare('UPDATE customers SET name = ?, phone = ?, email = ?, address = ? WHERE id = ?'); - $stmt->execute([$_POST['name'], $_POST['phone'] ?? '', $_POST['email'] ?? '', $_POST['address'] ?? '', $_POST['id']]); + $stmt->execute([$_POST['name'], $phone, $_POST['email'] ?? '', $_POST['address'] ?? '', $_POST['id']]); set_flash('success', tr('تم التحديث بنجاح', 'Updated successfully')); redirect_to('customers.php'); } elseif ($action === 'delete') { @@ -37,7 +51,8 @@ $search = $_GET['q'] ?? ''; $where = '1=1'; $params = []; if ($search) { - $where .= ' AND (name LIKE ? OR phone LIKE ? OR email LIKE ?)'; + $where .= " AND (name LIKE ? OR phone LIKE ? OR CONCAT('968', phone) LIKE ? OR email LIKE ?)"; + $params[] = "%$search%"; $params[] = "%$search%"; $params[] = "%$search%"; $params[] = "%$search%"; @@ -95,7 +110,7 @@ require __DIR__ . '/includes/header.php';