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

1,039 lines 37.5 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 $bookings_count = Booking::all();
253
254 $response = [
255 'success' => 0,
256 'status_code' => 502,
257 'message' => esc_html__( 'Something went wrong', 'timetics' ),
258 'data' => [],
259 ];
260
261 if ( apply_filters( 'timetics/staff/booking/count_check', false, $bookings_count ) == true ) {
262 return new WP_HTTP_Response( apply_filters( 'timetics/admin/booking/error_data', $response, 'count_check' ), 403 );
263 }
264
265 $data = json_decode( $request->get_body(), true );
266
267 if ( apply_filters( 'timetics/booking/appointment/type_check', false, $request ) == true ) {
268 return new WP_HTTP_Response( apply_filters( 'timetics/admin/booking/error_data', $response, 'type_check' ), 403 );
269 }
270
271 $recurring_booking = ! empty( $data['recurring_dates'] ) ? $data['recurring_dates'] : [];
272
273 if ( $recurring_booking && apply_filters( 'timetics/booking/appointment/recurring_check', false, $recurring_booking ) == true ) {
274 $response = [
275 'status_code' => 403,
276 'success' => 0,
277 'message' => esc_html__( 'Recurring booking limit exit', 'timetics' ),
278 ];
279
280 return new WP_HTTP_Response( $response, 403 );
281 } // End.
282
283 return $this->save_bookings( $request );
284 }
285
286 /**
287 * Update booking
288 *
289 * @param WP_Rest_Request $request
290 *
291 * @return JSON
292 */
293 public function update_item( $request ) {
294 $booking_id = (int) $request['booking_id'];
295 $booking = new Booking( $booking_id );
296
297 if ( ! $booking->is_booking() ) {
298 return [
299 'status_code' => 404,
300 'message' => esc_html__( 'Invalid booking id.', 'timetics' ),
301 'data' => [],
302 ];
303 }
304
305 if ( apply_filters( 'timetics/booking/appointment/custom_form_data', false, $request ) == true ) {
306 $response = [
307 'status_code' => 409,
308 'success' => 0,
309 'message' => esc_html__( 'Custom Field Booking Restricted ', 'timetics' ),
310 ];
311
312 return new WP_HTTP_Response( $response, 403 );
313 }
314
315 return $this->save_bookings( $request, $booking_id );
316 }
317
318 /**
319 * Delete booking
320 *
321 * @param WP_Rest_Request $request
322 *
323 * @return JSON
324 */
325 public function delete_item( $request ) {
326 $booking_id = (int) $request['booking_id'];
327
328 $delete = $this->delete( $booking_id );
329
330 if ( ! $delete ) {
331 $data = [
332 'success' => 1,
333 'status_code' => 409,
334 'message' => esc_html__( 'Something went wrong, Please try again.', 'timetics' ),
335 'data' => [],
336 ];
337
338 return new WP_HTTP_Response( $data, 409 );
339 }
340
341 $data = [
342 'success' => 1,
343 'status_code' => 200,
344 'message' => esc_html__( 'Successfully deleted booking', 'timetics' ),
345 'data' => [],
346 ];
347
348 return rest_ensure_response( $data );
349 }
350
351 /**
352 * Delete multiples
353 *
354 * @param WP_Rest_Request $request
355 *
356 * @return JSON
357 */
358 public function bulk_delete( $request ) {
359 $bookings = json_decode( $request->get_body(), true );
360
361 foreach ( $bookings as $booking ) {
362 $delete = $this->delete( $booking );
363
364 if ( ! $delete ) {
365 return [
366 'success' => 0,
367 'status_code' => 404,
368 'message' => esc_html__( 'Invalid booking id.', 'timetics' ),
369 'data' => [],
370 ];
371 }
372 }
373
374 /**
375 * Added temporary for leagacy sass. It will remove in future.
376 */
377 do_action( 'timetics/admin/booking/bulk_delete', $bookings );
378
379 return [
380 'success' => 1,
381 'status_code' => 200,
382 'message' => esc_html__( 'Successfully deleted booking', 'timetics' ),
383 ];
384 }
385
386 /**
387 * Get payment methods
388 *
389 * @return array
390 */
391 public function get_payment_methods() {
392
393 $payment_methods = timetics_get_payment_methods();
394
395 return [
396 'success' => 1,
397 'status_code' => 200,
398 'data' => $payment_methods,
399 ];
400 }
401
402 /**
403 * Search bookings
404 *
405 * @param WP_Rest_Request $request
406 *
407 * @return JSON
408 */
409 public function search_items( $request ) {
410 // Prepare search args.
411 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
412 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
413 $search = ! empty( $request['search'] ) ? sanitize_text_field( $request['search'] ) : '';
414
415 // Get search.
416 $booking = new WP_Query(
417 array(
418 'post_type' => 'timetics-booking',
419 'posts_per_page' => $per_page,
420 'paged' => $paged,
421 'post_status' => 'any',
422
423 // @codingStandardsIgnoreStart
424 'meta_query' => array(
425 'relation' => 'OR',
426 array(
427 'key' => '_tt_booking_customer_fname',
428 'value' => $search,
429 'compare' => 'LIKE',
430 ),
431 array(
432 'key' => '_tt_booking_customer_lname',
433 'value' => $search,
434 'compare' => 'LIKE',
435 ),
436 array(
437 'key' => '_tt_booking_customer_email',
438 'value' => $search,
439 'compare' => 'LIKE',
440 ),
441 array(
442 'key' => '_tt_booking_customer_phone',
443 'value' => $search,
444 'compare' => 'LIKE',
445 ),
446 array(
447 'key' => '_tt_booking_staff_fname',
448 'value' => $search,
449 'compare' => 'LIKE',
450 ),
451 array(
452 'key' => '_tt_booking_staff_lname',
453 'value' => $search,
454 'compare' => 'LIKE',
455 ),
456 array(
457 'key' => '_tt_booking_staff_email',
458 'value' => $search,
459 'compare' => 'LIKE',
460 ),
461 array(
462 'key' => '_tt_booking_meeting_name',
463 'value' => $search,
464 'compare' => 'LIKE',
465 ),
466 array(
467 'key' => '_tt_booking_meeting_description',
468 'value' => $search,
469 'compare' => 'LIKE',
470 ),
471 array(
472 'key' => '_tt_booking_meeting_type',
473 'value' => $search,
474 'compare' => 'LIKE',
475 ),
476 ),
477 // @codingStandardsIgnoreEnd
478 )
479 );
480
481 // Prepare items for response.
482 $items = [];
483
484 foreach ( $booking->posts as $item ) {
485 $items[] = $this->prepare_item( $item->ID );
486 }
487
488 /**
489 * Added temporary for leagacy sass. It will remove in future.
490 */
491 $items = apply_filters( 'timetics/admin/booking/search_items', $items );
492
493 $data = [
494 'success' => 1,
495 'status' => 200,
496 'data' => [
497 'total' => $booking->found_posts,
498 'items' => $items,
499 ],
500 ];
501
502 return rest_ensure_response( $data );
503 }
504
505 /**
506 * Get all booking entries
507 *
508 * @param WP_Rest_Request $request
509 *
510 * @return JSON
511 */
512 public function get_entries( $request ) {
513 $staff_id = ! empty( $request['staff_id'] ) ? intval( $request['staff_id'] ) : 0;
514 $meeting_id = ! empty( $request['meeting_id'] ) ? intval( $request['meeting_id'] ) : 0;
515 $start_date = ! empty( $request['start_date'] ) ? sanitize_text_field( $request['start_date'] ) : 0;
516 $timezone = ! empty( $request['timezone'] ) ? sanitize_text_field( $request['timezone'] ) : 0;
517 $end_date = ! empty( $request['end_date'] ) ? sanitize_text_field( $request['end_date'] ) : 0;
518
519 $meeting = new Appointment( $meeting_id );
520
521 // Validate timezone.
522 if ( ! timetics_is_valid_timezone( $timezone ) ) {
523 return new WP_Error( 'timezone_error', __( 'Your booking timezone is invalid', 'timetics' ) );
524 }
525
526 // Validate meeting timezone.
527 if ( ! timetics_is_valid_timezone( $meeting->get_timezone() ) ) {
528 return new WP_Error( 'timezone_error', __( 'Your meeting timezone is invalid. Please update your meeting timezone with proper timezone.', 'timetics' ) );
529 }
530
531 $days = $meeting->prepare_schedule( $start_date, $end_date, $staff_id, $timezone );
532
533 $data = [
534 'today' => gmdate( 'Y-m-d' ),
535 'availability_timezone' => $meeting->get_timezone(),
536 'days' => $days,
537 ];
538
539 /**
540 * Added temporary for leagacy sass. It will remove in future.
541 */
542 $data = apply_filters( 'timetics/admin/booking/get_entries', $data );
543
544 return [
545 'success' => true,
546 'status_code' => 200,
547 'message' => esc_html__( 'Get all entries', 'timetics' ),
548 'data' => $data,
549 ];
550 }
551
552 /**
553 * Make payment transaction for the current booking
554 *
555 * @param WP_Rest_Request $request
556 *
557 * @return JSON
558 */
559 public function make_payment( $request ) {
560 $booking_id = intval( $request['booking_id'] );
561 $booking = new Booking( $booking_id );
562 $data = json_decode( $request->get_body(), true );
563 $status = ! empty( $data['status'] ) ? sanitize_text_field( $data['status'] ) : '';
564 $post_status = 'succeeded' === $status ? 'completed' : 'pending';
565 $payment_method = ! empty( $data['payment_method'] ) ? sanitize_text_field( $data['payment_method'] ) : '';
566 $payment_details = ! empty( $data['payment_details'] ) ? $data['payment_details'] : '';
567
568 if ( ! $booking->is_booking() ) {
569 return [
570 'status_code' => 404,
571 'message' => esc_html__( 'Invalid booking id.', 'timetics' ),
572 'data' => [],
573 ];
574 }
575
576 $update = $booking->update(
577 [
578 'post_status' => $post_status,
579 'payment_status' => $status,
580 'payment_details' => $payment_details,
581 'payment_method' => $payment_method,
582 ]
583 );
584
585 if ( is_wp_error( $update ) ) {
586 $data = [
587 'success' => 0,
588 'status_code' => 409,
589 /* translators: Action */
590 'message' => $update->get_error_message(),
591 ];
592
593 return new WP_HTTP_Response( $data, 409 );
594 }
595
596 if ( 'completed' === $post_status ) {
597 $booking->create_event();
598 $new_event_email = new New_Event_Email( $booking );
599 $new_event_email->send();
600
601 $new_event_customer_email = new New_Event_Customer_Email( $booking );
602 $new_event_customer_email->send();
603
604 do_action( 'timetics_booking_payment', $booking );
605 }
606
607 /**
608 * Added temporary for leagacy sass. It will remove in future.
609 */
610 do_action( 'timetics/admin/booking/make_payment', $post_status );
611
612 $data = [
613 'success' => 1,
614 'status_code' => 200,
615 /* translators: Action */
616 'message' => sprintf( esc_html__( 'Payment %s', 'timetics' ), $post_status ),
617 ];
618
619 return new WP_HTTP_Response( $data, 200 );
620 }
621
622 /**
623 * Save booking
624 *
625 * @param WP_Rest_Request $request
626 * @param integer $id Booking id
627 *
628 * @return JSON
629 */
630 public function save_bookings( $request, $id = 0 ) {
631 $data = json_decode( $request->get_body(), true );
632
633 $first_name = ! empty( $data['first_name'] ) ? sanitize_text_field( $data['first_name'] ) : '';
634 $last_name = ! empty( $data['last_name'] ) ? sanitize_text_field( $data['last_name'] ) : '';
635 $email = ! empty( $data['email'] ) ? sanitize_text_field( $data['email'] ) : '';
636 $phone = ! empty( $data['phone'] ) ? sanitize_text_field( $data['phone'] ) : '';
637 $city = ! empty( $data['city'] ) ? sanitize_text_field( $data['city'] ) : '';
638 $state = ! empty( $data['state'] ) ? sanitize_text_field( $data['state'] ) : '';
639 $post_code = ! empty( $data['post_code'] ) ? sanitize_text_field( $data['post_code'] ) : '';
640 $country = ! empty( $data['country'] ) ? sanitize_text_field( $data['country'] ) : '';
641 $payment_method = ! empty( $data['payment_method'] ) ? sanitize_text_field( $data['payment_method'] ) : '';
642 $address_1 = ! empty( $data['address_1'] ) ? sanitize_text_field( $data['address_1'] ) : '';
643 $address_2 = ! empty( $data['address_2'] ) ? sanitize_text_field( $data['address_2'] ) : '';
644 $appointment = ! empty( $data['appointment'] ) ? intval( $data['appointment'] ) : 0;
645 $staff = ! empty( $data['staff'] ) ? intval( $data['staff'] ) : 0;
646 $start_date = ! empty( $data['start_date'] ) ? sanitize_text_field( $data['start_date'] ) : '';
647 $date = ! empty( $data['date'] ) ? sanitize_text_field( $data['date'] ) : '';
648 $end_date = ! empty( $data['end_date'] ) ? sanitize_text_field( $data['end_date'] ) : $start_date;
649 $start_time = ! empty( $data['start_time'] ) ? sanitize_text_field( $data['start_time'] ) : '';
650 $end_time = ! empty( $data['end_time'] ) ? sanitize_text_field( $data['end_time'] ) : '';
651 $order_total = ! empty( $data['order_total'] ) ? intval( $data['order_total'] ) : 0;
652 $status = ! empty( $data['status'] ) ? sanitize_text_field( $data['status'] ) : timetics_get_option( 'default_booking_status' );
653 $location = ! empty( $data['location'] ) ? sanitize_text_field( $data['location'] ) : '';
654 $location_type = ! empty( $data['location_type'] ) ? sanitize_text_field( $data['location_type'] ) : '';
655 $description = ! empty( $data['description'] ) ? sanitize_text_field( $data['description'] ) : '';
656 $timezone = ! empty( $data['timezone'] ) ? sanitize_text_field( $data['timezone'] ) : '';
657 $recurring_dates = ! empty( $data['recurring_dates'] ) ? $data['recurring_dates'] : [];
658 $action = $id ? 'updated' : 'created';
659
660 $validate = $this->validate(
661 $data, [
662 'first_name',
663 'email',
664 'payment_method',
665 'appointment',
666 'start_date',
667 'start_time',
668 'end_time',
669 ]
670 );
671
672 if ( is_wp_error( $validate ) ) {
673 $data = [
674 'status_code' => 403,
675 'success' => 0,
676 'message' => $validate->get_error_messages(),
677 ];
678 return new WP_HTTP_Response( $data, 403 );
679 }
680
681 $customer = new Customer();
682 $meeting = new Appointment( $appointment );
683 $staff = new Staff( $staff );
684 $booking = new Booking( $id );
685 $booking_entry = new Booking_Entry();
686
687 if ( 'created' === $action && ! $this->is_available_slot( $meeting, [
688 'staff_id' => $staff->get_id(),
689 'start_date' => $start_date,
690 'start_time' => $start_time,
691 'timezone' => $timezone,
692 ] ) ) {
693 return new WP_Error( 'time_slot_error', sprintf( __( '%s time slot is not available', 'timetics' ), $start_time ) );
694 }
695
696 if ( $meeting->is_recurring() ) {
697 $valid_recurrence = apply_filters( 'timetics_validate_recurring_booking', $recurring_dates, $start_time, $staff->get_id(), $meeting->get_id() );
698
699 if ( ! $valid_recurrence ) {
700 $recurring_error = [
701 'status_code' => 403,
702 'success' => 0,
703 'message' => __( 'Couldn\'t possible to book. Plese try another time.', 'timetics' ),
704 ];
705
706 return new WP_HTTP_Response( $recurring_error, 403 );
707 }
708 }
709
710 $customer->make(
711 [
712 'first_name' => $first_name,
713 'last_name' => $last_name,
714 'email' => $email,
715 'phone' => $phone,
716 ]
717 );
718
719 // Update booking schedule.
720 if ( $id ) {
721 $entries = $booking_entry->find(
722 [
723 'staff_id' => $booking->get_staff_id(),
724 'meeting_id' => $booking->get_appointment(),
725 'date' => $booking->get_start_date(),
726 'start' => $booking->get_start_time(),
727 ]
728 );
729
730 if ( $entries ) {
731 $entry = $booking_entry->first();
732
733 if ( 'one-to-one' == strtolower( $meeting->get_type() ) ) {
734 $entry->delete();
735 } else {
736 $booked = intval( $entry->get_booked() ) - 1;
737 $booked_data = apply_filters( 'timetics_booking_update_schedule', $entry, ['booked' => $booked], $data, $booking );
738 $entry->update( $booked_data );
739 }
740 }
741 }
742
743 if ( $id && $booking->get_status() == 'cancel' && $status == 'cancel' ) {
744 return new WP_Error( 'booking_cancel_error', __( 'This booking alreay canceled', 'timetics' ) );
745 }
746
747 $booking->set_props(
748 [
749 'customer' => $customer->get_id(),
750 'appointment' => $meeting->get_id(),
751 'staff' => $staff->get_id(),
752 'customer_fname' => $customer->get_first_name(),
753 'customer_lname' => $customer->get_last_name(),
754 'customer_email' => $customer->get_email(),
755 'customer_phone' => $customer->get_phone(),
756 'staff_fname' => $staff->get_first_name(),
757 'staff_lname' => $staff->get_last_name(),
758 'staff_email' => $staff->get_email(),
759 'meeting_name' => $meeting->get_name(),
760 'meeting_description' => $meeting->get_description(),
761 'meeting_type' => $meeting->get_type(),
762 'description' => $description,
763 'start_date' => $start_date,
764 'date' => $date,
765 'end_date' => $end_date,
766 'start_time' => $start_time,
767 'end_time' => $end_time,
768 'order_total' => $order_total,
769 'post_status' => $status,
770 'location' => $location,
771 'location_type' => $location_type,
772 'timezone' => $timezone,
773 ]
774 );
775
776 $booking->save();
777
778 // Fire when booking is completed.
779 do_action( 'timetics_after_booking_create', $booking->get_id(), $customer->get_id(), $meeting->get_id(), $data );
780
781 // Create or update calendar event.
782 if ( $id ) {
783 if ( 'cancel' === $status ) {
784 $booking->delete_event();
785 $cancel_event_email = new Cancel_Event_Email( $booking );
786 $cancel_event_email->send();
787
788 /**
789 * Added temporary for leagacy sass. It will remove in future.
790 */
791 do_action( 'timetics/admin/booking/after_delete_item', $booking );
792 } else {
793 $booking->update_event();
794 $update_event_email = new Update_Event_Email( $booking );
795 $update_event_email->send();
796
797 $update_event_customer_email = new Update_Event_Customer_Email( $booking );
798 $update_event_customer_email->send();
799 }
800 }
801
802 // Convert booking time to staff/meeting time.
803 $date_time = timetics_convert_timezone( $start_date .' '. $start_time, $timezone, $meeting->get_timezone() );
804 $end_time = timetics_convert_timezone( $start_date . ' ' . $end_time, $timezone, $meeting->get_timezone() );
805
806 // Create booking schedule.
807 $entries = $booking_entry->find(
808 [
809 'staff_id' => $staff->get_id(),
810 'meeting_id' => $meeting->get_id(),
811 'date' => $date_time->format('Y-m-d'),
812 'start' => $date_time->format('h:i a'),
813 ]
814 );
815
816 if ( $entries ) {
817 $entry = $booking_entry->first();
818
819 if ( 'cancel' === $status ) {
820 $booked = intval( $entry->get_booked() ) - 1;
821 } else {
822 $booked = intval( $entry->get_booked() ) + 1;
823 }
824
825 $booked_data = apply_filters( 'timetics_booking_update_schedule', $entry, ['booked' => $booked], $data, $booking );
826
827 if ( 'cancel' === $status && 'one-to-one' == strtolower( $meeting->get_type() ) ) {
828 $entry->delete();
829 } else {
830 $entry->update( $booked_data );
831 }
832
833 } else {
834 $book_entry_data = [
835 'meeting_id' => $meeting->get_id(),
836 'staff_id' => $staff->get_id(),
837 'customer_id' => $customer->get_id(),
838 'booking_id' => $booking->get_id(),
839 'booked' => 1,
840 'date' => $date_time->format('Y-m-d'),
841 'start' => $date_time->format('h:i a'),
842 'end' => $end_time->format('h:i a'),
843 ];
844
845 $book_entry_data = apply_filters( 'timetics_booking_schedule', $book_entry_data, $data );
846 $booking_entry->create( $book_entry_data );
847 }
848
849 // Fire after booking schedule create.
850 do_action( 'timetics_after_booking_schedule', $booking->get_id(), $customer->get_id(), $meeting->get_id(), $data );
851
852 $data = [
853 'success' => 1,
854 'status_code' => 200,
855 /* translators: Action */
856 'message' => sprintf( esc_html__( 'Successfully %s booking', 'timetics' ), $action ),
857 'data' => $this->prepare_item( $booking ),
858 ];
859
860 return new WP_HTTP_Response( $data, 200 );
861 }
862
863 /**
864 * Prepare item for response
865 *
866 * @param integer $booking_id
867 *
868 * @return array
869 */
870 public function prepare_item( $booking_id ) {
871 $booking = new Booking( $booking_id );
872 $appointment = new Appointment( $booking->get_appointment() );
873 $staff = new Staff( $booking->get_staff_id() );
874 $customer = new Customer( $booking->get_customer_id() );
875 $meeting_timezone = $appointment->get_timezone();
876 $booking_timezone = $booking->get_timezone();
877
878 $start_date_time = timetics_convert_timezone( $booking->get_start_date() . ' ' . $booking->get_start_time(), $booking_timezone, $meeting_timezone );
879 $end_date_time = timetics_convert_timezone( $booking->get_end_date() . ' ' . $booking->get_end_time(), $booking_timezone, $meeting_timezone );
880 $date = timetics_datetime( 'Y-m-d', $booking->get_date(), $meeting_timezone );
881
882 $event = $booking->get_event();
883 $join_link = 'google-meet' === $booking->get_location_type() && ! empty( $event['hangoutLink'] ) ? $event['hangoutLink'] : '';
884
885 $response = [
886 'id' => $booking->get_id(),
887 'random_id' => $booking->get_random_id(),
888 'status' => $booking->get_status(),
889 'order_total' => $booking->get_total(),
890 'start_date' => $start_date_time->format( 'Y-m-d' ),
891 'end_date' => $end_date_time->format( 'Y-m-d' ),
892 'date' => $date,
893 'start_time' => $start_date_time->format( 'h:i a' ),
894 'end_time' => $end_date_time->format( 'h:i a' ),
895 'location' => $booking->get_location(),
896 'location_type' => $booking->get_location_type(),
897 'description' => $booking->get_description(),
898 'customer' => [
899 'id' => $customer->get_id(),
900 'full_name' => $customer->get_display_name(),
901 'first_name' => $customer->get_first_name(),
902 'last_name' => $customer->get_last_name(),
903 'email' => $customer->get_email(),
904 'phone' => $customer->get_phone(),
905 ],
906 'appointment' => [
907 'id' => $appointment->get_id(),
908 'name' => $appointment->get_name(),
909 'duration' => $appointment->get_duration(),
910 'type' => $appointment->get_type(),
911 'price' => $appointment->get_price(),
912 'locations' => $appointment->get_locations(),
913 'timezone' => $appointment->get_timezone(),
914 'permalink' => $appointment->get_appointment_permalink(),
915 ],
916 'staff' => [
917 'id' => $staff->get_id(),
918 'full_name' => $staff->get_display_name(),
919 'first_name' => $staff->get_first_name(),
920 'last_name' => $staff->get_last_name(),
921 'email_name' => $staff->get_email(),
922 'phone' => $staff->get_phone(),
923 'image' => $staff->get_image(),
924 ],
925 ];
926
927 if ( $join_link ) {
928 $response['meeting_link'] = $join_link;
929 }
930
931 return apply_filters( 'timetics_booking_json_data', $response, $booking );
932 }
933
934 /**
935 * Delete booking
936 *
937 * @param integer $booking_id
938 *
939 * @return bool
940 */
941 private function delete( $booking_id ) {
942 $booking = new Booking( $booking_id );
943 $meeting = new Appointment( $booking->get_appointment() );
944
945 if ( ! $booking->is_booking() ) {
946 return false;
947 }
948
949 $current_user_id = get_current_user_id();
950
951 if ( $meeting->get_author() != $current_user_id ) {
952 $data = [
953 'success' => 0,
954 'message' => __( 'You are not allowed to delete this booking.', 'timetics' ),
955 ];
956
957 return new WP_HTTP_Response( $data, 403 );
958 }
959
960 $booking_entry = new Booking_Entry();
961
962 $date_time = timetics_convert_timezone( $booking->get_start_date() . ' ' . $booking->get_start_time(), $booking->get_timezone(), $meeting->get_timezone() );
963
964 $entries = $booking_entry->find(
965 [
966 'staff_id' => $booking->get_staff_id(),
967 'meeting_id' => $booking->get_appointment(),
968 'date' => $date_time->format('Y-m-d'),
969 'start' => $date_time->format('h:i a'),
970 ]
971 );
972
973 if ( $entries ) {
974 $entry = $booking_entry->first();
975
976 if ( 'one-to-one' == strtolower( $meeting->get_type() ) ) {
977 $entry->delete();
978 } else {
979 $booked = intval( $entry->get_booked() ) - 1;
980 $booked_seat = ! empty( $booking->get_seat() ) ? $booking->get_seat() : [];
981 $existing_seat = ! empty( $entry->get_seats() ) ? $entry->get_seats() : [];
982
983 $entry->update( [
984 'booked' => $booked,
985 'seats' => array_values( array_diff( $existing_seat, $booked_seat ) ),
986 ] );
987 }
988 }
989
990 $recurrences = $booking->get_recurrence();
991 $booking->delete_event();
992 $booking->delete();
993 $cancel_event_email = new Cancel_Event_Email( $booking );
994 $cancel_event_email->send();
995
996 do_action( 'timetics_after_booking_delete', $recurrences );
997
998 return true;
999 }
1000
1001 public function is_available_slot( $meeting, $booking_data = [] ) {
1002 $start_date = $booking_data['start_date'];
1003 $start_time = $booking_data['start_time'];
1004 $booking_timezone = $booking_data['timezone'];
1005 $booking_entry = new Booking_Entry();
1006 $meeting_id = $meeting->get_id();
1007 $staff_id = $booking_data['staff_id'];
1008
1009 $time = is_string( $start_time ) ? strtotime( $start_time ) : $start_time;
1010 $time = gmdate( 'H:i', $time );
1011 $booking_entries = new Booking_Entry();
1012 $meeting = new Appointment( $meeting_id );
1013
1014 $entries = $booking_entries->find( [
1015 'meeting_id' => $meeting_id,
1016 'staff_id' => $staff_id,
1017 'date' => $start_date,
1018 ] );
1019
1020 $booked = false;
1021
1022 foreach ( $entries as $entry ) {
1023 $booking = new Booking( $entry->get_booking_id() );
1024 $booking_time = timetics_convert_timezone( $booking->get_start_date() . ' ' . $entry->get_start(), $booking->get_timezone(), $booking_timezone )->format( 'H:i' );
1025
1026 if ( $booking_time == $time ) {
1027 $booked = $entry;
1028 break;
1029 }
1030 }
1031
1032 if ( $booked && $booked->get_booked() >= $meeting->get_capacity() ) {
1033 return false;
1034 }
1035
1036 return true;
1037 }
1038 }
1039