setTime(0, 0, 0); $endLocal = clone $nowDt; $endLocal->setTime(23, 59, 59); // 2. Convert to UTC strings for DB query $startUtc = clone $startLocal; $startUtc->setTimezone(new DateTimeZone('UTC')); $strStartUtc = $startUtc->format('Y-m-d H:i:s'); $endUtc = clone $endLocal; $endUtc->setTimezone(new DateTimeZone('UTC')); $strEndUtc = $endUtc->format('Y-m-d H:i:s'); // 3. Set DB Session to UTC so it interprets our UTC strings correctly try { $pdo->exec("SET time_zone = '+00:00'"); } catch (Exception $e) { cron_log("Warning: Could not set MySQL timezone to UTC: " . $e->getMessage()); } $reportTime = $settings['whatsapp_report_time']; $lastReportFile = __DIR__ . '/../storage/last_daily_report.txt'; $lastReportDate = file_exists($lastReportFile) ? trim(file_get_contents($lastReportFile)) : ''; // Check schedule relative to Local Time $targetTodayDt = clone $nowDt; $timeParts = explode(':', $reportTime); $targetTodayDt->setTime((int)$timeParts[0], (int)($timeParts[1] ?? 0), 0); $diffToday = $nowDt->getTimestamp() - $targetTodayDt->getTimestamp(); // Run if within 15 mins of schedule AND not already run for this local date if ($diffToday >= 0 && $diffToday <= 900 && $lastReportDate !== $nowDt->format('Y-m-d')) { cron_log("Condition met: sending daily report for " . $nowDt->format('Y-m-d')); cron_log("Querying UTC Range: $strStartUtc to $strEndUtc"); $todayDisplay = $nowDt->format('Y-m-d'); $companyName = $settings['company_name'] ?? 'Company'; // --- 1. Global Stats --- // Total Revenue $stmt = $pdo->prepare("SELECT SUM(total_amount) FROM orders WHERE created_at >= ? AND created_at <= ? AND status != 'cancelled'"); $stmt->execute([$strStartUtc, $strEndUtc]); $totalRevenue = $stmt->fetchColumn() ?: 0; // Total Orders $stmt = $pdo->prepare("SELECT COUNT(*) FROM orders WHERE created_at >= ? AND created_at <= ? AND status != 'cancelled'"); $stmt->execute([$strStartUtc, $strEndUtc]); $totalOrders = $stmt->fetchColumn() ?: 0; // --- 2. Build Message Header --- $message = "šŸ“Š *Daily Summary Report* šŸ“Š\n"; $message .= "šŸ¢ *" . $companyName . "*\n"; $message .= "šŸ“… Date: " . $todayDisplay . "\n"; $message .= "--------------------------------\n"; $message .= "šŸ›’ *Total Orders:* " . $totalOrders . "\n"; $message .= "šŸ’° *Total Revenue:* " . format_currency_custom($totalRevenue, $settings) . "\n"; // --- 3. Per-Outlet Stats --- // Fetch outlets dynamically from DB $stmtOutlets = $pdo->query("SELECT id, name, name_ar FROM outlets WHERE is_deleted = 0 ORDER BY id ASC"); $outlets = $stmtOutlets->fetchAll(); cron_log("Fetched " . count($outlets) . " outlets from database."); foreach ($outlets as $outlet) { $outletId = $outlet['id']; $outletName = !empty($outlet['name_ar']) ? $outlet['name_ar'] : $outlet['name']; // Outlet Revenue $stmtRev = $pdo->prepare("SELECT SUM(total_amount) FROM orders WHERE outlet_id = ? AND created_at >= ? AND created_at <= ? AND status != 'cancelled'"); $stmtRev->execute([$outletId, $strStartUtc, $strEndUtc]); $outletRevenue = $stmtRev->fetchColumn() ?: 0; $message .= "\nšŸŖ *" . $outletName . " Total:* " . format_currency_custom($outletRevenue, $settings) . "\n"; // Staff Breakdown $message .= "šŸ§‘ā€šŸ³ *Staff Breakdown:* "; $stmtStaff = $pdo->prepare(" SELECT u.username, u.full_name, COUNT(o.id) as order_count, SUM(o.total_amount) as revenue FROM orders o LEFT JOIN users u ON o.user_id = u.id WHERE o.outlet_id = ? AND o.created_at >= ? AND o.created_at <= ? AND o.status != 'cancelled' GROUP BY o.user_id "); $stmtStaff->execute([$outletId, $strStartUtc, $strEndUtc]); $staffStats = $stmtStaff->fetchAll(); if (empty($staffStats)) { $message .= "- No staff sales\n"; } else { foreach ($staffStats as $stat) { $staffName = !empty($stat['full_name']) ? $stat['full_name'] : ($stat['username'] ?? 'Unknown'); $message .= "- " . $staffName . ": " . format_currency_custom($stat['revenue'], $settings) . " (" . $stat['order_count'] . " orders)\n"; } } // Payment Breakdown $message .= "šŸ’³ *Payment Breakdown:* "; $stmtPayment = $pdo->prepare(" SELECT pt.name, SUM(o.total_amount) as revenue FROM orders o LEFT JOIN payment_types pt ON o.payment_type_id = pt.id WHERE o.outlet_id = ? AND o.created_at >= ? AND o.created_at <= ? AND o.status != 'cancelled' GROUP BY o.payment_type_id "); $stmtPayment->execute([$outletId, $strStartUtc, $strEndUtc]); $paymentStats = $stmtPayment->fetchAll(); if (empty($paymentStats)) { $message .= "- No payments\n"; } else { foreach ($paymentStats as $stat) { $paymentName = $stat['name'] ?? 'Unknown'; $message .= "- " . $paymentName . ": " . format_currency_custom($stat['revenue'], $settings) . "\n"; } } } $message .= "--------------------------------\n"; $message .= "Generated automatically at " . $nowDt->format('H:i:s') . "\n"; $message .= "Source: " . ($settings['company_name'] ?? 'System') . " Database"; // --- 4. Send Message --- $wablas = new WablasService($pdo); $recipients = explode(',', $settings['whatsapp_report_number']); $anySuccess = false; foreach ($recipients as $recipient) { $recipient = trim($recipient); if (empty($recipient)) continue; cron_log("Sending detailed report to: " . $recipient); $result = $wablas->sendMessage($recipient, $message); if (!empty($result['success']) && $result['success'] == true) { cron_log("Report sent successfully to " . $recipient); $anySuccess = true; } else { cron_log("Failed to send report to " . $recipient . ": " . ($result['message'] ?? 'Unknown error')); } } if ($anySuccess) { file_put_contents($lastReportFile, $todayDisplay); } } } catch (Exception $e) { cron_log("CRITICAL ERROR: " . $e->getMessage()); }