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/RecurringAvailabilityService.php +98 -7 3.0.4 → 3.0.16 View file →
@@ -350,9 +350,9 @@
350 350 }
351 351
352 352 if (in_array($dayOfWeek, $targetDays, true)) {
353 353 // Check if not excluded
354 - if (!in_array($dateStr, $excludedDates, true)) {
354 + if (!$this->isDateExcluded($dateStr, $excludedDates)) {
355 355 // Check cutoff
356 356 if ($this->isBookable($current, $rule)) {
357 357 $generatedDates = $this->createAvailabilityFromRule($rule, $dateStr, $dayOfWeek);
358 358 $dates = array_merge($dates, $generatedDates);
@@ -371,11 +371,21 @@
371 371 */
372 372 private function generateMonthlyDates(object $rule, string $fromDate, string $toDate): array
373 373 {
374 374 $dates = [];
375 - $weekOfMonth = $rule->week_of_month;
376 - $dayOfWeek = (int) $rule->day_of_week;
377 - $excludedDates = $rule->excluded_dates;
375 +
376 + // Legacy rows (migrated from the pre-3.x schema) can land here
377 + // with `week_of_month` NULL or missing entirely — the column is
378 + // nullable in the DB but {@see self::getNthWeekdayOfMonth()}'s
379 + // signature requires `string`, so calling it with NULL was
380 + // crashing the trip page on PHP 7.4+. Bail out cleanly instead.
381 + $weekOfMonth = $rule->week_of_month ?? null;
382 + if (!is_string($weekOfMonth) || $weekOfMonth === '') {
383 + return $dates;
384 + }
385 +
386 + $dayOfWeek = (int) ($rule->day_of_week ?? 0);
387 + $excludedDates = $rule->excluded_dates ?? [];
378 388 $selectedMonths = !empty($rule->months) ? $rule->months : [];
379 389
380 390 // Start from the first day of the starting month
381 391 $current = strtotime(date('Y-m-01', strtotime($fromDate)));
@@ -398,9 +408,9 @@
398 408
399 409 // Check if within range
400 410 if ($targetTimestamp >= strtotime($fromDate) && $targetTimestamp <= $end) {
401 411 // Check if not excluded
402 - if (!in_array($targetDate, $excludedDates, true)) {
412 + if (!$this->isDateExcluded($targetDate, $excludedDates)) {
403 413 // Check cutoff
404 414 if ($this->isBookable($targetTimestamp, $rule)) {
405 415 $generatedDates = $this->createAvailabilityFromRule($rule, $targetDate, $dayOfWeek);
406 416 $dates = array_merge($dates, $generatedDates);
@@ -479,9 +489,9 @@
479 489 $dayOfWeek = (int) date('w', $current);
480 490 $month = (int) date('n', $current); // 1-12
481 491
482 492 if (empty($selectedMonths) || in_array($month, $selectedMonths, true)) {
483 - if (!in_array($dateStr, $excludedDates, true)) {
493 + if (!$this->isDateExcluded($dateStr, $excludedDates)) {
484 494 if ($this->isBookable($current, $rule)) {
485 495 $generatedDates = $this->createAvailabilityFromRule($rule, $dateStr, $dayOfWeek);
486 496 $dates = array_merge($dates, $generatedDates);
487 497 }
@@ -649,13 +659,94 @@
649 659
650 660 /**
651 661 * Preview generated dates (for admin UI)
652 662 */
663 + /**
664 + * Has the operator explicitly closed this date on any of the trip's active
665 + * rules — a holiday, or a period such as a business vacation?
666 + *
667 + * Only answers for dates the operator deliberately listed. A date that is
668 + * simply not generated by a rule (a Tuesday on a Mon/Wed/Fri pattern) is
669 + * not "excluded" and is left to the rest of the resolution chain.
670 + */
671 + public function isDateExcludedForTrip(int $tripId, string $date): bool
672 + {
673 + if ($tripId < 1 || $date === '') {
674 + return false;
675 + }
676 +
677 + foreach ($this->repository->getActiveRulesForDateRange($tripId, $date, $date) as $rule) {
678 + if ($this->isDateExcluded($date, $rule->excluded_dates ?? [])) {
679 + return true;
680 + }
681 + }
682 +
683 + return false;
684 + }
685 +
686 + /**
687 + * Is this date excluded by the rule?
688 + *
689 + * An entry in `excluded_dates` is either a single 'Y-m-d' string (what the
690 + * admin has always been able to add one day at a time) or an inclusive
691 + * period ['start' => 'Y-m-d', 'end' => 'Y-m-d'] — a business vacation or
692 + * seasonal closure, added in one go instead of day by day.
693 + *
694 + * Both shapes share the one column so existing rules keep working
695 + * untouched: a stored list of plain strings behaves exactly as before.
696 + * Dates are ISO, so plain string comparison is chronological.
697 + *
698 + * @param string $date Date being generated, 'Y-m-d'.
699 + * @param mixed $excluded Stored exclusions (a list; anything else = none).
700 + */
701 + public function isDateExcluded(string $date, $excluded): bool
702 + {
703 + if (!is_array($excluded)) {
704 + return false;
705 + }
706 +
707 + foreach ($excluded as $entry) {
708 + if (is_string($entry)) {
709 + if ($entry === $date) {
710 + return true;
711 + }
712 +
713 + continue;
714 + }
715 +
716 + if (!is_array($entry)) {
717 + continue;
718 + }
719 +
720 + $start = isset($entry['start']) ? (string) $entry['start'] : '';
721 + $end = isset($entry['end']) ? (string) $entry['end'] : '';
722 +
723 + if ($start === '' || $end === '') {
724 + continue;
725 + }
726 +
727 + // Tolerate a reversed period rather than silently ignoring it.
728 + if ($start > $end) {
729 + [$start, $end] = [$end, $start];
730 + }
731 +
732 + if ($date >= $start && $date <= $end) {
733 + return true;
734 + }
735 + }
736 +
737 + return false;
738 + }
739 +
653 740 public function previewDates(array $ruleData, int $limit = 20): array
654 741 {
655 742 // Create a temporary rule object
656 743 $rule = (object) $ruleData;
657 - $rule->excluded_dates = $rule->excluded_dates ?? [];
744 + // Normalise once: a malformed value (e.g. a bare string from a
745 + // hand-rolled API call) must not reach count() below as a fatal.
746 + $rule->excluded_dates = is_array($rule->excluded_dates ?? null)
747 + ? $rule->excluded_dates
748 + : [];
658 749 $rule->day_overrides = $rule->day_overrides ?? [];
659 750 $rule->days_of_week_array = isset($rule->days_of_week)
660 751 ? (is_array($rule->days_of_week) ? $rule->days_of_week : array_map('intval', explode(',', (string) $rule->days_of_week)))
661 752 : [];