PluginProbe
Timetics – Appointment Booking Calendar & Scheduling / 1.0.58
Timetics – Appointment Booking Calendar & Scheduling v1.0.58
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 / hooks.php

hooks.php in Timetics – Appointment Booking Calendar & Scheduling 1.0.58, at core/bookings/hooks.php

476 lines 14.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Booking related hooks
4 *
5 * @package Timetics
6 */
7
8 namespace Timetics\Core\Bookings;
9
10 defined( 'ABSPATH' ) || exit;
11
12 use Timetics\Core\Appointments\Appointment;
13 use Timetics\Core\Emails\Customer_Booking_Reminder_Email;
14 use Timetics\Core\Emails\Staff_Booking_Reminder_Email;
15 use Timetics\Utils\Singleton;
16
17 /**
18 * Class Hooks
19 */
20 class Hooks {
21 use Singleton;
22
23 /**
24 * Initialization
25 *
26 * @return void
27 */
28 public function init() {
29 add_action( 'timetics_after_booking_create', [$this, 'register_schedule'] );
30 add_action( 'timetics_booking_remainder', [$this, 'send_reminder_email'], 10, 1 );
31 add_action( 'timetics_booking_clear_schedule', [$this, 'clear_booking_schedule'] );
32
33 add_action( 'timetics_after_booking_create', [$this, 'reschedule_booking'], 10, 4 );
34
35 add_action( 'init', [$this, 'register_booking_status'] );
36 add_action( 'init', [$this, 'maybe_migrate_reminder_schedules'], 99 );
37
38 add_action('woocommerce_before_calculate_totals', [ $this, 'timetics_variation_ticket_total_price' ] );
39
40 add_filter( 'woocommerce_add_cart_item_data', [ $this, 'timetics_add_cart_item_data' ], 10, 2 );
41
42 add_action( 'admin_init', [$this, 'delete_booking_before_paid'] );
43 }
44
45 /**
46 * Register cron job for schedule a reminder email
47 *
48 * @param integer $booking_id
49 *
50 * @return void
51 */
52 public function register_schedule( $booking_id ) {
53 $booking = new Booking( $booking_id );
54
55 $date = $booking->get_start_date();
56 $time = $booking->get_start_time();
57
58 $booking_timestamp = strtotime( $date . ' ' . $time );
59
60 $reminder_time = timetics_get_option( 'remainder_time' );
61
62 if ( ! $reminder_time ) {
63 return;
64 }
65
66 foreach ( $reminder_time as $time ) {
67 $timestamp = null;
68 $duration = $time['duration-time'];
69
70 switch ( $time['custom_duration_type'] ) {
71 case 'min':
72 $timestamp = $duration * 60;
73 break;
74 case 'hour':
75 $timestamp = $duration * 60 * 60;
76 break;
77 case 'day':
78 $timestamp = ( $duration * 24 ) * 60 * 60;
79 break;
80 }
81
82 $timestamp = intval($booking_timestamp)- intval($timestamp);
83
84 if ( ! wp_next_scheduled( 'timetics_booking_remainder', [$booking_id] ) ) {
85 wp_schedule_single_event( $timestamp, 'timetics_booking_remainder', [$booking_id] );
86 }
87 }
88 }
89
90 /**
91 * Send booking reminder email
92 *
93 * @param integer $booking_id
94 *
95 * @return void
96 */
97 public function send_reminder_email( $booking_id ) {
98 $booking_reminder_customer = timetics_get_option( 'booking_reminder_customer' );
99 $booking_reminder_host = timetics_get_option( 'booking_reminder_host' );
100
101 $booking = new Booking( $booking_id );
102
103 if ( $booking_reminder_customer ) {
104 $customer_reminder = new Customer_Booking_Reminder_Email( $booking );
105 $customer_reminder->send();
106 }
107
108 if ( $booking_reminder_host ) {
109 $staff_reminder = new Staff_Booking_Reminder_Email( $booking );
110 $staff_reminder->send();
111 }
112
113 }
114
115 /**
116 * Clear cron job schedule
117 *
118 * @return
119 */
120 public function clear_booking_schedule() {
121 $bookins = Booking::all();
122
123 if ( ! $bookins ) {
124 return;
125 }
126
127 // Run cron action.
128 foreach ( $bookins['items'] as $booking ) {
129 $timestamp = wp_next_scheduled( 'timetics_booking_remainder', [$booking->ID] );
130
131 if ( $timestamp && $timestamp < time() ) {
132 wp_unschedule_event( $timestamp, 'timetics_booking_remainder', [$booking->ID] );
133 }
134 }
135 }
136
137 /**
138 * Migrate any outstanding cron events scheduled with the legacy
139 * `timetics_booking_remainder_{id}` hook name to the unified
140 * `timetics_booking_remainder` hook with the booking id as an argument.
141 *
142 * Runs once per plugin version.
143 *
144 * @return void
145 */
146 public function maybe_migrate_reminder_schedules() {
147 $version = defined( 'TIMETICS_VERSION' ) ? TIMETICS_VERSION : '0';
148
149 if ( get_option( 'timetics_reminder_cron_migrated' ) === $version ) {
150 return;
151 }
152
153 $cron = _get_cron_array();
154
155 if ( ! is_array( $cron ) ) {
156 update_option( 'timetics_reminder_cron_migrated', $version, false );
157 return;
158 }
159
160 $changed = false;
161
162 foreach ( $cron as $timestamp => $hooks ) {
163 if ( ! is_array( $hooks ) ) {
164 continue;
165 }
166
167 foreach ( $hooks as $hook => $events ) {
168 if ( strpos( $hook, 'timetics_booking_remainder_' ) !== 0 ) {
169 continue;
170 }
171
172 $booking_id = (int) substr( $hook, strlen( 'timetics_booking_remainder_' ) );
173
174 if ( ! $booking_id ) {
175 unset( $cron[ $timestamp ][ $hook ] );
176 $changed = true;
177 continue;
178 }
179
180 $args = [$booking_id];
181 $key = md5( serialize( $args ) );
182
183 $cron[ $timestamp ]['timetics_booking_remainder'][ $key ] = [
184 'schedule' => false,
185 'args' => $args,
186 ];
187
188 unset( $cron[ $timestamp ][ $hook ] );
189 $changed = true;
190 }
191
192 if ( empty( $cron[ $timestamp ] ) ) {
193 unset( $cron[ $timestamp ] );
194 }
195 }
196
197 if ( $changed ) {
198 _set_cron_array( $cron );
199 }
200
201 update_option( 'timetics_reminder_cron_migrated', $version, false );
202 }
203
204 /**
205 * Update bookked entry if reschedule
206 *
207 * @param integer $booking_id
208 * @param integer $customer_id
209 * @param integer $meeting_id
210 * @param array $data
211 * @param integer $booking_entry
212 *
213 * @return void
214 */
215 public function reschedule_booking( $booking_id, $customer_id, $meeting_id, $data ) {
216 $reschedule = ! empty( $data['reschedule'] ) ? $data['reschedule'] : false;
217 $booking = new Booking( $booking_id );
218 $meeting = new Appointment( $meeting_id );
219 $booking_entry = new Booking_Entry();
220
221 if ( ! $reschedule ) {
222 return;
223 }
224
225 $entries = $booking_entry->find(
226 [
227 'staff_id' => $booking->get_staff_id(),
228 'meeting_id' => $meeting->get_id(),
229 'date' => $booking->get_start_date(),
230 'start' => $booking->get_start_time(),
231 ]
232 );
233
234 if ( ! $entries ) {
235 return;
236 }
237
238 $entry = $booking_entry->first();
239 $booked_seat = ! empty( $booking->get_seat() ) ? $booking->get_seat() : [];
240 $existing_seat = ! empty( $entry->get_seats() ) ? $entry->get_seats() : [];
241
242 if ( 'one-to-one' === strtolower( $meeting->get_type() ) ) {
243 $entry->delete();
244 } else {
245 $booked = intval( $entry->get_booked() ) - 1;
246
247 $entry->update( [
248 'booked' => $booked,
249 'seats' => array_values( array_diff( $existing_seat, $booked_seat ) ),
250 ] );
251 }
252 }
253
254 /**
255 * Register booking statuses
256 *
257 * @return void
258 */
259 public function register_booking_status() {
260 // Define label_count translations for each status
261 $label_counts = array(
262 /* translators: %s: Number of approved bookings */
263 'approved' => _n_noop(
264 'Approved <span class="count">(%s)</span>',
265 'Approved <span class="count">(%s)</span>',
266 'timetics'
267 ),
268
269 /* translators: %s: Number of pending bookings */
270 'pending' => _n_noop(
271 'Pending <span class="count">(%s)</span>',
272 'Pending <span class="count">(%s)</span>',
273 'timetics'
274 ),
275 /* translators: %s: Number of cancelled bookings */
276 'cancel' => _n_noop(
277 'Cancelled <span class="count">(%s)</span>',
278 'Cancelled <span class="count">(%s)</span>',
279 'timetics'
280 ),
281 /* translators: %s: Number of completed bookings */
282 'completed' => _n_noop(
283 'Completed <span class="count">(%s)</span>',
284 'Completed <span class="count">(%s)</span>',
285 'timetics'
286 ),
287 );
288
289 // Register each status
290 foreach ( $label_counts as $status => $label_count ) {
291 register_post_status( $status, array(
292 'public' => true,
293 'exclude_from_search' => false,
294 'show_in_admin_all_list' => false,
295 'show_in_admin_status_list' => false,
296 'label_count' => $label_count,
297 ) );
298 }
299 }
300
301 /**
302 * Delete bookings if unpaid before 30 mins
303 *
304 * @return void
305 */
306 public function delete_booking_before_paid() {
307 $args = [
308 'post_type' => 'timetics-booking',
309 'numberposts' => -1,
310 // phpcs:ignore WordPress.DB.SlowDBQuery.slow_db_query_meta_query -- Meta query is necessary for filtering bookings by payment method
311 'meta_query' => array(
312 'relation' => 'OR',
313 array(
314 'key' => '_tt_booking_payment_method',
315 'value' => 'stripe',
316 'compare' => '=',
317 ),
318 array(
319 'key' => '_tt_booking_payment_method',
320 'value' => 'paypal',
321 'compare' => '=',
322 ),
323 ),
324 ];
325
326 $bookings = get_posts( $args );
327
328 foreach ( $bookings as $booking ) {
329 $booking = new Booking( $booking->ID );
330
331 if ( 'completed' != $booking->get_status() && $this->is_booking_payment_expire( $booking ) ) {
332 $this->update_booking_entry( $booking->get_id() );
333 }
334 }
335 }
336
337 /**
338 * Check booking payment time expaire or not
339 *
340 * @param Object $booking
341 *
342 * @return bool
343 */
344 public function is_booking_payment_expire( $booking ) {
345 // Booking date and time
346 $post = get_post( $booking->get_id() );
347 $booking_datetime = $post->post_date;
348
349 // Convert the booking date and time to a DateTime object
350 $booking_datetime_object = new \DateTime( $booking_datetime );
351
352 // Calculate 30 minutes from the booking date and time
353 $target_datetime = clone $booking_datetime_object;
354 $target_datetime->modify( '+30 minutes' );
355
356 // Get the current date and time
357 $current_datetime = new \DateTime();
358
359 // Check if 30 minutes have passed
360 if ( $current_datetime > $target_datetime ) {
361 return true;
362 }
363
364 return false;
365 }
366
367 /**
368 * Update booking entry if payment time expire
369 *
370 * @param integer $booking_id
371 *
372 * @return void
373 */
374 public function update_booking_entry( $booking_id ) {
375 $booking = new Booking( $booking_id );
376 $meeting = new Appointment( $booking->get_appointment() );
377
378 if ( ! $booking->is_booking() ) {
379 return false;
380 }
381
382 $current_user_id = get_current_user_id();
383
384 if (
385 $meeting->is_appointment()
386 && ! user_can( $current_user_id, 'manage_options' )
387 && $meeting->get_author() != $current_user_id
388 ) {
389 $data = [
390 'success' => 0,
391 'message' => __( 'You are not allowed to delete this booking.', 'timetics' ),
392 ];
393
394 return new \WP_HTTP_Response( $data, 403 );
395 }
396
397 $booking_entry = new Booking_Entry();
398
399 $date_time = timetics_convert_timezone( $booking->get_start_date() . ' ' . $booking->get_start_time(), $booking->get_timezone(), $meeting->get_timezone() );
400
401 $entries = $booking_entry->find(
402 [
403 'staff_id' => $booking->get_staff_id(),
404 'meeting_id' => $booking->get_appointment(),
405 'date' => $date_time->format( 'Y-m-d' ),
406 'start' => $date_time->format( 'h:i a' ),
407 ]
408 );
409
410 if ( $entries ) {
411 $entry = $booking_entry->first();
412
413 if ( 'one-to-one' == strtolower( $meeting->get_type() ) ) {
414 $entry->delete();
415 } else {
416 $booked = intval( $entry->get_booked() ) - 1;
417 $booked_seat = ! empty( $booking->get_seat() ) ? $booking->get_seat() : [];
418 $existing_seat = ! empty( $entry->get_seats() ) ? $entry->get_seats() : [];
419
420 $entry->update( [
421 'booked' => $booked,
422 'seats' => array_values( array_diff( $existing_seat, $booked_seat ) ),
423 ] );
424 }
425 }
426 }
427
428 /**
429 * Change price for cart item
430 */
431 public function timetics_variation_ticket_total_price( $cart_object ) {
432 foreach ( $cart_object->cart_contents as $key => $value ) {
433 if ( ! empty( $value['booking_id'] ) && $value['booking_id'] !== 0 ) {
434 $order_total = !empty( $value['_timetics_variation_total_price'] ) ? $value['_timetics_variation_total_price'] : 0;
435
436 $value['data']->get_price();
437 $value['data']->set_price($order_total);
438 $value['data']->set_regular_price($order_total);
439 $value['data']->set_sale_price($order_total);
440
441 }
442 }
443
444 }
445
446 /**
447 * add booking_id as cart item data
448 *
449 * @param integer $booking_id
450 *
451 * @return void
452 */
453 public function timetics_add_cart_item_data( $cart_item_data ) {
454 $session_data = WC()->session->get( 'timetics_data' );
455 $booking_id = $session_data['booking_id'];
456 $booking = new Booking( $booking_id );
457 $total_price = floatval($booking->get_total()); // Ensure $total_price is a float
458
459 if ( is_array( $booking->get_seat() ) ) {
460 $total_quantity = count( $booking->get_seat() );
461 } else {
462 $total_quantity = 1;
463 }
464
465 if( ! empty( $booking_id ) && $total_price !== 0 ) {
466 $cart_item_data['_timetics_variation_total_quantity'] = $total_quantity;
467 $cart_item_data['booking_id'] = $booking_id;
468
469 // For balancing the cart item price
470 $cart_item_data['_timetics_variation_total_price'] = $total_price / $total_quantity;
471 }
472
473 return $cart_item_data;
474 }
475 }
476