params); // Check permissions $user = Eva::make('user'); $restaurant_id = $request->tenant; if (!$user->canCreate('bookings', $restaurant_id)) { return $this->response([ 'success' => false, 'error' => __eva("You cannot create bookings.") ]); } try { // Process booking data $params = $request->params; // Check if booking already exists to prevent duplicates if ($this->bookingExists($params, $restaurant_id)) { return $this->response([ 'success' => true, 'is_duplicate' => true, 'message' => __eva('Booking already exists') ]); } // Create new booking $booking = new Booking(); $booking->restaurant_id = $restaurant_id; // Get or create customer based on email $customer_id = $this->findOrCreateCustomer($params, $restaurant_id); // Set up the booking $booking->customer_id = $customer_id; $booking = $this->populateBookingData($booking, $params); $booking->save(); // Process tags if provided if (isset($params['tags']) && !empty($params['tags'])) { // Convert tag names to tag IDs before syncing $tag_ids = $this->mapTagsToIds($params['tags'], $restaurant_id); if (!empty($tag_ids)) { $booking->tags()->sync($tag_ids); } } // Process tables if provided if (isset($params['tables']) && !empty($params['tables'])) { $tables = json_decode($params['tables']); if (!empty($tables)) { $booking->syncTablesAndKeepCurrentSeats($tables); } } // Trigger event evavel_event(new EventBookingCreated($booking, $user)); return $this->response([ 'success' => true, 'booking' => $booking, 'message' => __eva('Booking imported successfully') ]); } catch (\Exception $e) { return $this->response([ 'success' => false, 'error' => __eva('Error importing booking: ') . $e->getMessage() ]); } } /** * Check if a booking with the same email, date, time already exists to prevent duplicates * * @param array $params * @param int $restaurant_id * @return bool */ private function bookingExists($params, $restaurant_id) { $email = isset($params['email']) && !empty($params['email']) ? $params['email'] : null; $date = isset($params['date']) ? $params['date'] : date('Y-m-d'); $time = isset($params['slot']) ? intval($params['slot']) : 0; $party = isset($params['party']) ? intval($params['party']) : 1; // If no email, generate a predictable one that would match what we'd create if (!$email) { $first_name = isset($params['first_name']) ? $params['first_name'] : 'customer'; $email = $this->generatePredictableEmail($first_name, $date, $time, $party); } // Look for an existing booking with the same key attributes $existing = Booking::where('restaurant_id', $restaurant_id) ->where('email', $email) ->where('date', $date) ->where('time', $time) ->first(); return $existing !== null; } /** * Generate a predictable email based on first_name, date, time and party size * * @param string $name * @param string $date * @param int $time * @param int $party * @return string */ private function generatePredictableEmail($name, $date, $time, $party) { // Clean the name $slug_name = Str::slug($name); // Format date without dashes $date_formatted = str_replace('-', '', $date); // Format time as hour-minute $hour = floor($time / 3600); $minute = floor(($time % 3600) / 60); $time_formatted = sprintf('%02d%02d', $hour, $minute); // Generate a consistent email return $slug_name . '_' . $date_formatted . '_' . $time_formatted . '_p' . $party . '@fake'; } /** * Find existing customer or create a new one based on import data * * @param array $params * @param int $restaurant_id * @return int|null */ private function findOrCreateCustomer($params, $restaurant_id) { // Check if we have an email $email = isset($params['email']) && !empty($params['email']) ? $params['email'] : null; if (!$email) { // Generate a predictable fake email for the customer if none provided $name = isset($params['first_name']) ? $params['first_name'] : 'customer'; $date = isset($params['date']) ? $params['date'] : date('Y-m-d'); $time = isset($params['slot']) ? intval($params['slot']) : 0; $party = isset($params['party']) ? intval($params['party']) : 1; $email = $this->generatePredictableEmail($name, $date, $time, $party); } // Look for existing customer $customer = Customer::where('restaurant_id', $restaurant_id) ->where('email', $email) ->first(); // If customer exists, return its ID if ($customer) { return $customer->id; } // Otherwise create a new customer $first_name = isset($params['first_name']) ? $params['first_name'] : ''; $last_name = isset($params['last_name']) ? $params['last_name'] : ''; $phone = isset($params['phone']) ? $params['phone'] : ''; $dial_code = isset($params['dial_code']) ? $params['dial_code'] : ''; $dial_code_country = isset($params['dial_code_country']) ? $params['dial_code_country'] : ''; $country_code = isset($params['country_code']) ? $params['country_code'] : ''; // If no first/last name but we have a name parameter if (empty($first_name) && empty($last_name) && isset($params['name']) && !empty($params['name'])) { $name_parts = explode(' ', $params['name'], 2); $first_name = $name_parts[0]; $last_name = isset($name_parts[1]) ? $name_parts[1] : ''; } // If still no name, set a default if (empty($first_name) && empty($last_name)) { $first_name = 'Imported'; $last_name = 'Customer'; } // Create the customer $customer = Customer::create([ 'restaurant_id' => $restaurant_id, 'first_name' => $first_name, 'last_name' => $last_name, 'name' => $first_name . ' ' . $last_name, 'email' => $email, 'phone' => $phone, 'dial_code' => $dial_code, 'dial_code_country' => $dial_code_country, 'country_code' => $country_code, 'language' => isset($params['language']) ? $params['language'] : 'en', 'agree_receive_email' => 1, 'agree_receive_sms' => !empty($phone) ? 1 : 0, ]); return $customer->id; } /** * Map tag names to tag IDs, creating any tags that don't exist yet * * @param string $tags_string Comma-separated string of tag names * @param int $restaurant_id Restaurant ID * @return array Array of tag IDs */ private function mapTagsToIds($tags_string, $restaurant_id) { // If tags is already a JSON array, decode it, otherwise split by comma if ($this->isJson($tags_string)) { $tags = json_decode($tags_string); } else { $tags = explode(',', $tags_string); } // Clean up the array $tags = array_map('trim', $tags); $tags = array_filter($tags, function($tag) { return !empty($tag); }); if (empty($tags)) { return []; } // Check for an "Imported" tag group or create one $tag_group = BTagGroup::where('name', 'Imported') ->where('restaurant_id', $restaurant_id) ->first(); if (!$tag_group) { $tag_group = BTagGroup::create([ 'restaurant_id' => $restaurant_id, 'name' => 'Imported', 'ordering' => 999, 'color' => '#FFFFFF', 'backcolor' => '#000000', 'is_deletable' => 1, 'is_vip' => 0, 'notes' => '' ]); } // Process each tag $tag_ids = []; foreach ($tags as $tag_name) { // Try to find existing tag $tag = BTag::where('name', $tag_name) ->where('group_id', $tag_group->id) ->first(); // Create tag if it doesn't exist if (!$tag) { $tag = BTag::create([ 'restaurant_id' => $restaurant_id, 'group_id' => $tag_group->id, 'name' => $tag_name, 'ordering' => 999, 'is_deletable' => 1, 'notes' => '' ]); } $tag_ids[] = $tag->id; } return $tag_ids; } /** * Check if a string is valid JSON * * @param string $string * @return bool */ private function isJson($string) { if (!is_string($string)) return false; json_decode($string); return (json_last_error() == JSON_ERROR_NONE); } /** * Populate booking data from import parameters * * @param Booking $booking * @param array $params * @return Booking */ private function populateBookingData(Booking &$booking, $params) { // Get shift/event information $service_name = 'Unknown'; $serviceId = isset($params['shift_event_id']) ? intval($params['shift_event_id']) : 0; // If shift_event_id is not present, try to use 'shift' parameter instead if ($serviceId === 0 && isset($params['shift'])) { $serviceId = intval($params['shift']); } if ($serviceId > 0) { $service = \Alexr\Settings\Shift::where('id', $serviceId)->first(); if (!$service) { $service = \Alexr\Settings\Event::where('id', $serviceId)->first(); } if ($service) { $service_name = $service->name; } } // Generate email if not provided $email = isset($params['email']) && !empty($params['email']) ? $params['email'] : null; if (!$email) { $name = isset($params['first_name']) ? $params['first_name'] : 'customer'; $date = isset($params['date']) ? $params['date'] : date('Y-m-d'); $time = isset($params['slot']) ? intval($params['slot']) : 0; $party = isset($params['party']) ? intval($params['party']) : 1; $email = $this->generatePredictableEmail($name, $date, $time, $party); } // Basic information $booking->first_name = isset($params['first_name']) ? $params['first_name'] : ''; $booking->last_name = isset($params['last_name']) ? $params['last_name'] : ''; $booking->email = $email; $booking->phone = isset($params['phone']) ? $params['phone'] : ''; $booking->country_code = isset($params['country_code']) ? $params['country_code'] : ''; $booking->dial_code = isset($params['dial_code']) ? $params['dial_code'] : ''; $booking->dial_code_country = isset($params['dial_code_country']) ? $params['dial_code_country'] : ''; // Booking details $booking->date = isset($params['date']) ? $params['date'] : date('Y-m-d'); $booking->time = isset($params['slot']) ? intval($params['slot']) : 0; $booking->party = isset($params['party']) ? intval($params['party']) : 1; $booking->duration = isset($params['duration']) ? intval($params['duration']) : 5400; // Default 1.5 hours (5400 seconds) $booking->status = isset($params['status']) ? $params['status'] : 'pending'; $booking->notes = isset($params['notes']) ? $params['notes'] : ''; $booking->private_notes = isset($params['private_notes']) ? $params['private_notes'] : ''; // Shift/Event information $booking->shift_event_id = $serviceId; $booking->shift_event_name = $service_name; // Type and special fields $booking->type = isset($params['type']) ? $params['type'] : 'in-house'; $booking->spend = isset($params['spend']) ? intval($params['spend']) : 0; $booking->blocked_table = isset($params['blocked_table']) ? intval($params['blocked_table']) : 0; // Opt-in status $booking->agree_receive_email = 1; // Default to opt-in for imported bookings $booking->agree_receive_sms = !empty($booking->phone) ? 1 : 0; $booking->sms_status = isset($params['sms_status']) && $params['sms_status'] !== 'null' ? $params['sms_status'] : null; // Language setting - default to 'en' if not provided $booking->language = isset($params['language']) ? $params['language'] : 'en'; return $booking; } }