false,'error'=>'Method not allowed'], 405); } $name = trim($_POST['name'] ?? ''); $email = trim($_POST['email'] ?? ''); $phone = trim($_POST['phone'] ?? ''); $dob = trim($_POST['dob'] ?? ''); $method = trim($_POST['payment_method'] ?? 'cash'); $notes = trim($_POST['notes'] ?? ''); $startD = trim($_POST['start_date'] ?? ''); $endD = trim($_POST['end_date'] ?? ''); // Validate required fields if (!$name || !$email || !$phone || !$dob || !$startD || !$endD) { jsonOut(['success'=>false,'error'=>'All fields are required.']); } if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { jsonOut(['success'=>false,'error'=>'Invalid email address.']); } // Validate dates $start = DateTime::createFromFormat('Y-m-d', $startD); $end = DateTime::createFromFormat('Y-m-d', $endD); if (!$start || !$end || $end <= $start) { jsonOut(['success'=>false,'error'=>'Invalid dates selected.']); } $days = (int)$start->diff($end)->days; $minDays = (int)(getSetting('min_days', 1)); $maxDays = (int)(getSetting('max_days', 3)); if ($days < $minDays || $days > $maxDays) { jsonOut(['success'=>false,'error'=>"Rental must be $minDays-$maxDays days."]); } // Validate age (25+) $dobDt = DateTime::createFromFormat('Y-m-d', $dob); if (!$dobDt) jsonOut(['success'=>false,'error'=>'Invalid date of birth.']); $age = (new DateTime())->diff($dobDt)->y; if ($age < 25) { jsonOut(['success'=>false,'error'=>'Must be 25 or older to rent.']); } // Check availability $conflict = db()->prepare(" SELECT COUNT(*) FROM pcs_bookings WHERE status IN ('approved','active','pending') AND NOT (end_date <= :start OR start_date >= :end) "); $conflict->execute(['start'=>$startD,'end'=>$endD]); if ($conflict->fetchColumn() > 0) { jsonOut(['success'=>false,'error'=>'Those dates are no longer available.']); } // Check blocked dates $cursor = clone $start; while ($cursor <= $end) { $blocked = db()->prepare("SELECT id FROM pcs_blocked_dates WHERE blocked_date=?"); $blocked->execute([$cursor->format('Y-m-d')]); if ($blocked->fetch()) { jsonOut(['success'=>false,'error'=>'One or more selected dates are unavailable.']); } $cursor->modify('+1 day'); } // Handle file uploads $licenseFile = null; $insuranceFile = null; function saveUpload(string $field, string $dir, string $prefix): ?string { if (empty($_FILES[$field]['tmp_name'])) return null; $file = $_FILES[$field]; $allowed = ['image/jpeg','image/png','image/gif','image/webp','application/pdf']; if (!in_array($file['type'], $allowed)) return null; if ($file['size'] > MAX_UPLOAD_BYTES) return null; if (!is_dir($dir)) mkdir($dir, 0755, true); $ext = pathinfo($file['name'], PATHINFO_EXTENSION); $name = $prefix.'_'.time().'_'.bin2hex(random_bytes(4)).'.'.$ext; $path = $dir.$name; move_uploaded_file($file['tmp_name'], $path); return $name; } $licenseFile = saveUpload('license', LICENSE_DIR, 'lic'); $insuranceFile = saveUpload('insurance', INSURANCE_DIR, 'ins'); if (!$licenseFile) { jsonOut(['success'=>false,'error'=>"Driver's license upload failed. Check file type and size."]); } if (!$insuranceFile) { jsonOut(['success'=>false,'error'=>'Insurance upload failed. Check file type and size.']); } // Create or find customer $customer = db()->prepare("SELECT id FROM pcs_customers WHERE email=?"); $customer->execute([$email]); $cust = $customer->fetch(); if ($cust) { $custId = $cust['id']; db()->prepare("UPDATE pcs_customers SET name=?,phone=?,dob=? WHERE id=?") ->execute([$name,$phone,$dob,$custId]); } else { db()->prepare("INSERT INTO pcs_customers (email,name,phone,dob) VALUES (?,?,?,?)") ->execute([$email,$name,$phone,$dob]); $custId = db()->lastInsertId(); } // Generate booking ref $ref = 'PSR-'.strtoupper(bin2hex(random_bytes(3))); $pricePerDay = (float)(getSetting('price_per_day', 150)); $total = $days * $pricePerDay; // Get available fleet $fleet = db()->query("SELECT id FROM pcs_fleet WHERE is_available=1 LIMIT 1")->fetch(); $fleetId = $fleet['id'] ?? null; // Insert booking db()->prepare("INSERT INTO pcs_bookings (booking_ref,customer_id,fleet_id,start_date,end_date,days,price_per_day,total_amount, payment_method,license_file,insurance_file,customer_notes) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)")->execute([ $ref, $custId, $fleetId, $startD, $endD, $days, $pricePerDay, $total, $method, $licenseFile, $insuranceFile, $notes ]); jsonOut(['success'=>true,'ref'=>$ref,'message'=>'Reservation submitted successfully.']);