PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.10
Timetics – Appointment Booking Calendar & Scheduling v1.0.10
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.10, at core/bookings/api-booking.php

957 lines 33.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 /**
566 * Added temporary for leagacy sass. It will remove in future.
567 */
568 do_action( 'timetics/admin/booking/make_payment', $post_status );
569
570 $data = [
571 'success' => 1,
572 'status_code' => 200,
573 /* translators: Action */
574 'message' => sprintf( esc_html__( 'Payment %s', 'timetics' ), $post_status ),
575 ];
576
577 return new WP_HTTP_Response( $data, 200 );
578 }
579
580 /**
581 * Save booking
582 *
583 * @param WP_Rest_Request $request
584 * @param integer $id Booking id
585 *
586 * @return JSON
587 */
588 public function save_bookings( $request, $id = 0 ) {
589 $data = json_decode( $request->get_body(), true );
590
591 $first_name = ! empty( $data['first_name'] ) ? sanitize_text_field( $data['first_name'] ) : '';
592 $last_name = ! empty( $data['last_name'] ) ? sanitize_text_field( $data['last_name'] ) : '';
593 $email = ! empty( $data['email'] ) ? sanitize_text_field( $data['email'] ) : '';
594 $phone = ! empty( $data['phone'] ) ? sanitize_text_field( $data['phone'] ) : '';
595 $city = ! empty( $data['city'] ) ? sanitize_text_field( $data['city'] ) : '';
596 $state = ! empty( $data['state'] ) ? sanitize_text_field( $data['state'] ) : '';
597 $post_code = ! empty( $data['post_code'] ) ? sanitize_text_field( $data['post_code'] ) : '';
598 $country = ! empty( $data['country'] ) ? sanitize_text_field( $data['country'] ) : '';
599 $payment_method = ! empty( $data['payment_method'] ) ? sanitize_text_field( $data['payment_method'] ) : '';
600 $address_1 = ! empty( $data['address_1'] ) ? sanitize_text_field( $data['address_1'] ) : '';
601 $address_2 = ! empty( $data['address_2'] ) ? sanitize_text_field( $data['address_2'] ) : '';
602 $appointment = ! empty( $data['appointment'] ) ? intval( $data['appointment'] ) : 0;
603 $staff = ! empty( $data['staff'] ) ? intval( $data['staff'] ) : 0;
604 $start_date = ! empty( $data['start_date'] ) ? sanitize_text_field( $data['start_date'] ) : '';
605 $date = ! empty( $data['date'] ) ? sanitize_text_field( $data['date'] ) : '';
606 $end_date = ! empty( $data['end_date'] ) ? sanitize_text_field( $data['end_date'] ) : $start_date;
607 $start_time = ! empty( $data['start_time'] ) ? sanitize_text_field( $data['start_time'] ) : '';
608 $end_time = ! empty( $data['end_time'] ) ? sanitize_text_field( $data['end_time'] ) : '';
609 $order_total = ! empty( $data['order_total'] ) ? intval( $data['order_total'] ) : 0;
610 $status = ! empty( $data['status'] ) ? sanitize_text_field( $data['status'] ) : timetics_get_option( 'default_booking_status' );
611 $location = ! empty( $data['location'] ) ? sanitize_text_field( $data['location'] ) : '';
612 $location_type = ! empty( $data['location_type'] ) ? sanitize_text_field( $data['location_type'] ) : '';
613 $description = ! empty( $data['description'] ) ? sanitize_text_field( $data['description'] ) : '';
614 $timezone = ! empty( $data['timezone'] ) ? sanitize_text_field( $data['timezone'] ) : '';
615 $recurring_dates = ! empty( $data['recurring_dates'] ) ? $data['recurring_dates'] : [];
616 $action = $id ? 'updated' : 'created';
617
618 $validate = $this->validate(
619 $data, [
620 'first_name',
621 'email',
622 'payment_method',
623 'appointment',
624 'start_date',
625 'start_time',
626 'end_time',
627 ]
628 );
629
630 if ( is_wp_error( $validate ) ) {
631 $data = [
632 'status_code' => 403,
633 'success' => 0,
634 'message' => $validate->get_error_messages(),
635 ];
636 return new WP_HTTP_Response( $data, 403 );
637 }
638
639 $customer = new Customer();
640 $meeting = new Appointment( $appointment );
641 $staff = new Staff( $staff );
642
643 if ( 'created' === $action && ! $this->is_available_slot( $meeting, [
644 'staff_id' => $staff->get_id(),
645 'start_date' => $start_date,
646 'start_time' => $start_time,
647 'timezone' => $timezone,
648 ] ) ) {
649 return new WP_Error( 'time_slot_error', sprintf( __( '%s time slot is not available', 'timetics' ), $start_time) );
650 }
651
652 if ( $meeting->is_recurring() ) {
653 $valid_recurrence = apply_filters( 'timetics_validate_recurring_booking', $recurring_dates, $start_time, $staff->get_id(), $meeting->get_id() );
654
655 if ( ! $valid_recurrence ) {
656 $recurring_error = [
657 'status_code' => 403,
658 'success' => 0,
659 'message' => __( 'Couldn\'t possible to book. Plese try another time.', 'timetics' ),
660 ];
661
662 return new WP_HTTP_Response( $recurring_error, 403 );
663 }
664 }
665
666 $customer->make(
667 [
668 'first_name' => $first_name,
669 'last_name' => $last_name,
670 'email' => $email,
671 'phone' => $phone,
672 ]
673 );
674
675 $booking = new Booking( $id );
676 $booking_entry = new Booking_Entry();
677
678 $booking->set_props(
679 [
680 'customer' => $customer->get_id(),
681 'appointment' => $meeting->get_id(),
682 'staff' => $staff->get_id(),
683 'customer_fname' => $customer->get_first_name(),
684 'customer_lname' => $customer->get_last_name(),
685 'customer_email' => $customer->get_email(),
686 'customer_phone' => $customer->get_phone(),
687 'staff_fname' => $staff->get_first_name(),
688 'staff_lname' => $staff->get_last_name(),
689 'staff_email' => $staff->get_email(),
690 'meeting_name' => $meeting->get_name(),
691 'meeting_description' => $meeting->get_description(),
692 'meeting_type' => $meeting->get_type(),
693 'description' => $description,
694 'start_date' => $start_date,
695 'date' => $date,
696 'end_date' => $end_date,
697 'start_time' => $start_time,
698 'end_time' => $end_time,
699 'order_total' => $order_total,
700 'post_status' => $status,
701 'location' => $location,
702 'location_type' => $location_type,
703 'timezone' => $timezone,
704 ]
705 );
706
707 $booking->save();
708
709 // Fire when booking is completed.
710 do_action( 'timetics_after_booking_create', $booking->get_id(), $customer->get_id(), $meeting->get_id(), $data );
711
712 // Create or update calendar event.
713 if ( $id ) {
714 if ( 'cancel' === $status ) {
715 $booking->delete_event();
716 $cancel_event_email = new Cancel_Event_Email( $booking );
717 $cancel_event_email->send();
718
719 /**
720 * Added temporary for leagacy sass. It will remove in future.
721 */
722 do_action( 'timetics/admin/booking/after_delete_item', $booking );
723 } else {
724 $booking->update_event();
725 $update_event_email = new Update_Event_Email( $booking );
726 $update_event_email->send();
727
728 $update_event_customer_email = new Update_Event_Customer_Email( $booking );
729 $update_event_customer_email->send();
730 }
731 } else {
732 $booking->create_event();
733 $new_event_email = new New_Event_Email( $booking );
734 $new_event_email->send();
735
736 $new_event_customer_email = new New_Event_Customer_Email( $booking );
737 $new_event_customer_email->send();
738 }
739
740 // Create booking schedule.
741 $entries = $booking_entry->find(
742 [
743 'staff_id' => $staff->get_id(),
744 'meeting_id' => $meeting->get_id(),
745 'date' => $start_date,
746 'start' => $start_time,
747 ]
748 );
749
750 if ( $entries ) {
751 $entry = $booking_entry->first();
752
753 if ( 'cancel' === $status ) {
754 $booked = intval( $entry->get_booked() ) - 1;
755 } else {
756 $booked = intval( $entry->get_booked() ) + 1;
757 }
758
759 $booked_data = apply_filters( 'timetics_booking_update_schedule', $entry, ['booked' => $booked], $data, $booking );
760
761 if ( 'cancel' === $status && 'one-to-one' == strtolower( $meeting->get_type() ) ) {
762 $entry->delete();
763 } else {
764 $entry->update( $booked_data );
765 }
766
767 } else {
768 $book_entry_data = [
769 'meeting_id' => $meeting->get_id(),
770 'staff_id' => $staff->get_id(),
771 'customer_id' => $customer->get_id(),
772 'booking_id' => $booking->get_id(),
773 'booked' => 1,
774 'date' => $start_date,
775 'start' => $start_time,
776 'end' => $end_time,
777 ];
778
779 $book_entry_data = apply_filters( 'timetics_booking_schedule', $book_entry_data, $data );
780 $booking_entry->create( $book_entry_data );
781 }
782
783 $data = [
784 'success' => 1,
785 'status_code' => 200,
786 /* translators: Action */
787 'message' => sprintf( esc_html__( 'Successfully %s booking', 'timetics' ), $action ),
788 'data' => $this->prepare_item( $booking ),
789 ];
790
791 return new WP_HTTP_Response( $data, 200 );
792 }
793
794 /**
795 * Prepare item for response
796 *
797 * @param integer $booking_id
798 *
799 * @return array
800 */
801 public function prepare_item( $booking_id ) {
802 $booking = new Booking( $booking_id );
803 $appointment = new Appointment( $booking->get_appointment() );
804 $staff = new Staff( $booking->get_staff_id() );
805 $customer = new Customer( $booking->get_customer_id() );
806 $meeting_timezone = $appointment->get_timezone();
807 $booking_timezone = $booking->get_timezone();
808
809 $start_date_time = timetics_convert_timezone( $booking->get_start_date() . ' ' . $booking->get_start_time(), $booking_timezone, $meeting_timezone );
810 $end_date_time = timetics_convert_timezone( $booking->get_end_date() . ' ' . $booking->get_end_time(), $booking_timezone, $meeting_timezone );
811 $date = timetics_datetime( 'Y-m-d', $booking->get_date(), $meeting_timezone );
812
813 $event = $booking->get_event();
814 $join_link = 'google-meet' === $booking->get_location_type() && ! empty( $event['hangoutLink'] ) ? $event['hangoutLink'] : '';
815
816 $response = [
817 'id' => $booking->get_id(),
818 'random_id' => $booking->get_random_id(),
819 'status' => $booking->get_status(),
820 'order_total' => $booking->get_total(),
821 'start_date' => $start_date_time->format( 'Y-m-d' ),
822 'end_date' => $end_date_time->format( 'Y-m-d' ),
823 'date' => $date,
824 'start_time' => $start_date_time->format( 'h:i a' ),
825 'end_time' => $end_date_time->format( 'h:i a' ),
826 'location' => $booking->get_location(),
827 'location_type' => $booking->get_location_type(),
828 'description' => $booking->get_description(),
829 'customer' => [
830 'id' => $customer->get_id(),
831 'full_name' => $customer->get_display_name(),
832 'first_name' => $customer->get_first_name(),
833 'last_name' => $customer->get_last_name(),
834 'email' => $customer->get_email(),
835 'phone' => $customer->get_phone(),
836 ],
837 'appointment' => [
838 'id' => $appointment->get_id(),
839 'name' => $appointment->get_name(),
840 'duration' => $appointment->get_duration(),
841 'type' => $appointment->get_type(),
842 'price' => $appointment->get_price(),
843 'locations' => $appointment->get_locations(),
844 'timezone' => $appointment->get_timezone(),
845 'permalink' => $appointment->get_appointment_permalink(),
846 ],
847 'staff' => [
848 'id' => $staff->get_id(),
849 'full_name' => $staff->get_display_name(),
850 'first_name' => $staff->get_first_name(),
851 'last_name' => $staff->get_last_name(),
852 'email_name' => $staff->get_email(),
853 'phone' => $staff->get_phone(),
854 'image' => $staff->get_image(),
855 ],
856 ];
857
858 if ( $join_link ) {
859 $response['meeting_link'] = $join_link;
860 }
861
862 return apply_filters( 'timetics_booking_json_data', $response, $booking );
863 }
864
865 /**
866 * Delete booking
867 *
868 * @param integer $booking_id
869 *
870 * @return bool
871 */
872 private function delete( $booking_id ) {
873 $booking = new Booking( $booking_id );
874 $meeting = new Appointment( $booking->get_appointment() );
875
876 if ( ! $booking->is_booking() ) {
877 return false;
878 }
879
880 $booking_entry = new Booking_Entry();
881 $entries = $booking_entry->find(
882 [
883 'staff_id' => $booking->get_staff_id(),
884 'meeting_id' => $meeting->get_id(),
885 'date' => $booking->get_start_date(),
886 'start' => $booking->get_start_time(),
887 ]
888 );
889
890 if ( $entries ) {
891 $entry = $booking_entry->first();
892
893 if ( 'one-to-one' == strtolower( $meeting->get_type() ) ) {
894 $entry->delete();
895 } else {
896 $booked = intval( $entry->get_booked() ) - 1;
897 $booked_seat = ! empty( $booking->get_seat() ) ? $booking->get_seat() : [];
898 $existing_seat = ! empty( $entry->get_seats() ) ? $entry->get_seats() : [];
899
900 $entry->update( [
901 'booked' => $booked,
902 'seats' => array_values( array_diff( $existing_seat, $booked_seat ) ),
903 ] );
904 }
905 }
906
907 $recurrences = $booking->get_recurrence();
908 $booking->delete_event();
909 $booking->delete();
910 $cancel_event_email = new Cancel_Event_Email( $booking );
911 $cancel_event_email->send();
912
913 do_action( 'timetics_after_booking_delete', $recurrences );
914
915 return true;
916 }
917
918 public function is_available_slot( $meeting, $booking_data = [] ) {
919 $start_date = $booking_data['start_date'];
920 $start_time = $booking_data['start_time'];
921 $booking_timezone = $booking_data['timezone'];
922 $booking_entry = new Booking_Entry();
923 $meeting_id = $meeting->get_id();
924 $staff_id = $booking_data['staff_id'];
925
926 $time = is_string( $start_time ) ? strtotime( $start_time ) : $start_time;
927 $time = gmdate( 'H:i', $time );
928 $booking_entries = new Booking_Entry();
929 $meeting = new Appointment( $meeting_id );
930
931 $entries = $booking_entries->find( [
932 'meeting_id' => $meeting_id,
933 'staff_id' => $staff_id,
934 'date' => $start_date,
935 ] );
936
937
938 $booked = false;
939
940 foreach ( $entries as $entry ) {
941 $booking = new Booking( $entry->get_booking_id() );
942 $booking_time = timetics_convert_timezone( $booking->get_start_date() .' '.$entry->get_start(), $booking->get_timezone(), $booking_timezone )->format('H:i');
943
944 if ( $booking_time == $time ) {
945 $booked = $entry;
946 break;
947 }
948 }
949
950 if ( $booked && $booked->get_booked() >= $meeting->get_capacity() ) {
951 return false;
952 }
953
954 return true;
955 }
956 }
957