diff --git a/public/admin_reprogram_booking.php b/public/admin_reprogram_booking.php new file mode 100644 index 0000000..4f2f13d --- /dev/null +++ b/public/admin_reprogram_booking.php @@ -0,0 +1,214 @@ + false, 'message' => 'Metodo non consentito.'], 405); +} + +$id = (int) ($_POST['id'] ?? 0); +$idSchedule = (int) ($_POST['idschedule'] ?? 0); +$sendEmail = ($_POST['send_email'] ?? 'N') === 'Y'; +$countReprog = ($_POST['count_reprog'] ?? 'N') === 'Y'; +$staffId = (int) $iduserlogin; + +if ($id <= 0 || $idSchedule <= 0) { + jsonOut(['success' => false, 'message' => 'Seleziona la nuova lezione.'], 400); +} + +$errors = [ + 'notfound' => 'Prenotazione non trovata (forse è stata cancellata).', + 'notarget' => 'La lezione scelta non esiste più.', + 'same' => 'Hai scelto la stessa lezione di adesso.', + 'past' => 'La lezione scelta è già iniziata o passata.', + 'already' => 'L\'allieva è già iscritta alla lezione scelta.', + 'full' => 'La lezione scelta è piena.', +]; + +$pdo = DBHandlerSelect::getInstance()->getConnection(); +$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + +$bk = null; +$target = null; +$incremented = 'N'; + +try { + $pdo->beginTransaction(); + + // Prenotazione attuale (bloccata) + $st = $pdo->prepare(" + SELECT bc.idbookingclass, bc.iduser, bc.idorder, bc.idserviceschedule, bc.bookingstart, + s.servicename AS old_servicename, ss.dateschedule AS old_date, + u.email, u.first_name + FROM bookingclass bc + LEFT JOIN service s ON s.idservice = bc.idservice + LEFT JOIN serviceschedule ss ON ss.idserviceschedule = bc.idserviceschedule + LEFT JOIN auth_users u ON u.id = bc.iduser + WHERE bc.idbookingclass = ? + FOR UPDATE + "); + $st->execute([$id]); + $bk = $st->fetch(PDO::FETCH_ASSOC); + if (!$bk) throw new RuntimeException('notfound'); + + if ((int) $bk['idserviceschedule'] === $idSchedule) throw new RuntimeException('same'); + + // Nuova lezione (bloccata: evita due spostamenti contemporanei sull'ultimo posto) + $st = $pdo->prepare(" + SELECT ss.idserviceschedule, ss.idservice, ss.dateschedule, s.servicename, s.maxcapacity + FROM serviceschedule ss + JOIN service s ON s.idservice = ss.idservice + WHERE ss.idserviceschedule = ? + FOR UPDATE + "); + $st->execute([$idSchedule]); + $target = $st->fetch(PDO::FETCH_ASSOC); + if (!$target) throw new RuntimeException('notarget'); + + if (strtotime($target['dateschedule']) <= time()) throw new RuntimeException('past'); + + // L'allieva è già su quella lezione? + $st = $pdo->prepare("SELECT COUNT(*) FROM bookingclass WHERE idserviceschedule = ? AND iduser = ?"); + $st->execute([$idSchedule, (int) $bk['iduser']]); + if ((int) $st->fetchColumn() > 0) throw new RuntimeException('already'); + + // Posti liberi (booked + pending) + if ($target['maxcapacity'] !== null) { + $st = $pdo->prepare("SELECT COUNT(*) FROM bookingclass WHERE idserviceschedule = ? AND status IN ('booked','pending')"); + $st->execute([$idSchedule]); + if ((int) $st->fetchColumn() >= (int) $target['maxcapacity']) throw new RuntimeException('full'); + } + + // Spostamento: la vecchia lezione resta in idprevserviceschedule / prevbookingstart + $up = $pdo->prepare(" + UPDATE bookingclass + SET idprevserviceschedule = :prevsched, + prevbookingstart = :prevstart, + idserviceschedule = :sched, + idservice = :svc, + bookingstart = :start, + is_reprogrammed = 'Y' + WHERE idbookingclass = :id + "); + $up->execute([ + ':prevsched' => $bk['idserviceschedule'] !== null ? (int) $bk['idserviceschedule'] : null, + ':prevstart' => $bk['bookingstart'], + ':sched' => $idSchedule, + ':svc' => (int) $target['idservice'], + ':start' => $target['dateschedule'], + ':id' => $id, + ]); + + // Conta come riprogrammazione + if ($countReprog && $bk['idorder'] !== null) { + $st = $pdo->prepare("UPDATE orderbook SET reprogrammed = reprogrammed + 1 WHERE idorderbook = ?"); + $st->execute([(int) $bk['idorder']]); + if ($st->rowCount() === 1) $incremented = 'Y'; + } + + $pdo->commit(); +} catch (PDOException $e) { + if ($pdo->inTransaction()) $pdo->rollBack(); + error_log("admin_reprogram_booking #{$id} -> {$idSchedule} (staff #{$staffId}): " . $e->getMessage()); + jsonOut(['success' => false, 'message' => 'Errore durante la riprogrammazione. Riprova.'], 500); +} catch (RuntimeException $e) { + if ($pdo->inTransaction()) $pdo->rollBack(); + $code = $e->getMessage(); + jsonOut(['success' => false, 'message' => $errors[$code] ?? 'Errore durante la riprogrammazione.'], 409); +} + +/* ------------------------------------------------------------------------- + * Email all'allieva (dopo il commit, solo se richiesta) + * ---------------------------------------------------------------------- */ +$fmt = fn($d) => $d ? date('d/m/Y H:i', strtotime($d)) : ''; +$emailNote = ''; + +if ($sendEmail) { + $to = trim((string) ($bk['email'] ?? '')); + + if (!filter_var($to, FILTER_VALIDATE_EMAIL)) { + $emailNote = ' Email non inviata: indirizzo dell\'allieva mancante o non valido.'; + } else { + try { + require_once(__DIR__ . '/class/mailer.php'); + + $clean = fn($v) => trim((string) $v, "\" \t\n\r\0\x0B"); + $enc = strtolower($clean($_ENV['MAIL_ENCRYPTION'] ?? 'ssl')); + + $mail = new \PHPMailer\PHPMailer\PHPMailer(true); + $mail->isSMTP(); + $mail->Host = $clean($_ENV['MAIL_HOST'] ?? ''); + $mail->SMTPAuth = true; + $mail->Username = $_ENV['MAIL_USERNAME'] ?? ''; + $mail->Password = $_ENV['MAIL_PASSWORD'] ?? ''; + $mail->Port = (int) ($_ENV['MAIL_PORT'] ?? 465); + if (in_array($enc, ['tls', 'ssl'], true)) { + $mail->SMTPSecure = $enc; + } + $mail->Timeout = 20; + $mail->CharSet = 'UTF-8'; + + $fromName = $clean($_ENV['MAIL_FROM_NAME'] ?? 'YogiBook'); + $mail->setFrom($clean($_ENV['MAIL_FROM_ADDRESS'] ?? ''), $fromName); + + $h = fn($v) => htmlspecialchars((string) $v, ENT_QUOTES, 'UTF-8'); + $nome = trim((string) ($bk['first_name'] ?? '')); + $oldName = $bk['old_servicename'] ?: 'Lezione'; + $oldDate = $fmt($bk['old_date'] ?: $bk['bookingstart']); + $newName = $target['servicename'] ?: 'Lezione'; + $newDate = $fmt($target['dateschedule']); + + $mail->addAddress($to, $nome); + $mail->isHTML(true); + $mail->Subject = 'Lezione riprogrammata - ' . $newName . ' ' . $newDate; + $mail->Body = " +
Ciao " . $h($nome !== '' ? $nome : 'Yogi') . ",
+la tua lezione è stata riprogrammata.
+
+ Prima: " . $h($oldName) . " – " . $h($oldDate) . "
+ Adesso: " . $h($newName) . " – " . $h($newDate) . "
+
Se hai domande, rispondi pure a questa email.
+A presto,
" . $h($fromName) . "