Files
2026-07-27 16:49:12 +02:00

231 lines
7.8 KiB
PHP

<?php
/**
* reschedule.php
* --------------------------------------------------------------------------
* Riprogramma una lezione esistente su un nuovo slot.
* Fedele a rebookandgo.php, ma con prepared statement e controlli di sicurezza.
*
* Posizione: public/api/reschedule.php
* Metodo: POST
* Auth: Bearer token (Sanctum, gestito da _bootstrap.php)
*
* Body (JSON):
* - booking_id (int) -> idbookingclass della lezione da spostare
* - new_schedule_id (int) -> idserviceschedule del nuovo slot scelto
*
* Logica (come rebookandgo.php):
* 1. Verifica che il booking sia dell'utente. Legge idorder, idservice vecchi.
* 2. Controlla maxreschedule (reprogrammed < maxreschedule).
* 3. Verifica che il nuovo slot esista e sia di un servizio COMPATIBILE.
* 4. Verifica posto libero e che l'utente non sia già su quello slot.
* 5. Cancella il vecchio booking, inserisce il nuovo con status 'pending',
* salvando prevbookingstart / idprevserviceschedule.
* 6. Incrementa orderbook.reprogrammed.
* --------------------------------------------------------------------------
*/
require_once __DIR__ . '/_bootstrap.php';
$userId = (int) $user->id;
// ==========================================================================
// INPUT
// ==========================================================================
$raw = file_get_contents('php://input');
$json = json_decode($raw, true);
if (is_array($json)) {
$bookingId = (int) ($json['booking_id'] ?? 0);
$newScheduleId = (int) ($json['new_schedule_id'] ?? 0);
} else {
$bookingId = (int) ($_POST['booking_id'] ?? 0);
$newScheduleId = (int) ($_POST['new_schedule_id'] ?? 0);
}
if ($bookingId <= 0 || $newScheduleId <= 0) {
http_response_code(400);
echo json_encode(['success' => false, 'error' => 'Parametri mancanti']);
exit;
}
// ==========================================================================
// 1) Vecchio booking (con ownership)
// ==========================================================================
$stmt = $db->prepare("
SELECT bc.idbookingclass, bc.iduser, bc.idservice, bc.idorder,
bc.bookingstart, bc.idserviceschedule
FROM bookingclass bc
WHERE bc.idbookingclass = :bid AND bc.iduser = :uid
LIMIT 1
");
$stmt->execute([':bid' => $bookingId, ':uid' => $userId]);
$old = $stmt->fetch();
if (!$old) {
http_response_code(404);
echo json_encode(['success' => false, 'error' => 'Prenotazione non trovata']);
exit;
}
$oldServiceId = (int) $old['idservice'];
$oldOrderId = (int) $old['idorder'];
$oldSchedId = (int) $old['idserviceschedule'];
$oldBookingStart = $old['bookingstart'];
// ==========================================================================
// 2) Controllo maxreschedule sull'ordine
// ==========================================================================
$stmt = $db->prepare("
SELECT maxreschedule, reprogrammed
FROM orderbook
WHERE idorderbook = :oid
LIMIT 1
");
$stmt->execute([':oid' => $oldOrderId]);
$order = $stmt->fetch();
if ($order) {
$maxr = (int) ($order['maxreschedule'] ?? 0);
$repr = (int) ($order['reprogrammed'] ?? 0);
if ($repr >= $maxr) {
http_response_code(409);
echo json_encode([
'success' => false,
'error' => 'Hai raggiunto il numero massimo di riprogrammazioni per questo pacchetto.',
'code' => 'max_reschedule',
]);
exit;
}
}
// ==========================================================================
// 3) Nuovo slot: esiste? servizio compatibile?
// ==========================================================================
$stmt = $db->prepare("
SELECT ss.idserviceschedule, ss.idservice, ss.dateschedule, s.maxcapacity
FROM serviceschedule ss
LEFT JOIN service s ON ss.idservice = s.idservice
WHERE ss.idserviceschedule = :sched
LIMIT 1
");
$stmt->execute([':sched' => $newScheduleId]);
$slot = $stmt->fetch();
if (!$slot) {
http_response_code(404);
echo json_encode(['success' => false, 'error' => 'Slot non trovato']);
exit;
}
$newServiceId = (int) $slot['idservice'];
$newTime = $slot['dateschedule'];
$maxCap = (int) ($slot['maxcapacity'] ?? 0);
// Servizi compatibili col vecchio servizio (associateclass) + se stesso
$compatible = [$oldServiceId];
$cs = $db->prepare("SELECT idassociateservice FROM associateclass WHERE idmainservice = :sid");
$cs->execute([':sid' => $oldServiceId]);
foreach ($cs->fetchAll() as $r) {
$compatible[] = (int) $r['idassociateservice'];
}
if (!in_array($newServiceId, $compatible, true)) {
http_response_code(409);
echo json_encode([
'success' => false,
'error' => 'Lo slot scelto non è compatibile con la tua lezione.',
'code' => 'not_compatible',
]);
exit;
}
// ==========================================================================
// 4) Posto libero + non già prenotato dall'utente su quello slot
// ==========================================================================
$stmt = $db->prepare("
SELECT COUNT(*) AS n FROM bookingclass
WHERE idserviceschedule = :sched AND status = 'booked'
");
$stmt->execute([':sched' => $newScheduleId]);
$bookedCount = (int) ($stmt->fetch()['n'] ?? 0);
if ($maxCap > 0 && $bookedCount >= $maxCap) {
http_response_code(409);
echo json_encode([
'success' => false,
'error' => 'La classe scelta è piena.',
'code' => 'full',
]);
exit;
}
$stmt = $db->prepare("
SELECT COUNT(*) AS n FROM bookingclass
WHERE idserviceschedule = :sched AND iduser = :uid
AND status IN ('booked','pending')
");
$stmt->execute([':sched' => $newScheduleId, ':uid' => $userId]);
if (((int) ($stmt->fetch()['n'] ?? 0)) > 0) {
http_response_code(409);
echo json_encode([
'success' => false,
'error' => 'Sei già prenotato/a su questa classe.',
'code' => 'already_booked',
]);
exit;
}
// ==========================================================================
// 5) + 6) Transazione: cancella vecchio, inserisci nuovo, incrementa reprogrammed
// ==========================================================================
try {
$db->beginTransaction();
// Cancella il vecchio (con ownership)
$del = $db->prepare("
DELETE FROM bookingclass WHERE idbookingclass = :bid AND iduser = :uid LIMIT 1
");
$del->execute([':bid' => $bookingId, ':uid' => $userId]);
// Inserisci il nuovo (status pending)
$ins = $db->prepare("
INSERT INTO bookingclass
(idserviceschedule, iduser, prevbookingstart, idprevserviceschedule,
idservice, idorder, bookingstart, status)
VALUES
(:sched, :uid, :prevstart, :prevsched, :service, :order, :newstart, 'pending')
");
$ins->execute([
':sched' => $newScheduleId,
':uid' => $userId,
':prevstart' => $oldBookingStart,
':prevsched' => $oldSchedId,
':service' => $newServiceId,
':order' => $oldOrderId,
':newstart' => $newTime,
]);
// Incrementa reprogrammed
$upd = $db->prepare("
UPDATE orderbook SET reprogrammed = reprogrammed + 1 WHERE idorderbook = :oid
");
$upd->execute([':oid' => $oldOrderId]);
$db->commit();
} catch (Throwable $e) {
if ($db->inTransaction()) {
$db->rollBack();
}
http_response_code(500);
echo json_encode(['success' => false, 'error' => 'Errore durante la riprogrammazione']);
exit;
}
// ==========================================================================
// OUTPUT
// ==========================================================================
echo json_encode([
'success' => true,
'message' => 'Riprogrammazione richiesta. In attesa di conferma.',
'new_datetime' => $newTime,
], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);