PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 1.7.1
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v1.7.1
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 1.7.2 All 33 releases
fluent-booking / app / Http / Controllers / SchedulesController.php

SchedulesController.php in Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution 1.7.1, at app/Http/Controllers/SchedulesController.php

487 lines 16.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace FluentBooking\App\Http\Controllers;
4
5 use FluentBooking\App\Models\Booking;
6 use FluentBooking\App\Models\Calendar;
7 use FluentBooking\App\Models\BookingActivity;
8 use FluentBooking\App\Services\EmailNotificationService;
9 use FluentBooking\App\Services\Helper;
10 use FluentBooking\App\Services\CurrenciesHelper;
11 use FluentBooking\Framework\Support\Arr;
12 use FluentBooking\Framework\Http\Request\Request;
13 use FluentBooking\App\Services\PermissionManager;
14 use FluentBooking\App\Services\CalendarService;
15
16 class SchedulesController extends Controller
17 {
18 public function index(Request $request)
19 {
20 $filters = $request->get('filters', []);
21
22 $period = Arr::get($filters, 'period', 'upcoming');
23
24 $eventId = Arr::get($filters, 'event');
25
26 $eventType = Arr::get($filters, 'event_type');
27
28 $author = Arr::get($filters, 'author');
29
30 $query = Booking::with(['calendar_event']);
31
32 if (is_numeric($author)) {
33 $author = (int)$author;
34 }
35
36 $hasPermission = PermissionManager::userCanSeeAllBookings();
37
38 if (!$hasPermission) {
39 if (!$author || $author == 'all') {
40 $authorCalendar = Calendar::where('user_id', get_current_user_id())
41 ->where('type', 'simple')
42 ->first();
43 if($authorCalendar) {
44 $author = $authorCalendar->id;
45 }
46 }
47 }
48
49 if ($author && $author !== 'all') {
50 if ($author == 'me') {
51 $query->where('host_user_id', get_current_user_id());
52 } else {
53 $query->where('calendar_id', $author);
54 if (!$hasPermission) {
55 $query->where('host_user_id', get_current_user_id());
56 }
57 }
58
59 if ($eventId && $eventId !== 'all') {
60 $query->where('event_id', $eventId);
61 }
62
63 if ($eventType && $eventType !== 'all') {
64 $query->where('event_type', $eventType);
65 }
66 }
67
68 do_action_ref_array('fluent_booking/schedules_query', [&$query]);
69
70 $query->applyComputedStatus($period);
71
72 $query->applyBookingOrderByStatus($period);
73
74 $query->groupBy('group_id');
75
76 $search = Arr::get($filters, 'search');
77
78 if (!empty($search)) {
79 $query = $query->orderBy('start_time', 'DESC');
80 $query = $query->searchBy($search);
81 }
82
83 $schedules = $query->paginate();
84
85 foreach ($schedules as $schedule) {
86 $this->formatBooking($schedule);
87 }
88
89 $data = [
90 'schedules' => $schedules,
91 'timezone' => 'UTC'
92 ];
93
94 $data['calendar_event_lists'] = CalendarService::getCalendarOptionsByTitle();
95
96 if ($author == 'me') {
97 $slotOptions = CalendarService::getSlotOptions(null, get_current_user_id());
98 $data['slot_options'] = $slotOptions;
99 }
100
101 if ($request->get('page') == 1) {
102 $pendingCount = Booking::query()
103 ->whereIn('status', ['pending', 'reserved'])
104 ->distinct('group_id')
105 ->when($author == 'me', function($query) {
106 return $query->where('host_user_id', get_current_user_id());
107 })
108 ->when($author && is_numeric($author), function($query) use ($author) {
109 return $query->where('calendar_id', $author);
110 })
111 ->count('group_id');
112
113 $data['pending_count'] = $pendingCount;
114 $data['no_show_count'] = Booking::where('status', 'no_show')->count();
115 $data['cancelled_count'] = Booking::where('status', 'cancelled')->count();
116 }
117
118 return $data;
119 }
120
121 public function patchBooking(Request $request, $bookingId)
122 {
123 $booking = Booking::findOrFail($bookingId);
124 $oldBooking = clone $booking;
125
126 $data = $request->all();
127
128 $this->validate($data, [
129 'column' => 'required',
130 ]);
131
132 do_action('fluent_booking/before_patch_booking_schedule', $booking, $data);
133
134 $value = $request->get('value');
135
136 $column = $data['column'];
137
138 if ($booking->{$column} == $value) {
139 return $this->sendError(['message' => __('No changes found', 'fluent-booking')]);
140 }
141
142 $validColumns = [
143 'internal_note',
144 'email',
145 'phone',
146 'first_name',
147 'last_name',
148 'status',
149 'payment_status'
150 ];
151
152 if (!in_array($column, $validColumns)) {
153 return $this->sendError(['message' => __('Invalid column', 'fluent-booking')]);
154 }
155
156 if ($column === 'email') {
157 if (!$value || !is_email($value)) {
158 return $this->sendError(['message' => __('Invalid email address', 'fluent-booking')]);
159 }
160 $value = sanitize_email($value);
161 } else {
162 $value = sanitize_text_field($value);
163 }
164
165 if ($column == 'status') {
166 if (!in_array($value, ['scheduled', 'completed', 'cancelled', 'rejected', 'no_show'])) {
167 return $this->sendError(['message' => __('Invalid status', 'fluent-booking')]);
168 }
169
170 if ($value == 'scheduled' && $booking->payment_method && $booking->payment_order) {
171 $order = $booking->payment_order;
172 $order->total_paid = $order->total_amount;
173 $order->completed_at = gmdate('Y-m-d H:i:s'); // phpcs:ignore WordPress.DateTime.RestrictedFunctions.date_date
174 $order->status = 'paid';
175 $order->save();
176
177 $updateData['payment_status'] = 'paid';
178
179 do_action('fluent_booking/log_booking_activity', $this->getPaymentPaidLog($booking->id));
180
181 do_action('fluent_booking/payment/update_payment_status_paid', $booking);
182
183 } else if ($value == 'scheduled') {
184 do_action('fluent_booking/log_booking_activity', $this->getConfirmLog($booking->id));
185 }
186
187 if ($value == 'cancelled') {
188 $cancelReason = sanitize_text_field($data['cancel_reason']);
189 $booking->cancelMeeting($cancelReason, 'host', get_current_user_id());
190 return [
191 'message' => __('The booking has been cancelled', 'fluent-booking')
192 ];
193 }
194
195 if ($value == 'rejected') {
196 $rejectReason = sanitize_text_field($data['reject_reason']);
197 $booking->rejectMeeting($rejectReason, get_current_user_id());
198 return [
199 'message' => __('The booking has been rejected', 'fluent-booking')
200 ];
201 }
202
203 if ($booking->payment_method && Arr::get($data, 'refund_payment') == 'yes' && in_array($value, ['cancelled', 'rejected'])) {
204 do_action('fluent_booking/refund_payment_' . $booking->payment_method, $booking, $booking->calendar_event);
205 }
206 }
207
208 if ($column == 'payment_status') {
209 if (!in_array($value, ['pending', 'paid'])) {
210 return $this->sendError(['message' => __('Invalid payment status', 'fluent-booking')]);
211 }
212
213 if ($value == 'paid') {
214 do_action('fluent_booking/log_booking_activity', $this->getPaymentPaidLog($booking->id));
215 }
216
217 if ($value == 'pending') {
218 do_action('fluent_booking/log_booking_activity', $this->getPaymentPendingLog($booking->id));
219 }
220
221 if ($booking->payment_order) {
222 do_action('fluent_booking/payment/status_changed', $booking->payment_order, $booking, $value);
223 }
224 }
225
226 $updateData[$column] = $value;
227 $booking->fill($updateData);
228 $booking->save();
229
230 if ($column === 'status') {
231 do_action('fluent_booking/booking_schedule_' . $value, $booking, $booking->calendar_event);
232
233 do_action('fluent_booking/pre_after_booking_' . $value, $booking, $booking->calendar_event);
234
235 $booking = Booking::with(['calendar_event', 'calendar'])->find($booking->id);
236
237 do_action('fluent_booking/after_booking_' . $value, $booking, $booking->calendar_event, $booking);
238 }
239
240 do_action('fluent_booking/after_patch_booking_schedule', $booking, $oldBooking);
241
242 do_action('fluent_booking/after_patch_booking_' . $column, $booking, $booking->calendar_event, $oldBooking->{$column});
243
244 return [
245 /* translators: Updated column name */
246 'message' => sprintf(__('%s has been updated', 'fluent-booking'), ucfirst($column))
247 ];
248 }
249
250 public function getBooking(Request $request, $bookingId)
251 {
252 $booking = Booking::with('calendar_event');
253
254 if (!PermissionManager::userCanSeeAllBookings()) {
255 $booking->whereHas('calendar', function ($q) {
256 $q->where('user_id', get_current_user_id());
257 });
258 }
259
260 $booking = $booking->findOrFail($bookingId);
261 $booking = $this->formatBooking($booking);
262
263 do_action_ref_array('fluent_booking/booking_schedule', [&$booking]);
264
265 $data = [
266 'schedule' => $booking
267 ];
268
269 if (in_array('all_data', $this->request->get('with', []))) {
270 $data = array_merge($data, $this->getBookingMetaInfo($request, $bookingId));
271 }
272
273 return $data;
274 }
275
276 public function deleteBooking(Request $request, $bookingId)
277 {
278 $booking = Booking::findOrFail($bookingId);
279
280 do_action('fluent_booking/before_delete_booking', $booking);
281
282 $booking->delete();
283
284 do_action('fluent_booking/after_delete_booking', $bookingId);
285
286 if ($booking->isMultiGuestBooking()) {
287 Booking::where('event_id', $booking->event_id)->where('group_id', $booking->group_id)->delete();
288 }
289
290 return [
291 'message' => __('Booking Deleted Successfully!', 'fluent-booking')
292 ];
293 }
294
295 public function sendConfirmationEmail(Request $request, $bookingId)
296 {
297 $booking = Booking::with(['calendar', 'calendar_event'])->find($bookingId);
298
299 $emailTo = $request->get('email_to', 'guest');
300
301 $notifications = $booking->calendar_event->getNotifications();
302
303 $email = Arr::get($notifications, 'booking_conf_attendee.email', []);
304 if ($emailTo == 'host') {
305 $email = Arr::get($notifications, 'booking_conf_host.email', []);
306 }
307
308 $result = EmailNotificationService::emailOnBooked($booking, $email, $emailTo, 'scheduled', true);
309
310 if (!$result) {
311 return $this->sendError(['message' => __('Notification sending failed', 'fluent-booking')]);
312 }
313
314 return [
315 'message' => __('Notification sent successfully', 'fluent-booking')
316 ];
317 }
318
319 public function getGroupAttendees(Request $request, $groupId)
320 {
321 $booking = Booking::with('slot');
322
323 if (!PermissionManager::userCanSeeAllBookings()) {
324 $booking->whereHas('calendar', function ($q) {
325 $q->where('user_id', get_current_user_id());
326 });
327 }
328
329 $booking = $booking->where('group_id', $groupId)->first();
330
331 if (!$booking || !$booking->isMultiGuestBooking()) {
332 return $this->sendError(['message' => __('Invalid group id or the event is not a group event', 'fluent-booking')]);
333 }
334
335 $attendees = Booking::where('group_id', $booking->group_id);
336 $search = sanitize_text_field($request->get('search'));
337
338 if (!empty($search)) {
339 $attendees = $attendees->searchBy($search);
340 }
341 $attendees = $attendees->paginate();
342
343 foreach ($attendees as $attendee) {
344 $attendee = $this->formatBooking($attendee);
345 }
346
347 return [
348 'attendees' => $attendees
349 ];
350 }
351
352 public function getBookingActivities(Request $request, $bookingId)
353 {
354 $activities = BookingActivity::where('booking_id', $bookingId)
355 ->orderBy('id', 'DESC')
356 ->get();
357
358 return [
359 'activities' => $activities
360 ];
361 }
362
363 public function getBookingMetaInfo(Request $request, $bookingId)
364 {
365 $booking = Booking::findOrFail($bookingId);
366
367 $activities = BookingActivity::where('booking_id', $booking->id)
368 ->orderBy('id', 'DESC')
369 ->get();
370
371 $activities->each(function ($activity) {
372 $activity->description = wp_unslash($activity->description);
373 });
374
375 $sidebarContents = [];
376 $mainBodyContents = [];
377
378 if (defined('FLUENTCRM')) {
379 $profileHtml = fluentcrm_get_crm_profile_html($booking->email, false);
380 if ($profileHtml) {
381 $sidebarContents[] = [
382 'id' => 'fluent_crm_profule',
383 'title' => __('CRM Profile', 'fluent-booking'),
384 'content' => $profileHtml
385 ];
386 }
387 }
388
389 $order = null;
390 if ($booking->payment_method && $booking->payment_order) {
391 $order = $booking->payment_order;
392 $order->load(['items', 'transaction']);
393 $order->currency_sign = CurrenciesHelper::getCurrencySign($order->currency);
394 }
395
396 $mainBodyContents = apply_filters('fluent_booking/booking_meta_info_main_meta', $mainBodyContents, $booking);
397 $mainBodyContents = apply_filters('fluent_booking/booking_meta_info_main_meta_' . $booking->source, $mainBodyContents, $booking);
398
399 return [
400 'activities' => $activities,
401 'sidebar_contents' => $sidebarContents,
402 'payment_order' => $order,
403 'main_body_contents' => $mainBodyContents
404 ];
405 }
406
407 private function formatBooking(&$booking)
408 {
409 $autoCompleteTimeOut = (int) Helper::getGlobalAdminSetting('auto_complete_timing', 60) * 60; // 10 minutes
410
411 if (in_array($booking->status, ['scheduled', 'pending']) && (time() - strtotime($booking->end_time)) > $autoCompleteTimeOut) {
412 $bookingStatus = $booking->status == 'pending' ? 'cancelled' : 'completed';
413 $booking->status = $bookingStatus;
414 $booking->save();
415 do_action('fluent_booking/booking_schedule_' . $bookingStatus, $booking, $booking->calendar_event);
416 }
417
418 if ($booking->isMultiHostBooking()) {
419 $booking->host_profiles = $booking->getHostProfiles();
420 }
421
422 if ($booking->isMultiGuestBooking()) {
423 $booking->booked_count = Booking::where('group_id', $booking->group_id)
424 ->whereIn('status', ['scheduled', 'completed'])->count();
425 } else {
426 $booking->additional_guests = $booking->getAdditionalGuests();
427 }
428
429 $booking->title = $booking->getBookingTitle(true);
430 $booking->author = $booking->getHostDetails(false);
431 $booking->location = $booking->getLocationDetailsHtml();
432 $booking->reschedule_url = $booking->getRescheduleUrl();
433 $booking->happening_status = $booking->getOngoingStatus();
434 $booking->booking_status_text = $booking->getBookingStatus();
435 $booking->payment_status_text = $booking->getPaymentStatus();
436 $booking->custom_form_data = $booking->getCustomFormData();
437
438 do_action_ref_array('fluent_booking/format_booking_schedule', [&$booking]);
439
440 return $booking;
441 }
442
443 private function getConfirmedBy()
444 {
445 $confirmedBy = 'host';
446 $userId = get_current_user_id();
447 if ($userId && $user = get_user_by('ID', $userId)) {
448 $confirmedBy = $user->display_name;
449 }
450
451 return $confirmedBy;
452 }
453
454 private function getPaymentPaidLog($bookingId)
455 {
456 return [
457 'booking_id' => $bookingId,
458 'status' => 'closed',
459 'type' => 'success',
460 'title' => __('Payment Successfully Completed', 'fluent-booking'),
461 'description' => __('Payment marked as paid by ', 'fluent-booking') . $this->getConfirmedBy()
462 ];
463 }
464
465 private function getPaymentPendingLog($bookingId)
466 {
467 return [
468 'booking_id' => $bookingId,
469 'status' => 'closed',
470 'type' => 'success',
471 'title' => __('Payment Successfully Marked as Pending', 'fluent-booking'),
472 'description' => __('Payment marked as pending by ', 'fluent-booking') . $this->getConfirmedBy()
473 ];
474 }
475
476 private function getConfirmLog($bookingId)
477 {
478 return [
479 'booking_id' => $bookingId,
480 'status' => 'closed',
481 'type' => 'success',
482 'title' => __('Booking Confirmed', 'fluent-booking'),
483 'description' => __('Booking has been confirmed by ', 'fluent-booking') . $this->getConfirmedBy()
484 ];
485 }
486 }
487