diff --git a/public/api/api_teacher_available_classes.php b/public/api/api_teacher_available_classes.php new file mode 100644 index 00000000..3f5f342a --- /dev/null +++ b/public/api/api_teacher_available_classes.php @@ -0,0 +1,109 @@ + (la prenotazione che si vuole spostare) + * -------------------------------------------------------------------------- + */ + +require_once __DIR__ . '/_bootstrap.php'; + +// Gate staff +$roleId = (int) $user->role_id; +if (!in_array($roleId, [1, 3], true)) { + http_response_code(403); + echo json_encode(['success' => false, 'message' => 'Forbidden']); + exit; +} + +$bookingId = isset($_GET['booking_id']) ? (int) $_GET['booking_id'] : 0; +if ($bookingId <= 0) { + http_response_code(400); + echo json_encode(['success' => false, 'message' => 'ID prenotazione non valido']); + exit; +} + +try { + // Trova utente e classe attuale della prenotazione da spostare + $stmt = $db->prepare( + "SELECT iduser, idserviceschedule + FROM bookingclass + WHERE idbookingclass = :id + LIMIT 1" + ); + $stmt->execute([':id' => $bookingId]); + $booking = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$booking) { + http_response_code(404); + echo json_encode(['success' => false, 'message' => 'Prenotazione non trovata']); + exit; + } + + $iduser = (int) $booking['iduser']; + $currentSchedule = (int) $booking['idserviceschedule']; + + // Classi future con conteggio prenotati (status='booked', coerente col pannello) + $sql = "SELECT + ss.idserviceschedule, + ss.idservice, + s.servicename, + COALESCE(s.maxcapacity, 0) AS maxcapacity, + ss.dateschedule, + (SELECT COUNT(*) FROM bookingclass bc + WHERE bc.idserviceschedule = ss.idserviceschedule + AND bc.status = 'booked') AS booked_count, + (SELECT COUNT(*) FROM bookingclass bc2 + WHERE bc2.idserviceschedule = ss.idserviceschedule + AND bc2.iduser = :iduser + AND bc2.status = 'booked') AS already_here + FROM serviceschedule ss + LEFT JOIN service s ON ss.idservice = s.idservice + WHERE ss.dateschedule >= NOW() + ORDER BY ss.dateschedule ASC"; + $stmt = $db->prepare($sql); + $stmt->execute([':iduser' => $iduser]); + $rows = $stmt->fetchAll(PDO::FETCH_ASSOC); + + $classes = []; + foreach ($rows as $r) { + $sid = (int) $r['idserviceschedule']; + $maxcap = (int) $r['maxcapacity']; + $booked = (int) $r['booked_count']; + $alreadyHere = ((int) $r['already_here']) > 0; + $isFull = $maxcap > 0 && $booked >= $maxcap; + + // Escludi: classe attuale, classi piene, classi dove è già iscritto + if ($sid === $currentSchedule) continue; + if ($isFull) continue; + if ($alreadyHere) continue; + + $classes[] = [ + 'schedule_id' => $sid, + 'service_id' => (int) $r['idservice'], + 'class_name' => (string) ($r['servicename'] ?? ''), + 'date_time' => $r['dateschedule'], + 'max_capacity' => $maxcap, + 'booked_count' => $booked, + ]; + } + + echo json_encode([ + 'success' => true, + 'classes' => $classes, + ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); +} catch (Throwable $ex) { + error_log('api_teacher_available_classes error: ' . $ex->getMessage()); + http_response_code(500); + echo json_encode(['success' => false, 'message' => 'Errore recupero classi']); +} diff --git a/public/api/api_teacher_reprogram.php,.php b/public/api/api_teacher_reprogram.php,.php new file mode 100644 index 00000000..1bc48bcd --- /dev/null +++ b/public/api/api_teacher_reprogram.php,.php @@ -0,0 +1,159 @@ +&schedule_id=&is_reprogrammed= + * + * Regole (identiche alla webapp): + * - status resta 'booked', lostlesson non toccato + * - is_reprogrammed sulla prenotazione va sempre a 'Y' + * - orderbook.reprogrammed +1 solo se flag='Y' e idorder valido + * -------------------------------------------------------------------------- + */ + +require_once __DIR__ . '/_bootstrap.php'; + +// Gate staff +$roleId = (int) $user->role_id; +if (!in_array($roleId, [1, 3], true)) { + http_response_code(403); + echo json_encode(['success' => false, 'message' => 'Forbidden']); + exit; +} + +$bookingId = isset($_POST['booking_id']) ? (int) $_POST['booking_id'] : 0; +$newSchedId = isset($_POST['schedule_id']) ? (int) $_POST['schedule_id'] : 0; +$flagReprogrammed = + (isset($_POST['is_reprogrammed']) && $_POST['is_reprogrammed'] === 'Y') ? 'Y' : 'N'; + +if ($bookingId <= 0 || $newSchedId <= 0) { + http_response_code(400); + echo json_encode(['success' => false, 'message' => 'Dati mancanti']); + exit; +} + +try { + $db->beginTransaction(); + + // Prenotazione corrente (lock) + $stmt = $db->prepare( + "SELECT idbookingclass, idserviceschedule, bookingstart, idorder, iduser + FROM bookingclass + WHERE idbookingclass = :id + FOR UPDATE" + ); + $stmt->execute([':id' => $bookingId]); + $booking = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$booking) { + $db->rollBack(); + http_response_code(404); + echo json_encode(['success' => false, 'message' => 'Prenotazione non trovata']); + exit; + } + + // Classe destinazione + $stmt = $db->prepare( + "SELECT idserviceschedule, idservice, dateschedule + FROM serviceschedule + WHERE idserviceschedule = :id" + ); + $stmt->execute([':id' => $newSchedId]); + $newSchedule = $stmt->fetch(PDO::FETCH_ASSOC); + + if (!$newSchedule) { + $db->rollBack(); + http_response_code(404); + echo json_encode(['success' => false, 'message' => 'Classe destinazione non trovata']); + exit; + } + + // Stessa classe: niente da fare + if ((int) $booking['idserviceschedule'] === $newSchedId) { + $db->rollBack(); + http_response_code(400); + echo json_encode(['success' => false, 'message' => 'Classe destinazione uguale a quella attuale']); + exit; + } + + // Anti-duplicato: già prenotato (attivo) nella destinazione? + $dup = $db->prepare( + "SELECT COUNT(*) + FROM bookingclass + WHERE idserviceschedule = :newsched + AND iduser = :iduser + AND status = 'booked' + AND idbookingclass <> :id" + ); + $dup->execute([ + ':newsched' => $newSchedId, + ':iduser' => (int) $booking['iduser'], + ':id' => $bookingId, + ]); + if ((int) $dup->fetchColumn() > 0) { + $db->rollBack(); + http_response_code(409); + echo json_encode(['success' => false, 'message' => 'Allievo già prenotato in questa classe']); + exit; + } + + // Valori precedenti da conservare + $prevSchedule = $booking['idserviceschedule']; + $prevStart = $booking['bookingstart']; + + // Aggiorna in place + $upd = $db->prepare( + "UPDATE bookingclass SET + idserviceschedule = :newsched, + idservice = :newservice, + bookingstart = :newstart, + idprevserviceschedule = :prevsched, + prevbookingstart = :prevstart, + is_reprogrammed = 'Y', + status = 'booked' + WHERE idbookingclass = :id" + ); + $upd->execute([ + ':newsched' => (int) $newSchedule['idserviceschedule'], + ':newservice' => (int) $newSchedule['idservice'], + ':newstart' => $newSchedule['dateschedule'], + ':prevsched' => $prevSchedule, + ':prevstart' => $prevStart, + ':id' => $bookingId, + ]); + + // Incrementa contatore riprogrammazioni se richiesto e possibile + $idorder = $booking['idorder'] !== null ? (int) $booking['idorder'] : 0; + if ($flagReprogrammed === 'Y' && $idorder > 0) { + $incr = $db->prepare( + "UPDATE orderbook + SET reprogrammed = reprogrammed + 1 + WHERE idorderbook = :idorder" + ); + $incr->execute([':idorder' => $idorder]); + } + + $db->commit(); + + echo json_encode([ + 'success' => true, + 'booking_id' => $bookingId, + 'schedule_id' => $newSchedId, + 'reprogrammed_counted' => ($flagReprogrammed === 'Y' && $idorder > 0), + ], JSON_UNESCAPED_UNICODE); +} catch (Throwable $ex) { + if ($db->inTransaction()) { + $db->rollBack(); + } + error_log('api_teacher_reprogram error: ' . $ex->getMessage()); + http_response_code(500); + echo json_encode(['success' => false, 'message' => 'Errore riprogrammazione']); +} \ No newline at end of file diff --git a/public/reprogramclass.php b/public/reprogramclass.php new file mode 100644 index 00000000..6a22b3c4 --- /dev/null +++ b/public/reprogramclass.php @@ -0,0 +1,148 @@ + $msg]); + exit; +} + +$idbookingclass = isset($_POST['idbookingclass']) ? (int) $_POST['idbookingclass'] : 0; +$idnewschedule = isset($_POST['idserviceschedule']) ? (int) $_POST['idserviceschedule'] : 0; +$flagReprogrammed = (isset($_POST['is_reprogrammed']) && $_POST['is_reprogrammed'] === 'Y') ? 'Y' : 'N'; + +if ($idbookingclass <= 0 || $idnewschedule <= 0) { + fail('Dati mancanti per la riprogrammazione.'); +} + +$pdo = DBHandlerSelect::getInstance()->getConnection(); + +try { + $pdo->beginTransaction(); + + // Prenotazione corrente (lock per evitare doppie riprogrammazioni concorrenti) + $stmt = $pdo->prepare( + "SELECT idbookingclass, idserviceschedule, bookingstart, idorder, iduser + FROM bookingclass + WHERE idbookingclass = :id + FOR UPDATE" + ); + $stmt->execute([':id' => $idbookingclass]); + $booking = $stmt->fetch(); + + if (!$booking) { + $pdo->rollBack(); + fail('Prenotazione non trovata.', 404); + } + + // Classe destinazione + $stmt = $pdo->prepare( + "SELECT idserviceschedule, idservice, dateschedule + FROM serviceschedule + WHERE idserviceschedule = :id" + ); + $stmt->execute([':id' => $idnewschedule]); + $newSchedule = $stmt->fetch(); + + if (!$newSchedule) { + $pdo->rollBack(); + fail('Classe di destinazione non trovata.', 404); + } + + // Nessun senso spostare sulla stessa classe + if ((int) $booking['idserviceschedule'] === $idnewschedule) { + $pdo->rollBack(); + fail('La classe di destinazione coincide con quella attuale.'); + } + + // Anti-duplicato: l'utente e gia prenotato (attivo) nella classe destinazione? + $dupStmt = $pdo->prepare( + "SELECT COUNT(*) + FROM bookingclass + WHERE idserviceschedule = :newsched + AND iduser = :iduser + AND status = 'booked' + AND idbookingclass <> :id" + ); + $dupStmt->execute([ + ':newsched' => $idnewschedule, + ':iduser' => (int) $booking['iduser'], + ':id' => $idbookingclass, + ]); + if ((int) $dupStmt->fetchColumn() > 0) { + $pdo->rollBack(); + fail('L\'allieva e gia prenotata in questa classe.', 409); + } + + // Valori precedenti da conservare + $prevSchedule = $booking['idserviceschedule']; // puo essere NULL + $prevStart = $booking['bookingstart']; + + // Aggiorna la prenotazione in place + $upd = $pdo->prepare( + "UPDATE bookingclass SET + idserviceschedule = :newsched, + idservice = :newservice, + bookingstart = :newstart, + idprevserviceschedule = :prevsched, + prevbookingstart = :prevstart, + is_reprogrammed = 'Y', + status = 'booked' + WHERE idbookingclass = :id" + ); + $upd->execute([ + ':newsched' => $newSchedule['idserviceschedule'], + ':newservice' => $newSchedule['idservice'], + ':newstart' => $newSchedule['dateschedule'], + ':prevsched' => $prevSchedule, + ':prevstart' => $prevStart, + ':id' => $idbookingclass, + ]); + + // Incrementa il contatore riprogrammazioni sull'ordine, se richiesto e possibile + $idorder = $booking['idorder'] !== null ? (int) $booking['idorder'] : 0; + if ($flagReprogrammed === 'Y' && $idorder > 0) { + $incr = $pdo->prepare( + "UPDATE orderbook + SET reprogrammed = reprogrammed + 1 + WHERE idorderbook = :idorder" + ); + $incr->execute([':idorder' => $idorder]); + } + + $pdo->commit(); + + echo json_encode(['success' => true]); +} catch (Throwable $ex) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + fail('Errore durante la riprogrammazione: ' . $ex->getMessage(), 500); +}