146 lines
5.6 KiB
PHP
146 lines
5.6 KiB
PHP
<?php
|
|
// book_save.php — Conferma prenotazione: crea/trova il customer e inserisce l'appointment (pending)
|
|
// Riceve POST da book.php. Richiede utente loggato.
|
|
|
|
if (session_status() === PHP_SESSION_NONE) {
|
|
session_start();
|
|
}
|
|
|
|
require_once(__DIR__ . '/userarea/class/db-functions.php');
|
|
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
|
|
|
// Helper flash (se non già definito altrove nel progetto)
|
|
if (!function_exists('setFlash')) {
|
|
function setFlash($type, $text)
|
|
{
|
|
$_SESSION['flash'] = ['type' => $type, 'text' => $text];
|
|
}
|
|
}
|
|
|
|
// --- 1. Deve essere loggato ---
|
|
if (empty($_SESSION['iduserlogin'])) {
|
|
// Torna a book.php mantenendo la selezione, che poi rimanderà al login
|
|
$qs = http_build_query([
|
|
'shop' => (int)($_POST['shop_id'] ?? 0),
|
|
'service' => (int)($_POST['service_id'] ?? 0),
|
|
'staff' => (int)($_POST['staff_id'] ?? 0),
|
|
'date' => $_POST['date'] ?? '',
|
|
'time' => $_POST['time'] ?? '',
|
|
'need_login' => 1,
|
|
]);
|
|
header('Location: book.php?' . $qs);
|
|
exit;
|
|
}
|
|
|
|
$user_id = (int)$_SESSION['iduserlogin'];
|
|
$shop_id = (int)($_POST['shop_id'] ?? 0);
|
|
$service_id = (int)($_POST['service_id'] ?? 0);
|
|
$staff_id = (int)($_POST['staff_id'] ?? 0);
|
|
$date = trim($_POST['date'] ?? '');
|
|
$time = trim($_POST['time'] ?? '');
|
|
$notes = trim($_POST['notes'] ?? '');
|
|
|
|
// --- 2. Validazione input ---
|
|
if ($shop_id <= 0 || $service_id <= 0 || $staff_id <= 0 || $date === '' || $time === '') {
|
|
setFlash('danger', 'Dati di prenotazione incompleti. Riprova.');
|
|
header('Location: book.php?shop=' . $shop_id . '&service=' . $service_id);
|
|
exit;
|
|
}
|
|
|
|
$startDT = DateTime::createFromFormat('Y-m-d H:i', $date . ' ' . $time);
|
|
if (!$startDT) {
|
|
setFlash('danger', 'Data o ora non valida.');
|
|
header('Location: book.php?shop=' . $shop_id . '&service=' . $service_id);
|
|
exit;
|
|
}
|
|
|
|
try {
|
|
// --- 3. Servizio → durata per calcolare end_at ---
|
|
$svc = $pdo->prepare("SELECT duration_minutes FROM services WHERE id = ? AND shop_id = ? AND is_active = 1");
|
|
$svc->execute([$service_id, $shop_id]);
|
|
$service = $svc->fetch(PDO::FETCH_ASSOC);
|
|
if (!$service) {
|
|
setFlash('danger', 'Servizio non disponibile.');
|
|
header('Location: search.php');
|
|
exit;
|
|
}
|
|
$duration = (int)$service['duration_minutes'] ?: 30;
|
|
$endDT = (clone $startDT)->modify("+{$duration} minutes");
|
|
|
|
$startStr = $startDT->format('Y-m-d H:i:s');
|
|
$endStr = $endDT->format('Y-m-d H:i:s');
|
|
|
|
// --- 4. Ricontrollo lato server che lo slot sia ancora libero ---
|
|
// (fondamentale: tra quando l'utente ha visto lo slot e ora, qualcuno
|
|
// potrebbe averlo preso. Non ci fidiamo solo del JS.)
|
|
$clash = $pdo->prepare("
|
|
SELECT id FROM appointments
|
|
WHERE staff_id = ?
|
|
AND status IN ('pending','confirmed','completed')
|
|
AND start_at < ? AND end_at > ?
|
|
LIMIT 1
|
|
");
|
|
$clash->execute([$staff_id, $endStr, $startStr]);
|
|
if ($clash->fetch()) {
|
|
setFlash('warning', 'Spiacenti, questo orario è appena stato prenotato. Scegline un altro.');
|
|
header('Location: book.php?shop=' . $shop_id . '&service=' . $service_id . '&staff=' . $staff_id . '&date=' . urlencode($date));
|
|
exit;
|
|
}
|
|
|
|
// --- 5. Trova o crea il customer di QUESTO salone per l'utente loggato ---
|
|
// customers è per-salone (multi-tenant): stesso utente = record diverso per ogni shop.
|
|
$cust = $pdo->prepare("SELECT id FROM customers WHERE shop_id = ? AND user_id = ? LIMIT 1");
|
|
$cust->execute([$shop_id, $user_id]);
|
|
$customerRow = $cust->fetch(PDO::FETCH_ASSOC);
|
|
|
|
if ($customerRow) {
|
|
$customer_id = (int)$customerRow['id'];
|
|
} else {
|
|
// Recupero i dati anagrafici dall'utente loggato per popolare il customer.
|
|
// Nomi colonne reali di auth_users: first_name, last_name, phone, email.
|
|
$u = $pdo->prepare("SELECT first_name, last_name, email, phone FROM auth_users WHERE id = ?");
|
|
$u->execute([$user_id]);
|
|
$urow = $u->fetch(PDO::FETCH_ASSOC) ?: [];
|
|
|
|
// customers.phone è NOT NULL: se l'utente non ha telefono usiamo stringa vuota,
|
|
// così l'INSERT non fallisce. Lo aggiornerà poi lui/il salone.
|
|
$ins = $pdo->prepare("
|
|
INSERT INTO customers (shop_id, user_id, first_name, last_name, phone, email)
|
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
");
|
|
$ins->execute([
|
|
$shop_id,
|
|
$user_id,
|
|
($urow['first_name'] ?? '') !== '' ? $urow['first_name'] : 'Cliente',
|
|
$urow['last_name'] ?? '',
|
|
$urow['phone'] ?? '', // NOT NULL → mai null
|
|
$urow['email'] ?? null, // email ammette NULL, ok
|
|
]);
|
|
$customer_id = (int)$pdo->lastInsertId();
|
|
}
|
|
|
|
// --- 6. Inserimento appuntamento (stato pending: il salone deve confermare) ---
|
|
$insAppt = $pdo->prepare("
|
|
INSERT INTO appointments (shop_id, customer_id, staff_id, service_id, start_at, end_at, status, notes)
|
|
VALUES (?, ?, ?, ?, ?, ?, 'pending', ?)
|
|
");
|
|
$insAppt->execute([
|
|
$shop_id,
|
|
$customer_id,
|
|
$staff_id,
|
|
$service_id,
|
|
$startStr,
|
|
$endStr,
|
|
($notes !== '' ? $notes : null)
|
|
]);
|
|
|
|
setFlash('success', 'Prenotazione inviata! Il salone la confermerà a breve. La trovi in "I miei appuntamenti".');
|
|
header('Location: userarea/my_appointments.php');
|
|
exit;
|
|
} catch (Throwable $e) {
|
|
error_log('book_save: ' . $e->getMessage());
|
|
setFlash('danger', 'Si è verificato un errore durante la prenotazione. Riprova.');
|
|
header('Location: book.php?shop=' . $shop_id . '&service=' . $service_id);
|
|
exit;
|
|
}
|