query("SELECT MAX(CAST(SUBSTRING(booking_id, 4) AS UNSIGNED)) as max_id FROM bookings"); $row = $result->fetch_assoc(); $next_id = ($row['max_id'] ?? 9999) + 1; return $prefix . str_pad($next_id, 5, '0', STR_PAD_LEFT); } function generateInvoiceNo() { global $conn; $prefix = 'INV-'; $result = $conn->query("SELECT MAX(CAST(SUBSTRING(invoice_no, 5) AS UNSIGNED)) as max_id FROM invoices"); $row = $result->fetch_assoc(); $next_id = ($row['max_id'] ?? 9999) + 1; return $prefix . str_pad($next_id, 5, '0', STR_PAD_LEFT); } function isLoggedIn() { return isset($_SESSION['customer_id']); } function isAdmin() { return isset($_SESSION['admin_id']); } function redirect($url) { header("Location: $url"); exit; } function getSetting($key) { global $conn; $stmt = $conn->prepare("SELECT setting_value FROM settings WHERE setting_key = ?"); $stmt->bind_param("s", $key); $stmt->execute(); $result = $stmt->get_result(); $row = $result->fetch_assoc(); return $row ? $row['setting_value'] : ''; } function getCategories() { global $conn; $result = $conn->query("SELECT * FROM categories WHERE status = 1 ORDER BY sort_order ASC"); return $result->fetch_all(MYSQLI_ASSOC); } function getPackages($category_id = null) { global $conn; $sql = "SELECT p.*, c.name as category_name FROM packages p JOIN categories c ON p.category_id = c.id WHERE p.status = 1"; if ($category_id) { $sql .= " AND p.category_id = " . intval($category_id); } $sql .= " ORDER BY p.sort_order ASC, p.id DESC"; $result = $conn->query($sql); return $result->fetch_all(MYSQLI_ASSOC); } function getFeaturedPackages() { global $conn; $result = $conn->query("SELECT p.*, c.name as category_name FROM packages p JOIN categories c ON p.category_id = c.id WHERE p.status = 1 AND p.is_featured = 1 ORDER BY p.sort_order ASC LIMIT 3"); return $result->fetch_all(MYSQLI_ASSOC); } function getPackage($id) { global $conn; $stmt = $conn->prepare("SELECT p.*, c.name as category_name, c.slug as category_slug FROM packages p JOIN categories c ON p.category_id = c.id WHERE p.id = ?"); $stmt->bind_param("i", $id); $stmt->execute(); return $stmt->get_result()->fetch_assoc(); } function getPackageImages($package_id) { global $conn; $stmt = $conn->prepare("SELECT * FROM package_images WHERE package_id = ? ORDER BY sort_order ASC"); $stmt->bind_param("i", $package_id); $stmt->execute(); return $stmt->get_result()->fetch_all(MYSQLI_ASSOC); } function getGallery($category_id = null) { global $conn; $sql = "SELECT g.*, c.name as category_name FROM gallery g LEFT JOIN categories c ON g.category_id = c.id WHERE g.status = 1"; if ($category_id) { $sql .= " AND g.category_id = " . intval($category_id); } $sql .= " ORDER BY g.sort_order ASC, g.id DESC"; return $conn->query($sql)->fetch_all(MYSQLI_ASSOC); } function getRentalItems($category_id = null) { global $conn; $sql = "SELECT r.*, rc.name as category_name FROM rental_items r JOIN rental_categories rc ON r.category_id = rc.id WHERE r.status = 1"; if ($category_id) { $sql .= " AND r.category_id = " . intval($category_id); } $sql .= " ORDER BY r.id DESC"; return $conn->query($sql)->fetch_all(MYSQLI_ASSOC); } function getRentalCategories() { global $conn; return $conn->query("SELECT * FROM rental_categories WHERE status = 1 ORDER BY sort_order ASC")->fetch_all(MYSQLI_ASSOC); } function getTestimonials() { global $conn; return $conn->query("SELECT * FROM testimonials WHERE status = 1 ORDER BY sort_order ASC, id DESC")->fetch_all(MYSQLI_ASSOC); } function getBookings($customer_id = null) { global $conn; $sql = "SELECT b.*, p.name as package_name, c.name as category_name FROM bookings b JOIN packages p ON b.package_id = p.id JOIN categories c ON b.category_id = c.id"; if ($customer_id) { $sql .= " WHERE b.customer_id = " . intval($customer_id); } $sql .= " ORDER BY b.created_at DESC"; return $conn->query($sql)->fetch_all(MYSQLI_ASSOC); } function getBooking($id) { global $conn; $stmt = $conn->prepare("SELECT b.*, p.name as package_name, p.description as package_description, c.name as category_name, cu.name as customer_name, cu.email as customer_email, cu.phone as customer_phone FROM bookings b JOIN packages p ON b.package_id = p.id JOIN categories c ON b.category_id = c.id JOIN customers cu ON b.customer_id = cu.id WHERE b.id = ?"); $stmt->bind_param("i", $id); $stmt->execute(); return $stmt->get_result()->fetch_assoc(); } function getCustomer($id) { global $conn; $stmt = $conn->prepare("SELECT * FROM customers WHERE id = ?"); $stmt->bind_param("i", $id); $stmt->execute(); return $stmt->get_result()->fetch_assoc(); } function getPayments($booking_id) { global $conn; $stmt = $conn->prepare("SELECT * FROM payments WHERE booking_id = ? ORDER BY created_at DESC"); $stmt->bind_param("i", $booking_id); $stmt->execute(); return $stmt->get_result()->fetch_all(MYSQLI_ASSOC); } function getNotifications($user_type = 'admin', $limit = 5) { global $conn; $stmt = $conn->prepare("SELECT * FROM notifications WHERE user_type = ? ORDER BY created_at DESC LIMIT ?"); $stmt->bind_param("si", $user_type, $limit); $stmt->execute(); return $stmt->get_result()->fetch_all(MYSQLI_ASSOC); } function getUnreadNotifications($user_type = 'admin') { global $conn; $stmt = $conn->prepare("SELECT COUNT(*) as count FROM notifications WHERE user_type = ? AND is_read = 0"); $stmt->bind_param("s", $user_type); $stmt->execute(); return $stmt->get_result()->fetch_assoc()['count']; } function addNotification($title, $message, $link = null, $user_type = 'admin', $user_id = null) { global $conn; $stmt = $conn->prepare("INSERT INTO notifications (user_id, user_type, title, message, link) VALUES (?, ?, ?, ?, ?)"); $stmt->bind_param("issss", $user_id, $user_type, $title, $message, $link); return $stmt->execute(); } function addActivityLog($action, $description, $user_type = null, $user_id = null) { global $conn; $ip = $_SERVER['REMOTE_ADDR'] ?? ''; $ua = $_SERVER['HTTP_USER_AGENT'] ?? ''; $stmt = $conn->prepare("INSERT INTO activity_logs (user_id, user_type, action, description, ip_address, user_agent) VALUES (?, ?, ?, ?, ?, ?)"); $stmt->bind_param("isssss", $user_id, $user_type, $action, $description, $ip, $ua); return $stmt->execute(); } function timeAgo($datetime) { $time = strtotime($datetime); $diff = time() - $time; $units = [ 31536000 => 'year', 2592000 => 'month', 604800 => 'week', 86400 => 'day', 3600 => 'hour', 60 => 'minute', 1 => 'second' ]; foreach ($units as $unit => $name) { if ($diff >= $unit) { $count = floor($diff / $unit); return $count . ' ' . $name . ($count > 1 ? 's' : '') . ' ago'; } } return 'just now'; } function formatPrice($amount) { $currency = getSetting('currency') ?: 'LKR'; return $currency . ' ' . number_format($amount, 2); } function getStatusBadge($status) { $badges = [ 'pending' => 'warning', 'approved' => 'info', 'waiting_payment' => 'secondary', 'confirmed' => 'primary', 'completed' => 'success', 'cancelled' => 'danger', 'advance_paid' => 'info', 'full_paid' => 'success', 'available' => 'success', 'blocked' => 'danger', 'holiday' => 'warning', 'booked' => 'secondary', ]; $class = $badges[$status] ?? 'secondary'; return "{$status}"; } function uploadFile($file, $target_dir, $allowed_types = ['jpg','jpeg','png','gif','webp']) { $ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION)); if (!in_array($ext, $allowed_types)) return false; $filename = time() . '_' . bin2hex(random_bytes(8)) . '.' . $ext; $target_file = $target_dir . $filename; if (move_uploaded_file($file['tmp_name'], $target_file)) return $filename; return false; } function checkAvailability($date) { global $conn; $stmt = $conn->prepare("SELECT * FROM availability WHERE date = ?"); $stmt->bind_param("s", $date); $stmt->execute(); $result = $stmt->get_result()->fetch_assoc(); if ($result && $result['status'] !== 'available') return false; $stmt = $conn->prepare("SELECT COUNT(*) as count FROM bookings WHERE event_date = ? AND status NOT IN ('cancelled')"); $stmt->bind_param("s", $date); $stmt->execute(); $count = $stmt->get_result()->fetch_assoc()['count']; return $count < 3; }