PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.3
Booktics – Appointment Booking Calendar for Service Businesses v1.0.3
1.0.29 1.0.28 1.0.27 1.0.26 1.0.25 1.0.24 1.0.23 1.0.22 1.0.21 1.0.20 1.0.19 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.2 1.0.3 All 31 releases
booktics / core / appointment / controllers / appointment-controller.php

appointment-controller.php in Booktics – Appointment Booking Calendar for Service Businesses 1.0.3, at core/appointment/controllers/appointment-controller.php

873 lines 31.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Booktics\Appointment\Controllers;
4
5 use Booktics\Abstracts\Base_Rest_Controller;
6 use Booktics\Abstracts\Booktics_Database;
7 use Booktics\Appointment\Schedule\Default_Schedule;
8 use Booktics\Appointment\Schedule\Service_Schedule;
9 use Booktics\Appointment\Schedule\Team_Member_Schedule;
10 use Booktics\Appointment\Schedule\Time_Slot_Manager;
11 use Booktics\Appointment\Services\Appointment_Service;
12 use Booktics\Appointment\Services\Appointment_Validator;
13 use Booktics\Models\Appointment_Model;
14 use Booktics\Models\Service_Model;
15 use Exception;
16 use WP_Error;
17 use WP_HTTP_Response;
18 use WP_REST_Request;
19 use WP_REST_Response;
20 use WP_REST_Server;
21
22 /**
23 * Appointment controller
24 *
25 * @package Booktics/Appointment
26 */
27 class Appointment_Controller extends Base_Rest_Controller {
28 /**
29 * Endpoint namespace
30 *
31 * @var string
32 */
33 protected $namespace = 'booktics/v1';
34
35 /**
36 * Route name
37 *
38 * @var string
39 */
40 protected $base = 'appointments';
41
42 /**
43 * Register routes
44 *
45 * @return void
46 */
47 public function register_routes(): void {
48 register_rest_route(
49 $this->namespace,
50 $this->base,
51 array(
52 array(
53 'methods' => WP_REST_Server::CREATABLE,
54 'callback' => array( $this, 'create_item' ),
55 'permission_callback' => array(
56 $this,
57 'create_item_permissions_check',
58 ),
59 ),
60 )
61 );
62
63 register_rest_route(
64 $this->namespace,
65 $this->base,
66 array(
67 array(
68 'methods' => WP_REST_Server::READABLE,
69 'callback' => array( $this, 'get_items' ),
70 'permission_callback' => array(
71 $this,
72 'get_items_permissions_check',
73 ),
74 ),
75 )
76 );
77
78 register_rest_route(
79 $this->namespace,
80 $this->base,
81 array(
82 array(
83 'methods' => WP_REST_Server::DELETABLE,
84 'callback' => array( $this, 'delete_items' ),
85 'permission_callback' => array(
86 $this,
87 'delete_items_permissions_check',
88 ),
89 ),
90 )
91 );
92
93 register_rest_route(
94 $this->namespace,
95 $this->base . '/(?P<id>[\d]+)',
96 array(
97 array(
98 'methods' => WP_REST_Server::READABLE,
99 'callback' => array( $this, 'get_item' ),
100 'permission_callback' => array(
101 $this,
102 'get_item_permissions_check',
103 ),
104 ),
105 )
106 );
107
108 register_rest_route(
109 $this->namespace,
110 $this->base . '/(?P<id>[\d]+)',
111 array(
112 array(
113 'methods' => WP_REST_Server::EDITABLE,
114 'callback' => array( $this, 'update_item' ),
115 'permission_callback' => array(
116 $this,
117 'update_item_permissions_check',
118 ),
119 ),
120 )
121 );
122
123 register_rest_route(
124 $this->namespace,
125 $this->base . '/(?P<id>[\d]+)',
126 array(
127 array(
128 'methods' => WP_REST_Server::DELETABLE,
129 'callback' => array( $this, 'delete_item' ),
130 'permission_callback' => array(
131 $this,
132 'delete_item_permissions_check',
133 ),
134 ),
135 )
136 );
137
138 register_rest_route(
139 $this->namespace,
140 $this->base . '/(?P<id>[\d]+)/reschedule',
141 array(
142 array(
143 'methods' => WP_REST_Server::EDITABLE,
144 'callback' => array( $this, 'reschedule_item' ),
145 'permission_callback' => array(
146 $this,
147 'reschedule_item_permissions_check',
148 ),
149 ),
150 )
151 );
152
153 register_rest_route(
154 $this->namespace,
155 $this->base . '/(?P<id>[\d]+)/cancel',
156 array(
157 array(
158 'methods' => WP_REST_Server::EDITABLE,
159 'callback' => array( $this, 'cancel_item' ),
160 'permission_callback' => array(
161 $this,
162 'cancel_item_permissions_check',
163 ),
164 ),
165 )
166 );
167
168 register_rest_route(
169 $this->namespace,
170 $this->base . '/time-slots',
171 array(
172 array(
173 'methods' => WP_REST_Server::READABLE,
174 'callback' => array( $this, 'get_time_slots' ),
175 'permission_callback' => array(
176 $this,
177 'get_time_slots_permissions_check',
178 ),
179 ),
180 )
181 );
182
183 register_rest_route(
184 $this->namespace,
185 $this->base . '/calendar',
186 array(
187 array(
188 'methods' => WP_REST_Server::READABLE,
189 'callback' => array(
190 $this,
191 'get_all_calendar_items'
192 ),
193 'permission_callback' => array(
194 $this,
195 'get_items_permissions_check'
196 ),
197 'args' => array(
198 'month' => array(
199 'description' => __( 'Month in YYYY-MM format', 'booktics' ),
200 'type' => 'string',
201 'validate_callback' => function ( $param ) {
202 return preg_match( '/^\d{4}-\d{2}$/', $param );
203 },
204 ),
205 ),
206 ),
207 )
208 );
209 }
210
211 /**
212 * Get a collection of items.
213 *
214 * @param WP_REST_Request $request Full data about the request.
215 *
216 * @return WP_HTTP_Response
217 */
218 public function get_items( $request ) {
219 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 10;
220 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
221 $date_from = ! empty( $request['date_from'] ) ? sanitize_text_field( $request['date_from'] ) : null;
222
223 $service_id = ! empty( $request['service_id'] ) ? intval( $request['service_id'] ) : null;
224 $customer_id = ! empty( $request['customer_id'] ) ? intval( $request['customer_id'] ) : null;
225 $payment_status = ! empty( $request['payment_status'] ) ? sanitize_text_field( $request['payment_status'] ) : null;
226 $team_member_id = ! empty( $request['team_member_id'] ) ? intval( $request['team_member_id'] ) : null;
227 $status = ! empty( $request['status'] ) ? sanitize_text_field( $request['status'] ) : null;
228
229 $search = ! empty( $request['search'] ) ? sanitize_text_field( $request['search'] ) : null;
230 $args = array( 'meta_query' => array() );
231
232 if ( $search ) {
233 $service_ids = [];
234 $customer_ids = [];
235 $team_member_ids = [];
236 // Search Services
237 $services = Service_Model::all( [
238 's' => $search,
239 ] );
240 $service_ids = array_map( function ( $service ) {
241 return $service->id;
242 }, $services );
243
244 // Search Customers
245 $customer_query = new \WP_User_Query( [
246 'search' => '*' . esc_attr( $search ) . '*',
247 'search_columns' => [ 'display_name', 'user_email' ],
248 'role' => 'booktics_customer',
249 'fields' => 'ID'
250 ] );
251 $customer_ids = $customer_query->get_results();
252
253 // Search Team Members
254 $team_member_query = new \WP_User_Query( [
255 'search' => '*' . esc_attr( $search ) . '*',
256 'search_columns' => [ 'display_name', 'user_email' ],
257 'role' => 'booktics_team_member',
258 'fields' => 'ID'
259 ] );
260 $team_member_ids = $team_member_query->get_results();
261
262 if ( $service_ids || $customer_ids || $team_member_ids ) {
263 $args['meta_query'] = [ 'relation' => 'OR' ];
264 if ( $service_ids ) {
265 $args['meta_query'][] = [
266 'key' => 'service_id',
267 'value' => $service_ids,
268 'compare' => 'IN'
269 ];
270 }
271 if ( $customer_ids ) {
272 $args['meta_query'][] = [
273 'key' => 'customer_id',
274 'value' => $customer_ids,
275 'compare' => 'IN'
276 ];
277 }
278 if ( $team_member_ids ) {
279 $args['meta_query'][] = [
280 'key' => 'team_member_id',
281 'value' => $team_member_ids,
282 'compare' => 'IN'
283 ];
284 }
285 } else {
286 return $this->response( [
287 'items' => [],
288 'total' => 0,
289 'per_page' => $per_page,
290 'current_page' => $paged,
291 'last_page' => 0,
292 ], __( 'No appointments found', 'booktics' ) );
293 }
294 }
295
296 if ( $date_from ) {
297 $args['meta_query'][] = array(
298 'key' => 'date',
299 'value' => $date_from,
300 'compare' => '>=',
301 'type' => 'DATE',
302 );
303 }
304
305 if ( $service_id ) {
306 $args['meta_query'][] = array(
307 'key' => 'service_id',
308 'value' => $service_id,
309 'compare' => '=',
310 );
311 }
312
313 if ( $customer_id ) {
314 $args['meta_query'][] = array(
315 'key' => 'customer_id',
316 'value' => $customer_id,
317 'compare' => '=',
318 );
319 }
320
321 if ( $payment_status ) {
322 $args['meta_query'][] = array(
323 'key' => 'payment_status',
324 'value' => $payment_status,
325 'compare' => '=',
326 );
327 }
328
329 if ( $team_member_id ) {
330 $args['meta_query'][] = array(
331 'key' => 'team_member_id',
332 'value' => $team_member_id,
333 'compare' => '=',
334 );
335 }
336
337 if ( $status ) {
338 $args['meta_query'][] = array(
339 'key' => 'status',
340 'value' => $status,
341 'compare' => '=',
342 );
343 }
344
345 $data = Appointment_Model::paginate( $per_page, $paged, $args );
346
347 if ( ! $data ) {
348 return $this->error( __( 'No appointment found', 'booktics' ), 404 );
349 }
350
351 return $this->response( $data );
352 }
353
354 /**
355 * Get one item from the collection.
356 *
357 * @param WP_REST_Request $request Full data about the request.
358 *
359 * @return WP_Error|WP_REST_Response
360 */
361 public function get_item( $request ) {
362 $id = intval( $request['id'] );
363
364 $appointment = Appointment_Model::find( $id );
365
366 if ( ! $appointment ) {
367 return $this->error( __( 'Appointment not found', 'booktics' ), 404 );
368 }
369
370 return $this->response( $appointment );
371 }
372
373 /**
374 * Get all calendar items within a date range
375 *
376 * @param WP_REST_Request $request Full data about the request.
377 *
378 * @return WP_Error|WP_REST_Response
379 */
380 public function get_all_calendar_items( $request ) {
381 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 500;
382 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
383
384 // Check if custom date range is provided
385 if ( ! empty( $request['start_date'] ) && ! empty( $request['end_date'] ) ) {
386 $adjusted_start_date = sanitize_text_field( $request['start_date'] );
387 $adjusted_end_date = sanitize_text_field( $request['end_date'] );
388 $month = date( 'Y-m', strtotime( $adjusted_start_date ) );
389 } else {
390 // Fall back to month-based date range with week adjustment
391 $month = ! empty( $request['month'] ) ? sanitize_text_field( $request['month'] ) : gmdate( 'Y-m' );
392 $start_date = gmdate( 'Y-m-01', strtotime( $month ) );
393 $end_date = gmdate( 'Y-m-t', strtotime( $month ) );
394
395 // Get the first day of the week (Sunday = 0, Monday = 1, etc.)
396 $first_day_of_week = get_option( 'start_of_week', 1 );
397
398 // Adjust start date to include days from previous month to complete the first week
399 $start_date_obj = new \DateTime( $start_date );
400 $start_week_day = (int) $start_date_obj->format( 'w' ); // 0 (Sun) to 6 (Sat)
401 $days_to_subtract = ( $start_week_day - $first_day_of_week + 7 ) % 7;
402 $start_date_obj->modify( "-$days_to_subtract days" );
403 $adjusted_start_date = $start_date_obj->format( 'Y-m-d' );
404
405 // Adjust end date to include days from next month to complete the last week
406 $end_date_obj = new \DateTime( $end_date );
407 $end_week_day = (int) $end_date_obj->format( 'w' ); // 0 (Sun) to 6 (Sat)
408 $days_to_add = ( 6 - $end_week_day + $first_day_of_week ) % 7;
409 $end_date_obj->modify( "+$days_to_add days" );
410 $adjusted_end_date = $end_date_obj->format( 'Y-m-d' );
411 }
412
413 // Get all appointments within the date range using meta_query
414 $args = array(
415 'meta_query' => array(
416 'relation' => 'AND',
417 array(
418 'key' => 'date',
419 'value' => array(
420 $adjusted_start_date,
421 $adjusted_end_date
422 ),
423 'compare' => 'BETWEEN',
424 'type' => 'DATE',
425 ),
426 ),
427 'posts_per_page' => - 1, // Get all appointments in the range
428 'orderby' => 'meta_value',
429 'meta_key' => 'date',
430 'order' => 'ASC',
431 );
432
433 $service_id = ! empty( $request['service_id'] ) ? intval( $request['service_id'] ) : null;
434 $customer_id = ! empty( $request['customer_id'] ) ? intval( $request['customer_id'] ) : null;
435 $payment_status = ! empty( $request['payment_status'] ) ? sanitize_text_field( $request['payment_status'] ) : null;
436 $team_member_id = ! empty( $request['team_member_id'] ) ? intval( $request['team_member_id'] ) : null;
437 $status = ! empty( $request['status'] ) ? sanitize_text_field( $request['status'] ) : null;
438
439 if ( $service_id ) {
440 $args['meta_query'][] = array(
441 'key' => 'service_id',
442 'value' => $service_id,
443 'compare' => '=',
444 );
445 }
446
447 if ( $customer_id ) {
448 $args['meta_query'][] = array(
449 'key' => 'customer_id',
450 'value' => $customer_id,
451 'compare' => '=',
452 );
453 }
454
455 if ( $payment_status ) {
456 $args['meta_query'][] = array(
457 'key' => 'payment_status',
458 'value' => $payment_status,
459 'compare' => '=',
460 );
461 }
462
463 if ( $team_member_id ) {
464 $args['meta_query'][] = array(
465 'key' => 'team_member_id',
466 'value' => $team_member_id,
467 'compare' => '=',
468 );
469 }
470
471 if ( $status ) {
472 $args['meta_query'][] = array(
473 'key' => 'status',
474 'value' => $status,
475 'compare' => '=',
476 );
477 }
478
479 $appointments = Appointment_Model::paginate( $per_page, $paged, $args );
480
481 // Format the response
482 $formatted_appointments = array();
483
484 if ( ! empty( $appointments ) ) {
485 foreach ( $appointments['items'] as $appointment ) {
486 if ( ! empty( $appointment ) ) {
487 $appointment->source = 'booktics';
488 }
489
490 $formatted_appointments[] = $appointment;
491 }
492 }
493
494 // Allow other developers to add/modify appointments
495 $formatted_appointments = apply_filters(
496 'booktics_appointments_for_calendar_view',
497 $formatted_appointments,
498 array(
499 'start_date' => $adjusted_start_date,
500 'end_date' => $adjusted_end_date,
501 )
502 );
503
504 $response = array(
505 'month' => gmdate( 'Y-m', strtotime( $month ) ),
506 'date_range' => array(
507 'start' => $adjusted_start_date,
508 'end' => $adjusted_end_date,
509 ),
510 'appointments' => $formatted_appointments,
511 );
512
513 return $this->response( $response, __( 'Calendar appointments retrieved successfully', 'booktics' ) );
514 }
515
516 /**
517 * Check if a given request has access to get a specific item.
518 *
519 * @param WP_REST_Request $request Full data about the request.
520 *
521 * @return WP_Error|boolean
522 */
523 public function get_item_permissions_check( $request ) {
524 return true;
525 }
526
527 /**
528 * Check if a given request has access to get items.
529 *
530 * @param WP_REST_Request $request Full data about the request.
531 *
532 * @return WP_Error|boolean
533 */
534 public function get_items_permissions_check( $request ) {
535 return true;
536 }
537
538 /**
539 * Create one item from the collection.
540 *
541 * @param WP_REST_Request $request Full data about the request.
542 *
543 * @return WP_Error|WP_REST_Response
544 * @throws Exception
545 */
546 public function create_item( $request ) {
547 $data = json_decode( $request->get_body(), true );
548 $error = Appointment_Validator::instance()->validate_create( $data );
549 if ( $error ) {
550 return $this->error( $error->get_error_message() );
551 }
552 $appointment = Appointment_Service::instance()->create( $data );
553 if ( is_wp_error( $appointment ) ) {
554 return $this->error( $appointment->get_error_message() );
555 }
556 do_action( 'booktics_after_appointment_created', $appointment );
557
558 return $this->response( $appointment, __( 'Successfully created appointment', 'booktics' ) );
559 }
560
561 /**
562 * Check if a given request has access to create items.
563 *
564 * @param WP_REST_Request $request Full data about the request.
565 *
566 * @return WP_Error|boolean
567 */
568 public function create_item_permissions_check( $request ) {
569 return current_user_can( 'manage_options' );
570 }
571
572 /**
573 * Update one item from the collection.
574 *
575 * @param WP_REST_Request $request Full data about the request.
576 *
577 * @return WP_REST_Response
578 */
579 public function update_item( $request ) {
580 $id = intval( $request['id'] );
581 $data = json_decode( $request->get_body(), true );
582 $error = Appointment_Validator::instance()->validate_update( $data );
583 if ( $error ) {
584 return $this->error( $error->get_error_message() );
585 }
586 $updated_appointment = Appointment_Service::instance()->update( $id, $data );
587 if ( is_wp_error( $updated_appointment ) ) {
588 return $this->error( $updated_appointment->get_error_message() );
589 }
590 do_action( 'booktics_after_appointment_updated', $updated_appointment );
591
592 return $this->response( $updated_appointment, __( 'Appointment updated successfully', 'booktics' ) );
593 }
594
595 /**
596 * Check if a given request has access to update a specific item.
597 *
598 * @param WP_REST_Request $request Full data about the request.
599 *
600 * @return WP_Error|boolean
601 */
602 public function update_item_permissions_check( $request ) {
603 return current_user_can( 'manage_options' );
604 }
605
606 /**
607 * Delete one item from the collection.
608 *
609 * @param WP_REST_Request $request Full data about the request.
610 *
611 * @return \WP_Error|\WP_HTTP_Response
612 */
613 public function delete_item( $request ) {
614 $id = intval( $request['id'] );
615 $result = Appointment_Service::instance()->delete( $id );
616 if ( is_wp_error( $result ) ) {
617 return $this->error( $result->get_error_message() );
618 }
619 do_action( 'booktics_after_appointment_deleted', $result );
620
621 return $this->response( $result, __( 'Successfully deleted appointment', 'booktics' ) );
622 }
623
624 /**
625 * Check if a given request has access to delete a specific item.
626 *
627 * @param WP_REST_Request $request Full data about the request.
628 *
629 * @return WP_Error|boolean
630 */
631 public function delete_item_permissions_check( $request ) {
632 return current_user_can( 'manage_options' );
633 }
634
635 /**
636 * Delete a collection of items
637 *
638 * @param WP_Rest_Request $request User request
639 */
640 public function delete_items( $request ) {
641 $ids = $request['ids'];
642 $result = Appointment_Service::instance()->delete_many( $ids );
643 if ( is_wp_error( $result ) ) {
644 return $this->error( $result->get_error_message() );
645 }
646
647 return $this->response( $result['data'], $result['message'] );
648 }
649
650 /**
651 * Check if a given request has access to delete a collection of items.
652 *
653 * @param WP_REST_Request $request Full data about the request.
654 *
655 * @return WP_Error|boolean
656 */
657 public function delete_items_permissions_check( $request ) {
658 return current_user_can( 'manage_options' );
659 }
660
661 /**
662 * Reschedule an appointment
663 *
664 * @param $request Full data about the request.
665 *
666 * @throws Exception
667 */
668 public function reschedule_item( $request ) {
669 $id = intval( $request['id'] );
670 $data = json_decode( $request->get_body(), true );
671 $error = Appointment_Validator::instance()->validate_reschedule( $data );
672 if ( $error ) {
673 return $this->error( $error->get_error_message() );
674 }
675 $result = Appointment_Service::instance()->reschedule( $id, $data );
676 if ( is_wp_error( $result ) ) {
677 return $this->error( $result->get_error_message() );
678 }
679 do_action( 'booktics_after_appointment_rescheduled', $result );
680
681 return $this->response( $result, __( 'Successfully rescheduled appointment', 'booktics' ) );
682 }
683
684 /**
685 * Check if a given request has access to reschedule a specific item.
686 *
687 * @param $request Full data about the request.
688 */
689 public function reschedule_item_permissions_check( $request ) {
690 return true;
691 }
692
693 /**
694 * Cancel an appointment
695 *
696 * @param WP_REST_Request $request Full data about the request.
697 *
698 * @throws Exception
699 */
700 public function cancel_item( $request ) {
701 $id = intval( $request['id'] );
702 $result = Appointment_Service::instance()->cancel( $id );
703 if ( is_wp_error( $result ) ) {
704 return $this->error( $result->get_error_message() );
705 }
706 do_action( 'booktics_after_appointment_cancelled', $result );
707
708 return $this->response( $result, __( 'Successfully cancelled appointment', 'booktics' ) );
709 }
710
711 /**
712 * Check if a given request has access to cancel a specific item.
713 *
714 * @param WP_REST_Request $request Full data about the request.
715 *
716 * @return WP_Error|boolean
717 */
718 public function cancel_item_permissions_check( $request ) {
719 return current_user_can( 'manage_options' );
720 }
721
722 /**
723 * Prepare the item for create or update operation.
724 *
725 * @param WP_REST_Request $request Request object.
726 *
727 * @return bool|\WP_Error $prepared_item
728 */
729 public function prepare_item_for_database( $request ) {
730 $data = json_decode( $request->get_body(), true );
731 $validate = booktics_validate(
732 $data,
733 array(
734 'start_time' => array(
735 'required',
736 ),
737 'end_time' => array(
738 'required',
739 ),
740 'date' => array( 'required' ),
741 'service_id' => array(
742 'required',
743 ),
744 'team_member_id' => array(
745 'required',
746 ),
747 )
748 );
749
750 if ( is_wp_error( $validate ) ) {
751 return $validate;
752 }
753
754 $data = $this->get_sanitized_data( $data );
755
756 return $data;
757 }
758
759 private function get_sanitized_data( $data ) {
760 $data['start_time'] = ! empty( $data['start_time'] ) ? sanitize_text_field( $data['start_time'] ) : '';
761 $data['end_time'] = ! empty( $data['end_time'] ) ? sanitize_text_field( $data['end_time'] ) : '';
762 $data['date'] = ! empty( $data['date'] ) ? sanitize_text_field( $data['date'] ) : '';
763 $data['service_id'] = ! empty( $data['service_id'] ) ? sanitize_text_field( $data['service_id'] ) : '';
764 $data['team_member_id'] = ! empty( $data['team_member_id'] ) ? sanitize_text_field( $data['team_member_id'] ) : '';
765 $data['customer_id'] = ! empty( $data['customer_id'] ) ? sanitize_text_field( $data['customer_id'] ) : '';
766 $data['total_attendees'] = ! empty( $data['total_attendees'] ) ? sanitize_text_field( $data['total_attendees'] ) : '';
767 $data['status'] = isset( $data['status'] ) ? $data['status'] : 'pending';
768
769 return $data;
770 }
771
772 /**
773 * Check if a given request has access to get time slots.
774 *
775 * @param $request
776 *
777 * @return boolean
778 */
779 public function get_time_slots_permissions_check( $request ) {
780 return true;
781 }
782
783 /**
784 * Fetch appointment time slots
785 *
786 * @param WP_REST_Request $request Request object.
787 *
788 * @return array|\WP_HTTP_Response
789 * @throws Exception If there's an error getting time slots.
790 */
791 public function get_time_slots( $request ) {
792 $service_id = $request['service_id'];
793 $additional_service_id = $request['additional_service_id'];
794 $team_member_id = $request['team_member_id'];
795 $start_date = $request['start_date'];
796 $end_date = $request['end_date'];
797
798 $db = new Booktics_Database();
799
800 $service_schedules = $db->table( booktics_get_table_name( 'booktics_schedules' ) )
801 ->where( 'service_id', '=', $service_id )
802 ->where_group(
803 function ( $q ) use ( $start_date, $end_date ) {
804 $q->where( 'custom_date', '=', '0' )
805 ->or_where_group(
806 function ( $q2 ) use ( $start_date, $end_date ) {
807 $q2->where( 'custom_date', '>', $start_date )
808 ->where( 'custom_date', '<', $end_date );
809 }
810 );
811 }
812 )
813 ->to_array();
814 $service_schedules = new Service_Schedule( $service_schedules, $service_id );
815
816 $team_member_schedules = $db->table( booktics_get_table_name( 'booktics_schedules' ) )
817 ->where( 'team_member_id', '=', $team_member_id )
818 ->where_group(
819 function ( $q ) use ( $start_date, $end_date ) {
820 $q->where( 'custom_date', '=', '0' )
821 ->or_where_group(
822 function ( $q2 ) use ( $start_date, $end_date ) {
823 $q2->where( 'custom_date', '>', $start_date )
824 ->where( 'custom_date', '<', $end_date );
825 }
826 );
827 }
828 )
829 ->to_array();
830 $team_member_schedules = new Team_Member_Schedule( $team_member_schedules, $team_member_id );
831
832 $default_schedules = $db->table( booktics_get_table_name( 'booktics_schedules' ) )
833 ->where( 'service_id', '=', 0 )
834 ->where( 'team_member_id', '=', 0 )
835 ->where_group(
836 function ( $q ) use ( $start_date, $end_date ) {
837 $q->where( 'custom_date', '=', '0' )
838 ->or_where_group(
839 function ( $q2 ) use ( $start_date, $end_date ) {
840 $q2->where( 'custom_date', '>', $start_date )
841 ->where( 'custom_date', '<', $end_date );
842 }
843 );
844 }
845 )
846 ->to_array();
847 $default_schedules = new Default_Schedule( $default_schedules );
848
849 $service = Service_Model::find( $service_id );
850 $service_additional_services = ! empty( $service->additional_durations )
851 ? array_filter(
852 $service->additional_durations,
853 function ( $duration ) use ( $additional_service_id ) {
854 return $duration['id'] === $additional_service_id;
855 }
856 ) : array();
857 $service_duration = ! empty( $service_additional_services ) ? reset( $service_additional_services )['duration_time'] : 30;
858
859 $slots = ( new Time_Slot_Manager( $service_schedules, $team_member_schedules, $default_schedules, $service_duration ) )
860 ->generate_slots_for_range( $start_date, $end_date );
861 $response = array(
862 'service_id' => $service_id,
863 'team_member_id' => $team_member_id,
864 'additional_service_id' => $additional_service_id,
865 'start_date' => $start_date,
866 'end_date' => $end_date,
867 'slots' => $slots,
868 );
869
870 return $this->response( $response, __( 'Successfully fetched time slots', 'booktics' ) );
871 }
872 }
873