utente autenticato ($user->id == bookingclass.iduser) * - $db -> connessione PDO (da DBHandlerSelect) * * Parametri GET: * - month=YYYY-MM (opzionale, default = mese corrente) * * Logica ricalcata su userpanel.php: * - summary: conteggi utente-globali (acquistate/praticate/prenotate/ * da confermare/da programmare/perse) * - lessons: lezioni "booked" del mese selezionato, con regole * can_reschedule / can_delete identiche alla webapp. * -------------------------------------------------------------------------- */ require_once __DIR__ . '/_bootstrap.php'; // A questo punto $user e $db esistono già (altrimenti _bootstrap ha fatto exit 401). $userId = (int) $user->id; // Location fissa, come nella webapp (userpanel.php la stampa hardcoded). const LESSON_LOCATION = 'via Valassina 62/B Seregno - Sala Contesto Yoga'; // ========================================================================== // PARAMETRI: mese // ========================================================================== $month = $_GET['month'] ?? date('Y-m'); // Validazione: deve essere YYYY-MM, altrimenti fallback al mese corrente. $monthDate = DateTime::createFromFormat('Y-m-d', $month . '-01'); if ($monthDate === false) { $month = date('Y-m'); $monthDate = new DateTime(date('Y-m') . '-01'); } // Finestra del mese. Replichiamo la logica di userpanel.php: // se il mese richiesto è quello corrente, si parte da OGGI (non dal giorno 1), // così non si mostrano lezioni già passate del mese in corso. $monthStart = $monthDate->format('Y-m-01'); $today = date('Y-m-d'); if ($today > $monthStart && date('Y-m') === $month) { $monthStart = $today; } // Fine mese + 1 giorno (bound superiore esclusivo), come nella webapp. $monthEndPlus1 = (clone $monthDate)->modify('first day of next month')->format('Y-m-d'); // ========================================================================== // SUMMARY: conteggi utente-globali // (tradotti 1:1 dalle query mysqli di userpanel.php in PDO) // ========================================================================== // -- Totale biglietti acquistati (SUM nticket sugli ordini dell'utente) ----- $stmt = $db->prepare(" SELECT COALESCE(SUM(nticket), 0) AS total_tickets FROM orderbook WHERE iduser = :uid "); $stmt->execute([':uid' => $userId]); $purchased = (int) $stmt->fetchColumn(); // -- Stato prenotazioni (passate/future/perse/pending) ---------------------- $now = date('Y-m-d H:i:s'); $stmt = $db->prepare(" SELECT SUM(CASE WHEN ss.dateschedule <= :now1 AND bc.status = 'booked' AND bc.lostlesson = 'N' THEN 1 ELSE 0 END) AS passed, SUM(CASE WHEN ss.dateschedule > :now2 AND bc.status = 'booked' AND bc.lostlesson = 'N' THEN 1 ELSE 0 END) AS future, SUM(CASE WHEN bc.lostlesson = 'Y' AND bc.status != 'cancelled' THEN 1 ELSE 0 END) AS lost, SUM(CASE WHEN bc.status = 'pending' THEN 1 ELSE 0 END) AS pending FROM bookingclass bc LEFT JOIN serviceschedule ss ON bc.idserviceschedule = ss.idserviceschedule WHERE bc.iduser = :uid AND bc.status != 'cancelled' AND bc.is_gift = 'N' "); $stmt->execute([ ':now1' => $now, ':now2' => $now, ':uid' => $userId, ]); $counts = $stmt->fetch() ?: []; $practiced = (int) ($counts['passed'] ?? 0); $booked = (int) ($counts['future'] ?? 0); $lost = (int) ($counts['lost'] ?? 0); $pending = (int) ($counts['pending'] ?? 0); // "Da programmare" = acquistate - praticate - prenotate - da confermare - perse // (identico al calcolo $toprogram di userpanel.php) $toSchedule = $purchased - $practiced - $booked - $pending - $lost; if ($toSchedule < 0) { $toSchedule = 0; } $summary = [ 'purchased' => $purchased, 'practiced' => $practiced, 'booked' => $booked, 'pending' => $pending, 'to_schedule' => $toSchedule, 'lost' => $lost, ]; // ========================================================================== // LESSONS: lezioni "booked" del mese selezionato // (query principale di userpanel.php, tradotta in PDO) // ========================================================================== $stmt = $db->prepare(" SELECT bc.idbookingclass, bc.status, bc.idservice, bc.idorder, bc.lostlesson, bc.is_gift, ss.dateschedule, s.servicename, s.colorclass, ob.expireon, ob.maxreschedule, ob.reprogrammed FROM bookingclass bc LEFT JOIN service s ON bc.idservice = s.idservice LEFT JOIN serviceschedule ss ON bc.idserviceschedule = ss.idserviceschedule LEFT JOIN orderbook ob ON bc.idorder = ob.idorderbook WHERE bc.iduser = :uid AND bc.status = 'booked' AND ss.dateschedule >= :start AND ss.dateschedule < :end ORDER BY ss.dateschedule ASC "); $stmt->execute([ ':uid' => $userId, ':start' => $monthStart . ' 00:00:00', ':end' => $monthEndPlus1 . ' 00:00:00', ]); $rows = $stmt->fetchAll(); $lessons = []; $nowDt = new DateTime(); foreach ($rows as $r) { $dateschedule = $r['dateschedule'] ?? null; if ($dateschedule === null) { continue; // riga orfana senza schedule, la saltiamo } $classDt = new DateTime($dateschedule); // ------------------------------------------------------------------ // Regola oraria can_delete (identica a userpanel.php) // ------------------------------------------------------------------ $isSameDay = $classDt->format('Y-m-d') === $nowDt->format('Y-m-d'); $classHour = (int) $classDt->format('H'); $classMinute = (int) $classDt->format('i'); // "prima delle 17:00" = ora < 17, oppure esattamente 17:00 $isBefore1700 = ($classHour < 17) || ($classHour === 17 && $classMinute === 0); if ($isSameDay) { if ($isBefore1700) { // lezioni prima delle 17: valido fino alle 00:01 dello stesso giorno $deadline = new DateTime($classDt->format('Y-m-d 00:01:00')); } else { // lezioni dalle 17 in poi: valido fino alle 12:00 dello stesso giorno $deadline = new DateTime($classDt->format('Y-m-d 12:00:00')); } $canByTime = $nowDt <= $deadline; } else { // giorni futuri: sempre consentito $canByTime = true; } // ------------------------------------------------------------------ // Limite riprogrammazioni dell'ordine (identico a userpanel.php) // ------------------------------------------------------------------ $maxreschedule = (int) ($r['maxreschedule'] ?? 0); $reprogrammed = (int) ($r['reprogrammed'] ?? 0); $canReprogram = $reprogrammed < $maxreschedule; $isGift = ($r['is_gift'] ?? 'N') === 'Y'; if ($isGift) { // Omaggio: mai riprogrammabile, cancellabile in base al tempo. $canReschedule = false; $canDelete = $canByTime; } else { // Lezione normale: entrambi soggetti a tempo + limite riprogrammazioni. $canReschedule = $canByTime && $canReprogram; $canDelete = $canByTime && $canReprogram; } $lessons[] = [ 'booking_id' => (int) $r['idbookingclass'], 'service_id' => (int) ($r['idservice'] ?? 0), 'datetime' => $classDt->format('Y-m-d H:i:s'), 'date' => $classDt->format('Y-m-d'), 'time' => $classDt->format('H:i'), 'class_name' => (string) ($r['servicename'] ?? ''), 'color' => (string) ($r['colorclass'] ?? '#1ebf73'), 'location' => LESSON_LOCATION, 'expire_on' => $r['expireon'] ?? null, 'lost_lesson' => ($r['lostlesson'] ?? 'N') === 'Y', 'can_reschedule' => $canReschedule, 'can_delete' => $canDelete, ]; } // ========================================================================== // OUTPUT // ========================================================================== echo json_encode([ 'success' => true, 'month' => $month, 'summary' => $summary, 'lessons' => $lessons, ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);