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

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