PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.21
Timetics – Appointment Booking Calendar & Scheduling v1.0.21
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 1.0.22 1.0.23 1.0.24 All 62 releases
timetics / core / bookings / api-booking.php

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

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