prepare("SELECT setting_value FROM company_settings WHERE company_id = ? AND setting_key = ?");
$stmt->bind_param("is", $companyId, $key);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
$stmt->close();
return $row ? $row['setting_value'] : $default;
}
$baseMileageRate = (float) getCompanySetting($conn, $companyId, 'base_mileage_rate', '2.00');
$baseHireRate = (float) getCompanySetting($conn, $companyId, 'base_hire_rate', '3.50');
$airportSurcharge = (float) getCompanySetting($conn, $companyId, 'airport_surcharge', '5.00');
$autoDispatchEnabled = getCompanySetting($conn, $companyId, 'auto_dispatch_enabled', '0') == '1';
$autoDispatchMaxDistance = (int) getCompanySetting($conn, $companyId, 'auto_dispatch_max_distance', '10');
if ($editId > 0) {
$stmt = $conn->prepare("SELECT * FROM bookings WHERE id = ? AND company_id = ?");
$stmt->bind_param("ii", $editId, $companyId);
$stmt->execute();
$result = $stmt->get_result();
$editBooking = $result->fetch_assoc();
$stmt->close();
}
if ($copyId > 0 && !$editBooking) {
$stmt = $conn->prepare("SELECT * FROM bookings WHERE id = ? AND company_id = ?");
$stmt->bind_param("ii", $copyId, $companyId);
$stmt->execute();
$result = $stmt->get_result();
$copyData = $result->fetch_assoc();
if ($copyData) {
unset($copyData['id']);
$copyData['status'] = 'pending_dispatch';
$editBooking = $copyData;
}
$stmt->close();
}
$drivers = [];
$stmt = $conn->prepare("SELECT id, driver_name, callsign, status, latitude, longitude, current_zone, phone FROM drivers WHERE company_id = ? AND is_active = 1 ORDER BY driver_name");
$stmt->bind_param("i", $companyId);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) { $drivers[] = $row; }
$stmt->close();
$vehicles = [];
$stmt = $conn->prepare("SELECT id, callsign FROM vehicles WHERE company_id = ? AND status = 'active' ORDER BY callsign");
$stmt->bind_param("i", $companyId);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) { $vehicles[] = $row; }
$stmt->close();
$accounts = [];
$stmt = $conn->prepare("SELECT id, company_name FROM accounts WHERE company_id = ? AND status = 'active' ORDER BY company_name");
$stmt->bind_param("i", $companyId);
$stmt->execute();
$result = $stmt->get_result();
while ($row = $result->fetch_assoc()) { $accounts[] = $row; }
$stmt->close();
$zones = [];
$zoneStmt = $conn->prepare("SELECT zone_code, zone_name FROM zones WHERE company_id = ? AND is_active = 1");
$zoneStmt->bind_param("i", $companyId);
$zoneStmt->execute();
$zoneResult = $zoneStmt->get_result();
while ($zoneRow = $zoneResult->fetch_assoc()) { $zones[] = $zoneRow; }
$zoneStmt->close();
$vehicleTypes = ['Saloon', 'Estate', 'MPV6', 'MPV8', 'Hatchback', 'SUV', 'Minibus', 'Luxury', 'Executive', 'WAV'];
$priorityOptions = ['Low', 'Medium', 'High', 'Emergency'];
$luggageOptions = range(0, 15);
$passengerOptions = range(1, 20);
$repeatDaysList = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun', 'Fortnightly'];
$capabilitiesList = [
'wheelchair' => '♿ Wheelchair Access',
'child_seat' => '👶 Child Seat',
'pet_friendly' => '🐾 Pet Friendly',
'xl_luggage' => '🧳 XL Luggage',
'airport_meet' => '✈️ Airport Meet',
'corporate' => '🏢 Corporate',
'female_driver' => '👩 Female Driver',
'dbs_checked' => '✅ DBS Checked',
'multilingual' => '🌍 Multilingual'
];
function autoAssignDriver($pickup_lat, $pickup_lng, $companyId, $conn, $maxDistance = 10) {
$stmt = $conn->prepare("
SELECT id, driver_name, callsign, status,
( 6371 * acos( GREATEST(-1, LEAST(1,
cos(radians(?)) * cos(radians(COALESCE(latitude, 0))) *
cos(radians(COALESCE(longitude, 0)) - radians(?)) +
sin(radians(?)) * sin(radians(COALESCE(latitude, 0)))
)) ) ) AS distance_km
FROM drivers
WHERE company_id = ? AND status = 'available' AND latitude IS NOT NULL AND longitude IS NOT NULL
HAVING distance_km < ?
ORDER BY distance_km ASC LIMIT 1
");
$stmt->bind_param("dddid", $pickup_lat, $pickup_lng, $pickup_lat, $companyId, $maxDistance);
$stmt->execute();
$result = $stmt->get_result();
$driver = $result->fetch_assoc();
$stmt->close();
return $driver;
}
$isAjax = isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$response = ['success' => false, 'message' => '', 'booking_ref' => ''];
try {
$bookingRef = $editBooking['booking_ref'] ?? 'BK-' . date('Ymd') . '-' . rand(100, 999);
$bookingDate = !empty($_POST['booking_date']) ? $_POST['booking_date'] : date('Y-m-d');
$bookingTime = !empty($_POST['booking_time']) ? $_POST['booking_time'] : date('H:i:s');
$assigned_driver_id = !empty($_POST['driver_id']) ? $_POST['driver_id'] : null;
if ($autoDispatchEnabled && !$assigned_driver_id && !empty($_POST['pickup_lat']) && !empty($_POST['pickup_lng'])) {
$nearestDriver = autoAssignDriver($_POST['pickup_lat'], $_POST['pickup_lng'], $companyId, $conn, $autoDispatchMaxDistance);
if ($nearestDriver) $assigned_driver_id = $nearestDriver['id'];
}
$capabilities_selected = isset($_POST['capabilities']) && is_array($_POST['capabilities']) ? implode(',', $_POST['capabilities']) : '';
if (isset($_POST['booking_id']) && !empty($_POST['booking_id'])) {
$stmt = $conn->prepare("UPDATE bookings SET
customer_name = ?, customer_phone = ?, email = ?, pickup_address = ?, pickup_note = ?,
destination = ?, dest_note = ?, via = ?, via_note = ?, vehicle_type = ?, driver_id = ?,
price = ?, distance = ?, duration = ?, notes = ?, office_note = ?, company = ?,
your_ref = ?, our_ref = ?, priority = ?, flight_number = ?, flight_origin = ?,
passengers = ?, suitcases = ?, capabilities = ?, repeat_days = ?, repeat_end = ?,
return_time = ?, account_id = ?, loyalty_card = ?, booking_date = ?, booking_time = ?,
pickup_time = ?, status = ?, pickup_lat = ?, pickup_lng = ?, zone_code = ?,
updated_at = NOW()
WHERE id = ? AND company_id = ?");
$stmt->bind_param(
"ssssssssssiddssssssssiississsssssddssii",
$_POST['customer_name'], $_POST['customer_phone'], $_POST['email'],
$_POST['pickup_address'], $_POST['pickup_note'], $_POST['destination'],
$_POST['dest_note'], $_POST['via'], $_POST['via_note'], $_POST['vehicle_type'],
$assigned_driver_id, $_POST['price'], $_POST['distance'], $_POST['duration'],
$_POST['notes'], $_POST['office_note'], $_POST['company'], $_POST['your_ref'],
$_POST['our_ref'], $_POST['priority'], $_POST['flight_number'], $_POST['flight_origin'],
$_POST['passengers'], $_POST['luggage'], $capabilities_selected, $_POST['repeat_days'],
$_POST['repeat_end'], $_POST['return_time'], $_POST['account_id'] ?: null,
$_POST['loyalty_card'], $bookingDate, $bookingTime, $bookingTime, $_POST['status'],
$_POST['pickup_lat'] ?? null, $_POST['pickup_lng'] ?? null, $_POST['zone_code'] ?? 'TWN',
$_POST['booking_id'], $companyId
);
if (!$stmt->execute()) throw new Exception($stmt->error);
$response = ['success' => true, 'message' => 'Booking updated successfully', 'booking_ref' => $bookingRef];
} else {
$now = date('Y-m-d H:i:s');
$pendingStatus = 'pending_dispatch';
$defaultZone = 'TWN';
$sql = "INSERT INTO bookings SET
company_id = ?, booking_ref = ?, customer_name = ?, customer_phone = ?, email = ?,
pickup_address = ?, pickup_note = ?, destination = ?, dest_note = ?, via = ?, via_note = ?,
vehicle_type = ?, driver_id = ?, price = ?, distance = ?, duration = ?, notes = ?,
office_note = ?, company = ?, your_ref = ?, our_ref = ?, priority = ?, flight_number = ?,
flight_origin = ?, passengers = ?, suitcases = ?, capabilities = ?, repeat_days = ?,
repeat_end = ?, return_time = ?, account_id = ?, loyalty_card = ?, booking_date = ?,
booking_time = ?, pickup_time = ?, status = ?, pickup_lat = ?, pickup_lng = ?, zone_code = ?,
created_at = ?, updated_at = ?";
$stmt = $conn->prepare($sql);
if (!$stmt) throw new Exception("Prepare failed: " . $conn->error);
$values = [
$companyId, $bookingRef,
!empty($_POST['customer_name']) ? $_POST['customer_name'] : '',
!empty($_POST['customer_phone']) ? $_POST['customer_phone'] : '',
!empty($_POST['email']) ? $_POST['email'] : '',
!empty($_POST['pickup_address']) ? $_POST['pickup_address'] : '',
!empty($_POST['pickup_note']) ? $_POST['pickup_note'] : '',
!empty($_POST['destination']) ? $_POST['destination'] : '',
!empty($_POST['dest_note']) ? $_POST['dest_note'] : '',
!empty($_POST['via']) ? $_POST['via'] : '',
!empty($_POST['via_note']) ? $_POST['via_note'] : '',
!empty($_POST['vehicle_type']) ? $_POST['vehicle_type'] : '',
$assigned_driver_id,
!empty($_POST['price']) ? (float)$_POST['price'] : 0,
!empty($_POST['distance']) ? (float)$_POST['distance'] : 0,
!empty($_POST['duration']) ? (int)$_POST['duration'] : 0,
!empty($_POST['notes']) ? $_POST['notes'] : '',
!empty($_POST['office_note']) ? $_POST['office_note'] : '',
!empty($_POST['company']) ? $_POST['company'] : '',
!empty($_POST['your_ref']) ? $_POST['your_ref'] : '',
!empty($_POST['our_ref']) ? $_POST['our_ref'] : '',
!empty($_POST['priority']) ? $_POST['priority'] : 'Medium',
!empty($_POST['flight_number']) ? $_POST['flight_number'] : '',
!empty($_POST['flight_origin']) ? $_POST['flight_origin'] : '',
!empty($_POST['passengers']) ? (int)$_POST['passengers'] : 1,
!empty($_POST['luggage']) ? (int)$_POST['luggage'] : 0,
$capabilities_selected,
!empty($_POST['repeat_days']) ? $_POST['repeat_days'] : '',
!empty($_POST['repeat_end']) ? $_POST['repeat_end'] : null,
!empty($_POST['return_time']) ? $_POST['return_time'] : null,
!empty($_POST['account_id']) ? (int)$_POST['account_id'] : null,
!empty($_POST['loyalty_card']) ? $_POST['loyalty_card'] : '',
!empty($_POST['booking_date']) ? $_POST['booking_date'] : date('Y-m-d'),
!empty($_POST['booking_time']) ? $_POST['booking_time'] : date('H:i:s'),
!empty($_POST['booking_time']) ? $_POST['booking_time'] : date('H:i:s'),
$pendingStatus,
isset($_POST['pickup_lat']) && $_POST['pickup_lat'] !== '' ? (float)$_POST['pickup_lat'] : null,
isset($_POST['pickup_lng']) && $_POST['pickup_lng'] !== '' ? (float)$_POST['pickup_lng'] : null,
!empty($_POST['zone_code']) ? $_POST['zone_code'] : $defaultZone,
$now, $now
];
$types = '';
foreach ($values as $v) {
if (is_int($v)) $types .= 'i';
elseif (is_float($v)) $types .= 'd';
else $types .= 's';
}
$stmt->bind_param($types, ...$values);
if (!$stmt->execute()) throw new Exception($stmt->error);
$response = ['success' => true, 'message' => 'Booking created successfully', 'booking_ref' => $bookingRef];
}
$stmt->close();
} catch(Exception $e) {
$response = ['success' => false, 'message' => $e->getMessage()];
error_log("Booking error: " . $e->getMessage());
}
if ($isAjax) {
header('Content-Type: application/json');
echo json_encode($response);
exit;
} else {
$savedBookingRef = $response['success'] ? $response['booking_ref'] : '';
$message = $response['message'];
$messageType = $response['success'] ? 'success' : 'error';
}
}
$savedCapabilities = [];
if (!empty($editBooking['capabilities'])) $savedCapabilities = explode(',', $editBooking['capabilities']);
$currentDate = date('Y-m-d');
$currentTime = date('H:i');
$roleDisplay = ucfirst(str_replace('_', ' ', $userType));
$availableCount = count(array_filter($drivers, fn($d) => $d['status'] == 'available'));
$busyCount = count(array_filter($drivers, fn($d) => $d['status'] == 'busy'));
$totalCount = count($drivers);
?>
FleetCortex -
VEH0ETA0min
AVERAGE ETA0min
Distance mi
Duration min
Price£
Tariff: Mileage £/mi + Hire £
Enter a phone number to check active bookings
Press F2 or click the history button after entering a phone number
No Booking History
Enter a phone number to view booking history
No Favorites
No saved addresses found
No Call Records
No call details found
No Change History
No audit trail found
No Transactions
No payment records found
🖼️
NO PICTURES ATTACHED
No images uploaded for this journey
📞 PhoneNo phone
✉️ EmailNo email
F2 Passenger HistoryF9 Tariff & Estimation
Insert Manual Price OverrideAlt+A Add Via / Mid-Trip Stop
Alt+R Return JourneyCtrl+Enter Save & Book
Home Save & HoldEnd Save & Book
Ctrl+P Call PassengerCtrl+D Call Driver
Ctrl+H Passenger HistoryEsc Close / Clear Form