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; }