PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 2.5.0
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v2.5.0
2.5.0 2.4.0 2.3.0 2.2.5 2.2.0 2.1.2 2.1.1 trunk 1.10.0 1.10.01 1.10.02 1.5.0 1.5.01 1.5.02 1.5.1 1.5.10 1.5.20 1.5.21 1.5.22 1.5.23 1.5.24 1.5.25 1.6.0 1.7.0 1.7.1 All 34 releases
← All changes | app/Http/Controllers/SchedulesController.php +373 -75 1.10.0 → 2.5.0 View file →
@@ -3,9 +3,11 @@
3 3 namespace FluentBooking\App\Http\Controllers;
4 4
5 5 use FluentBooking\App\Models\Booking;
6 6 use FluentBooking\App\Models\Calendar;
7 +use FluentBooking\App\Models\CalendarSlot;
7 8 use FluentBooking\App\Models\BookingActivity;
9 +use FluentBooking\Framework\Database\Orm\ModelNotFoundException;
8 10 use FluentBooking\App\Services\EmailNotificationService;
9 11 use FluentBooking\App\Services\Helper;
10 12 use FluentBooking\Framework\Support\Arr;
11 13 use FluentBooking\App\Services\CurrenciesHelper;
@@ -11,110 +13,174 @@
11 13 use FluentBooking\App\Services\CurrenciesHelper;
12 14 use FluentBooking\Framework\Http\Request\Request;
13 15 use FluentBooking\App\Services\PermissionManager;
14 16 use FluentBooking\App\Services\CalendarService;
17 +use FluentBooking\App\Services\ExportHelper;
18 +use FluentBooking\App\Services\Integrations\FluentCRM\CrmContactService;
19 +use FluentBooking\App\Services\Integrations\FluentCart\CustomerProfileService;
20 +use FluentCrm\App\Services\PermissionManager as CrmPermissionManager;
15 21
16 22 class SchedulesController extends Controller
17 23 {
18 24 public function index(Request $request)
19 25 {
20 - $filters = $request->get('filters', []);
26 + $author = $this->resolveAuthor($request);
21 27
22 - $search = $request->getSafe('search', '');
28 + $query = $this->buildSchedulesQuery($request, $author);
23 29
24 - $eventId = Arr::get($filters, 'event');
30 + $query->groupBy('group_id');
25 31
26 - $author = Arr::get($filters, 'author');
32 + $schedules = $query->paginate();
27 33
28 - $eventType = sanitize_text_field(Arr::get($filters, 'event_type'));
34 + foreach ($schedules as $schedule) {
35 + $this->formatBooking($schedule);
36 + }
29 37
30 - $period = sanitize_text_field(Arr::get($filters, 'period', 'upcoming'));
38 + $data = [
39 + 'schedules' => $schedules,
40 + 'timezone' => 'UTC'
41 + ];
31 42
32 - $range = array_map('sanitize_text_field', Arr::get($filters, 'range', []));
43 + $data['calendar_event_lists'] = CalendarService::getCalendarOptionsByTitle();
33 44
34 - $query = Booking::with(['calendar_event']);
45 + if ($author == 'me') {
46 + $slotOptions = CalendarService::getSlotOptions(null, get_current_user_id());
47 + $data['slot_options'] = $slotOptions;
48 + }
35 49
50 + if ($request->get('page') == 1) {
51 + $this->addCountsForFirstPage($author, $data);
52 + }
53 +
54 + return $data;
55 + }
56 +
57 + public function export(Request $request)
58 + {
59 + $limit = (int) apply_filters('fluent_booking/data_export_limit', 2000);
60 +
61 + $author = $this->resolveAuthor($request);
62 +
63 + $query = $this->buildSchedulesQuery($request, $author);
64 +
65 + $relations = [
66 + 'calendar_event',
67 + 'user',
68 + 'booking_activities' => function ($q) {
69 + $q->where('type', 'cancel_reason');
70 + },
71 + 'booking_meta',
72 + ];
73 +
74 + if (defined('FLUENT_BOOKING_PRO_DIR_FILE')) {
75 + $relations[] = 'payment_order.transaction';
76 + }
77 +
78 + $query->with($relations);
79 +
80 + $total = (clone $query)->withoutEagerLoads()->count();
81 + $limited = $total > $limit;
82 +
83 + $bookings = $query->take($limit)->get();
84 +
85 + $rows = $bookings->map(function ($booking) {
86 + $row = ExportHelper::mapBooking($booking);
87 + return apply_filters('fluent_booking/booking_export_row', $row, $booking);
88 + })->all();
89 +
90 + $rows = apply_filters('fluent_booking/booking_export_columns', $rows, $bookings);
91 +
92 + return [
93 + 'bookings' => $rows,
94 + 'limited' => $limited,
95 + 'total' => $total,
96 + ];
97 + }
98 +
99 + protected function resolveAuthor(Request $request)
100 + {
101 + $author = Arr::get($request->get('filters', []), 'author');
102 +
36 103 if (is_numeric($author)) {
37 - $author = (int)$author;
104 + $author = (int) $author;
38 105 } else {
39 106 $author = sanitize_text_field($author);
40 107 }
41 108
42 - $currentHostId = get_current_user_id();
109 + $hasPermission = PermissionManager::userCanSeeAllBookings();
43 110
44 - $hasPermission = PermissionManager::userCanSeeAllBookings();
111 + if ($hasPermission) {
112 + return $author;
113 + }
45 114
46 - if (!$hasPermission && (!$author || $author == 'all')) {
47 - $authorCalendar = Calendar::where('user_id', $currentHostId)
115 + if (!$author || $author == 'all') {
116 + $authorCalendar = Calendar::where('user_id', get_current_user_id())
48 117 ->where('type', 'simple')
49 118 ->first();
50 119
51 - if ($authorCalendar) {
52 - $author = $authorCalendar->id;
53 - }
120 + $author = $authorCalendar ? $authorCalendar->id : '';
54 121 }
55 122
123 + return $author;
124 + }
125 +
126 + protected function buildSchedulesQuery(Request $request, $author = null)
127 + {
128 + $filters = $request->get('filters', []);
129 + $search = $request->getSafe('search');
130 + $eventVal = Arr::get($filters, 'event');
131 + $eventId = is_numeric($eventVal) ? (int) $eventVal : 0;
132 + $eventType = sanitize_text_field(Arr::get($filters, 'event_type'));
133 + $period = sanitize_text_field(Arr::get($filters, 'period', 'upcoming'));
134 + $range = array_map('sanitize_text_field', Arr::get($filters, 'range', []));
135 + $email = sanitize_email(Arr::get($filters, 'email', ''));
136 +
137 + $allowedPeriods = ['upcoming', 'completed', 'cancelled', 'pending', 'no_show', 'latest_bookings', 'all'];
138 + if (!in_array($period, $allowedPeriods, true)) {
139 + $period = 'upcoming';
140 + }
141 +
142 + $query = Booking::with(['calendar_event']);
143 +
144 + $hasPermission = PermissionManager::userCanSeeAllBookings();
145 +
146 + if (!$hasPermission || $author == 'me') {
147 + $query->where('host_user_id', get_current_user_id());
148 + }
149 +
56 150 if ($author && $author !== 'all') {
57 - if ($author == 'me' || !$hasPermission) {
58 - $query->where('host_user_id', $currentHostId);
59 - }
60 -
61 151 if ($author != 'me') {
62 152 $query->where('calendar_id', $author);
63 153 }
64 -
65 - if ($eventId && $eventId !== 'all') {
66 - $query->where('event_id', (int) $eventId);
154 + if ($eventId > 0) {
155 + $query->where('event_id', $eventId);
67 156 }
68 -
69 157 if ($eventType && $eventType !== 'all') {
70 158 $query->where('event_type', $eventType);
71 159 }
72 160 }
73 161
162 + if (!empty($email) && is_email($email)) {
163 + $query->where('email', $email);
164 + }
165 +
74 166 do_action_ref_array('fluent_booking/schedules_query', [&$query]);
75 167
76 168 $query->applyDateRangeFilter($range);
77 -
78 169 $query->applyComputedStatus($period);
79 -
80 170 $query->applyBookingOrderByStatus($period);
81 171
82 - $query->groupBy('group_id');
83 -
84 172 if (!empty($search)) {
85 173 $query->searchBy($search);
86 174 }
87 175
88 - $schedules = $query->paginate();
89 -
90 - foreach ($schedules as $schedule) {
91 - $this->formatBooking($schedule);
92 - }
93 -
94 - $data = [
95 - 'schedules' => $schedules,
96 - 'timezone' => 'UTC'
97 - ];
98 -
99 - $data['calendar_event_lists'] = CalendarService::getCalendarOptionsByTitle();
100 -
101 - if ($author == 'me') {
102 - $slotOptions = CalendarService::getSlotOptions(null, $currentHostId);
103 - $data['slot_options'] = $slotOptions;
104 - }
105 -
106 - if ($request->get('page') == 1) {
107 - $this->addCountsForFirstPage($author, $data);
108 - }
109 -
110 - return $data;
176 + return $query;
111 177 }
112 178
113 179 private function addCountsForFirstPage($author, &$data)
114 180 {
115 181 $bookingQuery = Booking::query()
116 - ->when($author == 'me', function($query) {
182 + ->when(!PermissionManager::userCanSeeAllBookings() || $author == 'me', function($query) {
117 183 return $query->where('host_user_id', get_current_user_id());
118 184 })
119 185 ->when($author && is_numeric($author), function($query) use ($author) {
120 186 return $query->where('calendar_id', $author);
@@ -174,10 +240,19 @@
174 240 if (!in_array($value, ['scheduled', 'completed', 'cancelled', 'rejected', 'no_show'])) {
175 241 return $this->sendError(['message' => __('Invalid status', 'fluent-booking')]);
176 242 }
177 243
244 + if (in_array($booking->status, ['cancelled', 'rejected'])) {
245 + return $this->sendError(['message' => __('A cancelled or rejected booking can not be changed', 'fluent-booking')]);
246 + }
247 +
178 248 if ($value == 'scheduled' && $booking->payment_method && $booking->payment_order) {
179 249 $order = $booking->payment_order;
250 +
251 + if (in_array($order->status, ['refunded', 'partially-refunded'])) {
252 + return $this->sendError(['message' => __('A refunded payment can not be marked as paid', 'fluent-booking')]);
253 + }
254 +
180 255 $order->total_paid = $order->total_amount;
181 256 $order->completed_at = gmdate('Y-m-d H:i:s'); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
182 257 $order->status = 'paid';
183 258 $order->save();
@@ -192,18 +267,24 @@
192 267 do_action('fluent_booking/log_booking_activity', $this->getConfirmLog($booking->id));
193 268 }
194 269
195 270 if ($value == 'cancelled') {
196 - $cancelReason = sanitize_text_field($data['cancel_reason']);
271 + $cancelReason = sanitize_text_field(Arr::get($data, 'cancel_reason', ''));
197 272 $booking->cancelMeeting($cancelReason, 'host', get_current_user_id());
198 273 }
199 274
200 275 if ($value == 'rejected') {
201 - $rejectReason = sanitize_text_field($data['reject_reason']);
276 + $rejectReason = sanitize_text_field(Arr::get($data, 'reject_reason', ''));
202 277 $booking->rejectMeeting($rejectReason, get_current_user_id());
203 278 }
204 279
205 280 if (in_array($value, ['cancelled', 'rejected'])) {
281 + // cancelMeeting() and rejectMeeting() refuse some statuses without changing the booking.
282 + if ($booking->status != $value) {
283 + /* translators: %s: Booking status */
284 + return $this->sendError(['message' => sprintf(__('This booking can not be %s', 'fluent-booking'), $value)]);
285 + }
286 +
206 287 if ($booking->payment_method && Arr::get($data, 'refund_payment') == 'yes') {
207 288 do_action('fluent_booking/refund_payment_' . $booking->payment_method, $booking, $booking->calendar_event);
208 289 }
209 290 return [
@@ -210,8 +291,22 @@
210 291 /* translators: %s: Booking status */
211 292 'message' => sprintf(__('The booking has been %s', 'fluent-booking'), $value)
212 293 ];
213 294 }
295 +
296 + $updateAll = Arr::isTrue($data, 'update_all') && $booking->isMultiGuestBooking();
297 +
298 + if ($updateAll && in_array($value, ['no_show', 'completed'])) {
299 + Booking::where('event_id', $booking->event_id)
300 + ->where('group_id', $booking->group_id)
301 + ->update([
302 + 'status' => $value
303 + ]);
304 + return [
305 + /* translators: %s: Booking status */
306 + 'message' => sprintf(__('The booking has been %s', 'fluent-booking'), $value)
307 + ];
308 + }
214 309 }
215 310
216 311 if ($column == 'payment_status') {
217 312 if (!in_array($value, ['pending', 'paid'])) {
@@ -259,11 +354,9 @@
259 354 {
260 355 $booking = Booking::with('calendar_event');
261 356
262 357 if (!PermissionManager::userCanSeeAllBookings()) {
263 - $booking->whereHas('calendar', function ($q) {
264 - $q->where('user_id', get_current_user_id());
265 - });
358 + $booking->whereHostAccess(get_current_user_id());
266 359 }
267 360
268 361 $booking = $booking->findOrFail($bookingId);
269 362 $booking = $this->formatBooking($booking);
@@ -301,9 +394,9 @@
301 394 }
302 395
303 396 public function sendConfirmationEmail(Request $request, $bookingId)
304 397 {
305 - $booking = Booking::with(['calendar', 'calendar_event'])->find($bookingId);
398 + $booking = Booking::with(['calendar', 'calendar_event'])->findOrFail($bookingId);
306 399
307 400 $emailTo = $request->get('email_to', 'guest');
308 401
309 402 $notifications = $booking->calendar_event->getNotifications();
@@ -328,11 +421,9 @@
328 421 {
329 422 $booking = Booking::with('slot');
330 423
331 424 if (!PermissionManager::userCanSeeAllBookings()) {
332 - $booking->whereHas('calendar', function ($q) {
333 - $q->where('user_id', get_current_user_id());
334 - });
425 + $booking->whereHostAccess(get_current_user_id());
335 426 }
336 427
337 428 $booking = $booking->where('group_id', $groupId)->first();
338 429
@@ -340,9 +431,9 @@
340 431 return $this->sendError(['message' => __('Invalid group id or the event is not a group event', 'fluent-booking')]);
341 432 }
342 433
343 434 $attendees = Booking::where('group_id', $booking->group_id);
344 - $search = sanitize_text_field($request->get('search'));
435 + $search = $request->getSafe('search');
345 436
346 437 if (!empty($search)) {
347 438 $attendees = $attendees->searchBy($search);
348 439 }
@@ -358,9 +449,12 @@
358 449 }
359 450
360 451 public function getBookingActivities(Request $request, $bookingId)
361 452 {
453 + $this->resolveOwnedBookingOrFail($bookingId);
454 +
362 455 $activities = BookingActivity::where('booking_id', $bookingId)
456 + ->where('type', '!=', BookingActivity::TYPE_NOTE)
363 457 ->orderBy('id', 'DESC')
364 458 ->get();
365 459
366 460 return [
@@ -369,14 +463,15 @@
369 463 }
370 464
371 465 public function getBookingMetaInfo(Request $request, $bookingId)
372 466 {
373 - $booking = Booking::findOrFail($bookingId);
467 + $booking = $this->resolveOwnedBookingOrFail($bookingId);
374 468
375 469 $activities = BookingActivity::where('booking_id', $booking->id)
470 + ->where('type', '!=', BookingActivity::TYPE_NOTE)
376 471 ->orderBy('id', 'DESC')
377 472 ->get();
378 -
473 +
379 474 $activities->each(function ($activity) {
380 475 $activity->description = wp_unslash($activity->description);
381 476 });
382 477
@@ -382,21 +477,33 @@
382 477
383 478 $sidebarContents = [];
384 479 $mainBodyContents = [];
385 480
386 - if (defined('FLUENTCRM')) {
387 - $profileHtml = fluentcrm_get_crm_profile_html($booking->email, false);
388 - if ($profileHtml) {
389 - $sidebarContents[] = [
390 - 'id' => 'fluent_crm_profule',
391 - 'title' => __('CRM Profile', 'fluent-booking'),
392 - 'content' => $profileHtml
393 - ];
394 - }
481 + $canReadCrm = CrmContactService::isActive() && CrmPermissionManager::currentUserCan('fcrm_read_contacts');
482 + $crmProfile = $canReadCrm ? CrmContactService::getProfileData($booking->email) : null;
483 + if ($crmProfile) {
484 + $sidebarContents[] = [
485 + 'id' => 'fluent_crm_profule',
486 + 'title' => __('CRM Profile', 'fluent-booking'),
487 + 'type' => 'crm_profile',
488 + 'profile' => $crmProfile,
489 + ];
395 490 }
396 491
492 + $cartProfile = CustomerProfileService::canView()
493 + ? CustomerProfileService::getProfileData($booking->email)
494 + : null;
495 + if ($cartProfile) {
496 + $sidebarContents[] = [
497 + 'id' => 'fluent_cart_profile',
498 + 'title' => __('Cart Profile', 'fluent-booking'),
499 + 'type' => 'cart_profile',
500 + 'profile' => $cartProfile,
501 + ];
502 + }
503 +
397 504 $order = null;
398 - if ($booking->payment_method && $booking->payment_order) {
505 + if ($booking->payment_status && $booking->payment_order) {
399 506 $order = $booking->payment_order;
400 507 $relations = ['items', 'transaction'];
401 508 if (method_exists($order, 'discounts')) {
402 509 $relations[] = 'discounts';
@@ -415,8 +522,198 @@
415 522 'main_body_contents' => $mainBodyContents
416 523 ];
417 524 }
418 525
526 + /**
527 + * Return the CRM contact state for a booking plus the full tag/list option
528 + * sets, so the admin can manage tags/lists inline from the CRM Profile card.
529 + */
530 + public function getCrmContact(Request $request, $bookingId)
531 + {
532 + $booking = $this->resolveOwnedBookingOrFail($bookingId);
533 +
534 + if (!CrmContactService::isActive()) {
535 + return $this->sendError([
536 + 'message' => __('FluentCRM is not active.', 'fluent-booking')
537 + ]);
538 + }
539 +
540 + if (!CrmPermissionManager::currentUserCan('fcrm_read_contacts')) {
541 + return $this->sendError([
542 + 'message' => __('You do not have permission to read CRM contacts.', 'fluent-booking')
543 + ], 403);
544 + }
545 +
546 + $state = CrmContactService::getContactState($booking->email);
547 +
548 + if (!$state) {
549 + return $this->sendError([
550 + 'message' => __('No CRM contact found for this booking.', 'fluent-booking')
551 + ], 404);
552 + }
553 +
554 + return $state;
555 + }
556 +
557 + /**
558 + * Guest-field prefill for the CRM "Book Appointment" action.
559 + * Keyed by contact id, so it carries its own CRM-read gate.
560 + */
561 + public function getCrmContactPrefill(Request $request)
562 + {
563 + if (!CrmContactService::isActive()) {
564 + return $this->sendError([
565 + 'message' => __('FluentCRM is not active.', 'fluent-booking')
566 + ]);
567 + }
568 +
569 + if (!CrmPermissionManager::currentUserCan('fcrm_read_contacts')) {
570 + return $this->sendError([
571 + 'message' => __('You do not have permission to read CRM contacts.', 'fluent-booking')
572 + ], 403);
573 + }
574 +
575 + $contactId = absint($request->get('contact_id'));
576 +
577 + if (!$contactId) {
578 + return $this->sendError([
579 + 'message' => __('Invalid contact.', 'fluent-booking')
580 + ], 422);
581 + }
582 +
583 + $prefill = CrmContactService::getBookingPrefillData($contactId);
584 +
585 + if (!$prefill) {
586 + return $this->sendError([
587 + 'message' => __('No CRM contact found.', 'fluent-booking')
588 + ], 404);
589 + }
590 +
591 + return [
592 + 'prefill' => $prefill,
593 + ];
594 + }
595 +
596 + /**
597 + * Typeahead search for the admin booking modal's CRM contact picker.
598 + * Same intersection gate as getCrmContactPrefill: booking access (policy) AND fcrm_read_contacts.
599 + */
600 + public function searchCrmContacts(Request $request)
601 + {
602 + if (!CrmContactService::isActive()) {
603 + return $this->sendError([
604 + 'message' => __('FluentCRM is not active.', 'fluent-booking')
605 + ]);
606 + }
607 +
608 + if (!CrmPermissionManager::currentUserCan('fcrm_read_contacts')) {
609 + return $this->sendError([
610 + 'message' => __('You do not have permission to read CRM contacts.', 'fluent-booking')
611 + ], 403);
612 + }
613 +
614 + $search = sanitize_text_field($request->get('search', ''));
615 +
616 + return [
617 + 'contacts' => CrmContactService::searchContacts($search),
618 + ];
619 + }
620 +
621 + /**
622 + * Bounded, searchable tag/list options for the CRM Profile picker.
623 + */
624 + public function getCrmOptions(Request $request, $bookingId)
625 + {
626 + $this->resolveOwnedBookingOrFail($bookingId);
627 +
628 + if (!CrmContactService::isActive()) {
629 + return $this->sendError([
630 + 'message' => __('FluentCRM is not active.', 'fluent-booking')
631 + ]);
632 + }
633 +
634 + if (!CrmPermissionManager::currentUserCan('fcrm_read_contacts')) {
635 + return $this->sendError([
636 + 'message' => __('You do not have permission to read CRM contacts.', 'fluent-booking')
637 + ], 403);
638 + }
639 +
640 + $type = $request->get('type') === 'lists' ? 'lists' : 'tags';
641 + $search = sanitize_text_field($request->get('search', ''));
642 +
643 + return [
644 + 'options' => CrmContactService::getOptions($type, $search),
645 + ];
646 + }
647 +
648 + public function updateCrmTags(Request $request, $bookingId)
649 + {
650 + return $this->updateCrmTaxonomy($request, $bookingId, 'tags');
651 + }
652 +
653 + public function updateCrmLists(Request $request, $bookingId)
654 + {
655 + return $this->updateCrmTaxonomy($request, $bookingId, 'lists');
656 + }
657 +
658 + private function updateCrmTaxonomy(Request $request, $bookingId, $type)
659 + {
660 + $booking = $this->resolveOwnedBookingOrFail($bookingId);
661 +
662 + if (!CrmContactService::isActive()) {
663 + return $this->sendError([
664 + 'message' => __('FluentCRM is not active.', 'fluent-booking')
665 + ]);
666 + }
667 +
668 + if (!CrmPermissionManager::currentUserCan('fcrm_manage_contacts')) {
669 + return $this->sendError([
670 + 'message' => __('You do not have permission to manage CRM contacts.', 'fluent-booking')
671 + ], 403);
672 + }
673 +
674 + $requestKey = $type === 'tags' ? 'tag_ids' : 'list_ids';
675 + $desired = (array) $request->get($requestKey, []);
676 +
677 + $result = CrmContactService::syncTaxonomy($booking->email, $type, $desired);
678 +
679 + if ($result === null) {
680 + return $this->sendError([
681 + 'message' => __('No CRM contact found for this booking.', 'fluent-booking')
682 + ], 404);
683 + }
684 +
685 + return $this->sendSuccess(array_merge([
686 + 'message' => __('CRM contact updated successfully.', 'fluent-booking'),
687 + ], $result));
688 + }
689 +
690 + private function resolveOwnedBookingOrFail($bookingId)
691 + {
692 + $booking = Booking::findOrFail($bookingId);
693 +
694 + if (PermissionManager::userCanSeeAllBookings()) {
695 + return $booking;
696 + }
697 +
698 + $allowedIds = $booking->getHostIds();
699 +
700 + if (PermissionManager::userCan('manage_own_calendar')) {
701 + if ($event = CalendarSlot::find($booking->event_id)) {
702 + $allowedIds = array_merge($allowedIds, $event->getHostIds());
703 + }
704 + }
705 +
706 + if (in_array(get_current_user_id(), $allowedIds)) {
707 + return $booking;
708 + }
709 +
710 + $exception = new ModelNotFoundException();
711 + $exception->setModel(Booking::class, [$bookingId]);
712 + // phpcs:ignore WordPress.Security.EscapeOutput.ExceptionNotEscaped
713 + throw $exception;
714 + }
715 +
419 716 private function formatBooking(&$booking)
420 717 {
421 718 $autoCompleteTimeOut = (int) Helper::getGlobalAdminSetting('auto_complete_timing', 60) * 60; // 10 minutes
422 719
@@ -423,9 +720,10 @@
423 720 if (in_array($booking->status, ['scheduled', 'pending']) && (time() - strtotime($booking->end_time)) > $autoCompleteTimeOut) {
424 721 $bookingStatus = $booking->status == 'pending' ? 'cancelled' : 'completed';
425 722 $booking->status = $bookingStatus;
426 723 $booking->save();
427 - do_action('fluent_booking/booking_schedule_' . $bookingStatus, $booking, $booking->calendar_event);
724 + $hookName = $bookingStatus === 'cancelled' ? 'auto_cancelled' : $bookingStatus;
725 + do_action('fluent_booking/booking_schedule_' . $hookName, $booking, $booking->calendar_event);
428 726 }
429 727
430 728 if ($booking->isMultiHostBooking()) {
431 729 $booking->host_profiles = $booking->getHostProfiles();