getConnection(); // Funzioni di colore per terminale (se lo lanci da CLI) function green($text) { return "\033[32m$text\033[0m"; } function yellow($text) { return "\033[33m$text\033[0m"; } function red($text) { return "\033[31m$text\033[0m"; } function cyan($text) { return "\033[36m$text\033[0m"; } function magenta($text) { return "\033[35m$text\033[0m"; } echo cyan("=== AVVIO PROPAGAZIONE AUTOMATICA - " . date('Y-m-d H:i:s') . " ===\n"); try { $pdo->beginTransaction(); $totalBookings = 0; $totalOrders = 0; $orderSql = " SELECT o.id, o.order_number, o.user_id, o.school_id, o.available_entries, o.product_id, o.variation_id, o.activation_date, p.name as product_name, pv.name as variation_name, u.email as user_email FROM orders o INNER JOIN products p ON o.product_id = p.id LEFT JOIN product_variations pv ON o.variation_id = pv.id AND pv.id IS NOT NULL LEFT JOIN auth_users u ON o.user_id = u.id WHERE o.status = 'completed' AND o.available_entries > 0 AND (p.auto_propagate_to_order = 1 OR pv.auto_propagate_to_order = 1) AND (o.activation_date IS NULL OR o.activation_date <= CURDATE()) ORDER BY o.created_at ASC "; $orderStmt = $pdo->prepare($orderSql); $orderStmt->execute(); $orders = $orderStmt->fetchAll(PDO::FETCH_ASSOC); echo yellow("Trovati " . count($orders) . " ordini da propagare automaticamente.\n\n"); foreach ($orders as $order) { $totalOrders++; $orderId = $order['id']; $orderNumber = $order['order_number']; $userId = $order['user_id']; $userEmail = $order['user_email']; $schoolId = $order['school_id']; $remaining = (int)$order['available_entries']; $productName = $order['variation_name'] ?? $order['product_name']; $activation = $order['activation_date'] ?: 'immediata'; echo magenta("──────────────────────────────────────────────────────\n"); echo green("ELABORAZIONE ORDINE #$orderNumber (ID: $orderId)\n"); echo green("Utente: $userEmail (ID: $userId) | Scuola ID: $schoolId\n"); echo green("Prodotto: $productName | Ingressi da propagare: $remaining | Attivazione: $activation\n"); // Recupera class_type consentiti $ctSql = "SELECT DISTINCT class_type_id FROM product_class_types WHERE product_id = ? AND (variation_id IS NULL OR variation_id = ? OR variation_id = ?)"; $ctStmt = $pdo->prepare($ctSql); $ctStmt->execute([$order['product_id'], $order['variation_id'], $order['variation_id']]); $classTypeIds = $ctStmt->fetchAll(PDO::FETCH_COLUMN); if (empty($classTypeIds)) { echo red("Nessun class_type associato al prodotto → SKIP\n"); continue; } // Nomi delle classi per debug - VERSIONE MIGLIORE $placeholders = str_repeat('?,', count($classTypeIds) - 1) . '?'; $namesSql = " SELECT ct.id, CONCAT(c.name, ' - ', ct.level, ' ', CASE ct.day_of_week WHEN 'monday' THEN 'Lun' WHEN 'tuesday' THEN 'Mar' WHEN 'wednesday' THEN 'Mer' WHEN 'thursday' THEN 'Gio' WHEN 'friday' THEN 'Ven' WHEN 'saturday' THEN 'Sab' WHEN 'sunday' THEN 'Dom' END, ' ', TIME_FORMAT(ct.start_time, '%H:%i') ) as name FROM class_types ct JOIN classes c ON ct.class_id = c.id WHERE ct.id IN ($placeholders) ORDER BY name "; $namesStmt = $pdo->prepare($namesSql); $namesStmt->execute($classTypeIds); $classNames = $namesStmt->fetchAll(PDO::FETCH_KEY_PAIR); echo cyan("Classi consentite (" . count($classTypeIds) . "): " . implode(', ', array_values($classNames)) . "\n"); // Giorni di chiusura $offStmt = $pdo->prepare("SELECT start_date, end_date FROM day_off WHERE school_id = ? AND end_date >= CURDATE()"); $offStmt->execute([$schoolId]); $offDates = []; foreach ($offStmt->fetchAll() as $off) { $start = new DateTime($off['start_date']); $end = new DateTime($off['end_date']); $end->modify('+1 day'); $period = new DatePeriod($start, new DateInterval('P1D'), $end); foreach ($period as $d) $offDates[$d->format('Y-m-d')] = true; } // Tutte le sessioni future $sessionSql = " SELECT cs.id, cs.session_date, cs.start_time, cs.max_capacity, cs.class_type_id, (SELECT COUNT(*) FROM session_bookings sb WHERE sb.session_id = cs.id AND sb.status IN ('booked','attended','rescheduled')) as booked FROM class_sessions cs WHERE cs.class_type_id IN ($placeholders) AND cs.school_id = ? AND cs.session_date >= ? AND cs.status = 'scheduled' ORDER BY cs.session_date ASC, cs.start_time ASC LIMIT 2000 "; $fromDate = $order['activation_date'] ?: date('Y-m-d'); $params = array_merge($classTypeIds, [$schoolId, $fromDate]); $sessionStmt = $pdo->prepare($sessionSql); $sessionStmt->execute($params); $sessions = $sessionStmt->fetchAll(PDO::FETCH_ASSOC); echo yellow("Trovate " . count($sessions) . " lezioni future per queste classi.\n"); $bookedThisOrder = 0; foreach ($sessions as $s) { if ($remaining <= 0) break; $date = $s['session_date']; $time = substr($s['start_time'], 0, 5); $className = $classNames[$s['class_type_id']] ?? 'Sconosciuta'; if (isset($offDates[$date])) { echo " ⚠️ $date $time → $className → GIORNO DI CHIUSURA → saltata\n"; continue; } $free = $s['max_capacity'] ? ($s['max_capacity'] - $s['booked']) : 999; if ($free <= 0) { echo red(" FULL $date $time → $className (Posti: {$s['booked']}/{$s['max_capacity']}) → saltata\n"); continue; } // PRENOTA! $insert = $pdo->prepare("INSERT INTO session_bookings (session_id, user_id, order_id, status, booked_at, created_at, updated_at) VALUES (?, ?, ?, 'booked', NOW(), NOW(), NOW())"); $insert->execute([$s['id'], $userId, $orderId]); $totalBookings++; $bookedThisOrder++; $remaining--; echo green(" PRENOTATA $date $time → $className | Posto libero: $free → rimanenti da propagare: $remaining\n"); } // Aggiornamento ordine if ($bookedThisOrder > 0) { $newStatus = ($remaining <= 0) ? 'propagated' : 'completed'; $pdo->prepare("UPDATE orders SET available_entries = ?, status = ? WHERE id = ?") ->execute([$remaining, $newStatus, $orderId]); echo green("Ordine aggiornato: available_entries = $remaining | status = $newStatus\n"); } else { echo yellow("Nessuna lezione disponibile al momento → ci riproverà alla prossima esecuzione\n"); } echo magenta("──────────────────────────────────────────────────────\n\n"); } $pdo->commit(); echo cyan("=== PROPAGAZIONE COMPLETATA ===\n"); echo green("Ordini processati: $totalOrders\n"); echo green("Prenotazioni automatiche create: $totalBookings\n"); echo cyan("Fine esecuzione: " . date('Y-m-d H:i:s') . "\n"); } catch (Exception $e) { $pdo->rollBack(); echo red("ERRORE CRITICO: " . $e->getMessage() . "\n"); error_log("Auto-propagate error: " . $e->getMessage()); }