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/Hooks/AvailabilityInventoryHooks.php +46 -3 3.0.3 → 3.0.16 View file →
@@ -57,8 +57,42 @@
57 57 $availabilityId = isset($booking->availability_id) ? (int) $booking->availability_id : 0;
58 58 $travelDate = (string) ($booking->travel_date ?? '');
59 59
60 60 if ($tripId > 0 && $availabilityId <= 0 && $travelDate !== '') {
61 + // Try to infer availability_id from resolved snapshot stored in booking meta.
62 + // This is more reliable when a trip has multiple departure times on the same date.
63 + $metaRaw = $booking->meta ?? null;
64 + $meta = [];
65 + if (is_string($metaRaw) && $metaRaw !== '') {
66 + $decoded = json_decode($metaRaw, true);
67 + $meta = is_array($decoded) ? $decoded : [];
68 + } elseif (is_array($metaRaw)) {
69 + $meta = $metaRaw;
70 + }
71 +
72 + if (!empty($meta) && isset($meta['resolved_availability']) && is_array($meta['resolved_availability'])) {
73 + $ra = $meta['resolved_availability'];
74 +
75 + $metaAvailabilityId = $ra['availability_id'] ?? null;
76 + if (is_numeric($metaAvailabilityId) && (int) $metaAvailabilityId > 0) {
77 + $availabilityId = (int) $metaAvailabilityId;
78 + } else {
79 + $metaDepartureTime = isset($ra['departure_time']) ? (string) $ra['departure_time'] : '';
80 + $metaTravelDate = isset($ra['travel_date']) ? (string) $ra['travel_date'] : $travelDate;
81 + if ($metaDepartureTime !== '') {
82 + $row = (new AvailabilityRepository())->findByTripIdAndDateTime(
83 + $tripId,
84 + $metaTravelDate,
85 + self::normalizeTimeForAvailabilityMatch($metaDepartureTime),
86 + true
87 + );
88 + if ($row && !empty($row->id)) {
89 + $availabilityId = (int) $row->id;
90 + }
91 + }
92 + }
93 + }
94 +
61 95 $availabilityId = self::resolveAvailabilityIdForBooking($bookingId, $tripId, $travelDate);
62 96 if ($availabilityId > 0) {
63 97 (new BookingRepository())->update($bookingId, ['availability_id' => $availabilityId]);
64 98 }
@@ -155,11 +189,20 @@
155 189 if (!$departure) {
156 190 return;
157 191 }
158 192
193 + // booked_count is sourced from the actual bookings table — it
194 + // is the truth. Reflect it to the departure row WITHOUT
195 + // clamping to max_capacity. Clamping previously hid oversells
196 + // from operators: if direct + OTA bookings legitimately
197 + // exceeded capacity (always an OTA-side acceptance — Yatra's
198 + // direct checkout enforces capacity), the departure showed
199 + // "full" instead of "oversold by N", and operators had no way
200 + // to spot the situation needing reconciliation. Allowing the
201 + // overshow makes the problem visible so it can be addressed.
159 202 $updated = [
160 203 'max_capacity' => $seatsTotal,
161 - 'booked_count' => min($seatsTotal, max(0, $bookedCount)),
204 + 'booked_count' => max(0, $bookedCount),
162 205 ];
163 206
164 207 $departureRepo->update((int) $departure->id, $updated);
165 208 }
@@ -196,9 +239,9 @@
196 239 $date = $departure->start_date ?: $departure->date;
197 240
198 241 // Get capacity from availability
199 242 $capacityService = new CapacityService();
200 - $correctCapacity = $capacityService->getCapacityForDate($departure->trip_id, $date);
243 + $correctCapacity = $capacityService->getCapacityForDate($departure->trip_id, $date, $departure->time ?? null);
201 244
202 245 if ($correctCapacity > 0 && $departure->max_capacity !== $correctCapacity) {
203 246 // Remove hook temporarily to prevent infinite loop
204 247 remove_action('yatra_departure_saved', [self::class, 'onDepartureSave'], 10);
@@ -215,9 +258,9 @@
215 258 unset($processing[$departureId]);
216 259 }
217 260
218 261 /**
219 - * Infer yatra_new_trip_availability_dates.id when the booking row missed availability_id
262 + * Infer yatra_trip_availability_dates.id when the booking row missed availability_id
220 263 * (common when checkout omits it). Uses booking→departure time to disambiguate multiple slots per day.
221 264 */
222 265 private static function resolveAvailabilityIdForBooking(int $bookingId, int $tripId, string $travelDate): int
223 266 {