PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.16
Yatra – Travel Booking & Tour Operator Software v3.0.16
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
← All changes | app/Controllers/SingleTripController.php +42 -7 3.0.5.1 → 3.0.16 View file →
@@ -396,13 +396,14 @@
396 396 {
397 397 // Use centralized AvailabilityResolutionService
398 398 $resolutionService = new \Yatra\Services\AvailabilityResolutionService();
399 399
400 - // Get dates for next 12 months
400 + // From today up to the configurable booking horizon (Settings → Booking;
401 + // 12 months unless changed — the previous hard-coded value).
401 402 $fromDate = date('Y-m-d');
402 - $toDate = date('Y-m-d', strtotime('+12 months'));
403 + $toDate = yatra_get_availability_horizon_date($fromDate);
403 404
404 - $availability = $resolutionService->getAllAvailabilityDates($trip_id, $fromDate, $toDate);
405 + $availability = $resolutionService->getAllAvailabilityDates($trip_id, $fromDate, $toDate, \Yatra\Services\SettingsService::isEnabled('show_sold_out'));
405 406
406 407 // Add calculated fields
407 408 foreach ($availability as $avail) {
408 409 // Calculate if limited availability
@@ -466,8 +467,9 @@
466 467 'age_min' => isset($meta['age_min']) ? (int) $meta['age_min'] : null,
467 468 'age_max' => isset($meta['age_max']) ? (int) $meta['age_max'] : null,
468 469 'min_pax' => isset($meta['min_pax']) ? (int) $meta['min_pax'] : null,
469 470 'max_pax' => isset($meta['max_pax']) ? (int) $meta['max_pax'] : null,
471 + 'group_overflow' => isset($meta['group_overflow']) && in_array($meta['group_overflow'], ['block', 'per_block'], true) ? $meta['group_overflow'] : 'block',
470 472 'max_quantity' => isset($meta['max_quantity']) ? (int) $meta['max_quantity'] : null,
471 473 'description' => $meta['description'] ?? '',
472 474 ];
473 475 }
@@ -497,8 +499,9 @@
497 499 'age_min' => $cat ? $cat->age_min : null,
498 500 'age_max' => $cat ? $cat->age_max : null,
499 501 'min_pax' => $cat ? $cat->min_pax : null,
500 502 'max_pax' => $cat ? $cat->max_pax : null,
503 + 'group_overflow' => $cat ? $cat->group_overflow : ($pt['group_overflow'] ?? 'block'),
501 504 'max_quantity' => $cat ? $cat->max_quantity : null,
502 505 'description' => $cat ? $cat->description : ($pt['description'] ?? ''),
503 506 ];
504 507 }
@@ -1092,9 +1095,13 @@
1092 1095 $patterns = [
1093 1096 '/youtube\.com\/watch\?v=([^&]+)/',
1094 1097 '/youtube\.com\/embed\/([^?]+)/',
1095 1098 '/youtu\.be\/([^?]+)/',
1096 - '/youtube\.com\/v\/([^?]+)/'
1099 + '/youtube\.com\/v\/([^?]+)/',
1100 + // Shorts + live URLs (e.g. youtube.com/shorts/XFR9Ti-4RbM?si=...).
1101 + // ID stops at ?, & or / so trailing query params are excluded.
1102 + '/youtube\.com\/shorts\/([^?&\/]+)/',
1103 + '/youtube\.com\/live\/([^?&\/]+)/'
1097 1104 ];
1098 1105
1099 1106 foreach ($patterns as $pattern) {
1100 1107 if (preg_match($pattern, $url, $matches)) {
@@ -1226,13 +1233,19 @@
1226 1233 */
1227 1234 private function getSimilarTrips(object $trip): array
1228 1235 {
1229 1236 $trip_id = (int) $trip->id;
1237 +
1238 + // Hour-based day tours (3.0.14+ column) — guarded so an install whose
1239 + // upgrade ALTER has not run yet keeps rendering similar trips.
1240 + $duration_hours_col = (new \Yatra\Repositories\TripRepository())->hasTripColumn('duration_hours')
1241 + ? ', duration_hours'
1242 + : '';
1230 1243
1231 1244 // Get similar trips based on category or difficulty
1232 1245 $similar = $this->wpdb->get_results(
1233 1246 $this->wpdb->prepare(
1234 - "SELECT id, title, slug, featured_image AS featured_image_id, '' AS featured_image_url, duration_days, duration_nights,
1247 + "SELECT id, title, slug, featured_image AS featured_image_id, '' AS featured_image_url, duration_days, duration_nights{$duration_hours_col},
1235 1248 original_price, sale_price, difficulty_level,
1236 1249 short_description
1237 1250 FROM {$this->table_trips} t
1238 1251 WHERE t.id != %d
@@ -1263,9 +1276,9 @@
1263 1276 // Fallback: Get any published trips if no similar found
1264 1277 if (empty($similar)) {
1265 1278 $similar = $this->wpdb->get_results(
1266 1279 $this->wpdb->prepare(
1267 - "SELECT id, title, slug, featured_image AS featured_image_id, '' AS featured_image_url, duration_days, duration_nights,
1280 + "SELECT id, title, slug, featured_image AS featured_image_id, '' AS featured_image_url, duration_days, duration_nights{$duration_hours_col},
1268 1281 original_price, sale_price, difficulty_level,
1269 1282 short_description
1270 1283 FROM {$this->table_trips}
1271 1284 WHERE id != %d
@@ -1884,8 +1897,16 @@
1884 1897
1885 1898 $input_id = 'traveler_' . $price_type->category_id;
1886 1899 $max_travelers = is_object($trip) && method_exists($trip, 'getMaxTravelers') ? $trip->getMaxTravelers() : ($trip->max_travelers ?? 20);
1887 1900 $pt_max_qty = (int) ($price_type->max_quantity ?: $max_travelers);
1901 + // A per-group category in "block" overflow mode caps the party at
1902 + // the max group size. In "per_block" mode the party may exceed it
1903 + // (it just buys additional group blocks), so we keep the trip's
1904 + // normal cap there.
1905 + if ($is_per_group && !empty($price_type->max_pax)
1906 + && (($price_type->group_overflow ?? 'block') !== 'per_block')) {
1907 + $pt_max_qty = (int) $price_type->max_pax;
1908 + }
1888 1909 $pt_value = ($index === $default_index) ? 1 : 0;
1889 1910
1890 1911 $traveler_rows[] = [
1891 1912 'label' => $price_type->category_label ?: __('Traveler', 'yatra'),
@@ -1894,8 +1915,10 @@
1894 1915 'row_attrs' => [
1895 1916 'data-category-id' => $price_type->category_id,
1896 1917 'data-price' => $price_type->effective_price,
1897 1918 'data-pricing-mode' => $pricing_mode,
1919 + 'data-group-overflow' => $price_type->group_overflow ?? 'block',
1920 + 'data-max-pax' => $price_type->max_pax ?? '',
1898 1921 ],
1899 1922 'minus_disabled' => ($index !== $default_index),
1900 1923 'plus_disabled' => false,
1901 1924 'minus_attrs' => [
@@ -1917,8 +1940,10 @@
1917 1940 'data-category' => $price_type->category_id,
1918 1941 'data-category-label' => $price_type->category_label,
1919 1942 'data-price' => $price_type->effective_price,
1920 1943 'data-pricing-mode' => $pricing_mode,
1944 + 'data-group-overflow' => $price_type->group_overflow ?? 'block',
1945 + 'data-max-pax' => $price_type->max_pax ?? '',
1921 1946 ],
1922 1947 ];
1923 1948 }
1924 1949
@@ -2093,9 +2118,15 @@
2093 2118 $pt_min_qty = 0;
2094 2119 $pt_max_qty = (int) min($seats_available, $max_travelers);
2095 2120 $pt_pricing_mode = $pt->pricing_mode ?? 'per_person';
2096 2121 $pt_is_per_group = ($pt_pricing_mode === 'per_group');
2097 -
2122 + // Cap a per-group "block" category at its max group size (still
2123 + // bounded by seats). "per_block" mode may exceed it, so skip.
2124 + if ($pt_is_per_group && !empty($pt->max_pax)
2125 + && (($pt->group_overflow ?? 'block') !== 'per_block')) {
2126 + $pt_max_qty = (int) min($pt_max_qty, (int) $pt->max_pax);
2127 + }
2128 +
2098 2129 // Build pricing label
2099 2130 $pricing_label = '';
2100 2131 if ($pt_is_per_group) {
2101 2132 if (!empty($pt->min_pax) && !empty($pt->max_pax)) {
@@ -2126,8 +2157,10 @@
2126 2157 'row_attrs' => [
2127 2158 'data-category-id' => $pt_category_id,
2128 2159 'data-price' => $pt_price,
2129 2160 'data-pricing-mode' => $pt_pricing_mode,
2161 + 'data-group-overflow' => $pt->group_overflow ?? 'block',
2162 + 'data-max-pax' => $pt->max_pax ?? '',
2130 2163 ],
2131 2164 'minus_disabled' => ($pt_default <= 0),
2132 2165 'plus_disabled' => false,
2133 2166 'minus_attrs' => [
@@ -2144,8 +2177,10 @@
2144 2177 'data-item' => $item_id,
2145 2178 'data-category' => $pt_category_id,
2146 2179 'data-price' => $pt_price,
2147 2180 'data-pricing-mode' => $pt_pricing_mode,
2181 + 'data-group-overflow' => $pt->group_overflow ?? 'block',
2182 + 'data-max-pax' => $pt->max_pax ?? '',
2148 2183 'value' => $pt_default,
2149 2184 'min' => $pt_min_qty,
2150 2185 'max' => $pt_max_qty,
2151 2186 ],