getDurationDays() ?? 1) > 1; // Check for group discounts availability early for the badge (premium feature) $sidebar_has_group_discounts = false; $sidebar_group_discounts_data = []; $sidebar_group_discount_summary = ''; try { // Check if group discounts method exists (premium feature) if (class_exists('\Yatra\Services\DiscountService') && method_exists('\Yatra\Services\DiscountService', 'getGroupDiscountsForTrip')) { $discountService = new \Yatra\Services\DiscountService(); $groupDiscountsResult = $discountService->getGroupDiscountsForTrip((int) $trip->getId()); if (!empty($groupDiscountsResult)) { $sidebar_has_group_discounts = true; $all_ranges = []; // Process ALL group discount rules foreach ($groupDiscountsResult as $discount) { $ranges = []; // Try different field names where ranges might be stored $possible_fields = ['group_discount_ranges', 'ranges', 'discount_ranges']; foreach ($possible_fields as $field) { if (!empty($discount->$field)) { if (is_string($discount->$field)) { $ranges = json_decode($discount->$field, true); } elseif (is_array($discount->$field)) { $ranges = $discount->$field; } break; } } // If no ranges found, create a basic structure from other fields if (empty($ranges)) { $ranges = []; // Check if it's a simple discount with min/max fields $min_size = !empty($discount->min_group_size) ? (int) $discount->min_group_size : 0; $max_size = !empty($discount->max_group_size) ? (int) $discount->max_group_size : null; // Handle different field names for discount value and type $discount_value = 0; $discount_type = 'percentage'; if (!empty($discount->amount)) { $discount_value = (float) $discount->amount; } elseif (!empty($discount->discount_amount)) { $discount_value = (float) $discount->discount_amount; } if (!empty($discount->type)) { $discount_type = $discount->type; } elseif (!empty($discount->discount_type)) { $discount_type = $discount->discount_type; } // If no min_size is set, assume it's a general group discount starting from 2 people if ($min_size === 0) { $min_size = 2; } if ($min_size > 0 && $discount_value > 0) { $ranges[] = [ 'min_group_size' => $min_size, 'max_group_size' => $max_size, 'discount_amount' => $discount_value, 'discount_type' => $discount_type, 'discount_percentage' => $discount_type === 'percentage' ? $discount_value : null, 'discount_code' => $discount->code ?? '' ]; } } else { // Add discount code to existing ranges if available foreach ($ranges as &$range) { if (!isset($range['discount_code'])) { $range['discount_code'] = $discount->code ?? ''; } } } // Merge ranges from this discount rule $all_ranges = array_merge($all_ranges, $ranges); } $sidebar_group_discounts_data = $all_ranges; } } } catch (\Exception $e) { // Silently fail if premium features are not available $sidebar_has_group_discounts = false; } // Prepare availability data for JavaScript $availability_json = []; if ($has_availability) { foreach ($trip->getAvailabilityDates() as $avail) { $availability_json[] = [ 'id' => (int) $avail->id, 'date' => $avail->departure_date, 'departure_date' => $avail->departure_date, 'return_date' => (isset($avail->return_date) && $avail->return_date !== '') ? $avail->return_date : (isset($avail->arrival_date) ? $avail->arrival_date : null), 'price' => $avail->effective_price ?? $avail->original_price, 'original_price' => $avail->original_price, 'discounted_price' => $avail->discounted_price, 'seats_available' => $avail->seats_available, 'seats_total' => $avail->seats_total, 'status' => $avail->status, 'is_limited' => $avail->is_limited ?? false, 'is_sold_out' => $avail->is_sold_out ?? false, 'pricing_type' => (!empty($avail->price_types) && is_array($avail->price_types)) ? 'traveler_based' : $pricing_type, 'price_types' => !empty($avail->price_types) ? $avail->price_types : [], ]; } } // 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(__('Save up to %d%%', 'yatra'), $displayPricing['max_discount_percentage']) : '', 'discount_percentage' => $displayPricing['max_discount_percentage'], ]; ?>
0 ? (int) $range['max_group_size'] : null; if ($max) { echo sprintf(esc_html__('%d-%d travelers', 'yatra'), $min, $max); } else { echo sprintf(esc_html__('%d+ travelers', 'yatra'), $min); } ?>
getBookingMode() === 'date_specific' && $has_availability): ?>
getAvailabilityDates()), 'yatra'), count($trip->getAvailabilityDates()))); ?>
getMinTravelers() ?: 1; $max = $trip->getMaxTravelers() ?: 20; $data_price = $base_price; $range_text = sprintf(__('Min: %d, Max: %d', 'yatra'), $min, $max); include YATRA_PLUGIN_PATH . 'templates/partials/traveler-input-simple.php'; ?>
getAvailableFrom()) || !empty($trip->getAvailableTo())): ?> getAvailableFrom()) && !empty($trip->getAvailableTo())) { echo esc_html(sprintf(__('Available: %s - %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(__('Available from: %s', 'yatra'), date_i18n(get_option('date_format'), strtotime($trip->getAvailableFrom())) )); } else { echo esc_html(sprintf(__('Available until: %s', 'yatra'), date_i18n(get_option('date_format'), strtotime($trip->getAvailableTo())) )); } ?>
getMinTravelers() ?: 1; $max = $trip->getMaxTravelers() ?: 20; $data_price = $base_price; $range_text = sprintf(__('Min: %d, Max: %d', 'yatra'), $min, $max); include YATRA_PLUGIN_PATH . 'templates/partials/traveler-input-simple.php'; ?>
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( __('Free cancellation up to %d %s%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'); } ?>