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/TripController.php +27 -3 3.0.6 → 3.0.16 View file →
@@ -1515,13 +1515,15 @@
1515 1515
1516 1516 // Fetch availability dates using centralized resolution service
1517 1517 $resolutionService = new \Yatra\Services\AvailabilityResolutionService();
1518 1518
1519 - // Always show all dates from today onwards (selected_date is only for highlighting)
1519 + // Always show all dates from today onwards (selected_date is only for
1520 + // highlighting), up to the configurable booking horizon (Settings →
1521 + // Booking; 12 months unless changed — the previous hard-coded value).
1520 1522 $fromDate = date('Y-m-d');
1521 - $toDate = date('Y-m-d', strtotime('+12 months'));
1523 + $toDate = yatra_get_availability_horizon_date($fromDate);
1522 1524
1523 - $availability_dates = $resolutionService->getAllAvailabilityDates($id, $fromDate, $toDate);
1525 + $availability_dates = $resolutionService->getAllAvailabilityDates($id, $fromDate, $toDate, \Yatra\Services\SettingsService::isEnabled('show_sold_out'));
1524 1526
1525 1527 // Determine if this is a day trip
1526 1528 $is_single_day = ($trip->duration_days ?? 1) <= 1;
1527 1529
@@ -1593,8 +1595,11 @@
1593 1595 'discounted_price' => isset($trip->discounted_price) ? (float) $trip->discounted_price : 0,
1594 1596 'sale_price' => isset($trip->sale_price) ? (float) $trip->sale_price : 0,
1595 1597 'currency' => SettingsService::getCurrency(),
1596 1598 'duration_days' => isset($trip->duration_days) ? (int) $trip->duration_days : 1,
1599 + // Hour-based day tours show "8 Hours" instead of "1 Day" on the
1600 + // availability cards. 0 for every existing (day-based) trip.
1601 + 'duration_hours' => isset($trip->duration_hours) ? (int) $trip->duration_hours : 0,
1597 1602 'max_travelers' => isset($trip->max_travelers) ? (int) $trip->max_travelers : 20,
1598 1603 'min_travelers' => isset($trip->min_travelers) ? (int) $trip->min_travelers : 1,
1599 1604 'pricing_type' => $trip->pricing_type ?? 'regular',
1600 1605 'price_types' => $trip->price_types ?? [], // Include price_types for traveler-based pricing
@@ -1835,8 +1840,9 @@
1835 1840 'age_min' => isset($meta['age_min']) ? (int) $meta['age_min'] : null,
1836 1841 'age_max' => isset($meta['age_max']) ? (int) $meta['age_max'] : null,
1837 1842 'min_pax' => isset($meta['min_pax']) ? (int) $meta['min_pax'] : null,
1838 1843 'max_pax' => isset($meta['max_pax']) ? (int) $meta['max_pax'] : null,
1844 + 'group_overflow' => isset($meta['group_overflow']) && in_array($meta['group_overflow'], ['block', 'per_block'], true) ? $meta['group_overflow'] : 'block',
1839 1845 ];
1840 1846 }
1841 1847 }
1842 1848
@@ -1886,8 +1892,9 @@
1886 1892 if (!isset($pt['age_min'])) $pt['age_min'] = $meta['age_min'];
1887 1893 if (!isset($pt['age_max'])) $pt['age_max'] = $meta['age_max'];
1888 1894 if (!isset($pt['min_pax'])) $pt['min_pax'] = $meta['min_pax'];
1889 1895 if (!isset($pt['max_pax'])) $pt['max_pax'] = $meta['max_pax'];
1896 + $pt['group_overflow'] = $meta['group_overflow'] ?? 'block';
1890 1897 }
1891 1898
1892 1899 // Payable amount (honors price / sale_price / discounted_price like TripPricingService)
1893 1900 if (!isset($pt['effective_price'])) {
@@ -2033,8 +2040,22 @@
2033 2040 $from_label = __('Departure', 'yatra');
2034 2041 $to_label = __('Return', 'yatra');
2035 2042 }
2036 2043
2044 + // Per-card duration: derive from THIS card's departure→return span
2045 + // so the displayed "X Days" always matches the departure/return
2046 + // dates shown on the same card. When no custom arrival is stored,
2047 + // $return_date is departure + (duration_days - 1), so the span
2048 + // equals the trip's duration_days (no visible change). Only when an
2049 + // operator stored an arrival that disagrees with the trip default
2050 + // does this diverge — and then the customer sees a self-consistent
2051 + // card (e.g. "10 Days" over a Jun 25 → Jul 04 span) instead of a
2052 + // "9 Days" badge contradicting the dates. round() (not floor())
2053 + // absorbs any ±1h DST drift between two local-midnight timestamps.
2054 + $card_duration_days = $is_single_day
2055 + ? max(1, (int) ($trip_data->duration_days ?? 1))
2056 + : max(1, (int) round(($return_date - $departure_date) / DAY_IN_SECONDS) + 1);
2057 +
2037 2058 // Use month-based keys for filtering for both day trips and multi-day trips
2038 2059 $filter_key = strtolower(date('M-Y', $departure_date));
2039 2060
2040 2061 // Must match {@see TripPricingService::resolveCardPricing}: trip-level mode wins; do not
@@ -2054,8 +2075,9 @@
2054 2075 'to_label' => $to_label,
2055 2076 'to_date' => $to_display,
2056 2077 'to_location' => $to_location,
2057 2078 'date_display' => $date_display, // For day trips: "Saturday, 30 Nov 2025"
2079 + 'duration_days' => $card_duration_days, // Inclusive span of THIS card's dates
2058 2080 'date' => $avail->departure_date, // Raw date for dynamic pricing
2059 2081 'spots_remaining' => $seats, // For dynamic pricing
2060 2082 'seats' => $seats > 10 ? '10+' : (string) $seats,
2061 2083 'seats_available' => $seats,
@@ -2078,8 +2100,10 @@
2078 2100 'is_sold_out' => $is_sold_out,
2079 2101 // Card-specific pricing
2080 2102 'pricing_type' => $card_pricing_type,
2081 2103 'traveler_pricing' => $card_traveler_pricing,
2104 + 'price_unit' => $cardPricing['price_unit'] ?? 'per_person',
2105 + 'price_unit_label' => $cardPricing['price_unit_label'] ?? __('per person', 'yatra'),
2082 2106 'is_recurring' => !empty($avail->is_recurring),
2083 2107 'rule_id' => $avail->rule_id ?? null,
2084 2108 ] + $dp_card_fields;
2085 2109 }