getId()); $sidebar_has_group_discounts = !empty($gd['has_group_discounts']); $sidebar_group_discount_cards = isset($gd['group_discounts_data']) && is_array($gd['group_discounts_data']) ? $gd['group_discounts_data'] : []; } // Pro: check whether this trip has booking disabled (enquiry-only mode) $booking_disabled = method_exists($trip, 'isBookingDisabled') && $trip->isBookingDisabled(); // Pricing — resolved via centralized TripPricingService (single source of truth) // SingleTripController pre-computes effective_price_min, min_category_original_price, max_discount_percentage $displayPricing = \Yatra\Services\TripPricingService::resolveDisplayPricing($trip); $pricing = [ 'has_price' => $displayPricing['effective_price_min'] > 0, 'current_price' => $displayPricing['effective_price_min'] > 0 ? yatra_format_price($displayPricing['effective_price_min']) : '', 'original_price' => $displayPricing['has_discount'] ? yatra_format_price($displayPricing['min_category_original_price']) : '', 'price_prefix' => $displayPricing['price_prefix'], 'has_discount' => $displayPricing['has_discount'], 'raw_current_price' => $displayPricing['effective_price_min'], 'raw_original_price' => $displayPricing['min_category_original_price'], 'is_traveler_based' => $displayPricing['has_traveler_pricing'], ]; $discount = [ 'has_discount' => $displayPricing['has_discount'] && $displayPricing['max_discount_percentage'] > 0, 'discount_text' => $displayPricing['max_discount_percentage'] > 0 ? sprintf( /* translators: %d: maximum discount percentage. */ __('Save up to %d%%', 'yatra'), $displayPricing['max_discount_percentage'] ) : '', 'discount_percentage' => $displayPricing['max_discount_percentage'], ]; $dp_display_sidebar = apply_filters('yatra_get_dynamic_pricing_display_settings', [ 'show_original_price' => true, 'show_savings_badge' => true, 'show_urgency_messages' => false, ]); $dp_show_savings_sidebar = filter_var($dp_display_sidebar['show_savings_badge'] ?? true, FILTER_VALIDATE_BOOLEAN); $show_strike_original_sidebar = filter_var($dp_display_sidebar['show_original_price'] ?? true, FILTER_VALIDATE_BOOLEAN); $yatra_sidebar_urgency = function_exists('yatra_trip_card_dynamic_pricing_urgency_lines') ? yatra_trip_card_dynamic_pricing_urgency_lines((int) $trip->getId(), [ 'base_sale_price' => (float) ($displayPricing['effective_price_min'] ?? 0), 'base_original_price' => (float) ($displayPricing['min_category_original_price'] ?? 0), 'sale_price' => (float) ($displayPricing['effective_price_min'] ?? 0), 'original_price' => (float) ($displayPricing['min_category_original_price'] ?? 0), 'departure_date' => null, 'spots_remaining' => null, 'availability_id' => null, 'surface' => 'single_sidebar', ]) : []; ?>
$trip]); ?>
getId(); yatra_get_template('partials/single-trip/group-discount-booking-popover', compact('group_discount_cards', 'popover_context', 'popover_uid')); ?>
getBookingMode() === 'date_specific' && $has_availability): ?>
getAvailabilityDates(); $today = date('Y-m-d'); // Sidebar count should reflect the same resolved availability set (priority: // manual > recurring rule > trip default). We keep it lightweight here: // count upcoming departures (date + optional time) from the resolved list. $filtered = array_filter($allAvailability, static function ($a) use ($today) { if (!is_object($a)) { return false; } $date = (string) ($a->departure_date ?? ''); if ($date === '' || $date < $today) { return false; } return true; }); // Priority: manual availability dates > recurring rules > trip default. $bySource = [ 'availability_date' => [], 'recurring_rule' => [], 'trip_default' => [], '' => [], ]; foreach ($filtered as $a) { $src = strtolower(trim((string) ($a->source ?? ''))); if (!isset($bySource[$src])) { $src = ''; } $bySource[$src][] = $a; } $pick = []; if (!empty($bySource['availability_date'])) { $pick = $bySource['availability_date']; } elseif (!empty($bySource['recurring_rule'])) { $pick = $bySource['recurring_rule']; } elseif (!empty($bySource['trip_default'])) { $pick = $bySource['trip_default']; } else { $pick = $filtered; } // Count departures: // - if availability entries include departure_time, count unique (date + time) as departures // - otherwise count unique dates. $hasTimeSlots = false; foreach ($pick as $a) { $t = isset($a->departure_time) ? trim((string) $a->departure_time) : ''; if ($t !== '' && $t !== '00:00:00') { $hasTimeSlots = true; break; } } $unique = []; foreach ($pick as $a) { $date = (string) ($a->departure_date ?? ''); if ($date === '') { continue; } if ($hasTimeSlots) { $t = isset($a->departure_time) ? trim((string) $a->departure_time) : ''; if ($t !== '' && $t !== '00:00:00') { $date .= '|' . $t; } else { // When some rows have time and others don't, avoid collapsing into date-only. $date .= '|__no_time__'; } } $unique[$date] = true; } $countAvailable = count($unique); // Pro feature: a Pro module can hook `yatra_use_date_dropdown` // to replace the flatpickr calendar with a
getMinTravelers() ?: 1; $max = $trip->getMaxTravelers() ?: 20; $data_price = $base_price; include YATRA_PLUGIN_PATH . 'templates/partials/traveler-input-simple.php'; ?>
getAvailableFrom()) || !empty($trip->getAvailableTo())): ?> getAvailableFrom()) && !empty($trip->getAvailableTo())) { echo esc_html(sprintf( /* translators: 1: available-from date, 2: available-to date. */ __('Available: %1$s - %2$s', 'yatra'), date_i18n(get_option('date_format'), strtotime($trip->getAvailableFrom())), date_i18n(get_option('date_format'), strtotime($trip->getAvailableTo())) )); } elseif (!empty($trip->getAvailableFrom())) { echo esc_html(sprintf( /* translators: %s: available-from date. */ __('Available from: %s', 'yatra'), date_i18n(get_option('date_format'), strtotime($trip->getAvailableFrom())) )); } else { echo esc_html(sprintf( /* translators: %s: available-until date. */ __('Available until: %s', 'yatra'), date_i18n(get_option('date_format'), strtotime($trip->getAvailableTo())) )); } ?>
getMinTravelers() ?: 1; $max = $trip->getMaxTravelers() ?: 20; $data_price = $base_price; include YATRA_PLUGIN_PATH . 'templates/partials/traveler-input-simple.php'; ?>
$trip]); ?> getCancellationPolicy(); } elseif (isset($trip->cancellation_policy)) { $cancellation_policy = $trip->cancellation_policy; } $cancellation_text = ''; if (!empty($cancellation_policy)) { // Extract cancellation info from policy $policy_lower = strtolower($cancellation_policy); if (strpos($policy_lower, 'free') !== false || strpos($policy_lower, 'no charge') !== false) { if (preg_match('/(\d+)\s*(hour|day|week)s?\s*before/i', $cancellation_policy, $matches)) { $time_value = $matches[1]; $time_unit = $matches[2]; $cancellation_text = sprintf( /* translators: 1: numeric time value, 2: time unit (hour/day/week), 3: plural suffix ("s" or empty). */ __('Free cancellation up to %1$d %2$s%3$s before', 'yatra'), $time_value, $time_unit, $time_value > 1 ? 's' : '' ); } else { $cancellation_text = __('Free cancellation', 'yatra'); } } else { // Non-free cancellation, show brief info $cancellation_text = __('Cancellation policy applies', 'yatra'); } } else { // Default fallback when no policy is set $cancellation_text = __('Cancellation policy may apply', 'yatra'); } ?>