new booking api
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* api_teacher_add_booking.php
|
||||
* --------------------------------------------------------------------------
|
||||
* Inserisce un partecipante in una classe. Solo staff (Admin=1 / teacher=3).
|
||||
* Replica inserisci_record.php della webapp (risposta JSON invece di redirect).
|
||||
*
|
||||
* Posizione: public/api/api_teacher_add_booking.php
|
||||
* Metodo: POST
|
||||
* Auth: Bearer token (Sanctum) via _bootstrap.php
|
||||
*
|
||||
* Body:
|
||||
* idserviceschedule=<int> classe destinazione
|
||||
* idservice=<int> servizio della classe
|
||||
* bookingstart=<datetime> data/ora lezione (dateschedule)
|
||||
* bkmode=<scala|omaggio|nuovo>
|
||||
* user_id=<int> utente esistente (0 se nuovo)
|
||||
* name, surname, email per creare l'utente se user_id=0
|
||||
* idorder=<int> solo per 'scala'
|
||||
* new_expiry=<YYYY-MM-DD> solo per 'nuovo'
|
||||
*
|
||||
* Regole identiche alla webapp:
|
||||
* - status sempre 'booked'
|
||||
* - omaggio: idorder NULL, is_gift='Y'
|
||||
* - nuovo: crea orderbook (1 ticket, maxreschedule 0), is_gift='N'
|
||||
* - scala: aggancia ordine esistente (verifica proprietà + scadenza)
|
||||
* - idorder mai 0 (FK): NULL o id reale
|
||||
* - crea utente se user_id=0 (riusa email esistente)
|
||||
* - anti-duplicato: stesso utente già in questa classe
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// --- Input ---
|
||||
$name = trim($_POST['name'] ?? '');
|
||||
$surname = trim($_POST['surname'] ?? '');
|
||||
$email = trim($_POST['email'] ?? '');
|
||||
$userid = isset($_POST['user_id']) ? (int) $_POST['user_id'] : 0;
|
||||
$idserviceschedule = isset($_POST['idserviceschedule']) ? (int) $_POST['idserviceschedule'] : 0;
|
||||
$idservice = isset($_POST['idservice']) ? (int) $_POST['idservice'] : 0;
|
||||
$bookingstart = trim($_POST['bookingstart'] ?? '');
|
||||
$bkmode = trim($_POST['bkmode'] ?? 'scala'); // scala | omaggio | nuovo
|
||||
$idorderRaw = $_POST['idorder'] ?? '';
|
||||
$newExpiry = trim($_POST['new_expiry'] ?? '');
|
||||
|
||||
$status = 'booked'; // inserimento da insegnante: sempre booked
|
||||
|
||||
// --- Validazione minima ---
|
||||
if ($idserviceschedule <= 0 || $idservice <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'Dati mancanti (classe/servizio)']);
|
||||
exit;
|
||||
}
|
||||
// Se nuovo utente, servono almeno nome e cognome
|
||||
if ($userid <= 0 && ($name === '' || $surname === '')) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'Nome e cognome obbligatori per un nuovo utente']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$db->beginTransaction();
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 1) Risolvi / crea utente
|
||||
// ------------------------------------------------------------------
|
||||
if ($userid <= 0) {
|
||||
if ($email !== '') {
|
||||
$check = $db->prepare("SELECT id FROM auth_users WHERE email = :email LIMIT 1");
|
||||
$check->execute([':email' => $email]);
|
||||
$existing = $check->fetchColumn();
|
||||
if ($existing) {
|
||||
$userid = (int) $existing;
|
||||
}
|
||||
}
|
||||
|
||||
if ($userid <= 0) {
|
||||
$randomPassword = bin2hex(random_bytes(8));
|
||||
$hashed = password_hash($randomPassword, PASSWORD_BCRYPT);
|
||||
|
||||
$insUser = $db->prepare(
|
||||
"INSERT INTO auth_users
|
||||
(first_name, last_name, email, password, role_id, status, created_at, avatar)
|
||||
VALUES
|
||||
(:first, :last, :email, :password, :role, :status, :created, :avatar)"
|
||||
);
|
||||
$insUser->execute([
|
||||
':first' => $name,
|
||||
':last' => $surname,
|
||||
':email' => $email !== '' ? $email : null,
|
||||
':password' => $hashed,
|
||||
':role' => 2,
|
||||
':status' => 'Active',
|
||||
':created' => date('Y-m-d H:i:s'),
|
||||
':avatar' => 'mediationb.png',
|
||||
]);
|
||||
$userid = (int) $db->lastInsertId();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 2) Determina idorder e is_gift in base alla modalità
|
||||
// ------------------------------------------------------------------
|
||||
$idorder = null;
|
||||
$isGift = 'N';
|
||||
|
||||
if ($bkmode === 'omaggio') {
|
||||
$idorder = null;
|
||||
$isGift = 'Y';
|
||||
} elseif ($bkmode === 'nuovo') {
|
||||
if ($newExpiry === '') {
|
||||
throw new RuntimeException('Scadenza mancante per il nuovo ordine.');
|
||||
}
|
||||
|
||||
$uStmt = $db->prepare(
|
||||
"SELECT first_name, last_name, email FROM auth_users WHERE id = :uid LIMIT 1"
|
||||
);
|
||||
$uStmt->execute([':uid' => $userid]);
|
||||
$uRow = $uStmt->fetch(PDO::FETCH_ASSOC) ?: [];
|
||||
$uEmail = $uRow['email'] ?? ($email !== '' ? $email : null);
|
||||
$uFirst = $uRow['first_name'] ?? $name;
|
||||
$uLast = $uRow['last_name'] ?? $surname;
|
||||
|
||||
$sStmt = $db->prepare(
|
||||
"SELECT servicename FROM service WHERE idservice = :sid LIMIT 1"
|
||||
);
|
||||
$sStmt->execute([':sid' => $idservice]);
|
||||
$serviceName = (string) ($sStmt->fetchColumn() ?: '');
|
||||
|
||||
$firstLessonDate = null;
|
||||
if ($bookingstart !== '') {
|
||||
try {
|
||||
$firstLessonDate = (new DateTime($bookingstart))->format('Y-m-d');
|
||||
} catch (Throwable $e) {
|
||||
$firstLessonDate = null;
|
||||
}
|
||||
}
|
||||
|
||||
$insOrder = $db->prepare(
|
||||
"INSERT INTO orderbook
|
||||
(iduser, idservice, nticket, quantity, quantityclass,
|
||||
expireon, maxreschedule, reprogrammed, status,
|
||||
order_billing_email, cod, product_name,
|
||||
first_name, last_name, first_lesson_date, order_date_created)
|
||||
VALUES
|
||||
(:uid, :service, 1, 1, 1,
|
||||
:expire, 0, 0, 'booked',
|
||||
:bemail, :cod, :pname,
|
||||
:first, :last, :firstlesson, :created)"
|
||||
);
|
||||
$insOrder->execute([
|
||||
':uid' => $userid,
|
||||
':service' => $idservice,
|
||||
':expire' => $newExpiry,
|
||||
':bemail' => $uEmail,
|
||||
':cod' => $serviceName,
|
||||
':pname' => $serviceName,
|
||||
':first' => $uFirst,
|
||||
':last' => $uLast,
|
||||
':firstlesson' => $firstLessonDate,
|
||||
':created' => date('Y-m-d'),
|
||||
]);
|
||||
$idorder = (int) $db->lastInsertId();
|
||||
$isGift = 'N';
|
||||
} else {
|
||||
// 'scala'
|
||||
$idorder = ($idorderRaw !== '' && (int) $idorderRaw > 0) ? (int) $idorderRaw : null;
|
||||
|
||||
if ($idorder !== null) {
|
||||
$chk = $db->prepare(
|
||||
"SELECT expireon FROM orderbook
|
||||
WHERE idorderbook = :oid AND iduser = :uid LIMIT 1"
|
||||
);
|
||||
$chk->execute([':oid' => $idorder, ':uid' => $userid]);
|
||||
$row = $chk->fetch(PDO::FETCH_ASSOC);
|
||||
if (!$row) {
|
||||
throw new RuntimeException('Ordine non valido per questo utente.');
|
||||
}
|
||||
$exp = $row['expireon'] ?? null;
|
||||
if ($exp !== null && $exp < date('Y-m-d')) {
|
||||
throw new RuntimeException('Il pacchetto selezionato è scaduto.');
|
||||
}
|
||||
} else {
|
||||
throw new RuntimeException('Nessun pacchetto selezionato.');
|
||||
}
|
||||
$isGift = 'N';
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 2b) Anti-duplicato
|
||||
// ------------------------------------------------------------------
|
||||
$dup = $db->prepare(
|
||||
"SELECT COUNT(*) FROM bookingclass
|
||||
WHERE iduser = :uid
|
||||
AND idserviceschedule = :idschedule
|
||||
AND status != 'cancelled'"
|
||||
);
|
||||
$dup->execute([':uid' => $userid, ':idschedule' => $idserviceschedule]);
|
||||
if ((int) $dup->fetchColumn() > 0) {
|
||||
$db->rollBack();
|
||||
http_response_code(409);
|
||||
echo json_encode(['success' => false, 'message' => 'Questo utente è già prenotato in questa classe.']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// 3) Inserimento prenotazione
|
||||
// ------------------------------------------------------------------
|
||||
$insBooking = $db->prepare(
|
||||
"INSERT INTO bookingclass
|
||||
(iduser, idserviceschedule, status, idorder, idservice, bookingstart, is_gift)
|
||||
VALUES
|
||||
(:iduser, :idschedule, :status, :idorder, :idservice, :bookingstart, :isgift)"
|
||||
);
|
||||
$insBooking->execute([
|
||||
':iduser' => $userid,
|
||||
':idschedule' => $idserviceschedule,
|
||||
':status' => $status,
|
||||
':idorder' => $idorder,
|
||||
':idservice' => $idservice,
|
||||
':bookingstart' => $bookingstart !== '' ? $bookingstart : null,
|
||||
':isgift' => $isGift,
|
||||
]);
|
||||
|
||||
$db->commit();
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'user_id' => $userid,
|
||||
'is_gift' => $isGift,
|
||||
'idorder' => $idorder,
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
} catch (Throwable $ex) {
|
||||
if ($db->inTransaction()) {
|
||||
$db->rollBack();
|
||||
}
|
||||
error_log('api_teacher_add_booking error: ' . $ex->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Errore inserimento: ' . $ex->getMessage()]);
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* api_teacher_search_users.php
|
||||
* --------------------------------------------------------------------------
|
||||
* Ricerca utenti per nome e/o cognome (OR). Solo staff (Admin=1 / teacher=3).
|
||||
* Replica searchemail.php della webapp.
|
||||
*
|
||||
* Posizione: public/api/api_teacher_search_users.php
|
||||
* Metodo: GET
|
||||
* Auth: Bearer token (Sanctum) via _bootstrap.php
|
||||
* Query: ?first=<str>&last=<str> (min 2 caratteri in almeno uno)
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$firstName = trim($_GET['first'] ?? '');
|
||||
$lastName = trim($_GET['last'] ?? '');
|
||||
|
||||
// Serve almeno 2 caratteri in uno dei due campi
|
||||
if (mb_strlen($firstName) < 2 && mb_strlen($lastName) < 2) {
|
||||
echo json_encode(['success' => true, 'results' => []], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$conditions = [];
|
||||
$params = [];
|
||||
|
||||
if (mb_strlen($firstName) >= 2) {
|
||||
$conditions[] = "first_name LIKE :fn";
|
||||
$params[':fn'] = '%' . $firstName . '%';
|
||||
}
|
||||
if (mb_strlen($lastName) >= 2) {
|
||||
$conditions[] = "last_name LIKE :ln";
|
||||
$params[':ln'] = '%' . $lastName . '%';
|
||||
}
|
||||
|
||||
$where = implode(' OR ', $conditions);
|
||||
|
||||
$sql = "SELECT id, first_name, last_name, email
|
||||
FROM auth_users
|
||||
WHERE $where
|
||||
ORDER BY last_name, first_name
|
||||
LIMIT 15";
|
||||
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$results = [];
|
||||
foreach ($rows as $r) {
|
||||
$results[] = [
|
||||
'id' => (int) $r['id'],
|
||||
'first_name' => (string) ($r['first_name'] ?? ''),
|
||||
'last_name' => (string) ($r['last_name'] ?? ''),
|
||||
'email' => (string) ($r['email'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'results' => $results], JSON_UNESCAPED_UNICODE);
|
||||
} catch (Throwable $ex) {
|
||||
error_log('api_teacher_search_users error: ' . $ex->getMessage());
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => 'Errore ricerca']);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* api_teacher_user_orders.php
|
||||
* --------------------------------------------------------------------------
|
||||
* Ordini attivi (non scaduti) di un utente, con ticket residui.
|
||||
* Solo staff (Admin=1 / teacher=3). Replica get_user_orders.php.
|
||||
*
|
||||
* Posizione: public/api/api_teacher_user_orders.php
|
||||
* Metodo: GET
|
||||
* Auth: Bearer token (Sanctum) via _bootstrap.php
|
||||
* Query: ?user_id=<int>
|
||||
*
|
||||
* Residui = ticket - (prenotazioni non cancellate e NON omaggio).
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
$userId = isset($_GET['user_id']) ? (int) $_GET['user_id'] : 0;
|
||||
if ($userId <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'Utente non valido', 'orders' => []]);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$today = date('Y-m-d');
|
||||
|
||||
$sql = "SELECT ob.idorderbook, ob.idservice, ob.nticket, ob.quantityclass,
|
||||
ob.expireon, ob.maxreschedule, ob.reprogrammed,
|
||||
s.servicename
|
||||
FROM orderbook ob
|
||||
LEFT JOIN service s ON ob.idservice = s.idservice
|
||||
WHERE ob.iduser = :uid
|
||||
AND (ob.expireon IS NULL OR ob.expireon >= :today)
|
||||
ORDER BY ob.idorderbook DESC";
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute([':uid' => $userId, ':today' => $today]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// conta le prenotazioni che consumano ticket (non cancellate, non omaggio)
|
||||
$usedStmt = $db->prepare(
|
||||
"SELECT COUNT(*) AS n
|
||||
FROM bookingclass
|
||||
WHERE idorder = :oid
|
||||
AND status != 'cancelled'
|
||||
AND is_gift = 'N'"
|
||||
);
|
||||
|
||||
$orders = [];
|
||||
foreach ($rows as $o) {
|
||||
$orderId = (int) $o['idorderbook'];
|
||||
|
||||
$tickets = (int) (($o['quantityclass'] !== null && $o['quantityclass'] !== '')
|
||||
? $o['quantityclass']
|
||||
: ($o['nticket'] ?? 0));
|
||||
|
||||
$usedStmt->execute([':oid' => $orderId]);
|
||||
$used = (int) ($usedStmt->fetch(PDO::FETCH_ASSOC)['n'] ?? 0);
|
||||
|
||||
$remaining = $tickets - $used;
|
||||
if ($remaining < 0) {
|
||||
$remaining = 0;
|
||||
}
|
||||
|
||||
$orders[] = [
|
||||
'idorderbook' => $orderId,
|
||||
'service_name' => (string) ($o['servicename'] ?? ''),
|
||||
'tickets' => $tickets,
|
||||
'used' => $used,
|
||||
'remaining' => $remaining,
|
||||
'expireon' => $o['expireon'] ??
|
||||
Reference in New Issue
Block a user