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

1,047 lines 37.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Booking api
4 *
5 * @package Timetics
6 */
7 namespace Timetics\Core\Bookings;
8
9 use Timetics\Base\Api;
10 use Timetics\Core\Appointments\Appointment;
11 use Timetics\Core\Customers\Customer;
12 use Timetics\Core\Emails\Cancel_Event_Email;
13 use Timetics\Core\Emails\New_Event_Customer_Email;
14 use Timetics\Core\Emails\New_Event_Email;
15 use Timetics\Core\Emails\Update_Event_Customer_Email;
16 use Timetics\Core\Emails\Update_Event_Email;
17 use Timetics\Core\Staffs\Staff;
18 use Timetics\Utils\Singleton;
19 use WP_Error;
20 use WP_HTTP_Response;
21 use WP_Query;
22
23 class Api_Booking extends Api {
24 use Singleton;
25
26 /**
27 * Store api namespace
28 *
29 * @var string
30 */
31 protected $namespace = 'timetics/v1';
32
33 /**
34 * Store rest base
35 *
36 * @var string
37 */
38 protected $rest_base = 'bookings';
39
40 /**
41 * Register rest routes
42 *
43 * @return void
44 */
45 public function register_routes() {
46 /**
47 * Register route
48 *
49 * @var void
50 */
51 register_rest_route(
52 $this->namespace, $this->rest_base, [
53 [
54 'methods' => \WP_REST_Server::READABLE,
55 'callback' => [$this, 'get_items'],
56 'permission_callback' => function () {
57 return current_user_can( 'manage_timetics' );
58 },
59 ],
60 [
61 'methods' => \WP_REST_Server::CREATABLE,
62 'callback' => [$this, 'create_item'],
63 'permission_callback' => function () {
64 return true;
65 },
66 ],
67 [
68 'methods' => \WP_REST_Server::DELETABLE,
69 'callback' => [$this, 'bulk_delete'],
70 'permission_callback' => function () {
71 return current_user_can( 'edit_booking' );
72 },
73 ],
74 ]
75 );
76
77 /**
78 * Register route
79 *
80 * @var void
81 */
82 register_rest_route(
83 $this->namespace, '/' . $this->rest_base . '/(?P<booking_id>[\d]+)', [
84 [
85 'methods' => \WP_REST_Server::READABLE,
86 'callback' => [$this, 'get_item'],
87 'permission_callback' => function () {
88 return true;
89 },
90 ],
91 [
92 'methods' => \WP_REST_Server::EDITABLE,
93 'callback' => [$this, 'update_item'],
94 'permission_callback' => function () {
95 return true;
96 },
97 ],
98 [
99 'methods' => \WP_REST_Server::DELETABLE,
100 'callback' => [$this, 'delete_item'],
101 'permission_callback' => function () {
102 return current_user_can( 'edit_booking' );
103 },
104 ],
105 ]
106 );
107
108 register_rest_route(
109 $this->namespace, '/' . $this->rest_base . '/(?P<booking_id>[\d]+)/payment', [
110 [
111 'methods' => \WP_REST_Server::EDITABLE,
112 'callback' => [$this, 'make_payment'],
113 'permission_callback' => function () {
114 return true;
115 },
116 ],
117 ]
118 );
119
120 register_rest_route(
121 $this->namespace, $this->rest_base . '/search', [
122 [
123 'methods' => \WP_REST_Server::READABLE,
124 'callback' => [$this, 'search_items'],
125 'permission_callback' => function () {
126 return current_user_can( 'edit_posts' );
127 },
128 ],
129 ]
130 );
131
132 register_rest_route(
133 $this->namespace, $this->rest_base . '/entries', [
134 [
135 'methods' => \WP_REST_Server::READABLE,
136 'callback' => [$this, 'get_entries'],
137 'permission_callback' => function () {
138 return true;
139 },
140 ],
141 ]
142 );
143
144 register_rest_route(
145 $this->namespace, $this->rest_base . '/payment_methods', [
146 [
147 'methods' => \WP_REST_Server::READABLE,
148 'callback' => [$this, 'get_payment_methods'],
149 'permission_callback' => function () {
150 return true;
151 },
152 ],
153 ]
154 );
155 }
156
157 /**
158 * Get all bookings
159 *
160 * @param WP_Rest_Request $request
161 *
162 * @return JSON
163 */
164 public function get_items( $request ) {
165 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
166 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
167 $meeting_id = ! empty( $request['meeting_id'] ) ? intval( $request['meeting_id'] ) : 0;
168 $start_date = ! empty( $request['start_date'] ) ? $request['start_date'] : '';
169 $staff_id = ! current_user_can( 'edit_booking' ) ? get_current_user_id() : 0;
170
171 $args = [
172 'posts_per_page' => $per_page,
173 'paged' => $paged,
174 'meeting' => $meeting_id,
175 'staff' => $staff_id,
176 ];
177
178 if ( $start_date ) {
179 $args['start_date'] = $start_date;
180 }
181
182 $appoint = Booking::all( $args );
183
184 $items = [];
185
186 foreach ( $appoint['items'] as $item ) {
187 $items[] = $this->prepare_item( $item->ID );
188 }
189
190 /**
191 * Added temporary for leagacy sass. It will remove in future.
192 */
193 $items = apply_filters( 'timetics/admin/booking/get_items', $items );
194
195 $data = [
196 'success' => 1,
197 'status_code' => 200,
198 'data' => [
199 'total' => $appoint['total'],
200 'items' => $items,
201 ],
202 ];
203
204 return rest_ensure_response( $data );
205 }
206
207 /**
208 * Get single booking
209 *
210 * @param WP_Rest_Request $request
211 *
212 * @return JSON
213 */
214 public function get_item( $request ) {
215 $booking_id = (int) $request['booking_id'];
216 $booking = new Booking( $booking_id );
217
218 if ( ! $booking->is_booking() ) {
219 return [
220 'success' => 0,
221 'status_code' => 404,
222 'message' => esc_html__( 'Invalid booking id.', 'timetics' ),
223 'data' => [],
224 ];
225 }
226
227 /**
228 * Added temporary for leagacy sass. It will remove in future.
229 */
230 do_action( 'timetics/admin/booking/get_item', $this->prepare_item( $booking ) );
231
232 $data = [
233 'success' => 1,
234 'status_code' => 200,
235 'data' => $this->prepare_item( $booking ),
236 ];
237
238 return rest_ensure_response( $data );
239 }
240
241 /**
242 * Create booking
243 *
244 * @param WP_Rest_Request $request
245 *
246 * @return JSON
247 */
248 public function create_item( $request ) {
249 /**
250 * Added temporary for leagacy sass. It will remove in future.
251 */
252 $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 $default_booking_status = timetics_get_option( 'default_booking_status', 'approved' );
565 $post_status = 'succeeded' === $status ? $default_booking_status : 'pending';
566 $payment_method = ! empty( $data['payment_method'] ) ? sanitize_text_field( $data['payment_method'] ) : '';
567 $payment_details = ! empty( $data['payment_details'] ) ? $data['payment_details'] : '';
568
569 if ( ! $booking->is_booking() ) {
570 return [
571 'status_code' => 404,
572 'message' => esc_html__( 'Invalid booking id.', 'timetics' ),
573 'data' => [],
574 ];
575 }
576
577 $update = $booking->update(
578 [
579 'post_status' => $post_status,
580 'payment_status' => $status,
581 'payment_details' => $payment_details,
582 'payment_method' => $payment_method,
583 ]
584 );
585
586 if ( is_wp_error( $update ) ) {
587 $data = [
588 'success' => 0,
589 'status_code' => 409,
590 /* translators: Action */
591 'message' => $update->get_error_message(),
592 ];
593
594 return new WP_HTTP_Response( $data, 409 );
595 }
596
597 if ( $default_booking_status === $post_status ) {
598 $booking->create_event();
599 $new_event_email = new New_Event_Email( $booking );
600 $new_event_email->send();
601
602 $new_event_customer_email = new New_Event_Customer_Email( $booking );
603 $new_event_customer_email->send();
604
605 do_action( 'timetics_booking_payment', $booking );
606 }
607
608 /**
609 * Added temporary for leagacy sass. It will remove in future.
610 */
611 do_action( 'timetics/admin/booking/make_payment', $post_status );
612
613 $data = [
614 'success' => 1,
615 'status_code' => 200,
616 /* translators: Action */
617 'message' => sprintf( esc_html__( 'Payment %s', 'timetics' ), $post_status ),
618 ];
619
620 return new WP_HTTP_Response( $data, 200 );
621 }
622
623 /**
624 * Save booking
625 *
626 * @param WP_Rest_Request $request
627 * @param integer $id Booking id
628 *
629 * @return JSON
630 */
631 public function save_bookings( $request, $id = 0 ) {
632 $data = json_decode( $request->get_body(), true );
633
634 $first_name = ! empty( $data['first_name'] ) ? sanitize_text_field( $data['first_name'] ) : '';
635 $last_name = ! empty( $data['last_name'] ) ? sanitize_text_field( $data['last_name'] ) : '';
636 $email = ! empty( $data['email'] ) ? sanitize_text_field( $data['email'] ) : '';
637 $phone = ! empty( $data['phone'] ) ? sanitize_text_field( $data['phone'] ) : '';
638 $city = ! empty( $data['city'] ) ? sanitize_text_field( $data['city'] ) : '';
639 $state = ! empty( $data['state'] ) ? sanitize_text_field( $data['state'] ) : '';
640 $post_code = ! empty( $data['post_code'] ) ? sanitize_text_field( $data['post_code'] ) : '';
641 $country = ! empty( $data['country'] ) ? sanitize_text_field( $data['country'] ) : '';
642 $payment_method = ! empty( $data['payment_method'] ) ? sanitize_text_field( $data['payment_method'] ) : '';
643 $address_1 = ! empty( $data['address_1'] ) ? sanitize_text_field( $data['address_1'] ) : '';
644 $address_2 = ! empty( $data['address_2'] ) ? sanitize_text_field( $data['address_2'] ) : '';
645 $appointment = ! empty( $data['appointment'] ) ? intval( $data['appointment'] ) : 0;
646 $staff = ! empty( $data['staff'] ) ? intval( $data['staff'] ) : 0;
647 $start_date = ! empty( $data['start_date'] ) ? sanitize_text_field( $data['start_date'] ) : '';
648 $date = ! empty( $data['date'] ) ? sanitize_text_field( $data['date'] ) : '';
649 $end_date = ! empty( $data['end_date'] ) ? sanitize_text_field( $data['end_date'] ) : $start_date;
650 $start_time = ! empty( $data['start_time'] ) ? sanitize_text_field( $data['start_time'] ) : '';
651 $end_time = ! empty( $data['end_time'] ) ? sanitize_text_field( $data['end_time'] ) : '';
652 $order_total = ! empty( $data['order_total'] ) ? intval( $data['order_total'] ) : 0;
653 $status = ! empty( $data['status'] ) ? sanitize_text_field( $data['status'] ) : timetics_get_option( 'default_booking_status', 'approved' );
654 $location = ! empty( $data['location'] ) ? sanitize_text_field( $data['location'] ) : '';
655 $location_type = ! empty( $data['location_type'] ) ? sanitize_text_field( $data['location_type'] ) : '';
656 $description = ! empty( $data['description'] ) ? sanitize_text_field( $data['description'] ) : '';
657 $timezone = ! empty( $data['timezone'] ) ? sanitize_text_field( $data['timezone'] ) : '';
658 $recurring_dates = ! empty( $data['recurring_dates'] ) ? $data['recurring_dates'] : [];
659 $action = $id ? 'updated' : 'created';
660
661 $validate = $this->validate(
662 $data, [
663 'first_name',
664 'email',
665 'payment_method',
666 'appointment',
667 'start_date',
668 'start_time',
669 'end_time',
670 ]
671 );
672
673 if ( is_wp_error( $validate ) ) {
674 $data = [
675 'status_code' => 403,
676 'success' => 0,
677 'message' => $validate->get_error_messages(),
678 ];
679 return new WP_HTTP_Response( $data, 403 );
680 }
681
682 $customer = new Customer();
683 $meeting = new Appointment( $appointment );
684 $staff = new Staff( $staff );
685 $booking = new Booking( $id );
686 $booking_entry = new Booking_Entry();
687
688 if ( 'created' === $action && ! $this->is_available_slot( $meeting, [
689 'staff_id' => $staff->get_id(),
690 'start_date' => $start_date,
691 'start_time' => $start_time,
692 'timezone' => $timezone,
693 ] ) ) {
694 return new WP_Error( 'time_slot_error', sprintf( __( '%s time slot is not available', 'timetics' ), $start_time ) );
695 }
696
697 if ( $meeting->is_recurring() ) {
698 $valid_recurrence = apply_filters( 'timetics_validate_recurring_booking', $recurring_dates, $start_time, $staff->get_id(), $meeting->get_id() );
699
700 if ( ! $valid_recurrence ) {
701 $recurring_error = [
702 'status_code' => 403,
703 'success' => 0,
704 'message' => __( 'Couldn\'t possible to book. Plese try another time.', 'timetics' ),
705 ];
706
707 return new WP_HTTP_Response( $recurring_error, 403 );
708 }
709 }
710
711 $customer->make(
712 [
713 'first_name' => $first_name,
714 'last_name' => $last_name,
715 'email' => $email,
716 'phone' => $phone,
717 ]
718 );
719
720 // Update booking schedule.
721 if ( $id ) {
722 $entries = $booking_entry->find(
723 [
724 'staff_id' => $booking->get_staff_id(),
725 'meeting_id' => $booking->get_appointment(),
726 'date' => $booking->get_start_date(),
727 'start' => $booking->get_start_time(),
728 ]
729 );
730
731 if ( $entries ) {
732 $entry = $booking_entry->first();
733
734 if ( 'one-to-one' == strtolower( $meeting->get_type() ) ) {
735 $entry->delete();
736 } else {
737 $booked = intval( $entry->get_booked() ) - 1;
738 $booked_data = apply_filters( 'timetics_booking_update_schedule', $entry, ['booked' => $booked], $data, $booking );
739 $entry->update( $booked_data );
740 }
741 }
742 }
743
744 if ( $id && $booking->get_status() == 'cancel' && $status == 'cancel' ) {
745 return new WP_Error( 'booking_cancel_error', __( 'This booking alreay canceled', 'timetics' ) );
746 }
747
748 $booking->set_props(
749 [
750 'customer' => $customer->get_id(),
751 'appointment' => $meeting->get_id(),
752 'appointment_name' => $meeting->get_name(),
753 'staff' => $staff->get_id(),
754 'customer_fname' => $customer->get_first_name(),
755 'customer_lname' => $customer->get_last_name(),
756 'customer_email' => $customer->get_email(),
757 'customer_phone' => $customer->get_phone(),
758 'staff_fname' => $staff->get_first_name(),
759 'staff_lname' => $staff->get_last_name(),
760 'staff_email' => $staff->get_email(),
761 'meeting_name' => $meeting->get_name(),
762 'meeting_description' => $meeting->get_description(),
763 'meeting_type' => $meeting->get_type(),
764 'description' => $description,
765 'start_date' => $start_date,
766 'date' => $date,
767 'end_date' => $end_date,
768 'start_time' => $start_time,
769 'end_time' => $end_time,
770 'order_total' => $order_total,
771 'post_status' => $status,
772 'location' => $location,
773 'location_type' => $location_type,
774 'timezone' => $timezone,
775 ]
776 );
777
778 $booking->save();
779
780 // Fire when booking is completed.
781 do_action( 'timetics_after_booking_create', $booking->get_id(), $customer->get_id(), $meeting->get_id(), $data );
782
783 // Create or update calendar event.
784 if ( $id ) {
785 if ( 'cancel' === $status ) {
786 $booking->delete_event();
787 $cancel_event_email = new Cancel_Event_Email( $booking );
788 $cancel_event_email->send();
789
790 /**
791 * Added temporary for leagacy sass. It will remove in future.
792 */
793 do_action( 'timetics/admin/booking/after_delete_item', $booking );
794 } else {
795 $booking->update_event();
796 $update_event_email = new Update_Event_Email( $booking );
797 $update_event_email->send();
798
799 $update_event_customer_email = new Update_Event_Customer_Email( $booking );
800 $update_event_customer_email->send();
801 }
802 }
803
804 // Convert booking time to staff/meeting time.
805 $date_time = timetics_convert_timezone( $start_date .' '. $start_time, $timezone, $meeting->get_timezone() );
806 $end_time = timetics_convert_timezone( $start_date . ' ' . $end_time, $timezone, $meeting->get_timezone() );
807
808 // Create booking schedule.
809 $entries = $booking_entry->find(
810 [
811 'staff_id' => $staff->get_id(),
812 'meeting_id' => $meeting->get_id(),
813 'date' => $date_time->format('Y-m-d'),
814 'start' => $date_time->format('h:i a'),
815 ]
816 );
817
818 if ( $entries ) {
819 $entry = $booking_entry->first();
820
821 if ( 'cancel' === $status ) {
822 $booked = intval( $entry->get_booked() ) - 1;
823 } else {
824 $booked = intval( $entry->get_booked() ) + 1;
825 }
826
827 $booked_data = apply_filters( 'timetics_booking_update_schedule', $entry, ['booked' => $booked], $data, $booking );
828
829 if ( 'cancel' === $status && 'one-to-one' == strtolower( $meeting->get_type() ) ) {
830 $entry->delete();
831 } else {
832 $entry->update( $booked_data );
833 }
834
835 } else {
836 $book_entry_data = [
837 'meeting_id' => $meeting->get_id(),
838 'staff_id' => $staff->get_id(),
839 'customer_id' => $customer->get_id(),
840 'booking_id' => $booking->get_id(),
841 'booked' => 1,
842 'date' => $date_time->format('Y-m-d'),
843 'start' => $date_time->format('h:i a'),
844 'end' => $end_time->format('h:i a'),
845 ];
846
847 $book_entry_data = apply_filters( 'timetics_booking_schedule', $book_entry_data, $data );
848 $booking_entry->create( $book_entry_data );
849 }
850
851 // Fire after booking schedule create.
852 do_action( 'timetics_after_booking_schedule', $booking->get_id(), $customer->get_id(), $meeting->get_id(), $data );
853
854 $data = [
855 'success' => 1,
856 'status_code' => 200,
857 /* translators: Action */
858 'message' => sprintf( esc_html__( 'Successfully %s booking', 'timetics' ), $action ),
859 'data' => $this->prepare_item( $booking ),
860 ];
861
862 return new WP_HTTP_Response( $data, 200 );
863 }
864
865 /**
866 * Prepare item for response
867 *
868 * @param integer $booking_id
869 *
870 * @return array
871 */
872 public function prepare_item( $booking_id ) {
873 $booking = new Booking( $booking_id );
874 $appointment = new Appointment( $booking->get_appointment() );
875 $staff = new Staff( $booking->get_staff_id() );
876 $customer = new Customer( $booking->get_customer_id() );
877 $meeting_timezone = $appointment->get_timezone();
878 $booking_timezone = $booking->get_timezone();
879
880 $start_date_time = timetics_convert_timezone( $booking->get_start_date() . ' ' . $booking->get_start_time(), $booking_timezone, $meeting_timezone );
881 $end_date_time = timetics_convert_timezone( $booking->get_end_date() . ' ' . $booking->get_end_time(), $booking_timezone, $meeting_timezone );
882 $date = timetics_datetime( 'Y-m-d', $booking->get_date(), $meeting_timezone );
883
884 $event = $booking->get_event();
885 $join_link = 'google-meet' === $booking->get_location_type() && ! empty( $event['hangoutLink'] ) ? $event['hangoutLink'] : '';
886
887 $booking_title = $appointment->is_appointment() ? $appointment->get_name() : $booking->get_appointment_name();
888
889 $response = [
890 'id' => $booking->get_id(),
891 'random_id' => $booking->get_random_id(),
892 'status' => $booking->get_status(),
893 'order_total' => $booking->get_total(),
894 'start_date' => $start_date_time->format( 'Y-m-d' ),
895 'end_date' => $end_date_time->format( 'Y-m-d' ),
896 'date' => $date,
897 'start_time' => $start_date_time->format( 'h:i a' ),
898 'end_time' => $end_date_time->format( 'h:i a' ),
899 'location' => $booking->get_location(),
900 'location_type' => $booking->get_location_type(),
901 'description' => $booking->get_description(),
902 'customer' => [
903 'id' => $customer->get_id(),
904 'full_name' => $customer->get_display_name(),
905 'first_name' => $customer->get_first_name(),
906 'last_name' => $customer->get_last_name(),
907 'email' => $customer->get_email(),
908 'phone' => $customer->get_phone(),
909 ],
910 'appointment' => [
911 'id' => $appointment->get_id(),
912 'name' => $booking_title,
913 'duration' => $appointment->get_duration(),
914 'type' => $appointment->get_type(),
915 'price' => $appointment->get_price(),
916 'locations' => $appointment->get_locations(),
917 'timezone' => $appointment->get_timezone(),
918 'permalink' => $appointment->get_appointment_permalink(),
919 ],
920 'staff' => [
921 'id' => $staff->get_id(),
922 'full_name' => $staff->get_display_name(),
923 'first_name' => $staff->get_first_name(),
924 'last_name' => $staff->get_last_name(),
925 'email_name' => $staff->get_email(),
926 'phone' => $staff->get_phone(),
927 'image' => $staff->get_image(),
928 ],
929 ];
930
931 if ( $join_link ) {
932 $response['meeting_link'] = $join_link;
933 }
934
935 return apply_filters( 'timetics_booking_json_data', $response, $booking );
936 }
937
938 /**
939 * Delete booking
940 *
941 * @param integer $booking_id
942 *
943 * @return bool
944 */
945 private function delete( $booking_id ) {
946 $booking = new Booking( $booking_id );
947 $meeting = new Appointment( $booking->get_appointment() );
948
949 if ( ! $booking->is_booking() ) {
950 return false;
951 }
952
953 $current_user_id = get_current_user_id();
954
955 if (
956 $meeting->is_appointment()
957 && ! user_can( $current_user_id, 'manage_options' )
958 && $meeting->get_author() != $current_user_id
959 ) {
960 $data = [
961 'success' => 0,
962 'message' => __( 'You are not allowed to delete this booking.', 'timetics' ),
963 ];
964
965 return new WP_HTTP_Response( $data, 403 );
966 }
967
968 $booking_entry = new Booking_Entry();
969
970 $date_time = timetics_convert_timezone( $booking->get_start_date() . ' ' . $booking->get_start_time(), $booking->get_timezone(), $meeting->get_timezone() );
971
972 $entries = $booking_entry->find(
973 [
974 'staff_id' => $booking->get_staff_id(),
975 'meeting_id' => $booking->get_appointment(),
976 'date' => $date_time->format('Y-m-d'),
977 'start' => $date_time->format('h:i a'),
978 ]
979 );
980
981 if ( $entries ) {
982 $entry = $booking_entry->first();
983
984 if ( 'one-to-one' == strtolower( $meeting->get_type() ) ) {
985 $entry->delete();
986 } else {
987 $booked = intval( $entry->get_booked() ) - 1;
988 $booked_seat = ! empty( $booking->get_seat() ) ? $booking->get_seat() : [];
989 $existing_seat = ! empty( $entry->get_seats() ) ? $entry->get_seats() : [];
990
991 $entry->update( [
992 'booked' => $booked,
993 'seats' => array_values( array_diff( $existing_seat, $booked_seat ) ),
994 ] );
995 }
996 }
997
998 $recurrences = $booking->get_recurrence();
999 $booking->delete_event();
1000 $booking->delete();
1001 $cancel_event_email = new Cancel_Event_Email( $booking );
1002 $cancel_event_email->send();
1003
1004 do_action( 'timetics_after_booking_delete', $recurrences );
1005
1006 return true;
1007 }
1008
1009 public function is_available_slot( $meeting, $booking_data = [] ) {
1010 $start_date = $booking_data['start_date'];
1011 $start_time = $booking_data['start_time'];
1012 $booking_timezone = $booking_data['timezone'];
1013 $booking_entry = new Booking_Entry();
1014 $meeting_id = $meeting->get_id();
1015 $staff_id = $booking_data['staff_id'];
1016
1017 $time = is_string( $start_time ) ? strtotime( $start_time ) : $start_time;
1018 $time = gmdate( 'H:i', $time );
1019 $booking_entries = new Booking_Entry();
1020 $meeting = new Appointment( $meeting_id );
1021
1022 $entries = $booking_entries->find( [
1023 'meeting_id' => $meeting_id,
1024 'staff_id' => $staff_id,
1025 'date' => $start_date,
1026 ] );
1027
1028 $booked = false;
1029
1030 foreach ( $entries as $entry ) {
1031 $booking = new Booking( $entry->get_booking_id() );
1032 $booking_time = timetics_convert_timezone( $booking->get_start_date() . ' ' . $entry->get_start(), $booking->get_timezone(), $booking_timezone )->format( 'H:i' );
1033
1034 if ( $booking_time == $time ) {
1035 $booked = $entry;
1036 break;
1037 }
1038 }
1039
1040 if ( $booked && $booked->get_booked() >= $meeting->get_capacity() ) {
1041 return false;
1042 }
1043
1044 return true;
1045 }
1046 }
1047