38682-vm/api/daily_report_cron.php
2026-03-27 02:41:38 +00:00

209 lines
8.6 KiB
PHP

<?php
require_once __DIR__ . '/../db/config.php';
require_once __DIR__ . '/../includes/functions.php';
require_once __DIR__ . '/../includes/WablasService.php';
// Enable error logging for cron
ini_set('log_errors', 1);
$logFile = __DIR__ . '/../storage/cron_debug.log';
ini_set('error_log', $logFile);
// Helper to log with timestamp
function cron_log($msg) {
global $logFile;
file_put_contents($logFile, "[" . date('Y-m-d H:i:s') . "] " . $msg . "\n", FILE_APPEND);
}
// Helper to format currency
function format_currency_custom($amount, $settings) {
$symbol = $settings['currency_symbol'] ?? '$';
$decimals = (int)($settings['currency_decimals'] ?? 3);
$position = $settings['currency_position'] ?? 'after';
$formatted = number_format((float)$amount, $decimals);
return ($position === 'after') ? "$formatted $symbol" : "$symbol $formatted";
}
try {
$settings = get_company_settings();
if (empty($settings['whatsapp_report_enabled']) || empty($settings['whatsapp_report_number']) || empty($settings['whatsapp_report_time'])) {
exit;
}
$timezone = !empty($settings['timezone']) ? $settings['timezone'] : 'UTC';
date_default_timezone_set($timezone);
$pdo = db();
// Use UTC strategy for consistent querying
// 1. Calculate Local Start/End of Today
$nowDt = new DateTime('now', new DateTimeZone($timezone));
$startLocal = clone $nowDt;
$startLocal->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 FROM outlets WHERE is_deleted = 0 ORDER BY id ASC");
$outlets = $stmtOutlets->fetchAll();
cron_log("Fetched " . count($outlets) . " outlets from database.");
// Define excluded outlets (Demo data typically found in production databases seeded from dev)
// You can add more names here if you want to hide them from the report
$excludedOutlets = ['Main Downtown', 'Westside Hub', 'North Point Mall'];
foreach ($outlets as $outlet) {
$outletId = $outlet['id'];
$outletName = $outlet['name'];
// Skip excluded outlets
if (in_array($outletName, $excludedOutlets)) {
cron_log("Skipping excluded/demo outlet: " . $outletName);
continue;
}
// 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());
}