fixed finances my lesson etc
This commit is contained in:
@@ -1,135 +1,204 @@
|
||||
<?php
|
||||
require_once 'include/headscript.php'; // Include il file con la connessione al database
|
||||
require_once 'include/headscript.php';
|
||||
|
||||
// Connessione al database
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->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 {
|
||||
// Inizia una transazione
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// Seleziona gli ordini con status 'completed' e lezioni disponibili
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT o.id, o.user_id, o.class_type_id, o.available_entries, o.school_id
|
||||
FROM orders o
|
||||
WHERE o.status = 'completed' AND o.available_entries > 0
|
||||
");
|
||||
$stmt->execute();
|
||||
$orders = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
$totalBookings = 0;
|
||||
$totalOrders = 0;
|
||||
|
||||
$bookings_created = 0;
|
||||
$orders_propagated = 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) {
|
||||
$order_id = $order['id'];
|
||||
$user_id = $order['user_id'];
|
||||
$class_type_id = $order['class_type_id'];
|
||||
$available_entries = $order['available_entries'];
|
||||
$school_id = $order['school_id'];
|
||||
$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';
|
||||
|
||||
// Recupera i giorni di chiusura della scuola
|
||||
$offStmt = $pdo->prepare("
|
||||
SELECT start_date, end_date
|
||||
FROM day_off
|
||||
WHERE school_id = ? AND (
|
||||
(start_date >= CURDATE()) OR
|
||||
(end_date >= CURDATE()) OR
|
||||
(start_date <= CURDATE() AND end_date >= CURDATE())
|
||||
)
|
||||
");
|
||||
$offStmt->execute([$school_id]);
|
||||
$off_periods = $offStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
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");
|
||||
|
||||
// Crea un array di date di chiusura
|
||||
$off_dates = [];
|
||||
foreach ($off_periods as $period) {
|
||||
$start_date = new DateTime($period['start_date']);
|
||||
$end_date = new DateTime($period['end_date']);
|
||||
$interval = new DateInterval('P1D');
|
||||
$date_range = new DatePeriod($start_date, $interval, $end_date->modify('+1 day'));
|
||||
foreach ($date_range as $date) {
|
||||
$off_dates[] = $date->format('Y-m-d');
|
||||
}
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Trova le prossime sessioni disponibili per questo class_type_id
|
||||
$sessionStmt = $pdo->prepare("
|
||||
SELECT id, session_date
|
||||
FROM class_sessions
|
||||
WHERE class_type_id = ? AND session_date >= CURDATE() AND status = 'scheduled'
|
||||
ORDER BY session_date ASC, start_time ASC
|
||||
LIMIT ?
|
||||
");
|
||||
$sessionStmt->execute([$class_type_id, $available_entries]);
|
||||
// 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);
|
||||
|
||||
$bookings_for_order = 0;
|
||||
echo yellow("Trovate " . count($sessions) . " lezioni future per queste classi.\n");
|
||||
|
||||
foreach ($sessions as $session) {
|
||||
$session_id = $session['id'];
|
||||
$session_date = $session['session_date'];
|
||||
$bookedThisOrder = 0;
|
||||
|
||||
// Salta se la data è un giorno di chiusura
|
||||
if (in_array($session_date, $off_dates)) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Verifica la capacità massima della sessione
|
||||
$capacityStmt = $pdo->prepare("
|
||||
SELECT max_capacity
|
||||
FROM class_sessions
|
||||
WHERE id = ?
|
||||
");
|
||||
$capacityStmt->execute([$session_id]);
|
||||
$max_capacity = $capacityStmt->fetchColumn();
|
||||
$free = $s['max_capacity'] ? ($s['max_capacity'] - $s['booked']) : 999;
|
||||
|
||||
$bookingCountStmt = $pdo->prepare("
|
||||
SELECT COUNT(*)
|
||||
FROM session_bookings
|
||||
WHERE session_id = ? AND status IN ('booked', 'attended', 'rescheduled')
|
||||
");
|
||||
$bookingCountStmt->execute([$session_id]);
|
||||
$current_bookings = $bookingCountStmt->fetchColumn();
|
||||
|
||||
if ($max_capacity !== null && $current_bookings >= $max_capacity) {
|
||||
continue; // Salta se la sessione è piena
|
||||
if ($free <= 0) {
|
||||
echo red(" FULL $date $time → $className (Posti: {$s['booked']}/{$s['max_capacity']}) → saltata\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Inserisci la prenotazione in session_bookings
|
||||
$insertStmt = $pdo->prepare("
|
||||
INSERT INTO session_bookings (
|
||||
session_id, user_id, status, booked_at, created_at, updated_at
|
||||
)
|
||||
VALUES (?, ?, 'booked', NOW(), NOW(), NOW())
|
||||
");
|
||||
$insertStmt->execute([
|
||||
$session_id,
|
||||
$user_id
|
||||
]);
|
||||
$bookings_created++;
|
||||
$bookings_for_order++;
|
||||
// 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");
|
||||
}
|
||||
|
||||
// Aggiorna available_entries e lo stato dell'ordine
|
||||
if ($bookings_for_order > 0) {
|
||||
$updateStmt = $pdo->prepare("
|
||||
UPDATE orders
|
||||
SET available_entries = available_entries - ?,
|
||||
status = 'propagated'
|
||||
WHERE id = ?
|
||||
");
|
||||
$updateStmt->execute([$bookings_for_order, $order_id]);
|
||||
$orders_propagated++;
|
||||
// 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");
|
||||
}
|
||||
|
||||
// Conferma la transazione
|
||||
$pdo->commit();
|
||||
|
||||
echo "Propagazione completata con successo: $bookings_created prenotazioni create, $orders_propagated ordini aggiornati.";
|
||||
} catch (PDOException $e) {
|
||||
// In caso di errore, annulla la transazione
|
||||
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 "Errore durante la propagazione: " . $e->getMessage();
|
||||
echo red("ERRORE CRITICO: " . $e->getMessage() . "\n");
|
||||
error_log("Auto-propagate error: " . $e->getMessage());
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user