PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.11
Timetics – Appointment Booking Calendar & Scheduling v1.0.11
1.0.64 1.0.62 1.0.63 1.0.61 1.0.60 1.0.59 1.0.58 1.0.57 1.0.56 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.19 1.0.2 1.0.20 1.0.21 All 65 releases
timetics / core / bookings / api-booking.php

api-booking.php in Timetics – Appointment Booking Calendar & Scheduling 1.0.11, at core/bookings/api-booking.php

982 lines 34.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Booking api
4 *
5 * @package Timetics
6 */
7 namespace Timetics\Core\Bookings;
8
9 use Timetics\Base\Api;
10 use Timetics\Core\Appointments\Appointment;
11 use Timetics\Core\Customers\Customer;
12 use Timetics\Core\Emails\Cancel_Event_Email;
13 use Timetics\Core\Emails\New_Event_Customer_Email;
14 use Timetics\Core\Emails\New_Event_Email;
15 use Timetics\Core\Emails\Update_Event_Customer_Email;
16 use Timetics\Core\Emails\Update_Event_Email;
17 use Timetics\Core\Staffs\Staff;
18 use Timetics\Utils\Singleton;
19 use WP_Error;
20 use WP_HTTP_Response;
21 use WP_Query;
22
23 class Api_Booking extends Api {
24 use Singleton;
25
26 /**
27 * Store api namespace
28 *
29 * @var string
30 */
31 protected $namespace = 'timetics/v1';
32
33 /**
34 * Store rest base
35 *
36 * @var string
37 */
38 protected $rest_base = 'bookings';
39
40 /**
41 * Register rest routes
42 *
43 * @return void
44 */
45 public function register_routes() {
46 /**
47 * Register route
48 *
49 * @var void
50 */
51 register_rest_route(
52 $this->namespace, $this->rest_base, [
53 [
54 'methods' => \WP_REST_Server::READABLE,
55 'callback' => [$this, 'get_items'],
56 'permission_callback' => function () {
57 return current_user_can( 'manage_timetics' );
58 },
59 ],
60 [
61 'methods' => \WP_REST_Server::CREATABLE,
62 'callback' => [$this, 'create_item'],
63 'permission_callback' => function () {
64 return true;
65 },
66 ],
67 [
68 'methods' => \WP_REST_Server::DELETABLE,
69 'callback' => [$this, 'bulk_delete'],
70 'permission_callback' => function () {
71 return current_user_can( 'edit_booking' );
72 },
73 ],
74 ]
75 );
76
77 /**
78 * Register route
79 *
80 * @var void
81 */
82 register_rest_route(
83 $this->namespace, '/' . $this->rest_base . '/(?P<booking_id>[\d]+)', [
84 [
85 'methods' => \WP_REST_Server::READABLE,
86 'callback' => [$this, 'get_item'],
87 'permission_callback' => function () {
88 return true;
89 },
90 ],
91 [
92 'methods' => \WP_REST_Server::EDITABLE,
93 'callback' => [$this, 'update_item'],
94 'permission_callback' => function () {
95 return true;
96 },
97 ],
98 [
99 'methods' => \WP_REST_Server::DELETABLE,
100 'callback' => [$this, 'delete_item'],
101 'permission_callback' => function () {
102 return current_user_can( 'edit_booking' );
103 },
104 ],
105 ]
106 );
107
108 register_rest_route(
109 $this->namespace, '/' . $this->rest_base . '/(?P<booking_id>[\d]+)/payment', [
110 [
111 'methods' => \WP_REST_Server::EDITABLE,
112 'callback' => [$this, 'make_payment'],
113 'permission_callback' => function () {
114 return true;
115 },
116 ],
117 ]
118 );
119
120 register_rest_route(
121 $this->namespace, $this->rest_base . '/search', [
122 [
123 'methods' => \WP_REST_Server::READABLE,
124 'callback' => [$this, 'search_items'],
125 'permission_callback' => function () {
126 return current_user_can( 'edit_posts' );
127 },
128 ],
129 ]
130 );
131
132 register_rest_route(
133 $this->namespace, $this->rest_base . '/entries', [
134 [
135 'methods' => \WP_REST_Server::READABLE,
136 'callback' => [$this, 'get_entries'],
137 'permission_callback' => function () {
138 return true;
139 },
140 ],
141 ]
142 );
143
144 register_rest_route(
145 $this->namespace, $this->rest_base . '/payment_methods', [
146 [
147 'methods' => \WP_REST_Server::READABLE,
148 'callback' => [$this, 'get_payment_methods'],
149 'permission_callback' => function () {
150 return true;
151 },
152 ],
153 ]
154 );
155 }
156
157 /**
158 * Get all bookings
159 *
160 * @param WP_Rest_Request $request
161 *
162 * @return JSON
163 */
164 public function get_items( $request ) {
165 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
166 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
167 $meeting_id = ! empty( $request['meeting_id'] ) ? intval( $request['meeting_id'] ) : 0;
168 $start_date = ! empty( $request['start_date'] ) ? $request['start_date'] : '';
169 $staff_id = ! current_user_can( 'edit_booking' ) ? get_current_user_id() : 0;
170
171 $args = [
172 'posts_per_page' => $per_page,
173 'paged' => $paged,
174 'meeting' => $meeting_id,
175 'staff' => $staff_id,
176 ];
177
178 if ( $start_date ) {
179 $args['start_date'] = $start_date;
180 }
181
182 $appoint = Booking::all( $args );
183
184 $items = [];
185
186 foreach ( $appoint['items'] as $item ) {
187 $items[] = $this->prepare_item( $item->ID );
188 }
189
190 /**
191 * Added temporary for leagacy sass. It will remove in future.
192 */
193 $items = apply_filters( 'timetics/admin/booking/get_items', $items );
194
195 $data = [
196 'success' => 1,
197 'status_code' => 200,
198 'data' => [
199 'total' => $appoint['total'],
200 'items' => $items,
201 ],
202 ];
203
204 return rest_ensure_response( $data );
205 }
206
207 /**
208 * Get single booking
209 *
210 * @param WP_Rest_Request $request
211 *
212 * @return JSON
213 */
214 public function get_item( $request ) {
215 $booking_id = (int) $request['booking_id'];
216 $booking = new Booking( $booking_id );
217
218 if ( ! $booking->is_booking() ) {
219 return [
220 'success' => 0,
221 'status_code' => 404,
222 'message' => esc_html__( 'Invalid booking id.', 'timetics' ),
223 'data' => [],
224 ];
225 }
226
227 /**
228 * Added temporary for leagacy sass. It will remove in future.
229 */
230 do_action( 'timetics/admin/booking/get_item', $this->prepare_item( $booking ) );
231
232 $data = [
233 'success' => 1,
234 'status_code' => 200,
235 'data' => $this->prepare_item( $booking ),
236 ];
237
238 return rest_ensure_response( $data );
239 }
240
241 /**
242 * Create booking
243 *
244 * @param WP_Rest_Request $request
245 *
246 * @return JSON
247 */
248 public function create_item( $request ) {
249 /**
250 * Added temporary for leagacy sass. It will remove in future.
251 */
252 if ( apply_filters( 'timetics/booking/appointment/type_check', false, $request ) == true ) {
253 $response = [
254 'status_code' => 409,
255 'success' => 0,
256 'message' => esc_html__( 'Manual Booking Restricted ', 'timetics' ),
257 ];
258
259 return rest_ensure_response( $response );
260 } // End.
261
262 return $this->save_bookings( $request );
263 }
264
265 /**
266 * Update booking
267 *
268 * @param WP_Rest_Request $request
269 *
270 * @return JSON
271 */
272 public function update_item( $request ) {
273 $booking_id = (int) $request['booking_id'];
274 $booking = new Booking( $booking_id );
275
276 if ( ! $booking->is_booking() ) {
277 return [
278 'status_code' => 404,
279 'message' => esc_html__( 'Invalid booking id.', 'timetics' ),
280 'data' => [],
281 ];
282 }
283
284 return $this->save_bookings( $request, $booking_id );
285 }
286
287 /**
288 * Delete booking
289 *
290 * @param WP_Rest_Request $request
291 *
292 * @return JSON
293 */
294 public function delete_item( $request ) {
295 $booking_id = (int) $request['booking_id'];
296
297 $delete = $this->delete( $booking_id );
298
299 if ( ! $delete ) {
300 $data = [
301 'success' => 1,
302 'status_code' => 409,
303 'message' => esc_html__( 'Something went wrong, Please try again.', 'timetics' ),
304 'data' => [],
305 ];
306
307 return new WP_HTTP_Response( $data, 409 );
308 }
309
310 $data = [
311 'success' => 1,
312 'status_code' => 200,
313 'message' => esc_html__( 'Successfully deleted booking', 'timetics' ),
314 'data' => [],
315 ];
316
317 return rest_ensure_response( $data );
318 }
319
320 /**
321 * Delete multiples
322 *
323 * @param WP_Rest_Request $request
324 *
325 * @return JSON
326 */
327 public function bulk_delete( $request ) {
328 $bookings = json_decode( $request->get_body(), true );
329
330 foreach ( $bookings as $booking ) {
331 $delete = $this->delete( $booking );
332
333 if ( ! $delete ) {
334 return [
335 'success' => 0,
336 'status_code' => 404,
337 'message' => esc_html__( 'Invalid booking id.', 'timetics' ),
338 'data' => [],
339 ];
340 }
341 }
342
343 /**
344 * Added temporary for leagacy sass. It will remove in future.
345 */
346 do_action( 'timetics/admin/booking/bulk_delete', $bookings );
347
348 return [
349 'success' => 1,
350 'status_code' => 200,
351 'message' => esc_html__( 'Successfully deleted booking', 'timetics' ),
352 ];
353 }
354
355 /**
356 * Get payment methods
357 *
358 * @return array
359 */
360 public function get_payment_methods() {
361
362 $payment_methods = timetics_get_payment_methods();
363
364 return [
365 'success' => 1,
366 'status_code' => 200,
367 'data' => $payment_methods,
368 ];
369 }
370
371 /**
372 * Search bookings
373 *
374 * @param WP_Rest_Request $request
375 *
376 * @return JSON
377 */
378 public function search_items( $request ) {
379 // Prepare search args.
380 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
381 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
382 $search = ! empty( $request['search'] ) ? sanitize_text_field( $request['search'] ) : '';
383
384 // Get search.
385 $booking = new WP_Query(
386 array(
387 'post_type' => 'timetics-booking',
388 'posts_per_page' => $per_page,
389 'paged' => $paged,
390 'post_status' => 'any',
391
392 // @codingStandardsIgnoreStart
393 'meta_query' => array(
394 'relation' => 'OR',
395 array(
396 'key' => '_tt_booking_customer_fname',
397 'value' => $search,
398 'compare' => 'LIKE',
399 ),
400 array(
401 'key' => '_tt_booking_customer_lname',
402 'value' => $search,
403 'compare' => 'LIKE',
404 ),
405 array(
406 'key' => '_tt_booking_customer_email',
407 'value' => $search,
408 'compare' => 'LIKE',
409 ),
410 array(
411 'key' => '_tt_booking_customer_phone',
412 'value' => $search,
413 'compare' => 'LIKE',
414 ),
415 array(
416 'key' => '_tt_booking_staff_fname',
417 'value' => $search,
418 'compare' => 'LIKE',
419 ),
420 array(
421 'key' => '_tt_booking_staff_lname',
422 'value' => $search,
423 'compare' => 'LIKE',
424 ),
425 array(
426 'key' => '_tt_booking_staff_email',
427 'value' => $search,
428 'compare' => 'LIKE',
429 ),
430 array(
431 'key' => '_tt_booking_meeting_name',
432 'value' => $search,
433 'compare' => 'LIKE',
434 ),
435 array(
436 'key' => '_tt_booking_meeting_description',
437 'value' => $search,
438 'compare' => 'LIKE',
439 ),
440 array(
441 'key' => '_tt_booking_meeting_type',
442 'value' => $search,
443 'compare' => 'LIKE',
444 ),
445 ),
446 // @codingStandardsIgnoreEnd
447 )
448 );
449
450 // Prepare items for response.
451 $items = [];
452
453 foreach ( $booking->posts as $item ) {
454 $items[] = $this->prepare_item( $item->ID );
455 }
456
457 /**
458 * Added temporary for leagacy sass. It will remove in future.
459 */
460 $items = apply_filters( 'timetics/admin/booking/search_items', $items );
461
462 $data = [
463 'success' => 1,
464 'status' => 200,
465 'data' => [
466 'total' => $booking->found_posts,
467 'items' => $items,
468 ],
469 ];
470
471 return rest_ensure_response( $data );
472 }
473
474 /**
475 * Get all booking entries
476 *
477 * @param WP_Rest_Request $request
478 *
479 * @return JSON
480 */
481 public function get_entries( $request ) {
482 $staff_id = ! empty( $request['staff_id'] ) ? intval( $request['staff_id'] ) : 0;
483 $meeting_id = ! empty( $request['meeting_id'] ) ? intval( $request['meeting_id'] ) : 0;
484 $start_date = ! empty( $request['start_date'] ) ? sanitize_text_field( $request['start_date'] ) : 0;
485 $timezone = ! empty( $request['timezone'] ) ? sanitize_text_field( $request['timezone'] ) : 0;
486 $end_date = ! empty( $request['end_date'] ) ? sanitize_text_field( $request['end_date'] ) : 0;
487
488 $meeting = new Appointment( $meeting_id );
489
490 // Validate timezone.
491 if ( ! timetics_is_valid_timezone( $timezone ) ) {
492 return new WP_Error( 'timezone_error', __( 'Your booking timezone is invalid', 'timetics' ) );
493 }
494
495 // Validate meeting timezone.
496 if ( ! timetics_is_valid_timezone( $meeting->get_timezone() ) ) {
497 return new WP_Error( 'timezone_error', __( 'Your meeting timezone is invalid. Please update your meeting timezone with proper timezone.', 'timetics' ) );
498 }
499
500 $days = $meeting->prepare_schedule( $start_date, $end_date, $staff_id, $timezone );
501
502 $data = [
503 'today' => gmdate( 'Y-m-d' ),
504 'availability_timezone' => $meeting->get_timezone(),
505 'days' => $days,
506 ];
507
508 /**
509 * Added temporary for leagacy sass. It will remove in future.
510 */
511 $data = apply_filters( 'timetics/admin/booking/get_entries', $data );
512
513 return [
514 'success' => true,
515 'status_code' => 200,
516 'message' => esc_html__( 'Get all entries', 'timetics' ),
517 'data' => $data,
518 ];
519 }
520
521 /**
522 * Make payment transaction for the current booking
523 *
524 * @param WP_Rest_Request $request
525 *
526 * @return JSON
527 */
528 public function make_payment( $request ) {
529 $booking_id = intval( $request['booking_id'] );
530 $booking = new Booking( $booking_id );
531 $data = json_decode( $request->get_body(), true );
532 $status = ! empty( $data['status'] ) ? sanitize_text_field( $data['status'] ) : '';
533 $post_status = 'succeeded' === $status ? 'completed' : 'pending';
534 $payment_method = ! empty( $data['payment_method'] ) ? sanitize_text_field( $data['payment_method'] ) : '';
535 $payment_details = ! empty( $data['payment_details'] ) ? $data['payment_details'] : '';
536
537 if ( ! $booking->is_booking() ) {
538 return [
539 'status_code' => 404,
540 'message' => esc_html__( 'Invalid booking id.', 'timetics' ),
541 'data' => [],
542 ];
543 }
544
545 $update = $booking->update(
546 [
547 'post_status' => $post_status,
548 'payment_status' => $status,
549 'payment_details' => $payment_details,
550 'payment_method' => $payment_method,
551 ]
552 );
553
554 if ( is_wp_error( $update ) ) {
555 $data = [
556 'success' => 0,
557 'status_code' => 409,
558 /* translators: Action */
559 'message' => $update->get_error_message(),
560 ];
561
562 return new WP_HTTP_Response( $data, 409 );
563 }
564
565 if ( 'completed' === $post_status ) {
566 $booking->create_event();
567 $new_event_email = new New_Event_Email( $booking );
568 $new_event_email->send();
569
570 $new_event_customer_email = new New_Event_Customer_Email( $booking );
571 $new_event_customer_email->send();
572 }
573
574 /**
575 * Added temporary for leagacy sass. It will remove in future.
576 */
577 do_action( 'timetics/admin/booking/make_payment', $post_status );
578
579 $data = [
580 'success' => 1,
581 'status_code' => 200,
582 /* translators: Action */
583 'message' => sprintf( esc_html__( 'Payment %s', 'timetics' ), $post_status ),
584 ];
585
586 return new WP_HTTP_Response( $data, 200 );
587 }
588
589 /**
590 * Save booking
591 *
592 * @param WP_Rest_Request $request
593 * @param integer $id Booking id
594 *
595 * @return JSON
596 */
597 public function save_bookings( $request, $id = 0 ) {
598 $data = json_decode( $request->get_body(), true );
599
600 $first_name = ! empty( $data['first_name'] ) ? sanitize_text_field( $data['first_name'] ) : '';
601 $last_name = ! empty( $data['last_name'] ) ? sanitize_text_field( $data['last_name'] ) : '';
602 $email = ! empty( $data['email'] ) ? sanitize_text_field( $data['email'] ) : '';
603 $phone = ! empty( $data['phone'] ) ? sanitize_text_field( $data['phone'] ) : '';
604 $city = ! empty( $data['city'] ) ? sanitize_text_field( $data['city'] ) : '';
605 $state = ! empty( $data['state'] ) ? sanitize_text_field( $data['state'] ) : '';
606 $post_code = ! empty( $data['post_code'] ) ? sanitize_text_field( $data['post_code'] ) : '';
607 $country = ! empty( $data['country'] ) ? sanitize_text_field( $data['country'] ) : '';
608 $payment_method = ! empty( $data['payment_method'] ) ? sanitize_text_field( $data['payment_method'] ) : '';
609 $address_1 = ! empty( $data['address_1'] ) ? sanitize_text_field( $data['address_1'] ) : '';
610 $address_2 = ! empty( $data['address_2'] ) ? sanitize_text_field( $data['address_2'] ) : '';
611 $appointment = ! empty( $data['appointment'] ) ? intval( $data['appointment'] ) : 0;
612 $staff = ! empty( $data['staff'] ) ? intval( $data['staff'] ) : 0;
613 $start_date = ! empty( $data['start_date'] ) ? sanitize_text_field( $data['start_date'] ) : '';
614 $date = ! empty( $data['date'] ) ? sanitize_text_field( $data['date'] ) : '';
615 $end_date = ! empty( $data['end_date'] ) ? sanitize_text_field( $data['end_date'] ) : $start_date;
616 $start_time = ! empty( $data['start_time'] ) ? sanitize_text_field( $data['start_time'] ) : '';
617 $end_time = ! empty( $data['end_time'] ) ? sanitize_text_field( $data['end_time'] ) : '';
618 $order_total = ! empty( $data['order_total'] ) ? intval( $data['order_total'] ) : 0;
619 $status = ! empty( $data['status'] ) ? sanitize_text_field( $data['status'] ) : timetics_get_option( 'default_booking_status' );
620 $location = ! empty( $data['location'] ) ? sanitize_text_field( $data['location'] ) : '';
621 $location_type = ! empty( $data['location_type'] ) ? sanitize_text_field( $data['location_type'] ) : '';
622 $description = ! empty( $data['description'] ) ? sanitize_text_field( $data['description'] ) : '';
623 $timezone = ! empty( $data['timezone'] ) ? sanitize_text_field( $data['timezone'] ) : '';
624 $recurring_dates = ! empty( $data['recurring_dates'] ) ? $data['recurring_dates'] : [];
625 $action = $id ? 'updated' : 'created';
626
627 $validate = $this->validate(
628 $data, [
629 'first_name',
630 'email',
631 'payment_method',
632 'appointment',
633 'start_date',
634 'start_time',
635 'end_time',
636 ]
637 );
638
639 if ( is_wp_error( $validate ) ) {
640 $data = [
641 'status_code' => 403,
642 'success' => 0,
643 'message' => $validate->get_error_messages(),
644 ];
645 return new WP_HTTP_Response( $data, 403 );
646 }
647
648 $customer = new Customer();
649 $meeting = new Appointment( $appointment );
650 $staff = new Staff( $staff );
651
652 if ( 'created' === $action && ! $this->is_available_slot( $meeting, [
653 'staff_id' => $staff->get_id(),
654 'start_date' => $start_date,
655 'start_time' => $start_time,
656 'timezone' => $timezone,
657 ] ) ) {
658 return new WP_Error( 'time_slot_error', sprintf( __( '%s time slot is not available', 'timetics' ), $start_time ) );
659 }
660
661 if ( $meeting->is_recurring() ) {
662 $valid_recurrence = apply_filters( 'timetics_validate_recurring_booking', $recurring_dates, $start_time, $staff->get_id(), $meeting->get_id() );
663
664 if ( ! $valid_recurrence ) {
665 $recurring_error = [
666 'status_code' => 403,
667 'success' => 0,
668 'message' => __( 'Couldn\'t possible to book. Plese try another time.', 'timetics' ),
669 ];
670
671 return new WP_HTTP_Response( $recurring_error, 403 );
672 }
673 }
674
675 $customer->make(
676 [
677 'first_name' => $first_name,
678 'last_name' => $last_name,
679 'email' => $email,
680 'phone' => $phone,
681 ]
682 );
683
684 $booking = new Booking( $id );
685 $booking_entry = new Booking_Entry();
686
687 // Update booking schedule.
688 if ( $id ) {
689 $entries = $booking_entry->find(
690 [
691 'staff_id' => $booking->get_staff_id(),
692 'meeting_id' => $booking->get_appointment(),
693 'date' => $booking->get_start_date(),
694 'start' => $booking->get_start_time(),
695 ]
696 );
697
698 if ( $entries ) {
699 $entry = $booking_entry->first();
700
701 if ( 'one-to-one' == strtolower( $meeting->get_type() ) ) {
702 $entry->delete();
703 } else {
704 $booked = intval( $entry->get_booked() ) - 1;
705 $booked_data = apply_filters( 'timetics_booking_update_schedule', $entry, ['booked' => $booked], $data, $booking );
706 $entry->update( $booked_data );
707 }
708 }
709 }
710
711 $booking->set_props(
712 [
713 'customer' => $customer->get_id(),
714 'appointment' => $meeting->get_id(),
715 'staff' => $staff->get_id(),
716 'customer_fname' => $customer->get_first_name(),
717 'customer_lname' => $customer->get_last_name(),
718 'customer_email' => $customer->get_email(),
719 'customer_phone' => $customer->get_phone(),
720 'staff_fname' => $staff->get_first_name(),
721 'staff_lname' => $staff->get_last_name(),
722 'staff_email' => $staff->get_email(),
723 'meeting_name' => $meeting->get_name(),
724 'meeting_description' => $meeting->get_description(),
725 'meeting_type' => $meeting->get_type(),
726 'description' => $description,
727 'start_date' => $start_date,
728 'date' => $date,
729 'end_date' => $end_date,
730 'start_time' => $start_time,
731 'end_time' => $end_time,
732 'order_total' => $order_total,
733 'post_status' => $status,
734 'location' => $location,
735 'location_type' => $location_type,
736 'timezone' => $timezone,
737 ]
738 );
739
740 $booking->save();
741
742 // Fire when booking is completed.
743 do_action( 'timetics_after_booking_create', $booking->get_id(), $customer->get_id(), $meeting->get_id(), $data );
744
745 // Create or update calendar event.
746 if ( $id ) {
747 if ( 'cancel' === $status ) {
748 $booking->delete_event();
749 $cancel_event_email = new Cancel_Event_Email( $booking );
750 $cancel_event_email->send();
751
752 /**
753 * Added temporary for leagacy sass. It will remove in future.
754 */
755 do_action( 'timetics/admin/booking/after_delete_item', $booking );
756 } else {
757 $booking->update_event();
758 $update_event_email = new Update_Event_Email( $booking );
759 $update_event_email->send();
760
761 $update_event_customer_email = new Update_Event_Customer_Email( $booking );
762 $update_event_customer_email->send();
763 }
764 }
765
766 // Create booking schedule.
767 $entries = $booking_entry->find(
768 [
769 'staff_id' => $staff->get_id(),
770 'meeting_id' => $meeting->get_id(),
771 'date' => $start_date,
772 'start' => $start_time,
773 ]
774 );
775
776 if ( $entries ) {
777 $entry = $booking_entry->first();
778
779 if ( 'cancel' === $status ) {
780 $booked = intval( $entry->get_booked() ) - 1;
781 } else {
782 $booked = intval( $entry->get_booked() ) + 1;
783 }
784
785 $booked_data = apply_filters( 'timetics_booking_update_schedule', $entry, ['booked' => $booked], $data, $booking );
786
787 if ( 'cancel' === $status && 'one-to-one' == strtolower( $meeting->get_type() ) ) {
788 $entry->delete();
789 } else {
790 $entry->update( $booked_data );
791 }
792
793 } else {
794 $book_entry_data = [
795 'meeting_id' => $meeting->get_id(),
796 'staff_id' => $staff->get_id(),
797 'customer_id' => $customer->get_id(),
798 'booking_id' => $booking->get_id(),
799 'booked' => 1,
800 'date' => $start_date,
801 'start' => $start_time,
802 'end' => $end_time,
803 ];
804
805 $book_entry_data = apply_filters( 'timetics_booking_schedule', $book_entry_data, $data );
806 $booking_entry->create( $book_entry_data );
807 }
808
809 $data = [
810 'success' => 1,
811 'status_code' => 200,
812 /* translators: Action */
813 'message' => sprintf( esc_html__( 'Successfully %s booking', 'timetics' ), $action ),
814 'data' => $this->prepare_item( $booking ),
815 ];
816
817 return new WP_HTTP_Response( $data, 200 );
818 }
819
820 /**
821 * Prepare item for response
822 *
823 * @param integer $booking_id
824 *
825 * @return array
826 */
827 public function prepare_item( $booking_id ) {
828 $booking = new Booking( $booking_id );
829 $appointment = new Appointment( $booking->get_appointment() );
830 $staff = new Staff( $booking->get_staff_id() );
831 $customer = new Customer( $booking->get_customer_id() );
832 $meeting_timezone = $appointment->get_timezone();
833 $booking_timezone = $booking->get_timezone();
834
835 $start_date_time = timetics_convert_timezone( $booking->get_start_date() . ' ' . $booking->get_start_time(), $booking_timezone, $meeting_timezone );
836 $end_date_time = timetics_convert_timezone( $booking->get_end_date() . ' ' . $booking->get_end_time(), $booking_timezone, $meeting_timezone );
837 $date = timetics_datetime( 'Y-m-d', $booking->get_date(), $meeting_timezone );
838
839 $event = $booking->get_event();
840 $join_link = 'google-meet' === $booking->get_location_type() && ! empty( $event['hangoutLink'] ) ? $event['hangoutLink'] : '';
841
842 $response = [
843 'id' => $booking->get_id(),
844 'random_id' => $booking->get_random_id(),
845 'status' => $booking->get_status(),
846 'order_total' => $booking->get_total(),
847 'start_date' => $start_date_time->format( 'Y-m-d' ),
848 'end_date' => $end_date_time->format( 'Y-m-d' ),
849 'date' => $date,
850 'start_time' => $start_date_time->format( 'h:i a' ),
851 'end_time' => $end_date_time->format( 'h:i a' ),
852 'location' => $booking->get_location(),
853 'location_type' => $booking->get_location_type(),
854 'description' => $booking->get_description(),
855 'customer' => [
856 'id' => $customer->get_id(),
857 'full_name' => $customer->get_display_name(),
858 'first_name' => $customer->get_first_name(),
859 'last_name' => $customer->get_last_name(),
860 'email' => $customer->get_email(),
861 'phone' => $customer->get_phone(),
862 ],
863 'appointment' => [
864 'id' => $appointment->get_id(),
865 'name' => $appointment->get_name(),
866 'duration' => $appointment->get_duration(),
867 'type' => $appointment->get_type(),
868 'price' => $appointment->get_price(),
869 'locations' => $appointment->get_locations(),
870 'timezone' => $appointment->get_timezone(),
871 'permalink' => $appointment->get_appointment_permalink(),
872 ],
873 'staff' => [
874 'id' => $staff->get_id(),
875 'full_name' => $staff->get_display_name(),
876 'first_name' => $staff->get_first_name(),
877 'last_name' => $staff->get_last_name(),
878 'email_name' => $staff->get_email(),
879 'phone' => $staff->get_phone(),
880 'image' => $staff->get_image(),
881 ],
882 ];
883
884 if ( $join_link ) {
885 $response['meeting_link'] = $join_link;
886 }
887
888 return apply_filters( 'timetics_booking_json_data', $response, $booking );
889 }
890
891 /**
892 * Delete booking
893 *
894 * @param integer $booking_id
895 *
896 * @return bool
897 */
898 private function delete( $booking_id ) {
899 $booking = new Booking( $booking_id );
900 $meeting = new Appointment( $booking->get_appointment() );
901
902 if ( ! $booking->is_booking() ) {
903 return false;
904 }
905
906 $booking_entry = new Booking_Entry();
907 $entries = $booking_entry->find(
908 [
909 'staff_id' => $booking->get_staff_id(),
910 'meeting_id' => $meeting->get_id(),
911 'date' => $booking->get_start_date(),
912 'start' => $booking->get_start_time(),
913 ]
914 );
915
916 if ( $entries ) {
917 $entry = $booking_entry->first();
918
919 if ( 'one-to-one' == strtolower( $meeting->get_type() ) ) {
920 $entry->delete();
921 } else {
922 $booked = intval( $entry->get_booked() ) - 1;
923 $booked_seat = ! empty( $booking->get_seat() ) ? $booking->get_seat() : [];
924 $existing_seat = ! empty( $entry->get_seats() ) ? $entry->get_seats() : [];
925
926 $entry->update( [
927 'booked' => $booked,
928 'seats' => array_values( array_diff( $existing_seat, $booked_seat ) ),
929 ] );
930 }
931 }
932
933 $recurrences = $booking->get_recurrence();
934 $booking->delete_event();
935 $booking->delete();
936 $cancel_event_email = new Cancel_Event_Email( $booking );
937 $cancel_event_email->send();
938
939 do_action( 'timetics_after_booking_delete', $recurrences );
940
941 return true;
942 }
943
944 public function is_available_slot( $meeting, $booking_data = [] ) {
945 $start_date = $booking_data['start_date'];
946 $start_time = $booking_data['start_time'];
947 $booking_timezone = $booking_data['timezone'];
948 $booking_entry = new Booking_Entry();
949 $meeting_id = $meeting->get_id();
950 $staff_id = $booking_data['staff_id'];
951
952 $time = is_string( $start_time ) ? strtotime( $start_time ) : $start_time;
953 $time = gmdate( 'H:i', $time );
954 $booking_entries = new Booking_Entry();
955 $meeting = new Appointment( $meeting_id );
956
957 $entries = $booking_entries->find( [
958 'meeting_id' => $meeting_id,
959 'staff_id' => $staff_id,
960 'date' => $start_date,
961 ] );
962
963 $booked = false;
964
965 foreach ( $entries as $entry ) {
966 $booking = new Booking( $entry->get_booking_id() );
967 $booking_time = timetics_convert_timezone( $booking->get_start_date() . ' ' . $entry->get_start(), $booking->get_timezone(), $booking_timezone )->format( 'H:i' );
968
969 if ( $booking_time == $time ) {
970 $booked = $entry;
971 break;
972 }
973 }
974
975 if ( $booked && $booked->get_booked() >= $meeting->get_capacity() ) {
976 return false;
977 }
978
979 return true;
980 }
981 }
982