PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.10
Timetics – Appointment Booking Calendar & Scheduling v1.0.10
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 / appointments / appointment.php

appointment.php in Timetics – Appointment Booking Calendar & Scheduling 1.0.10, at core/appointments/appointment.php

857 lines 20.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Class Apointment
4 *
5 * @since 1.0.0
6 *
7 * @package Timetics
8 */
9 namespace Timetics\Core\Appointments;
10
11 use DateTime;
12 use Timetics\Base\PostModel;
13 use Timetics\Core\Bookings\Booking;
14 use Timetics\Core\Bookings\Booking_Entry;
15 use Timetics\Core\Staffs\Staff;
16 use WP_Query;
17
18 /**
19 * Class Appointments.
20 *
21 * @since 1.0.0
22 */
23 class Appointment extends PostModel {
24 /**
25 * Store post type
26 *
27 * @since 1.0.0
28 *
29 * @var string $post_type
30 */
31 public $post_type = 'timetics-appointment';
32
33 /**
34 * Store timetics custom taxonomy
35 *
36 * @since 1.0.0
37 *
38 * @var string $taxonomy
39 */
40 protected $taxonomy = 'timetics-meeting-category';
41
42 /**
43 * Store id
44 *
45 * @since 1.0.0
46 *
47 * @var int $id
48 */
49 protected $id;
50
51 /**
52 * Store post metadata
53 *
54 * @since 1.0.0
55 *
56 * @var array $data
57 */
58 public $data = [
59 'name' => '',
60 'type' => '',
61 'description' => '',
62 'staff' => '',
63 'locations' => '',
64 'duration' => '',
65 'schedule' => '',
66 'blocked_schedule' => '',
67 'price' => '',
68 'buffer_time' => '',
69 'timezone' => '',
70 'availability' => '',
71 'visibility' => '',
72 'capacity' => '',
73 'seat_plan' => '',
74 'duplicate' => 0,
75 'custom_fields' => [],
76 'wc_product_id' => 0,
77 'seat_plan_settings' => '',
78 'fleunt_crm_webhook' => '',
79 'fluent_hook_overwrite' => '',
80 'pabbly_hook_overwrite' => '',
81 'zapier_hook_overwrite' => '',
82 'pabbly_webook' => '',
83 'zapier_webook' => '',
84 'min_notice_time' => '',
85 'notifications' => [
86 'booking_created_email_form' => '',
87 'booking_created_email_title' => '',
88 'booking_created_email_body' => '',
89 'booking_canceled_email_form' => '',
90 'booking_canceled_email_title' => '',
91 'booking_canceled_email_body' => '',
92 'booking_reschedule_email_form' => '',
93 'booking_reschedule_email_title' => '',
94 'booking_reschedule_email_body' => '',
95 'booking_reminder_email_form' => '',
96 'booking_reminder_email_title' => '',
97 'booking_reminder_email_body' => '',
98 'booking_reminder_time' => 30,
99 ],
100 'permalink' => '',
101 'recurring' => false,
102 'recurring_limit' => 0,
103 'recurring_interval' => 1,
104 'recurring_interval_name' => 'day',
105 ];
106
107 /**
108 * Store meta key prefix
109 *
110 * @since 1.0.0
111 *
112 * @var string $prefix
113 */
114 public $prefix = '_tt_apointment_';
115
116 /**
117 * Appointments Constructor
118 *
119 * @since 1.0.0
120 *
121 * @param int $appointment Optional. Default 0.
122 * @return void
123 */
124 public function __construct( $appointment = 0 ) {
125 if ( $appointment instanceof self ) {
126 $this->set_id( $appointment->get_id() );
127 } elseif ( ! empty( $appointment->ID ) ) {
128 $this->set_id( $appointment->ID );
129 } elseif ( is_numeric( $appointment ) && $appointment > 0 ) {
130 $this->set_id( $appointment );
131 }
132 }
133
134 /**
135 * Get appoint id.
136 *
137 * @since 1.0.0
138 *
139 * @return int
140 */
141 public function get_id() {
142 return $this->id;
143 }
144
145 /**
146 * Get woocommerce product id
147 *
148 * @return integer
149 */
150 public function get_wc_product_id() {
151 return $this->get_prop( 'wc_product_id' );
152 }
153
154 /**
155 * Get name.
156 *
157 * @since 1.0.0
158 *
159 * @return string
160 */
161 public function get_name() {
162 return $this->get_prop( 'name' );
163 }
164
165 /**
166 * Get coach
167 *
168 * @since 1.0.0
169 *
170 * @return int
171 */
172 public function get_staff() {
173 $staff_ids = $this->get_prop( 'staff' );
174 $staffs = [];
175
176 if ( $staff_ids ) {
177 foreach ( $staff_ids as $staff_id ) {
178 $staff = new Staff( $staff_id );
179
180 if ( ! $staff->is_staff() ) {
181 continue;
182 }
183
184 $staffs[] = $staff->get_data();
185 }
186 }
187 return $staffs;
188 }
189
190 /**
191 * Get staff ids
192 *
193 * @return array
194 */
195 public function get_staff_ids() {
196 return $this->get_prop( 'staff' );
197 }
198
199 /**
200 * Get duplicate number
201 *
202 * @return integer
203 */
204 public function get_duplicate_nuber() {
205 return $this->get_prop( 'duplicate' );
206 }
207
208 /**
209 * Get capacity
210 *
211 * @return integer
212 */
213 public function get_capacity() {
214 return $this->get_prop( 'capacity' );
215 }
216
217 /**
218 * Get location
219 *
220 * @since 1.0.0
221 *
222 * @return string
223 */
224 public function get_locations() {
225 return $this->get_prop( 'locations' );
226 }
227
228 /**
229 * Get Permalink
230 *
231 * @since 1.0.0
232 *
233 * @return string
234 */
235 public function get_appointment_permalink() {
236 return get_post_permalink( $this->id );
237 }
238
239 /**
240 * Get duration
241 *
242 * @since 1.0.0
243 *
244 * @return string
245 */
246 public function get_duration() {
247 return $this->get_prop( 'duration' );
248 }
249
250 /**
251 * Get interval for the duration
252 *
253 * @return integer
254 */
255 public function get_interval() {
256 $duration = $this->get_duration();
257 if ( $duration ) {
258 $duration = explode( ' ', $duration );
259 $duration = 'hr' === $duration[1] ? intval( $duration[0] ) * 60 * 60 : intval( $duration[0] ) * 60;
260 }
261
262 return $duration;
263 }
264
265 /**
266 * Get schedule
267 *
268 * @since 1.0.0
269 *
270 * @return array
271 */
272 public function get_schedule() {
273 return $this->get_prop( 'schedule' );
274 }
275
276 /**
277 * Get blocked schedule
278 *
279 * @since 1.0.0
280 *
281 * @return array
282 */
283 public function get_blocked_schedule() {
284 return $this->get_prop( 'blocked_schedule' );
285 }
286
287 /**
288 * Get price
289 *
290 * @since 1.0.0
291 *
292 * @return string
293 */
294 public function get_price() {
295 return $this->get_prop( 'price' );
296 }
297
298 /**
299 * Get type
300 *
301 * @since 1.0.0
302 *
303 * @return string
304 */
305 public function get_type() {
306 return $this->get_prop( 'type' );
307 }
308
309 /**
310 * Get description
311 *
312 * @since 1.0.0
313 *
314 * @return string
315 */
316 public function get_description() {
317 return $this->get_prop( 'description' );
318 }
319
320 public function get_buffer_time() {
321 return $this->get_prop( 'buffer_time' );
322 }
323
324 public function get_timezone() {
325 return $this->get_prop( 'timezone' );
326 }
327
328 public function get_availability() {
329 return $this->get_prop( 'availability' );
330 }
331
332 public function get_visibility() {
333 return $this->get_prop( 'visibility' );
334 }
335
336 public function get_fleunt_crm_webhook() {
337 return $this->get_prop( 'fleunt_crm_webhook' );
338 }
339 public function get_fluent_hook_overwrite() {
340 return $this->get_prop( 'fluent_hook_overwrite' );
341 }
342 public function get_pabbly_webook() {
343 return $this->get_prop( 'pabbly_webook' );
344 }
345 public function get_pabbly_hook_overwrite() {
346 return $this->get_prop( 'pabbly_hook_overwrite' );
347 }
348 public function get_zapier_webook() {
349 return $this->get_prop( 'zapier_webook' );
350 }
351 public function get_zapier_hook_overwrite() {
352 return $this->get_prop( 'zapier_hook_overwrite' );
353 }
354 public function get_min_notice_time() {
355 return $this->get_prop( 'min_notice_time' );
356 }
357
358 /**
359 * Get Custom fields
360 *
361 * @return array
362 */
363 public function get_custom_fields() {
364 return $this->get_prop( 'custom_fields' );
365 }
366
367 /**
368 * Get notifications
369 *
370 * @return array
371 */
372 public function get_notifications() {
373 return $this->get_prop( 'notifications' );
374 }
375
376 /**
377 * Get appointment categories
378 *
379 * @since 1.0.0
380 *
381 * @return array
382 */
383 public function get_categories() {
384 $categories = wp_get_post_terms( $this->id, $this->taxonomy );
385
386 if ( is_wp_error( $categories ) ) {
387 return [];
388 }
389
390 return $categories;
391 }
392
393 /**
394 * Get category ids
395 *
396 * @return array
397 */
398 public function get_category_ids() {
399 $categories = $this->get_categories();
400
401 $ids = [];
402
403 foreach ( $categories as $category ) {
404 $ids[] = $category->term_id;
405 }
406
407 return $ids;
408 }
409
410 /**
411 * Get post thumbnail.
412 *
413 * @param string $size Image size
414 *
415 * @return string
416 */
417 public function get_image( $size = 'post-thumbnail' ) {
418 return get_the_post_thumbnail_url( $size );
419 }
420
421 /**
422 * Get permalink
423 *
424 * @return string
425 */
426 public function get_link() {
427 return get_post_permalink( $this->id );
428 }
429
430 /**
431 * Get Seat plan for a meeting
432 *
433 * @return array
434 */
435 public function get_seats() {
436 return $this->get_prop( 'seat_plan' );
437 }
438
439 /**
440 * Get seat plan settings
441 *
442 * @return array
443 */
444 public function get_seat_settings() {
445 return $this->get_prop( 'seat_plan_settings' );
446 }
447
448 // Recurring options.
449
450 /**
451 * Check it is recurring or not
452 *
453 * @return bool
454 */
455 public function is_recurring() {
456 return boolval( $this->get_prop( 'recurring' ) );
457 }
458
459 /**
460 * Get recurring limit
461 *
462 * @return integer
463 */
464 public function get_recurring_limit() {
465 return intval( $this->get_prop( 'recurring_limit' ) );
466 }
467
468 /**
469 * Get recurring interval
470 *
471 * @return integer
472 */
473 public function get_recurring_interval() {
474 return intval( $this->get_prop( 'recurring_interval' ) );
475 }
476
477 /**
478 * Get recurring interval name
479 *
480 * @return string
481 */
482 public function get_recurring_interval_name() {
483 return $this->get_prop( 'recurring_interval_name' );
484 }
485
486 /**
487 * Get appointment data
488 *
489 * @param string $prop
490 *
491 * @return mixed
492 */
493 public function get_prop( $prop = '' ) {
494 return $this->get_metadata( $prop );
495 }
496
497 /**
498 * Get metadata
499 *
500 * @since 1.0.0
501 *
502 * @param string $prop Optional. Default empty string.
503 *
504 * @return mixed
505 */
506 private function get_metadata( $prop = '' ) {
507 $meta_key = $this->prefix . $prop;
508 return get_post_meta( $this->id, $meta_key, true );
509 }
510
511 /**
512 * Set appointment id
513 *
514 * @since 1.0.0
515 *
516 * @param int $id
517 *
518 * @return void
519 */
520 public function set_id( $id ) {
521 $this->id = $id;
522 }
523
524 /**
525 * Set props
526 *
527 * @param array $data Metadata array. Default empty array.
528 *
529 * @return void
530 */
531 public function set_props( $data = [] ) {
532 $this->data = $data;
533 }
534
535 /**
536 * Save appointment
537 *
538 * @since 1.0.0
539 *
540 * @return void
541 */
542 public function save() {
543
544 $args = [
545 'post_title' => $this->data['name'],
546 'post_type' => $this->post_type,
547 'post_status' => 'publish',
548 'post_author' => 1,
549 ];
550
551 if ( ! empty( $this->id ) ) {
552 $args['ID'] = $this->id;
553 }
554
555 // Insert or Update appointment.
556 $appoint_id = wp_insert_post( $args );
557
558 if ( ! is_wp_error( $appoint_id ) ) {
559 $this->set_id( $appoint_id );
560 $this->save_metadata();
561 }
562 }
563
564 /**
565 * Update appointment meta data.
566 *
567 * @since 1.0.0
568 *
569 * @return void
570 */
571 private function save_metadata() {
572 foreach ( $this->data as $key => $value ) {
573 // Prepare meta key.
574 $meta_key = $this->prefix . $key;
575
576 // Update appointment meta data.
577 update_post_meta( $this->id, $meta_key, $value );
578 }
579 }
580
581 /**
582 * Update meeting data
583 *
584 * @param array $args meeting data
585 *
586 * @return bool
587 */
588 public function update( $args = [] ) {
589
590 foreach ( $args as $key => $value ) {
591 if ( ! array_key_exists( $key, $this->data ) ) {
592 continue;
593 }
594
595 // Prepare meta key.
596 $meta_key = $this->prefix . $key;
597
598 // Update appointment meta data.
599 update_post_meta( $this->id, $meta_key, $value );
600 }
601 }
602
603 /**
604 * Delete appointment
605 *
606 * @return bool | WP_Error
607 */
608 public function delete() {
609 return wp_delete_post( $this->id, true );
610 }
611
612 /**
613 * Check the appoint is valid or not
614 *
615 * @return bool
616 */
617 public function is_appointment() {
618 $post = get_post( $this->id );
619
620 if ( $post && $this->post_type === $post->post_type ) {
621 return true;
622 }
623
624 return false;
625 }
626
627 /**
628 * Get all appointments
629 *
630 * @param array $args Appointments args. Default empty array.
631 *
632 * @return array
633 */
634 public static function all( $args = [] ) {
635 $defaults = [
636 'post_type' => ( new self )->post_type,
637 'post_status' => 'publish',
638 'posts_per_page' => 20,
639 'paged' => 1,
640 'orderby' => 'ID',
641 'order' => 'DESC',
642 ];
643
644 $args = wp_parse_args( $args, $defaults );
645
646 if ( ! empty( $args['staff'] ) ) {
647 $args['meta_query'][] = [
648 'key' => '_tt_apointment_staff',
649 'value' => $args['staff'],
650 'compare' => 'LIKE',
651 ];
652 }
653
654 if ( ! empty( $args['visibility'] ) ) {
655 $args['meta_query'][] = [
656 'key' => '_tt_apointment_visibility',
657 'value' => $args['visibility'],
658 'compare' => 'LIKE',
659 ];
660 }
661
662 if ( ! empty( $args['type'] ) ) {
663 $args['meta_query'][] = [
664 'key' => '_tt_apointment_type',
665 'value' => $args['type'],
666 'compare' => 'LIKE',
667 ];
668 }
669
670 if ( ! empty( $args['category'] ) ) {
671 $args['tax_query'][] = [
672 'taxonomy' => 'timetics-meeting-category',
673 'terms' => $args['category'],
674 'include_children' => false,
675 ];
676 }
677
678 $post = new WP_Query( $args );
679
680 return [
681 'total' => $post->found_posts,
682 'items' => $post->posts,
683 ];
684 }
685
686 /**
687 * Duplicate appointment
688 *
689 * @since 1.0.0
690 *
691 * @return void
692 */
693 public function duplicate() {
694 $this->set_props( [
695 'name' => $this->get_name(),
696 'description' => $this->get_description(),
697 'type' => $this->get_type(),
698 'locations' => $this->get_locations(),
699 'staff' => $this->get_staff_ids(),
700 'duplicate' => intval( $this->get_duplicate_nuber() ) + 1,
701 'duration' => $this->get_duration(),
702 'price' => $this->get_price(),
703 'capacity' => $this->get_capacity(),
704 'schedule' => $this->get_schedule(),
705 'categories' => $this->get_categories(),
706 'timezone' => $this->get_timezone(),
707 'availability' => $this->get_availability(),
708 'visibility' => $this->get_visibility(),
709 'buffer_time' => $this->get_buffer_time(),
710 'notifications' => $this->get_notifications(),
711 'fluent_hook_overwrite' => $this->get_fluent_hook_overwrite(),
712 'fleunt_crm_webhook' => $this->get_fleunt_crm_webhook(),
713 'pabbly_hook_overwrite' => $this->get_pabbly_hook_overwrite(),
714 'pabbly_webook' => $this->get_pabbly_webook(),
715 'zapier_webook' => $this->get_zapier_webook(),
716 'min_notice_time' => $this->get_min_notice_time(),
717 'zapier_hook_overwrite' => $this->get_zapier_hook_overwrite(),
718 'permalink' => $this->get_appointment_permalink(),
719 ] );
720
721 $this->set_id( 0 );
722 $this->save();
723 }
724
725 /**
726 * Prepare meeting schedule
727 *
728 * @param string $start
729 * @param string $end
730 * @param integer $staff_id
731 *
732 * @return array
733 */
734 public function prepare_schedule( $start, $end, $staff_id, $timze_zone = '' ) {
735 $start = new \DateTime( $start );
736 $end = new \DateTime( $end );
737 $schedule = [];
738
739 // Prepare schedule for the current meeting
740 for ( $date = $start; $date <= $end; $date->modify( '+1 day' ) ) {
741 $schedule[] = $this->get_schedule_by_date( $date->format( 'Y-m-d' ), $staff_id, $timze_zone );
742 }
743
744 return $schedule;
745 }
746
747 /**
748 * Get meeting schedule by date
749 *
750 * @param string $date [$date description]
751 * @param integer $staff_id [$staff_id description]
752 *
753 * @return array
754 */
755 private function get_schedule_by_date( $date, $staff_id, $timze_zone = '' ) {
756 $day_name = ucfirst( date( 'D', strtotime( $date ) ) );
757 $schedule = $this->get_schedule();
758 $interval = $this->get_interval();
759 $tartget_schedule = [];
760
761 $slots = [];
762
763 if ( ! empty( $schedule[$staff_id] ) ) {
764 $tartget_schedule = $schedule[$staff_id][$day_name];
765 }
766
767 foreach ( $tartget_schedule as $day ) {
768 $start = strtotime( $day['start'] );
769 $end = strtotime( $day['end'] );
770
771 for ( $time = $start; $time <= $end; $time += $interval ) {
772 $booked_entry = $this->get_booking_entries( $date, $time, $staff_id );
773 $status = 'available';
774 $capacity = $this->get_capacity();
775 $booked = 0;
776
777 if ( $booked_entry ) {
778 if ( $booked_entry->get_booked() >= $capacity ) {
779 $status = 'unavailable';
780 }
781
782 $booked = $booked_entry->get_booked();
783 }
784
785 $datetime = $this->convert_timezone( $time, $timze_zone );
786
787 $slot = [
788 'status' => $status,
789 'start_time' => $datetime->format( 'g:ia' ),
790 'booked' => $booked,
791 ];
792
793 $slot = apply_filters( 'timetics_booking_slot', $slot, $this, $booked_entry );
794
795 $slots[] = $slot;
796 }
797 }
798
799 return [
800 'date' => $date,
801 'status' => empty( $slots ) ? 'unavailable' : 'available',
802 'slots' => $slots,
803 ];
804 }
805
806 /**
807 * Get booked entry status
808 *
809 * @param string $date
810 * @param string $time
811 * @param interger $staff_id
812 *
813 * @return bool
814 */
815 private function get_booking_entries( $date, $time, $staff_id ) {
816 $time = gmdate( 'H:i', $time );
817 $booking_entries = new Booking_Entry();
818 $entries = $booking_entries->find( [
819 'meeting_id' => $this->id,
820 'staff_id' => $staff_id,
821 'date' => $date,
822 ] );
823
824 foreach ( $entries as $entry ) {
825 $booking = new Booking( $entry->get_booking_id() );
826 $booking_time = timetics_convert_timezone( $booking->get_start_date() . ' ' . $entry->get_start(), $booking->get_timezone(), $this->get_timezone() )->format( 'H:i' );
827
828 if ( $booking_time == $time ) {
829 return $entry;
830 }
831 }
832
833 return false;
834 }
835
836 /**
837 * Convert timestamp in target timezone
838 *
839 * @param integer $timestamp
840 * @param string $timze_zone
841 *
842 * @return DateTime
843 */
844 public function convert_timezone( $date, $timze_zone ) {
845 $date = is_int( $date ) ? gmdate( 'Y-m-d g:ia', $date ) : $date;
846
847 // Create a DateTime object with the provided timestamp and time zone
848 $datetime = new \DateTime( $date, new \DateTimeZone( $this->get_timezone() ) );
849
850 // Convert the time zone
851 $datetime->setTimezone( new \DateTimeZone( $timze_zone ) );
852
853 // Get the converted timestamp
854 return $datetime;
855 }
856 }
857