Files

234 lines
8.2 KiB
PHP

<?php
/**
* my_lessons.php
* --------------------------------------------------------------------------
* Endpoint API delle lezioni per l'app Flutter (scuola YogaSoul).
*
* Posizione prevista sul server: public/api/my_lessons.php
* (accanto a _bootstrap.php)
*
* Autenticazione: gestita da _bootstrap.php (Vanguard, token Bearer).
* Dopo il require sono già disponibili:
* - $user -> 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);