PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.38
Timetics – Appointment Booking Calendar & Scheduling v1.0.38
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 1.0.22 All 64 releases
← All changes | core/bookings/api-booking.php +323 -75 1.0.15 → 1.0.38 View file →
@@ -5,11 +5,14 @@
5 5 * @package Timetics
6 6 */
7 7 namespace Timetics\Core\Bookings;
8 8
9 +use Error;
9 10 use Timetics\Base\Api;
11 +use Timetics\Core\Appointments\Api_Appointment;
10 12 use Timetics\Core\Appointments\Appointment;
11 13 use Timetics\Core\Customers\Customer;
14 +use Timetics\Core\Emails\Cancel_Event_Customer_Email;
12 15 use Timetics\Core\Emails\Cancel_Event_Email;
13 16 use Timetics\Core\Emails\New_Event_Customer_Email;
14 17 use Timetics\Core\Emails\New_Event_Email;
15 18 use Timetics\Core\Emails\Update_Event_Customer_Email;
@@ -15,8 +18,9 @@
15 18 use Timetics\Core\Emails\Update_Event_Customer_Email;
16 19 use Timetics\Core\Emails\Update_Event_Email;
17 20 use Timetics\Core\Staffs\Staff;
18 21 use Timetics\Utils\Singleton;
22 +use TimeticsPro\Core\SeatPlan\SeatPlan;
19 23 use WP_Error;
20 24 use WP_HTTP_Response;
21 25 use WP_Query;
22 26
@@ -37,8 +41,15 @@
37 41 */
38 42 protected $rest_base = 'bookings';
39 43
40 44 /**
45 + * Booking Type
46 + *
47 + * @var string
48 + */
49 + protected $type = '';
50 +
51 + /**
41 52 * Register rest routes
42 53 *
43 54 * @return void
44 55 */
@@ -83,18 +94,14 @@
83 94 $this->namespace, '/' . $this->rest_base . '/(?P<booking_id>[\d]+)', [
84 95 [
85 96 'methods' => \WP_REST_Server::READABLE,
86 97 'callback' => [$this, 'get_item'],
87 - 'permission_callback' => function () {
88 - return true;
89 - },
98 + 'permission_callback' => [$this, 'get_item_permission_callback'],
90 99 ],
91 100 [
92 101 'methods' => \WP_REST_Server::EDITABLE,
93 102 'callback' => [$this, 'update_item'],
94 - 'permission_callback' => function () {
95 - return true;
96 - },
103 + 'permission_callback' => [$this, 'update_item_permission_callback'],
97 104 ],
98 105 [
99 106 'methods' => \WP_REST_Server::DELETABLE,
100 107 'callback' => [$this, 'delete_item'],
@@ -174,17 +181,24 @@
174 181 'meeting' => $meeting_id,
175 182 'staff' => $staff_id,
176 183 ];
177 184
185 + $args = apply_filters( 'timetics/add/item/data', $args, $request );
186 +
178 187 if ( $start_date ) {
179 188 $args['start_date'] = $start_date;
180 189 }
181 190
182 - $appoint = Booking::all( $args );
191 + $bookings = Booking::all( $args );
192 + $items = [];
183 193
184 - $items = [];
194 + if ( ! current_user_can( 'manage_options' ) ) {
195 + $user_id = get_current_user_id();
196 + $appointment_ids = Appointment::get_appointment_ids_by_author( $user_id );
197 + $bookings = Booking::get_booking_ids_by_appointment( $appointment_ids, $per_page );
198 + }
185 199
186 - foreach ( $appoint['items'] as $item ) {
200 + foreach ( $bookings['items'] as $item ) {
187 201 $items[] = $this->prepare_item( $item->ID );
188 202 }
189 203
190 204 /**
@@ -195,9 +209,9 @@
195 209 $data = [
196 210 'success' => 1,
197 211 'status_code' => 200,
198 212 'data' => [
199 - 'total' => $appoint['total'],
213 + 'total' => $bookings['total'],
200 214 'items' => $items,
201 215 ],
202 216 ];
203 217
@@ -245,11 +259,9 @@
245 259 *
246 260 * @return JSON
247 261 */
248 262 public function create_item( $request ) {
249 - /**
250 - * Added temporary for leagacy sass. It will remove in future.
251 - */
263 +
252 264 $bookings_count = Booking::all();
253 265
254 266 $response = [
255 267 'success' => 0,
@@ -290,8 +302,9 @@
290 302 *
291 303 * @return JSON
292 304 */
293 305 public function update_item( $request ) {
306 +
294 307 $booking_id = (int) $request['booking_id'];
295 308 $booking = new Booking( $booking_id );
296 309
297 310 if ( ! $booking->is_booking() ) {
@@ -322,8 +335,9 @@
322 335 *
323 336 * @return JSON
324 337 */
325 338 public function delete_item( $request ) {
339 +
326 340 $booking_id = (int) $request['booking_id'];
327 341
328 342 $delete = $this->delete( $booking_id );
329 343
@@ -355,8 +369,9 @@
355 369 *
356 370 * @return JSON
357 371 */
358 372 public function bulk_delete( $request ) {
373 +
359 374 $bookings = json_decode( $request->get_body(), true );
360 375
361 376 foreach ( $bookings as $booking ) {
362 377 $delete = $this->delete( $booking );
@@ -406,8 +421,9 @@
406 421 *
407 422 * @return JSON
408 423 */
409 424 public function search_items( $request ) {
425 +
410 426 // Prepare search args.
411 427 $per_page = ! empty( $request['per_page'] ) ? intval( $request['per_page'] ) : 20;
412 428 $paged = ! empty( $request['paged'] ) ? intval( $request['paged'] ) : 1;
413 429 $search = ! empty( $request['search'] ) ? sanitize_text_field( $request['search'] ) : '';
@@ -528,8 +544,9 @@
528 544 return new WP_Error( 'timezone_error', __( 'Your meeting timezone is invalid. Please update your meeting timezone with proper timezone.', 'timetics' ) );
529 545 }
530 546
531 547 $days = $meeting->prepare_schedule( $start_date, $end_date, $staff_id, $timezone );
548 + $days = apply_filters( 'timetics_schedule_data_for_selected_date', $days, $staff_id, $meeting_id, $timezone );
532 549
533 550 $data = [
534 551 'today' => gmdate( 'Y-m-d' ),
535 552 'availability_timezone' => $meeting->get_timezone(),
@@ -556,16 +573,17 @@
556 573 *
557 574 * @return JSON
558 575 */
559 576 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'] ) : '';
577 + $booking_id = intval( $request['booking_id'] );
578 + $booking = new Booking( $booking_id );
579 + $data = json_decode( $request->get_body(), true );
580 + $status = ! empty( $data['status'] ) ? sanitize_text_field( $data['status'] ) : '';
564 581 $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'] : '';
582 + $post_status = 'succeeded' === $status ? $default_booking_status : 'pending';
583 + $payment_method = ! empty( $data['payment_method'] ) ? sanitize_text_field( $data['payment_method'] ) : '';
584 + $payment_details = ! empty( $data['payment_details'] ) ? $data['payment_details'] : '';
585 + $type = $booking->get_type();
568 586
569 587 if ( ! $booking->is_booking() ) {
570 588 return [
571 589 'status_code' => 404,
@@ -595,15 +613,28 @@
595 613 }
596 614
597 615 if ( $default_booking_status === $post_status ) {
598 616 $booking->create_event();
599 - $new_event_email = new New_Event_Email( $booking );
600 - $new_event_email->send();
601 617
602 - $new_event_customer_email = new New_Event_Customer_Email( $booking );
603 - $new_event_customer_email->send();
618 + if( 'timetics-event' == $type ){
619 + return;
620 + }
604 621
622 + $is_email_to_customer = timetics_get_option( 'booking_created_customer');
623 + $is_email_to_host = timetics_get_option( 'booking_created_host');
624 +
625 + if ( $is_email_to_host ) {
626 + $new_event_email = new New_Event_Email( $booking );
627 + $new_event_email->send();
628 + }
629 +
630 + if ( $is_email_to_customer ) {
631 + $new_event_customer_email = new New_Event_Customer_Email( $booking );
632 + $new_event_customer_email->send();
633 + }
634 +
605 635 do_action( 'timetics_booking_payment', $booking );
636 +
606 637 }
607 638
608 639 /**
609 640 * Added temporary for leagacy sass. It will remove in future.
@@ -630,8 +661,25 @@
630 661 */
631 662 public function save_bookings( $request, $id = 0 ) {
632 663 $data = json_decode( $request->get_body(), true );
633 664
665 + if( isset( $data['type'] ) && 'timetics-event' == $data['type'] ) {
666 + $this->type = $data['type'];
667 + return apply_filters('timetics_booking_event', $data, $id );
668 + }else {
669 + return $this->booking_appointment($data, $id);
670 + }
671 + }
672 +
673 + /**
674 + * Booking Appointment
675 + *
676 + * @param array $data All the data of booking
677 + * @param integer $id Booking id
678 + *
679 + * @return JSON
680 + */
681 + protected function booking_appointment ($data, $id) {
634 682 $first_name = ! empty( $data['first_name'] ) ? sanitize_text_field( $data['first_name'] ) : '';
635 683 $last_name = ! empty( $data['last_name'] ) ? sanitize_text_field( $data['last_name'] ) : '';
636 684 $email = ! empty( $data['email'] ) ? sanitize_text_field( $data['email'] ) : '';
637 685 $phone = ! empty( $data['phone'] ) ? sanitize_text_field( $data['phone'] ) : '';
@@ -642,9 +690,9 @@
642 690 $payment_method = ! empty( $data['payment_method'] ) ? sanitize_text_field( $data['payment_method'] ) : '';
643 691 $address_1 = ! empty( $data['address_1'] ) ? sanitize_text_field( $data['address_1'] ) : '';
644 692 $address_2 = ! empty( $data['address_2'] ) ? sanitize_text_field( $data['address_2'] ) : '';
645 693 $appointment = ! empty( $data['appointment'] ) ? intval( $data['appointment'] ) : 0;
646 - $staff = ! empty( $data['staff'] ) ? intval( $data['staff'] ) : 0;
694 + $staff_id = ! empty( $data['staff'] ) ? intval( $data['staff'] ) : 0;
647 695 $start_date = ! empty( $data['start_date'] ) ? sanitize_text_field( $data['start_date'] ) : '';
648 696 $date = ! empty( $data['date'] ) ? sanitize_text_field( $data['date'] ) : '';
649 697 $end_date = ! empty( $data['end_date'] ) ? sanitize_text_field( $data['end_date'] ) : $start_date;
650 698 $start_time = ! empty( $data['start_time'] ) ? sanitize_text_field( $data['start_time'] ) : '';
@@ -655,9 +703,13 @@
655 703 $location_type = ! empty( $data['location_type'] ) ? sanitize_text_field( $data['location_type'] ) : '';
656 704 $description = ! empty( $data['description'] ) ? sanitize_text_field( $data['description'] ) : '';
657 705 $timezone = ! empty( $data['timezone'] ) ? sanitize_text_field( $data['timezone'] ) : '';
658 706 $recurring_dates = ! empty( $data['recurring_dates'] ) ? $data['recurring_dates'] : [];
707 + $seats = ! empty( $data['seats'] ) ? $data['seats'] : [];
708 + $cancel_reason = ! empty( $data['cancel_reason'] ) ? $data['cancel_reason'] : [];
709 + $booking_time = ! empty( $data['booking_createAt'] ) ? $data['booking_createAt'] : '';
659 710 $action = $id ? 'updated' : 'created';
711 + $appointment_token = ! empty( $data['appointment_token'] ) ? sanitize_text_field( $data['appointment_token'] ) : '';
660 712
661 713 $validate = $this->validate(
662 714 $data, [
663 715 'first_name',
@@ -678,14 +730,23 @@
678 730 ];
679 731 return new WP_HTTP_Response( $data, 403 );
680 732 }
681 733
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();
734 + $customer = new Customer();
735 + $meeting = new Appointment( $appointment );
736 + $staff = new Staff( $staff_id );
737 + $booking = new Booking( $id );
738 + $booking_entry = new Booking_Entry();
687 739
740 + // Validate booking
741 +
742 + $validation = $this->validate_booking( $appointment, $data );
743 + if(is_wp_error($validation)){
744 + return $validation;
745 + }
746 +
747 +
748 +
688 749 if ( 'created' === $action && ! $this->is_available_slot( $meeting, [
689 750 'staff_id' => $staff->get_id(),
690 751 'start_date' => $start_date,
691 752 'start_time' => $start_time,
@@ -718,8 +779,9 @@
718 779 );
719 780
720 781 // Update booking schedule.
721 782 if ( $id ) {
783 +
722 784 $entries = $booking_entry->find(
723 785 [
724 786 'staff_id' => $booking->get_staff_id(),
725 787 'meeting_id' => $booking->get_appointment(),
@@ -725,8 +787,9 @@
725 787 'meeting_id' => $booking->get_appointment(),
726 788 'date' => $booking->get_start_date(),
727 789 'start' => $booking->get_start_time(),
728 790 ]
791 +
729 792 );
730 793
731 794 if ( $entries ) {
732 795 $entry = $booking_entry->first();
@@ -744,38 +807,47 @@
744 807 if ( $id && $booking->get_status() == 'cancel' && $status == 'cancel' ) {
745 808 return new WP_Error( 'booking_cancel_error', __( 'This booking alreay canceled', 'timetics' ) );
746 809 }
747 810
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 - );
811 + $booking_props = [
812 + 'customer' => $customer->get_id(),
813 + 'appointment' => $meeting->get_id(),
814 + 'appointment_name' => $meeting->get_name(),
815 + 'staff' => $staff->get_id(),
816 + 'customer_fname' => $customer->get_first_name(),
817 + 'customer_lname' => $customer->get_last_name(),
818 + 'customer_email' => $customer->get_email(),
819 + 'customer_phone' => $customer->get_phone(),
820 + 'staff_fname' => $staff->get_first_name(),
821 + 'staff_lname' => $staff->get_last_name(),
822 + 'staff_email' => $staff->get_email(),
823 + 'meeting_name' => $meeting->get_name(),
824 + 'meeting_description' => $meeting->get_description(),
825 + 'meeting_type' => $meeting->get_type(),
826 + 'booking_time' => $booking_time,
827 + 'description' => $description,
828 + 'start_date' => $start_date,
829 + 'date' => $date,
830 + 'end_date' => $end_date,
831 + 'start_time' => $start_time,
832 + 'end_time' => $end_time,
833 + 'order_total' => $this->calculate_order_total( $data ),
834 + 'post_status' => $status,
835 + 'location' => $location,
836 + 'location_type' => $location_type,
837 + 'timezone' => $timezone,
838 + 'cancel_reason' => $cancel_reason,
839 + ];
777 840
841 + if( 'created' == $action ){
842 + $booking_props['security_token'] = $booking->generate_security_token();
843 + }
844 +
845 + $booking->set_props( $booking_props );
846 +
847 +
848 + $booking = apply_filters( 'timetics/bookings/booking/set', $booking );
849 +
778 850 $booking->save();
779 851
780 852 // Fire when booking is completed.
781 853 do_action( 'timetics_after_booking_create', $booking->get_id(), $customer->get_id(), $meeting->get_id(), $data );
@@ -783,11 +855,21 @@
783 855 // Create or update calendar event.
784 856 if ( $id ) {
785 857 if ( 'cancel' === $status ) {
786 858 $booking->delete_event();
787 - $cancel_event_email = new Cancel_Event_Email( $booking );
788 - $cancel_event_email->send();
859 + $is_email_to_customer = timetics_get_option( 'booking_canceled_customer');
860 + $is_email_to_host = timetics_get_option( 'booking_canceled_host');
789 861
862 + if ( $is_email_to_host ) {
863 + $cancel_event_email = new Cancel_Event_Email( $booking );
864 + $cancel_event_email->send();
865 + }
866 +
867 + if ( $is_email_to_customer ) {
868 + $customer_cancel_event_email = new Cancel_Event_Customer_Email( $booking );
869 + $customer_cancel_event_email->send();
870 + }
871 +
790 872 /**
791 873 * Added temporary for leagacy sass. It will remove in future.
792 874 */
793 875 do_action( 'timetics/admin/booking/after_delete_item', $booking );
@@ -792,19 +874,28 @@
792 874 */
793 875 do_action( 'timetics/admin/booking/after_delete_item', $booking );
794 876 } else {
795 877 $booking->update_event();
878 + $is_email_to_reschedule_customer = timetics_get_option( 'booking_rescheduled_customer');
879 + $is_email_to_reschedule_host = timetics_get_option( 'booking_rescheduled_host');
880 +
881 + if ( $is_email_to_reschedule_host ) {
796 882 $update_event_email = new Update_Event_Email( $booking );
797 883 $update_event_email->send();
884 + }
798 885
799 - $update_event_customer_email = new Update_Event_Customer_Email( $booking );
800 - $update_event_customer_email->send();
886 + if ( $is_email_to_reschedule_customer ) {
887 + $update_event_customer_email = new Update_Event_Customer_Email( $booking );
888 + $update_event_customer_email->send();
889 + }
890 +
891 +
801 892 }
802 893 }
803 894
804 895 // 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() );
896 + $date_time = timetics_convert_timezone( $start_date . ' ' . $start_time, $timezone, $meeting->get_timezone() );
897 + $end_time = timetics_convert_timezone( $start_date . ' ' . $end_time, $timezone, $meeting->get_timezone() );
807 898
808 899 // Create booking schedule.
809 900 $entries = $booking_entry->find(
810 901 [
@@ -809,10 +900,10 @@
809 900 $entries = $booking_entry->find(
810 901 [
811 902 'staff_id' => $staff->get_id(),
812 903 'meeting_id' => $meeting->get_id(),
813 - 'date' => $date_time->format('Y-m-d'),
814 - 'start' => $date_time->format('h:i a'),
904 + 'date' => $date_time->format( 'Y-m-d' ),
905 + 'start' => $date_time->format( 'h:i a' ),
815 906 ]
816 907 );
817 908
818 909 if ( $entries ) {
@@ -838,11 +929,11 @@
838 929 'staff_id' => $staff->get_id(),
839 930 'customer_id' => $customer->get_id(),
840 931 'booking_id' => $booking->get_id(),
841 932 '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'),
933 + 'date' => $date_time->format( 'Y-m-d' ),
934 + 'start' => $date_time->format( 'h:i a' ),
935 + 'end' => $end_time->format( 'h:i a' ),
845 936 ];
846 937
847 938 $book_entry_data = apply_filters( 'timetics_booking_schedule', $book_entry_data, $data );
848 939 $booking_entry->create( $book_entry_data );
@@ -895,11 +986,14 @@
895 986 'end_date' => $end_date_time->format( 'Y-m-d' ),
896 987 'date' => $date,
897 988 'start_time' => $start_date_time->format( 'h:i a' ),
898 989 'end_time' => $end_date_time->format( 'h:i a' ),
990 + 'booking_time' => $booking->get_booking_time(),
899 991 'location' => $booking->get_location(),
900 992 'location_type' => $booking->get_location_type(),
901 993 'description' => $booking->get_description(),
994 + 'cancel_reason' => $booking->get_cancel_reason(),
995 + 'security_token'=> $booking->get_security_token(),
902 996 'customer' => [
903 997 'id' => $customer->get_id(),
904 998 'full_name' => $customer->get_display_name(),
905 999 'first_name' => $customer->get_first_name(),
@@ -955,9 +1049,9 @@
955 1049 if (
956 1050 $meeting->is_appointment()
957 1051 && ! user_can( $current_user_id, 'manage_options' )
958 1052 && $meeting->get_author() != $current_user_id
959 - ) {
1053 + ) {
960 1054 $data = [
961 1055 'success' => 0,
962 1056 'message' => __( 'You are not allowed to delete this booking.', 'timetics' ),
963 1057 ];
@@ -972,10 +1066,10 @@
972 1066 $entries = $booking_entry->find(
973 1067 [
974 1068 'staff_id' => $booking->get_staff_id(),
975 1069 'meeting_id' => $booking->get_appointment(),
976 - 'date' => $date_time->format('Y-m-d'),
977 - 'start' => $date_time->format('h:i a'),
1070 + 'date' => $date_time->format( 'Y-m-d' ),
1071 + 'start' => $date_time->format( 'h:i a' ),
978 1072 ]
979 1073 );
980 1074
981 1075 if ( $entries ) {
@@ -997,11 +1091,25 @@
997 1091
998 1092 $recurrences = $booking->get_recurrence();
999 1093 $booking->delete_event();
1000 1094 $booking->delete();
1001 - $cancel_event_email = new Cancel_Event_Email( $booking );
1002 - $cancel_event_email->send();
1003 1095
1096 + $is_email_to_customer = timetics_get_option( 'booking_canceled_customer');
1097 + $is_email_to_host = timetics_get_option( 'booking_canceled_host');
1098 +
1099 + if ( $is_email_to_host ) {
1100 + $cancel_event_email = new Cancel_Event_Email( $booking );
1101 + $cancel_event_email->send();
1102 + }
1103 +
1104 + if ( $is_email_to_customer ) {
1105 +
1106 + $customer_cancel_event_email = new Cancel_Event_Customer_Email( $booking );
1107 + $customer_cancel_event_email->send();
1108 + }
1109 +
1110 +
1111 +
1004 1112 do_action( 'timetics_after_booking_delete', $recurrences );
1005 1113
1006 1114 return true;
1007 1115 }
@@ -1041,6 +1149,146 @@
1041 1149 return false;
1042 1150 }
1043 1151
1044 1152 return true;
1153 + }
1154 +
1155 + /**
1156 + * Validates a booking.
1157 + *
1158 + * @param int $appointment_id The ID of the appointment.
1159 + * @param array $data The data for the booking.
1160 + * @throws None
1161 + * @return mixed Returns an error response if the validation fails, otherwise returns nothing.
1162 + */
1163 + public function validate_booking($appointment_id, $data) {
1164 + $meeting = new Appointment($appointment_id);
1165 + $all_seats = (array) $meeting->get_seats();
1166 + $meeting_price = $meeting->get_price();
1167 + $meeting_locations = (array) $meeting->get_locations();
1168 + $total_price = 0;
1169 +
1170 + $staff_id = ! empty( $data['staff'] ) ? intval( $data['staff'] ) : 0;
1171 + $order_total = ! empty( $data['order_total'] ) ? floatval( $data['order_total'] ) : 0;
1172 + $location_type = ! empty( $data['location_type'] ) ? sanitize_text_field( $data['location_type'] ) : '';
1173 + $start_date = ! empty( $data['start_date'] ) ? sanitize_text_field( $data['start_date'] ) : '';
1174 + $timezone = ! empty( $data['timezone'] ) ? sanitize_text_field( $data['timezone'] ) : '';
1175 + $start_time = ! empty( $data['start_time'] ) ? sanitize_text_field( $data['start_time'] ) : '';
1176 + $status = ! empty( $data['status'] ) ? sanitize_text_field( $data['status'] ) : '';
1177 + $seats = ! empty( $data['seats'] ) ? $data['seats'] : [];
1178 + $timeslots = $meeting->get_avilable_timeslots( $start_date, $staff_id, $timezone );
1179 + $meeting_has_buffer_time = $meeting->get_buffer_time_after_in_seconds() > 0 || $meeting->get_buffer_time_before_in_seconds() > 0;
1180 +
1181 + if ( ! $meeting->is_appointment() ) {
1182 + return $this->create_error_response( __( 'Invalid meeting.', 'timetics' ), 422 );
1183 + }
1184 +
1185 + if ( 'cancel' !== $status ) {
1186 + if ( ! $meeting_has_buffer_time && ! in_array( date('g:ia', strtotime( $start_time ) ), $timeslots ) ) {
1187 + return $this->create_error_response( __( 'Invalid timeslot.', 'timetics' ), 422 );
1188 + }
1189 +
1190 + // Check if the staff is matched
1191 + if ( ! in_array( $staff_id, $meeting->get_staff_ids() ) ) {
1192 + return $this->create_error_response(__('Team member not matched', 'timetics'), 403);
1193 +
1194 + }
1195 + // Check if the location type is matched
1196 + if ( ! in_array( $location_type, array_column( $meeting_locations, 'location_type' ) ) ) {
1197 + return $this->create_error_response(__('Location type not matched', 'timetics'), 403);
1198 + }
1199 + }
1200 + }
1201 +
1202 + /**
1203 + * Creates an error response with the given message and status code.
1204 + *
1205 + * @param string $message The error message.
1206 + * @param int $status_code The HTTP status code.
1207 + * @return WP_HTTP_Response The error response.
1208 + */
1209 + public function create_error_response($message, $status_code) {
1210 + return new WP_Error( 'timezone_error', $message, ['status' => $status_code] );
1211 + }
1212 +
1213 + /**
1214 + * Calculate order total
1215 + *
1216 + * @param array $data Request data
1217 + *
1218 + * @return integer
1219 + */
1220 + private function calculate_order_total($data) {
1221 + $seats = ! empty( $data['seats'] ) ? $data['seats'] : [];
1222 + $meeting_id = ! empty( $data['appointment'] ) ? $data['appointment'] : 0;
1223 + $total_price = 0;
1224 +
1225 + if ( class_exists( SeatPlan::class ) && $seats ) {
1226 + foreach( $seats as $seat ) {
1227 + $seat_object = SeatPlan::find( $seat );
1228 + $total_price += $seat_object->price;
1229 + }
1230 +
1231 + return $total_price;
1232 + }
1233 +
1234 + $meeting = new Appointment( $meeting_id );
1235 +
1236 + $prices = $meeting->get_price();
1237 +
1238 + if ( $prices && is_array( $prices ) ) {
1239 + return $prices[0]['ticket_price'];
1240 + }
1241 +
1242 + return 0;
1243 + }
1244 +
1245 + /**
1246 + * Update item permission callback
1247 + * @param WP_Rest_Request $request
1248 + * @return bool
1249 + */
1250 + public function update_item_permission_callback($request){
1251 + $nonce = $request->get_header('X-WP-Nonce');
1252 +
1253 + $booking_id = (int) $request->get_param('booking_id');
1254 + $appointment_token = $request->get_param('appointment_token');
1255 +
1256 + // If no token provided, this might be an old link. Verify with nonce for that case temporarily
1257 + if ( !empty($booking_id) && empty($appointment_token) && wp_verify_nonce($nonce, 'wp_rest')) {
1258 + return wp_verify_nonce($nonce, 'wp_rest');
1259 + }
1260 +
1261 + if (empty($booking_id) || empty($appointment_token)) {
1262 + return false;
1263 + }
1264 +
1265 + $booking = new Booking($booking_id);
1266 +
1267 + if (!$booking->is_booking()) {
1268 + return false;
1269 + }
1270 +
1271 + // Get the stored security token from booking meta
1272 + $stored_token = $booking->get_security_token();
1273 +
1274 + // Check if the provided token matches the stored token and have nonce verified
1275 + if ($appointment_token === $stored_token && !empty($stored_token) && wp_verify_nonce($nonce, 'wp_rest') ) {
1276 + return true;
1277 + }
1278 +
1279 + return false;
1280 + }
1281 +
1282 + /**
1283 + * Get item permission callback
1284 + * @param WP_Rest_Request $request
1285 + * @return bool
1286 + */
1287 + public function get_item_permission_callback($request){
1288 + $nonce = $request->get_header('X-WP-Nonce');
1289 + if (wp_verify_nonce($nonce, 'wp_rest')) {
1290 + return true;
1291 + }
1292 + return false;
1045 1293 }
1046 1294 }