reprogram api
This commit is contained in:
@@ -0,0 +1,109 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* api_teacher_available_classes.php
|
||||||
|
* --------------------------------------------------------------------------
|
||||||
|
* Classi schedulate candidate come destinazione di una riprogrammazione.
|
||||||
|
* Solo staff (Admin=1 / teacher=3).
|
||||||
|
*
|
||||||
|
* Restituisce le classi FUTURE con posti liberi (non piene), escludendo
|
||||||
|
* quelle dove l'allievo indicato è già prenotato.
|
||||||
|
*
|
||||||
|
* Posizione: public/api/api_teacher_available_classes.php
|
||||||
|
* Metodo: GET
|
||||||
|
* Auth: Bearer token (Sanctum) via _bootstrap.php
|
||||||
|
* Query: ?booking_id=<int> (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']);
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* api_teacher_reprogram.php
|
||||||
|
* --------------------------------------------------------------------------
|
||||||
|
* Sposta una prenotazione su una nuova classe schedulata.
|
||||||
|
* Replica reprogramclass.php della webapp, con gate staff.
|
||||||
|
* Solo staff (Admin=1 / teacher=3).
|
||||||
|
*
|
||||||
|
* Posizione: public/api/api_teacher_reprogram.php
|
||||||
|
* Metodo: POST
|
||||||
|
* Auth: Bearer token (Sanctum) via _bootstrap.php
|
||||||
|
* Body: booking_id=<int>&schedule_id=<int>&is_reprogrammed=<Y|N>
|
||||||
|
*
|
||||||
|
* 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']);
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* reprogramclass.php
|
||||||
|
*
|
||||||
|
* Sposta una prenotazione esistente su una nuova classe schedulata.
|
||||||
|
* La vecchia collocazione non viene duplicata: la riga di bookingclass
|
||||||
|
* viene aggiornata in place, conservando classe/data precedente nei
|
||||||
|
* campi idprevserviceschedule / prevbookingstart.
|
||||||
|
*
|
||||||
|
* Input (POST):
|
||||||
|
* - idbookingclass : int prenotazione da spostare
|
||||||
|
* - idserviceschedule : int nuova classe destinazione
|
||||||
|
* - is_reprogrammed : 'Y'|'N' se 'Y' incrementa orderbook.reprogrammed
|
||||||
|
*
|
||||||
|
* Regole:
|
||||||
|
* - status resta 'booked'
|
||||||
|
* - lostlesson NON viene toccato
|
||||||
|
* - is_reprogrammed sulla prenotazione va sempre a 'Y'
|
||||||
|
* - orderbook.reprogrammed +1 solo se il flag del modale è attivo e c'e un idorder valido
|
||||||
|
*
|
||||||
|
* Nota: include direttamente class/db-functions.php (dove sta DBHandlerSelect)
|
||||||
|
* invece di headscript.php, cosi non viene emesso output HTML prima del JSON.
|
||||||
|
*/
|
||||||
|
|
||||||
|
require_once('class/db-functions.php');
|
||||||
|
|
||||||
|
header('Content-Type: application/json; charset=utf-8');
|
||||||
|
|
||||||
|
function fail(string $msg, int $http = 400): void
|
||||||
|
{
|
||||||
|
http_response_code($http);
|
||||||
|
echo json_encode(['error' => $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);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user