false, 'error' => 'No data provided']); exit; } try { $pdo = db(); $pdo->beginTransaction(); // Validate order_type against allowed ENUM values $allowed_types = ['dine-in', 'takeaway', 'delivery', 'drive-thru']; $order_type = isset($data['order_type']) && in_array($data['order_type'], $allowed_types) ? $data['order_type'] : 'dine-in'; // Get outlet_id from input, default to 1 if missing $outlet_id = isset($data['outlet_id']) ? intval($data['outlet_id']) : 1; $table_id = null; $table_number = null; if ($order_type === 'dine-in') { $tid = $data['table_number'] ?? null; // Front-end sends ID as table_number if ($tid) { // Validate table exists AND belongs to the correct outlet $stmt = $pdo->prepare( "SELECT t.id, t.name FROM tables t JOIN areas a ON t.area_id = a.id WHERE t.id = ? AND a.outlet_id = ?" ); $stmt->execute([$tid, $outlet_id]); $table = $stmt->fetch(PDO::FETCH_ASSOC); if ($table) { $table_id = $table['id']; $table_number = $table['name']; } } // If not found or not provided, leave null (Walk-in/Counter) or try to find a default table for this outlet if (!$table_id) { // Optional: try to find the first available table for this outlet $stmt = $pdo->prepare( "SELECT t.id, t.name FROM tables t JOIN areas a ON t.area_id = a.id WHERE a.outlet_id = ? LIMIT 1" ); $stmt->execute([$outlet_id]); $table = $stmt->fetch(PDO::FETCH_ASSOC); if ($table) { $table_id = $table['id']; $table_number = $table['name']; } } } // Customer Handling $customer_id = $data['customer_id'] ?? null; $customer_name = $data['customer_name'] ?? null; $customer_phone = $data['customer_phone'] ?? null; // Fetch Loyalty Settings $settingsStmt = $pdo->query("SELECT points_per_order, points_for_free_meal FROM loyalty_settings WHERE id = 1"); $settings = $settingsStmt->fetch(PDO::FETCH_ASSOC); $points_per_order = $settings ? intval($settings['points_per_order']) : 10; $points_threshold = $settings ? intval($settings['points_for_free_meal']) : 70; $current_points = 0; $points_deducted = 0; $points_awarded = 0; if ($customer_id) { $stmt = $pdo->prepare("SELECT name, phone, points FROM customers WHERE id = ?"); $stmt->execute([$customer_id]); $cust = $stmt->fetch(PDO::FETCH_ASSOC); if ($cust) { $customer_name = $cust['name']; $customer_phone = $cust['phone']; $current_points = intval($cust['points']); } else { $customer_id = null; } } // Loyalty Redemption Logic $redeem_loyalty = !empty($data['redeem_loyalty']); if ($redeem_loyalty && $customer_id) { if ($current_points < $points_threshold) { throw new Exception("Insufficient loyalty points for redemption."); } // Deduct points $deductStmt = $pdo->prepare("UPDATE customers SET points = points - ? WHERE id = ?"); $deductStmt->execute([$points_threshold, $customer_id]); $points_deducted = $points_threshold; // --- OVERRIDE PAYMENT TYPE --- $ptStmt = $pdo->prepare("SELECT id FROM payment_types WHERE name = 'Loyalty Redeem' LIMIT 1"); $ptStmt->execute(); $loyaltyPt = $ptStmt->fetchColumn(); if ($loyaltyPt) { $data['payment_type_id'] = $loyaltyPt; } } // Award Points if ($customer_id) { $awardStmt = $pdo->prepare("UPDATE customers SET points = points + ? WHERE id = ?"); $awardStmt->execute([$points_per_order, $customer_id]); $points_awarded = $points_per_order; } // Payment Type $payment_type_id = isset($data['payment_type_id']) ? intval($data['payment_type_id']) : null; $discount = isset($data['discount']) ? floatval($data['discount']) : 0.00; $total_amount = isset($data['total_amount']) ? floatval($data['total_amount']) : 0.00; // Check for Existing Order ID (Update Mode) $order_id = isset($data['order_id']) ? intval($data['order_id']) : null; $is_update = false; if ($order_id) { // Verify existence and status (optional: only update pending orders?) $checkStmt = $pdo->prepare("SELECT id FROM orders WHERE id = ?"); $checkStmt->execute([$order_id]); if ($checkStmt->fetch()) { $is_update = true; } else { // If ID sent but not found, create new? Or error? // Let's treat as new if not found to avoid errors, or maybe user meant to update. // Safe bet: error if explicit update requested but failed. // But for now, let's just create new if ID is invalid, or better, stick to the plan: Update if ID present. $order_id = null; // Reset to create new } } if ($is_update) { // UPDATE Existing Order $stmt = $pdo->prepare("UPDATE orders SET outlet_id = ?, table_id = ?, table_number = ?, order_type = ?, customer_id = ?, customer_name = ?, customer_phone = ?, payment_type_id = ?, total_amount = ?, discount = ?, status = 'pending' WHERE id = ?"); $stmt->execute([ $outlet_id, $table_id, $table_number, $order_type, $customer_id, $customer_name, $customer_phone, $payment_type_id, $total_amount, $discount, $order_id ]); // Clear existing items to replace them $delStmt = $pdo->prepare("DELETE FROM order_items WHERE order_id = ?"); $delStmt->execute([$order_id]); } else { // INSERT New Order $stmt = $pdo->prepare("INSERT INTO orders (outlet_id, table_id, table_number, order_type, customer_id, customer_name, customer_phone, payment_type_id, total_amount, discount, status) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending')"); $stmt->execute([$outlet_id, $table_id, $table_number, $order_type, $customer_id, $customer_name, $customer_phone, $payment_type_id, $total_amount, $discount]); $order_id = $pdo->lastInsertId(); } // Insert Items (Common for both Insert and Update) $item_stmt = $pdo->prepare("INSERT INTO order_items (order_id, product_id, variant_id, quantity, unit_price) VALUES (?, ?, ?, ?, ?)"); // Pre-prepare statements for name fetching $prodNameStmt = $pdo->prepare("SELECT name FROM products WHERE id = ?"); $varNameStmt = $pdo->prepare("SELECT name FROM product_variants WHERE id = ?"); $order_items_list = []; // To store item details for notification if (!empty($data['items']) && is_array($data['items'])) { foreach ($data['items'] as $item) { $pid = $item['product_id'] ?? ($item['id'] ?? null); $qty = $item['quantity'] ?? 1; $price = $item['unit_price'] ?? ($item['price'] ?? 0); $vid = $item['variant_id'] ?? null; if ($pid) { $item_stmt->execute([$order_id, $pid, $vid, $qty, $price]); // Fetch names for notification $prodNameStmt->execute([$pid]); $pRow = $prodNameStmt->fetch(PDO::FETCH_ASSOC); $pName = $pRow ? $pRow['name'] : 'Item'; if ($vid) { $varNameStmt->execute([$vid]); $vRow = $varNameStmt->fetch(PDO::FETCH_ASSOC); if ($vRow) { $pName .= " (" . $vRow['name'] . ")"; } } $order_items_list[] = "$qty x $pName"; } } } $pdo->commit(); // --- Post-Transaction Actions --- // Send WhatsApp Notification if Customer exists if ($customer_id && $customer_phone) { try { // Calculate final points $final_points = $current_points - $points_deducted + $points_awarded; // Determine message content $wablas = new WablasService($pdo); // Fetch Company Name require_once __DIR__ . '/../includes/functions.php'; $companySettings = get_company_settings(); $company_name = $companySettings['company_name'] ?? 'Flatlogic POS'; // Fetch Template $stmt = $pdo->prepare("SELECT setting_value FROM integration_settings WHERE provider = 'wablas' AND setting_key = 'order_template'"); $stmt->execute(); $template = $stmt->fetchColumn(); // Default Template if not set if (!$template) { $template = "Dear *{customer_name}*, Thank you for dining with *{company_name}*! 🍽️ *Order Details:* {order_details} Total: *{total_amount}* OMR You've earned *{points_earned} points* with this order. 💰 *Current Balance: {new_balance} points* {loyalty_status}"; } // Calculate Loyalty Status String $loyalty_status = ""; if ($final_points >= $points_threshold) { $loyalty_status = "🎉 Congratulations! You have enough points for a *FREE MEAL* on your next visit!"; } else { $needed = $points_threshold - $final_points; $loyalty_status = "You need *$needed more points* to unlock a free meal."; } // Prepare replacements $replacements = [ '{customer_name}' => $customer_name, '{company_name}' => $company_name, '{order_id}' => $order_id, '{order_details}' => !empty($order_items_list) ? implode("\n", $order_items_list) : 'N/A', '{total_amount}' => number_format($total_amount, 3), '{points_earned}' => $points_awarded, '{points_redeemed}' => $points_deducted, '{new_balance}' => $final_points, '{loyalty_status}' => $loyalty_status ]; $msg = str_replace(array_keys($replacements), array_values($replacements), $template); // Send (fire and forget, don't block response on failure, just log) $res = $wablas->sendMessage($customer_phone, $msg); if (!$res['success']) { error_log("Wablas Notification Failed: " . $res['message']); } } catch (Exception $w) { error_log("Wablas Exception: " . $w->getMessage()); } } echo json_encode(['success' => true, 'order_id' => $order_id]); } catch (Exception $e) { if ($pdo->inTransaction()) { $pdo->rollBack(); } error_log("Order Error: " . $e->getMessage()); echo json_encode(['success' => false, 'error' => $e->getMessage()]); }