PluginProbe
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution / 1.5.25
Fluent Booking – The Ultimate Appointments Scheduling, Events Booking, Events Calendar Solution v1.5.25
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.5.25, at app/Http/Controllers/SchedulesController.php

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