192 lines
7.0 KiB
PHP
192 lines
7.0 KiB
PHP
<?php
|
|
|
|
/**
|
|
* BookingRules
|
|
* Regole di prenotazione in un unico posto, sempre limitate alla scuola corrente.
|
|
* Usata da: selectorder, bookingpanel, bookingpanelorder, bookandgo, rebookandgo,
|
|
* delete_lesson e (in seguito) dalle API dell'app.
|
|
*/
|
|
|
|
if (!class_exists('DBHandlerSelect')) {
|
|
require_once __DIR__ . '/db-functions.php';
|
|
}
|
|
|
|
class BookingRules
|
|
{
|
|
/* ================================================================== */
|
|
/* ORDINI / TICKET */
|
|
/* ================================================================== */
|
|
|
|
/** Ordine dell'utente nella scuola corrente, con nome servizio (null se non suo) */
|
|
public static function order(int $idorder, int $userId): ?array
|
|
{
|
|
$st = self::pdo()->prepare(
|
|
"SELECT ob.*, s.servicename
|
|
FROM orderbook ob
|
|
LEFT JOIN service s ON s.idservice = ob.idservice
|
|
WHERE ob.idorderbook = ? AND ob.iduser = ? AND ob.idschool = ?"
|
|
);
|
|
$st->execute([$idorder, $userId, SchoolContext::id()]);
|
|
return $st->fetch() ?: null;
|
|
}
|
|
|
|
/** Tutti gli ordini dell'utente nella scuola corrente */
|
|
public static function userOrders(int $userId): array
|
|
{
|
|
$st = self::pdo()->prepare(
|
|
"SELECT ob.*, s.servicename
|
|
FROM orderbook ob
|
|
LEFT JOIN service s ON s.idservice = ob.idservice
|
|
WHERE ob.iduser = ? AND ob.idschool = ?
|
|
ORDER BY ob.order_date_created DESC, ob.idorderbook DESC"
|
|
);
|
|
$st->execute([$userId, SchoolContext::id()]);
|
|
return $st->fetchAll();
|
|
}
|
|
|
|
/** Ticket totali dell'ordine: nticket, o quantityclass se nticket è vuoto */
|
|
public static function ticketsOf(array $order): int
|
|
{
|
|
$n = (int) ($order['nticket'] ?? 0);
|
|
return $n > 0 ? $n : (int) ($order['quantityclass'] ?? 0);
|
|
}
|
|
|
|
/** Ticket usati: prenotazioni non annullate e non omaggio collegate all'ordine */
|
|
public static function usedTickets(int $idorder): int
|
|
{
|
|
$st = self::pdo()->prepare(
|
|
"SELECT COUNT(*) FROM bookingclass
|
|
WHERE idorder = ? AND idschool = ?
|
|
AND status <> 'cancelled' AND is_gift = 'N'"
|
|
);
|
|
$st->execute([$idorder, SchoolContext::id()]);
|
|
return (int) $st->fetchColumn();
|
|
}
|
|
|
|
public static function remaining(array $order): int
|
|
{
|
|
return max(0, self::ticketsOf($order) - self::usedTickets((int) $order['idorderbook']));
|
|
}
|
|
|
|
public static function isExpired(array $order): bool
|
|
{
|
|
return !empty($order['expireon']) && $order['expireon'] < date('Y-m-d');
|
|
}
|
|
|
|
/** Si può ancora riprogrammare con questo ordine? (gli omaggi, senza ordine, no) */
|
|
public static function canReprogram(?array $order): bool
|
|
{
|
|
if (!$order) {
|
|
return false;
|
|
}
|
|
return (int) ($order['reprogrammed'] ?? 0) < (int) ($order['maxreschedule'] ?? 0);
|
|
}
|
|
|
|
/* ================================================================== */
|
|
/* CLASSI / POSTI */
|
|
/* ================================================================== */
|
|
|
|
/** Servizi prenotabili con un ordine: il servizio + le classi associate */
|
|
public static function allowedServices(int $idservice): array
|
|
{
|
|
$st = self::pdo()->prepare(
|
|
"SELECT idassociateservice FROM associateclass WHERE idmainservice = ? AND idschool = ?"
|
|
);
|
|
$st->execute([$idservice, SchoolContext::id()]);
|
|
$ids = array_map('intval', $st->fetchAll(PDO::FETCH_COLUMN));
|
|
array_unshift($ids, $idservice);
|
|
return array_values(array_unique($ids));
|
|
}
|
|
|
|
/** Lezione in calendario della scuola corrente, con dati del servizio */
|
|
public static function schedule(int $idschedule): ?array
|
|
{
|
|
$st = self::pdo()->prepare(
|
|
"SELECT ss.*, s.servicename, s.maxcapacity, s.colorclass, s.idteacher
|
|
FROM serviceschedule ss
|
|
JOIN service s ON s.idservice = ss.idservice
|
|
WHERE ss.idserviceschedule = ? AND ss.idschool = ?"
|
|
);
|
|
$st->execute([$idschedule, SchoolContext::id()]);
|
|
return $st->fetch() ?: null;
|
|
}
|
|
|
|
/** Posti occupati (prenotate + in attesa di conferma) */
|
|
public static function occupied(int $idschedule): int
|
|
{
|
|
$st = self::pdo()->prepare(
|
|
"SELECT COUNT(*) FROM bookingclass
|
|
WHERE idserviceschedule = ? AND idschool = ? AND status IN ('booked','pending')"
|
|
);
|
|
$st->execute([$idschedule, SchoolContext::id()]);
|
|
return (int) $st->fetchColumn();
|
|
}
|
|
|
|
/** L'utente ha già una prenotazione attiva (anche in coda) in questa lezione? */
|
|
public static function userHasBooking(int $idschedule, int $userId): bool
|
|
{
|
|
$st = self::pdo()->prepare(
|
|
"SELECT 1 FROM bookingclass
|
|
WHERE idserviceschedule = ? AND iduser = ? AND idschool = ? AND status <> 'cancelled'
|
|
LIMIT 1"
|
|
);
|
|
$st->execute([$idschedule, $userId, SchoolContext::id()]);
|
|
return (bool) $st->fetchColumn();
|
|
}
|
|
|
|
public static function waitingListEnabled(): bool
|
|
{
|
|
return SchoolSettings::get('waitinglist_enabled', 'N') === 'Y';
|
|
}
|
|
|
|
/**
|
|
* Stato di una nuova prenotazione da parte dell'allievo:
|
|
* 'pending' c'è posto (lo staff conferma)
|
|
* 'waiting' classe piena, lista d'attesa attiva
|
|
* 'full' classe piena, lista d'attesa disattivata
|
|
*/
|
|
public static function decideStatus(array $schedule): string
|
|
{
|
|
$max = (int) ($schedule['maxcapacity'] ?? 0);
|
|
if ($max <= 0 || self::occupied((int) $schedule['idserviceschedule']) < $max) {
|
|
return 'pending';
|
|
}
|
|
return self::waitingListEnabled() ? 'waiting' : 'full';
|
|
}
|
|
|
|
/* ================================================================== */
|
|
/* CANCELLAZIONE */
|
|
/* ================================================================== */
|
|
|
|
/**
|
|
* L'allievo può ancora cancellare/riprogrammare la lezione?
|
|
* Regola attuale (Seregno), da rendere configurabile per scuola:
|
|
* - lezione passata: no
|
|
* - lezione in un altro giorno: sì
|
|
* - lezione di oggi entro le 17:00: entro le 00:01 di oggi
|
|
* - lezione di oggi dopo le 17:00: entro le 12:00 di oggi
|
|
*/
|
|
public static function canCancel(string $dateschedule): bool
|
|
{
|
|
$now = new DateTime();
|
|
$class = new DateTime($dateschedule);
|
|
|
|
if ($class <= $now) {
|
|
return false;
|
|
}
|
|
if ($class->format('Y-m-d') !== $now->format('Y-m-d')) {
|
|
return true;
|
|
}
|
|
$h = (int) $class->format('H');
|
|
$m = (int) $class->format('i');
|
|
$before1700 = $h < 17 || ($h === 17 && $m === 0);
|
|
$deadline = new DateTime($class->format('Y-m-d') . ($before1700 ? ' 00:01:00' : ' 12:00:00'));
|
|
return $now <= $deadline;
|
|
}
|
|
|
|
private static function pdo(): PDO
|
|
{
|
|
return DBHandlerSelect::getInstance()->getConnection();
|
|
}
|
|
}
|