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/Services/CapacityService.php +82 -1 3.0.11 → 3.0.16 View file →
@@ -28,11 +28,14 @@
28 28 * Get capacity for a specific trip and date based on priority
29 29 *
30 30 * @param int $tripId Trip ID
31 31 * @param string $date Date in YYYY-MM-DD format
32 + * @param string|null $time Departure time (HH:MM or HH:MM:SS). When the matching
33 + * rule defines time slots, this selects the slot whose
34 + * seat count applies. Optional for backward compatibility.
32 35 * @return int Maximum capacity
33 36 */
34 - public function getCapacityForDate(int $tripId, string $date): int
37 + public function getCapacityForDate(int $tripId, string $date, ?string $time = null): int
35 38 {
36 39 // Normalize to Y-m-d. The availability-date and recurring-rule columns are
37 40 // DATE type, so a datetime input (e.g. "2026-07-18 12:30:00") breaks the
38 41 // rule's `end_date >= %s` boundary match (the DATE end_date is treated as
@@ -52,8 +55,19 @@
52 55 $recurringRules = $this->recurringAvailabilityRepository->findActiveRulesForDate($tripId, $date);
53 56 if (!empty($recurringRules)) {
54 57 // Sort by priority (if applicable) and get the first matching rule
55 58 $matchingRule = reset($recurringRules);
59 +
60 + // A rule may define its capacity per time slot rather than on the rule
61 + // itself (e.g. a private tour whose vehicle seats 9 but sells one group
62 + // booking per departure). The public availability path already honours
63 + // the slot seats, so resolve them here too or departures/capacity would
64 + // fall through to the trip default and report the wrong number.
65 + $slotSeats = $this->resolveSlotSeats($matchingRule, $time);
66 + if ($slotSeats > 0) {
67 + return $slotSeats;
68 + }
69 +
56 70 $seats = (int) ($matchingRule->seats_total ?? 0);
57 71 if ($seats <= 0 && !empty($matchingRule->capacity_value)) {
58 72 $capType = $matchingRule->capacity_type ?? 'fixed';
59 73 if ($capType === 'fixed') {
@@ -73,6 +87,73 @@
73 87
74 88 $cap = (int) ($trip->max_travelers ?? $trip->max_travellers ?? 0);
75 89
76 90 return $cap > 0 ? $cap : 0;
91 + }
92 +
93 + /**
94 + * Resolve the seat count a rule defines on its time slots.
95 + *
96 + * Returns 0 when the rule has no slots, or when no slot carries a positive
97 + * seat count, so the caller falls back to the existing rule/trip resolution
98 + * exactly as before.
99 + *
100 + * @param object $rule Recurring availability rule
101 + * @param string|null $time Departure time to match against (HH:MM or HH:MM:SS)
102 + * @return int
103 + */
104 + private function resolveSlotSeats(object $rule, ?string $time): int
105 + {
106 + $slots = $rule->time_slots ?? null;
107 +
108 + // Most finders hydrate this to an array, but some return the raw column.
109 + if (is_string($slots)) {
110 + $slots = json_decode($slots, true);
111 + }
112 +
113 + if (empty($slots) || !is_array($slots)) {
114 + return 0;
115 + }
116 +
117 + // With a known departure time, only the matching slot's seats apply.
118 + if ($time !== null && $time !== '') {
119 + $wanted = $this->normalizeTime($time);
120 + foreach ($slots as $slot) {
121 + if (!is_array($slot) || !isset($slot['departure_time'])) {
122 + continue;
123 + }
124 + if ($this->normalizeTime((string) $slot['departure_time']) === $wanted) {
125 + return max(0, (int) ($slot['seats'] ?? 0));
126 + }
127 + }
128 +
129 + return 0;
130 + }
131 +
132 + // Without a time, a single slot is unambiguous; multiple slots make up the
133 + // day's total capacity.
134 + $total = 0;
135 + foreach ($slots as $slot) {
136 + if (is_array($slot)) {
137 + $total += max(0, (int) ($slot['seats'] ?? 0));
138 + }
139 + }
140 +
141 + return $total;
142 + }
143 +
144 + /**
145 + * Normalize a time value to HH:MM for comparison, so "8:00", "08:00" and
146 + * "08:00:00" all match.
147 + *
148 + * @param string $time
149 + * @return string
150 + */
151 + private function normalizeTime(string $time): string
152 + {
153 + $parts = explode(':', trim($time));
154 + $hour = isset($parts[0]) ? (int) $parts[0] : 0;
155 + $minute = isset($parts[1]) ? (int) $parts[1] : 0;
156 +
157 + return sprintf('%02d:%02d', $hour, $minute);
77 158 }
78 159 }