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

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