availability_type !== 'tables_schedule') { return $service->getDuration($guests); } // Si no hay mesas asignadas, usar duración estándar if (empty($tables)) { return $service->getDuration($guests); } $duration_type = $service->list_of_tables_with_schedule['durationType'] ?? 'end_of_slot'; // Si hay más de una mesa, podría ser una combinación if (count($tables) > 1) { // Intentar calcular duración como combinación $tableIds = array_map(function($table) { return $table['id']; }, $tables); return $this->calculateDurationForTablesCombination( $tableIds, $service, $date, $time, $guests, $duration_type ); } else { // Si es una sola mesa, determinar duración según su schedule $tableId = $tables[0]['id']; return $this->calculateDurationForTableSchedule( $tableId, $service, $date, $time, $guests, $duration_type ); } } //--------------------------------------------------------------------------- // Duration for table schedule //--------------------------------------------------------------------------- protected function calculateDurationForTableSchedule($tableId, $service, $date_string, $time, $guests, $duration_type = 'end_of_slot') { if ($duration_type == 'next_slot') { return $this->calculateDurationForTableSchedule_extended($tableId, $service, $date_string, $time, $guests); } else { return $this->calculateDurationForTableSchedule_fixed($tableId, $service, $date_string, $time, $guests); } } protected function calculateDurationForTableSchedule_fixed($tableId, $service, $date_string, $time, $guests) { // Si no es tipo tables_schedule, usar la duración normal del servicio if ($service->availability_type !== 'tables_schedule') { return $service->getDuration($guests); } // Obtener el día de la semana $dayOfWeek = strtolower(date('D', strtotime($date_string))); // Verificar si la mesa tiene configuración específica en tableSchedules $tableSchedules = $service->list_of_tables_with_schedule['tableSchedules'] ?? []; // Si la mesa no tiene configuración específica o es tipo default, usar la duración normal if (!isset($tableSchedules[$tableId]) || !isset($tableSchedules[$tableId][$dayOfWeek]) || $tableSchedules[$tableId][$dayOfWeek]['type'] === 'default') { return $service->getDuration($guests); } // Si es custom schedule, calcular duración hasta el final del bloque $dayConfig = $tableSchedules[$tableId][$dayOfWeek]; if ($dayConfig['type'] === 'custom' && isset($dayConfig['schedule'])) { foreach ($dayConfig['schedule'] as $schedule) { if (in_array($time, $schedule['reserve'])) { // Duración desde la hora de inicio hasta el final del bloque $duration = $schedule['to'] - $time; return $duration; } } } // Si no se encuentra en ningún schedule, usar la duración normal return $service->getDuration($guests); } protected function calculateDurationForTableSchedule_extended($tableId, $service, $date_string, $time, $guests) { // Si no es tipo tables_schedule, usar la duración normal del servicio if ($service->availability_type !== 'tables_schedule') { return $service->getDuration($guests); } // Obtener el día de la semana $dayOfWeek = strtolower(date('D', strtotime($date_string))); // Verificar si la mesa tiene configuración específica en tableSchedules $tableSchedules = $service->list_of_tables_with_schedule['tableSchedules'] ?? []; // Si la mesa no tiene configuración específica o es tipo default, usar la duración normal if (!isset($tableSchedules[$tableId]) || !isset($tableSchedules[$tableId][$dayOfWeek]) || $tableSchedules[$tableId][$dayOfWeek]['type'] === 'default') { return $service->getDuration($guests); } // Si es custom schedule, calcular duración según las nuevas reglas $dayConfig = $tableSchedules[$tableId][$dayOfWeek]; if ($dayConfig['type'] === 'custom' && isset($dayConfig['schedule'])) { // Ordenar los schedules por hora de inicio usort($dayConfig['schedule'], function($a, $b) { return $a['from'] - $b['from']; }); // Encontrar el schedule actual basado en el tiempo de reserva $currentScheduleIndex = -1; foreach ($dayConfig['schedule'] as $index => $schedule) { if (in_array($time, $schedule['reserve'])) { $currentScheduleIndex = $index; break; } } // Si no encontramos el schedule, usar duración estándar if ($currentScheduleIndex === -1) { return $service->getDuration($guests); } $currentSchedule = $dayConfig['schedule'][$currentScheduleIndex]; // Por defecto, la duración es hasta el final del schedule actual $endTime = $currentSchedule['to']; // Verificar si hay un schedule posterior $nextScheduleExists = isset($dayConfig['schedule'][$currentScheduleIndex + 1]); if ($nextScheduleExists) { $nextSchedule = $dayConfig['schedule'][$currentScheduleIndex + 1]; // Verificar si hay reservas en ese intervalo que usen esta mesa $bookings = \Alexr\Models\Booking::where('restaurant_id', $service->restaurant_id) ->where('date', $date_string) ->whereIn('status', \Alexr\Enums\BookingStatus::occupied()) ->where('id', '!=', isset($_REQUEST['exclude_booking_id']) ? $_REQUEST['exclude_booking_id'] : 0) ->get(); $earliestNextBookingTime = null; foreach ($bookings as $booking) { // Verificar si la reserva usa esta mesa $hasTable = false; foreach ($booking->tables as $table) { if ($table->id == $tableId) { $hasTable = true; break; } } if ($hasTable && $booking->time > $currentSchedule['to'] && $booking->time <= $nextSchedule['to']) { // Esta reserva usa esta mesa y está en el siguiente schedule if ($earliestNextBookingTime === null || $booking->time < $earliestNextBookingTime) { $earliestNextBookingTime = $booking->time; } } } // Si hay una reserva en el siguiente schedule, limitar la duración hasta ella if ($earliestNextBookingTime !== null) { $endTime = $earliestNextBookingTime; } } // Calcular la duración final $duration = $endTime - $time; return max($duration, 1800); // Asegurar que la duración mínima sea de 15 minutos (900 segundos) } // Si no se encuentra en ningún schedule, usar la duración normal return $service->getDuration($guests); } protected function getBookingsYearMonth($tenantId, $year_month) { $bookings_year_month = \Alexr\Models\Booking::where('restaurant_id', $tenantId) ->where('date', 'like', $year_month) ->where('status', '!=', \Alexr\Enums\BookingStatus::SELECTED) ->where('status', '!=', \Alexr\Enums\BookingStatus::DELETED) ->orderBy('date', 'asc') ->get() ->map(function($booking){ return intval($booking->id); }) ->toArray(); return $bookings_year_month; } protected function findAlternativeDates($requested_date, $date_today, $days_before, $days_after, $search_interval_before, $search_interval_after, $payload) { $requested_date = new DateTime($requested_date); $date_today = new DateTime($date_today); $before_dates_result = []; $after_dates_result = []; // Extraer datos del payload para mayor claridad $restaurant = $payload['restaurant']; $service_id = $payload['service_id']; $service = $payload['service']; $guests = $payload['guests']; $widget_id = $payload['widget_id']; $days_in_advance = $payload['days_in_advance']; // Buscar fechas antes de la solicitada for ($i = 1; $i <= $search_interval_before; $i++) { $new_date = clone $requested_date; $new_date->modify("-$i day"); if ($new_date >= $date_today) { //ray('ALTERNATIVE DATE BEFORE: ' . $new_date->format('Y-m-d')); //$result_new_date = $this->calculateResultsSlotsAvailable($new_date->format('Y-m-d')); $result_new_date = $this->calculateResultsSlotsAvailable( $restaurant, $new_date->format('Y-m-d'), $service_id, $service, $guests, $widget_id, $days_in_advance ); if ($result_new_date['is_available'] === true) { $before_dates_result[] = $result_new_date['resultDate']; if (count($before_dates_result) == $days_before) break; } } } // Buscar fechas después de la solicitada for ($i = 1; $i <= $search_interval_after; $i++) { $new_date = clone $requested_date; $new_date->modify("+$i day"); //ray('ALTERNATIVE DATE AFTER: ' . $new_date->format('Y-m-d')); //$result_new_date = $this->calculateResultsSlotsAvailable($new_date->format('Y-m-d')); $result_new_date = $this->calculateResultsSlotsAvailable( $restaurant, $new_date->format('Y-m-d'), $service_id, $service, $guests, $widget_id, $days_in_advance ); if ($result_new_date['is_available'] === true) { $after_dates_result[] = $result_new_date['resultDate']; if (count($after_dates_result) == $days_after) break; } } return array_merge($before_dates_result, $after_dates_result); } //--------------------------------------------------------------------------- // Duration for group schedule //--------------------------------------------------------------------------- protected function calculateDurationForTablesCombination($tableIds, $service, $date_string, $time, $guests, $duration_type = 'end_of_slot') { // Si no es tipo tables_schedule, usar la duración normal del servicio if ($service->availability_type !== 'tables_schedule') { return $service->getDuration($guests); } // Buscar qué combinación contiene exactamente estas mesas $groupId = $this->findCombinationByTableIds($tableIds, $service->restaurant_id); if (!$groupId) { // Si no se encuentra una combinación exacta, usar duración estándar return $service->getDuration($guests); } // Calcular duración usando el schedule del grupo encontrado return $this->calculateDurationForGroupScheduleById($groupId, $service, $date_string, $time, $guests, $duration_type); } protected function findCombinationByTableIds($tableIds, $restaurant_id) { // Ordenar los IDs para comparación consistente sort($tableIds); // Buscar todas las combinaciones del restaurante $combinations = \Alexr\Models\Combination::where('restaurant_id', $restaurant_id) ->where('bookable_online', 1) ->get(); foreach ($combinations as $combination) { // Obtener los IDs de las mesas de esta combinación $combinationTableIds = $combination->listTablesId; sort($combinationTableIds); // Comparar si los arrays son idénticos if ($tableIds === $combinationTableIds) { return $combination->id; } } return null; // No se encontró una combinación exacta } protected function calculateDurationForGroupScheduleById($groupId, $service, $date_string, $time, $guests, $duration_type = 'end_of_slot') { $dayOfWeek = strtolower(date('D', strtotime($date_string))); // Verificar si el grupo tiene configuración específica en groupSchedules $groupSchedules = $service->list_of_tables_with_schedule['groupSchedules'] ?? []; // Si el grupo no tiene configuración específica o es tipo default, usar la duración normal if (!isset($groupSchedules[$groupId]) || !isset($groupSchedules[$groupId][$dayOfWeek]) || $groupSchedules[$groupId][$dayOfWeek]['type'] === 'default') { return $service->getDuration($guests); } // Si es custom schedule, calcular duración según las reglas $dayConfig = $groupSchedules[$groupId][$dayOfWeek]; if ($dayConfig['type'] === 'custom' && isset($dayConfig['schedule'])) { foreach ($dayConfig['schedule'] as $schedule) { if (in_array($time, $schedule['reserve'])) { if ($duration_type === 'next_slot') { //@TODO BUG AQUI: Buscar el siguiente slot para determinar duración más precisa $reserveSlots = $schedule['reserve']; sort($reserveSlots); $currentIndex = array_search($time, $reserveSlots); if ($currentIndex !== false && isset($reserveSlots[$currentIndex + 1])) { return $reserveSlots[$currentIndex + 1] - $time; } } // Por defecto, hasta el final del bloque return $schedule['to'] - $time; } } } // Si no se encuentra en ningún schedule, usar la duración normal return $service->getDuration($guests); } }