PluginProbe
Easy Hotel – Powerful Hotel Booking / 2.0.2
Easy Hotel – Powerful Hotel Booking v2.0.2
2.0.8 2.0.7 2.0.6 2.0.5 2.0.4 2.0.3 2.0.2 2.0.1 2.0.0 1.9.9 1.9.8 1.9.7 1.9.6 1.9.5 1.9.4 1.9.3 1.9.2 1.8.1 1.8.2 1.8.3 1.8.4 1.8.5 1.8.6 1.8.7 1.8.8 All 110 releases
easy-hotel / admin / includes / classes / class.helper.php

class.helper.php in Easy Hotel – Powerful Hotel Booking 2.0.2, at admin/includes/classes/class.helper.php

941 lines 37.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
3 class ESHB_Helper {
4
5 public static function eshb_nonce_field( $action = 'eshb_action', $field = 'eshb_nonce', $echo = true ) {
6 // Generate site-specific action using home_url()
7 $nonce_action = sanitize_key( $action ) . '_' . md5( home_url() );
8
9 // Create the nonce field HTML
10 $nonce_field = wp_nonce_field( $nonce_action, sanitize_key( $field ), true, false );
11
12 // Whitelist only the safe HTML tags and attributes
13 $allowed_tags = array(
14 'input' => array(
15 'type' => true,
16 'id' => true,
17 'name' => true,
18 'value' => true,
19 ),
20 );
21
22 // Sanitize output
23 $safe_nonce_field = wp_kses( $nonce_field, $allowed_tags );
24
25 if ( $echo ) {
26 // Escaping required by Plugin Check (escape before output)
27 echo wp_kses( $safe_nonce_field, $allowed_tags );
28 return null;
29 }
30
31 // Return sanitized version
32 return $safe_nonce_field;
33 }
34
35 public static function generate_secure_nonce_action($action) {
36 $nonce_action = $action . '_' . md5( home_url() );
37 return $nonce_action;
38 }
39
40 public static function eshb_insert_booking($order_id, $booking_status, $cart_item_data) {
41
42 if(empty($booking_status) || empty($cart_item_data) || $cart_item_data === null) return false;
43
44 $post_id = wp_insert_post(array(
45 'post_type' => 'eshb_booking',
46 'post_title' => 'Booking',
47 'post_status' => $booking_status,
48 'meta_input' => array(
49 'eshb_booking_metaboxes' => $cart_item_data
50 )
51 ));
52
53 // Check if the post was inserted successfully
54 $accomodation_id = !empty($cart_item_data['booking_accomodation_id']) ? $cart_item_data['booking_accomodation_id'] : 0;
55 $accomodation_title = get_the_title( $accomodation_id );
56
57 if (!is_wp_error($post_id)) {
58 // Update the post title to include the post ID
59 wp_update_post(array(
60 'ID' => $post_id,
61 'post_title' => 'Booking #' . $post_id . ' for: ' . $accomodation_title,
62 'post_status' => $booking_status, // Update status to 'publish'
63 ));
64
65
66 // Update Available Rooms Count For This Accomodation
67 $accomodation_metaboxes = get_post_meta( $accomodation_id, 'eshb_accomodation_metaboxes', true );
68 $total_rooms = floatval($accomodation_metaboxes['total_rooms']);
69 $current_available_rooms = floatval($accomodation_metaboxes['available_rooms']);
70 $room_quantity = isset($cart_item_data['room_quantity']) ? $cart_item_data['room_quantity'] : 1;
71
72 if(!empty($current_available_rooms)){
73 $available_rooms = $current_available_rooms - floatval($room_quantity);
74 }else{
75 $available_rooms = $total_rooms - floatval($room_quantity);
76 }
77
78 $accomodation_metaboxes['available_rooms'] = $available_rooms;
79 update_post_meta($accomodation_id, 'eshb_accomodation_metaboxes', $accomodation_metaboxes);
80
81 // Update Order Status
82 $new_status = $booking_status;
83 update_post_meta($order_id, '_booking_post_created', $post_id);
84
85
86 do_action( 'eshb_after_booking_created', $post_id, $order_id );
87
88 return $post_id;
89
90 }else{
91 $error_message = $post_id->get_error_message();
92 return false;
93 }
94 }
95
96 public static function get_clean_number($num){
97 if (intval($num) == $num) {
98 return intval($num);
99 }
100 return $num;
101 }
102
103 public static function get_product_id_for_cart($accomodation_id, $booking_type) {
104 $product_id = 0;
105
106 if ($booking_type == 'woocommerce') {
107 $thumbnail_id = get_post_thumbnail_id($accomodation_id);
108 $product_id = self::get_or_create_woocommerce_product($accomodation_id, $thumbnail_id);
109 }
110 return $product_id;
111 }
112
113 public static function get_main_post_id_for_translated ($post_id) {
114 $main_post_id = $post_id;
115 if ( function_exists( 'pll_get_post' )) {
116 $default_lang = pll_default_language() ? pll_default_language() : 'en';
117 $main_post_id = pll_get_post( $post_id, $default_lang ) ? pll_get_post( $post_id, $default_lang ) : $post_id ;
118
119 }elseif ( function_exists( 'apply_filters' ) && function_exists( 'icl_object_id' ) ) {
120 $main_post_id = apply_filters( 'wpml_original_element_id', NULL, $post_id, 'post_post' );
121 }
122 return $main_post_id;
123 }
124
125 public static function eshb_get_booking_statuses(){
126
127 $statuses = array(
128 'pending' => 'Pending payment',
129 'deposit-payment' => 'Deposit payment',
130 'processing' => 'Processing',
131 'on-hold' => 'On hold',
132 'completed' => 'Completed',
133 'cancelled' => 'Cancelled',
134 'refunded' => 'Refunded',
135 'failed' => 'Failed'
136 );
137 return apply_filters( 'eshb_booking_statuses', $statuses );
138
139 }
140
141 public static function eshb_get_payment_statuses(){
142
143 $order_status = array(
144 'pending' => 'Pending',
145 'processing' => 'Processing',
146 'on-hold' => 'On hold',
147 'completed' => 'Completed',
148 'cancelled' => 'Cancelled',
149 'refunded' => 'Refunded',
150 'failed' => 'Failed'
151 );
152 return $order_status;
153
154 }
155
156 public static function get_or_create_woocommerce_product($accomodation_id, $thumbnail_id) {
157
158 $product_id = get_post_meta($accomodation_id, '_woocommerce_product_id', true);
159 $product = (!empty($product_id) && get_post_status($product_id) === 'publish') ? wc_get_product($product_id) : false;
160
161 if (!$product) {
162 $product = new WC_Product();
163 $product->set_name(get_the_title($accomodation_id));
164 $product->set_price(1);
165 $product->set_regular_price(1);
166 $product->set_virtual(true);
167 $product->set_image_id($thumbnail_id);
168 $product->save();
169
170 $product_id = $product->get_id();
171
172 update_post_meta($accomodation_id, '_woocommerce_product_id', $product_id);
173 update_post_meta($accomodation_id, '_regular_price', 1);
174 }
175
176 return $product_id;
177 }
178
179 public static function update_product_price_by_id($product_id, $new_price) {
180 // Update the regular price
181 update_post_meta($product_id, '_regular_price', $new_price);
182
183 // Update the price (current price)
184 update_post_meta($product_id, '_price', $new_price);
185
186 // Clear WooCommerce product cache
187 wc_delete_product_transients($product_id);
188 }
189
190 public static function get_current_booking_metadata($booking_id, $key) {
191 if (empty($booking_id)) {
192 $booking_id = get_the_ID();
193 }
194 if (empty($booking_id)) {
195 return '';
196 }
197 $eshb_booking_metaboxes = get_post_meta($booking_id, 'eshb_booking_metaboxes', true);
198 return isset($eshb_booking_metaboxes[$key]) ? $eshb_booking_metaboxes[$key] : '';
199 }
200
201 public static function get_current_booking_customer_metadata($booking_id, $key = '', $default_value = 'default') {
202 if (empty($booking_id)) {
203 $booking_id = get_the_ID();
204 }
205
206 $address = get_post_meta($booking_id, 'eshb_booking_customer_details_metaboxes', true);
207 $value = $address ?? $default_value;
208
209 if(!empty($key)){
210 $value = $address[$key] ?? $default_value;
211 }
212 return $value;
213 }
214
215 public static function get_current_payment_customer_metadata($id, $key) {
216 if (empty($id)) {
217 $id = get_the_ID();
218 }
219 if (empty($id)) return '';
220
221 $address = get_post_meta($id, 'eshb_payment_customer_details_metaboxes', true);
222 return isset($address[$key]) ? $address[$key] : 'default';
223 }
224
225 public static function eshb_get_payment_ids() {
226
227 $args = array(
228 'post_type' => 'eshb_payment',
229 'posts_per_page' => -1,
230 'fields' => 'ids',
231 'post_status' => array( 'completed' ),
232 'orderby' => 'ID',
233 'order' => 'DESC',
234 );
235
236 $payments = get_posts($args);
237 $payment_ids = [];
238
239 foreach ($payments as $payment_id) {
240 if(get_post_status( $payment_id ) == 'completed'){
241 $payment_ids[$payment_id] = get_the_title($payment_id);
242 }
243 }
244
245 return $payment_ids;
246 }
247
248 public static function eshb_get_booking_ids() {
249
250 $booking_statuses = self::eshb_get_booking_statuses();
251 $booking_status_keys = array_keys( $booking_statuses );
252
253 $args = array(
254 'post_type' => 'eshb_booking',
255 'posts_per_page' => -1,
256 'fields' => 'ids',
257 'post_status' => 'any'
258 );
259
260 $bookings = get_posts($args);
261 $booking_ids = [];
262
263 foreach ($bookings as $booking_id) {
264 $booking_ids[$booking_id] = get_the_title($booking_id);
265 }
266
267 return $booking_ids;
268 }
269
270 public static function eshb_get_payment_gateways() {
271 $gateways = [
272 'manual' => 'Manual Payment',
273 'cod' => 'Cash On Delivey'
274 ];
275 return $gateways;
276 }
277
278 public static function get_total_price_from_order_meta($order){
279 $total_price = 0;
280 foreach ( $order->get_items() as $item ) {
281 $item_total_price = $item->get_meta('Total Price');
282 if (!empty($item_total_price)) {
283 $total_price += floatval($item_total_price);
284 }
285 }
286 return $total_price;
287 }
288
289 public static function eshb_get_wc_state_city_name ($country_code, $code) {
290 $states_file = WP_PLUGIN_DIR . '/woocommerce/i18n/states.php';
291 $state_city_name = '';
292 if ( file_exists( $states_file ) ) {
293 $all_states = include $states_file; // returns array of all countries' states
294 $all_states = apply_filters( 'woocommerce_states', $all_states );
295 $state_city_name = isset( $all_states[$country_code][ $code ] ) ? $all_states[$country_code][ $code ] : '';
296 }
297 return $state_city_name;
298 }
299
300 public static function eshb_get_extra_services () {
301
302 $services = [];
303
304 $posts = get_posts([
305 'post_type' => 'eshb_service',
306 'posts_per_page' => -1
307 ]);
308
309 foreach ( $posts as $post ) {
310 $services[ $post->ID ] = $post->post_title;
311 }
312 return $services;
313 }
314
315 // Record a payment (deposit or subsequent) against an order.
316 public static function eshb_assign_payment_to_booking( $payment_id, $order_id, $booking_id, $amount_paid, $update = false, $args = [] ) {
317
318 // error_log('payment assining' . $payment_id);
319
320 if ( $amount_paid <= 0 ) {
321 //error_log('invalid payment amount!');
322 return false;
323 }
324
325 $eshb_settings = get_option('eshb_settings');
326 $booking_type = isset($eshb_settings['booking-type']) ? $eshb_settings['booking-type'] : '';
327 $eshb_booking_metaboxes = get_post_meta( $booking_id, 'eshb_booking_metaboxes', true);
328 $eshb_payment_metaboxes = get_post_meta( $payment_id, 'eshb_payment_metaboxes', true);
329 $total_price = !empty($eshb_booking_metaboxes['total_price']) ? $eshb_booking_metaboxes['total_price'] : 0;
330 $total_paid = !empty($eshb_booking_metaboxes['total_paid']) ? $eshb_booking_metaboxes['total_paid'] : 0;
331 $due = (float) get_post_meta( $order_id, 'eshb_booking_due_amount', true );
332 $booking_new_status = 'deposit-payment';
333
334 $payment_ids = !empty($eshb_booking_metaboxes['payment_ids']) ? $eshb_booking_metaboxes['payment_ids'] : [];
335 if(!in_array($payment_id, $payment_ids)){
336 array_push($payment_ids, $payment_id);
337 }
338
339 if($booking_type == 'woocommerce' && class_exists( 'woocommerce' )){
340 $order = wc_get_order($order_id);
341 if(!$order) return;
342 $total_price = $order->get_subtotal();
343 $total_paid = $order->get_total();
344 }
345
346 $total_paid = 0;
347
348 foreach ( $payment_ids as $payment_id ) {
349 $metabox = get_post_meta( $payment_id, 'eshb_payment_metaboxes', true );
350
351 if ( ! empty( $metabox['amount'] ) ) {
352 $total_paid += floatval( $metabox['amount'] );
353 }
354 }
355
356 $new_due = (float) $total_price - (float)$total_paid;
357 $eshb_booking_metaboxes['payment_ids'] = $payment_ids;
358 $eshb_booking_metaboxes['total_paid'] = $total_paid;
359
360 if($new_due > 0){
361 $eshb_booking_metaboxes['due_amount'] = $new_due;
362 update_post_meta($order_id, 'eshb_booking_due_amount', $new_due);
363 }else{
364
365 if (!empty($eshb_settings['booking-auto-approval']) && $eshb_settings['booking-auto-approval'] == true) {
366 $booking_new_status = 'completed';
367 }else{
368 $booking_new_status = 'processing';
369 }
370 delete_post_meta( $order_id, 'eshb_booking_due_amount');
371 }
372
373 $eshb_booking_metaboxes['booking_status'] = $booking_new_status;
374
375
376 // update booking metabox
377 update_post_meta($booking_id, 'eshb_booking_metaboxes', $eshb_booking_metaboxes);
378
379
380 // update payment metabox
381 if(empty($eshb_payment_metaboxes['transaction_id'])){
382 $transaction_id = 'TXN-' . str_pad( $payment_id, 8, '0', STR_PAD_LEFT );
383 $eshb_payment_metaboxes['transaction_id'] = $transaction_id;
384 update_post_meta($payment_id, 'eshb_payment_metaboxes', $eshb_payment_metaboxes);
385 }
386
387 // update order
388 if($booking_type == 'woocommerce' && class_exists( 'woocommerce' )){
389 // update woocommerce order
390 $order = wc_get_order($order_id);
391 if($order){
392 $order->set_total($total_paid);
393 $order->update_status($booking_new_status);
394 if($new_due <= 0){
395 //$order->payment_complete();
396 $order->add_order_note('Payment received manually.');
397 }
398 $txn = $order->get_transaction_id();
399
400 $order->save();
401 }
402 }else{
403 // update booking status
404 wp_update_post( [
405 'ID' => $booking_id,
406 'post_status' => $booking_new_status
407 ]);
408 }
409
410 return $booking_id;
411 }
412
413 public static function get_eshb_default_start_end_date(){
414 $today_date = gmdate('Y-m-d'); // Get today's date
415
416 // Create a DateTime object from today's date
417 $date = new DateTime($today_date);
418
419 // Add one day
420 $date->modify('+1 day');
421
422 // Get the new date in 'Y-m-d' format
423 $tomorrow_date = $date->format('Y-m-d');
424
425 return array(
426 'start_date' => $today_date,
427 'end_date' => $tomorrow_date
428 );
429 }
430
431 public static function get_eshb_default_start_end_time(){
432 return array(
433 'start_time' => '10:00',
434 'end_time' => '11:00'
435 );
436 }
437
438 public static function eshb_capture_payment ($order_id){
439
440 if(!$order_id) return;
441
442 $order = wc_get_order( $order_id );
443 if ( !$order ) return;
444
445 $order_status = $order->get_status();
446 $due = (float) ( get_post_meta( $order_id, 'eshb_booking_due_amount', true ) ?: 0 );
447 $last_payment_type = get_post_meta( $order_id, 'last_payment_type', true );
448
449 if($due < 1 && $order_status == 'completed'){
450 $last_payment_type = 'full_payment';
451 }
452
453 if(!$last_payment_type) return;
454
455 $booking_id = get_post_meta($order_id, '_booking_post_created', true);
456 if(!$booking_id) return;
457
458 $payment_status = get_post_meta($booking_id, 'payment_status', true);
459
460 $eshb_booking_metaboxes = get_post_meta($booking_id, 'eshb_booking_metaboxes', true);
461 $total_price = ESHB_Helper::get_total_price_from_order_meta($order);
462 $total_paid = !empty($eshb_booking_metaboxes['total_paid']) ? $eshb_booking_metaboxes['total_paid'] : 0;
463
464 if($payment_status == 'completed' && $total_paid > 0) {
465 return;
466 }
467
468 if(in_array($last_payment_type, ['initial_deposit', 'remaining_payment']) && $total_paid >= $total_price) {
469 return;
470 };
471
472
473
474 $gateway = $order->get_payment_method();
475 $currency = $order->get_currency();
476 $fee = 0;
477 $amount = $total_paid;
478 $payment_type = 'Full Payment';
479 $initial_deposit = get_post_meta( $order_id, 'initial_deposit', true );
480 $payment_status = 'completed';
481 $new_due = 0;
482
483
484 if($last_payment_type == 'initial_deposit'){
485 $amount = $initial_deposit;
486 $payment_type = 'Initial Deposit';
487 $status = 'deposit-payment';
488 $new_due = $total_price - $initial_deposit;
489 $total_paid = $initial_deposit;
490 }elseif($last_payment_type == 'remaining_payment'){
491 $amount = $due;
492 $total_paid = $total_price;
493 $payment_type = 'Remaining Payment';
494 }else{
495 $amount = $total_price;
496 $total_paid = $total_price;
497 $payment_type = 'Full Payment';
498 }
499
500 // create payment options
501 $payment_options = [
502 'booking_id' => $booking_id,
503 'transaction_id' => '',
504 'gateway' => $gateway,
505 'gateway_mode' => 'live',
506 'amount' => $amount,
507 'fee' => $fee,
508 'currency' => $currency,
509 'payment_type' => $payment_type,
510 ];
511
512
513 // load customer details from order
514 $first_name = $order->get_billing_first_name();
515 $last_name = $order->get_billing_last_name();
516 $email = $order->get_billing_email();
517 $phone = $order->get_billing_phone();
518 $country = $order->get_billing_country();
519 $state = $order->get_billing_state();
520 $city = $order->get_billing_city();
521 $address_1 = $order->get_billing_address_1();
522 $address_2 = $order->get_billing_address_2();
523 $postcode = $order->get_billing_postcode();
524
525 // create customer details
526 $customer_details = [
527 'first_name' => $first_name,
528 'last_name' => $last_name,
529 'email' => $email,
530 'phone' => $phone,
531 'country' => $country,
532 'state' => $state,
533 'city' => $city,
534 'address_1' => $address_1,
535 'address_2' => $address_2,
536 'postcode' => $postcode,
537 ];
538
539 $post_title = 'Payment for Booking #' . $booking_id;
540
541 // insert payment to eshb_payment posttype
542 $payment_id = wp_insert_post(
543 [
544 'post_title' => $post_title,
545 'post_type' => 'eshb_payment',
546 'post_status' => $payment_status,
547 ]
548 );
549
550 // update payment metadata if payment success
551 if($payment_id){
552 $transaction_id = 'TXN-' . str_pad( $payment_id, 8, '0', STR_PAD_LEFT );
553 $payment_options['transaction_id'] = $transaction_id;
554 update_post_meta($payment_id, 'eshb_payment_metaboxes', $payment_options);
555 update_post_meta($payment_id, 'eshb_payment_customer_details_metaboxes', $customer_details);
556 update_post_meta($booking_id, 'eshb_booking_customer_details_metaboxes', $customer_details);
557
558 // update booking metaboxes
559 $payment_ids = !empty($eshb_booking_metaboxes['payment_ids']) ? $eshb_booking_metaboxes['payment_ids'] : [];
560 if(!in_array($payment_id, $payment_ids)){
561 array_push($payment_ids, $payment_id);
562 }
563
564
565 $eshb_booking_metaboxes['payment_ids'] = $payment_ids;
566 $eshb_booking_metaboxes['total_paid'] = $total_paid;
567
568
569 // update due meta
570 if($last_payment_type == 'initial_deposit'){
571 $eshb_booking_metaboxes['due_amount'] = $new_due;
572 update_post_meta($order_id, 'eshb_booking_due_amount', $new_due);
573 update_post_meta($order_id, 'last_payment_type', $last_payment_type);
574 }elseif($last_payment_type == 'remaining_payment'){
575 delete_post_meta( $order_id, 'eshb_booking_due_amount');
576 delete_post_meta( $order_id, 'last_payment_type');
577 }
578
579 if($new_due < 1 || !$new_due){
580 update_post_meta($booking_id, 'payment_status', 'completed');
581 }
582
583 // update booking
584 update_post_meta($booking_id, 'eshb_booking_metaboxes', $eshb_booking_metaboxes);
585
586
587 // allow other plugins to hook after capture payment
588 do_action('eshb_after_capture_payment', $order_id);
589
590
591 // delete last payment type
592 delete_post_meta( $order_id, 'last_payment_type');
593 delete_post_meta($order_id, 'last_requested_payment_amount');
594 }
595 return $payment_id;
596
597 }
598
599 public static function eshb_calculate_time_diff ($start_date, $end_date, $start_time, $end_time) {
600 // Require times; if missing return 0 hours
601 if (empty($start_time) || empty($end_time)) {
602 return 0;
603 }
604
605 // Default end date to start date when empty
606 $start_date = !empty($start_date) ? $start_date : gmdate('Y-m-d');
607 $end_date = !empty($end_date) ? $end_date : $start_date;
608
609 // Normalize special case where end time can be provided as 24:00
610 $is_end_24 = ($end_time === '24:00' || $end_time === '24:00:00');
611 $normalized_end_time = $is_end_24 ? '00:00' : $end_time;
612
613 $start_dt = DateTime::createFromFormat('Y-m-d H:i:s', $start_date . ' ' . $start_time);
614 if(!$start_dt){
615 $start_dt = DateTime::createFromFormat('Y-m-d H:i', $start_date . ' ' . $start_time);
616 }
617
618 $end_dt = DateTime::createFromFormat('Y-m-d H:i:s', $end_date . ' ' . $normalized_end_time);
619 if(!$end_dt){
620 $end_dt = DateTime::createFromFormat('Y-m-d H:i', $end_date . ' ' . $normalized_end_time);
621 }
622
623 if(!$start_dt || !$end_dt){
624 return 0;
625 }
626
627 // If the textual times are equal (ignoring seconds) and not a 24:00 case, treat as 0 hours for same-day
628 if(!$is_end_24 && $start_dt->format('H:i') === $end_dt->format('H:i') && $end_date === $start_date){
629 return 0;
630 }
631
632 // If end is not after start, or explicitly 24:00, assume it crosses midnight to the next day
633 if($is_end_24 || $end_dt <= $start_dt){
634 $end_dt->modify('+1 day');
635 }
636
637 $duration_seconds = $end_dt->getTimestamp() - $start_dt->getTimestamp();
638 $hours_count = (int) ceil($duration_seconds / 3600);
639
640 return max(0, (int) $hours_count);
641 }
642
643 public static function get_available_times_by_date($accommodation_id, $date, $excludes = []) {
644 // Get booking settings (working hours)
645 $settings = get_option('eshb_single_day_settings', []);
646 $start_time = !empty($settings['start_time']) ? $settings['start_time'] : '10:00';
647 $end_time = !empty($settings['end_time']) ? $settings['end_time'] : '22:00';
648
649 $date_str = gmdate('Y-m-d', strtotime($date));
650
651 // Fetch all bookings
652 $bookings = get_posts([
653 'post_type' => 'eshb_booking',
654 'posts_per_page' => -1,
655 'post_status' => ['publish','completed','processing','deposit-payment','pending'],
656 'fields' => 'ids',
657 ]);
658
659 $booked_slots = [];
660 $slot_keys = []; // Track unique slot keys
661
662 foreach ($bookings as $booking_id) {
663 $meta = get_post_meta($booking_id, 'eshb_booking_metaboxes', true);
664 if (!is_array($meta)) continue;
665
666 // Skip if accommodation does not match
667 if ((int)($meta['booking_accomodation_id'] ?? 0) !== (int)$accommodation_id) continue;
668
669 // Skip if booking date does not match
670 if (($meta['booking_start_date'] ?? '') !== $date_str) continue;
671
672 $b_start = $meta['booking_start_time'] ?? '';
673 $b_end = $meta['booking_end_time'] ?? '';
674
675 // skip if invalid
676 if (!$b_start || !$b_end) continue;
677
678 $slot_key = $b_start.'-'.$b_end;
679
680 // Add unique slot only once
681 if (!isset($slot_keys[$slot_key])) {
682 $slot_keys[$slot_key] = true;
683
684 $booked_slots[] = [$b_start, $b_end];
685 }
686 }
687
688 // Build available slots based on working window
689 $available_slots = [];
690
691 // skip for excludes
692 if (count($excludes) > 0) {
693 array_push($available_slots, $excludes);
694 }
695
696 $window_start_ts = strtotime("$date_str $start_time");
697 $window_end_ts = strtotime("$date_str $end_time");
698
699 if (empty($booked_slots)) {
700 // If no bookings, full window is available
701 $available_slots[] = [$start_time, $end_time];
702 } else {
703 // Sort booked slots by start time
704 usort($booked_slots, function($a, $b) use ($date_str) {
705 return strtotime("$date_str {$a[0]}") <=> strtotime("$date_str {$b[0]}");
706 });
707
708 $pointer = $window_start_ts;
709
710 foreach ($booked_slots as $slot) {
711 $slot_start_ts = strtotime("$date_str {$slot[0]}");
712 $slot_end_ts = strtotime("$date_str {$slot[1]}");
713
714 // Gap before the booking = available slot
715 if ($slot_start_ts > $pointer) {
716 $available_slots[] = [gmdate('H:i', $pointer), $slot[0]];
717 }
718
719 // Move pointer to end of current booked slot
720 $pointer = max($pointer, $slot_end_ts);
721 }
722
723 // After last booking, remaining time is available
724 if ($pointer < $window_end_ts) {
725 $available_slots[] = [gmdate('H:i', $pointer), $end_time];
726 }
727 }
728
729 return [
730 'booked_slots' => $booked_slots,
731 'available_slots' => $available_slots,
732 'next_start_time' => $available_slots[0][0] ?? '',
733 'next_end_time' => $available_slots[0][1] ?? '',
734 ];
735 }
736
737 public static function format_to_wp_time($time_string){
738 $timestamp = strtotime( $time_string );
739 return date_i18n( get_option('time_format'), $timestamp );
740 }
741
742 public static function eshb_set_accomodation_localize($accomodation_id = null) {
743 $cart_url = site_url('/cart');
744 $checkout_url = site_url('/checkout');
745 if(class_exists('woocommerce')) {
746 $cart_url = wc_get_cart_url();
747 $checkout_url = wc_get_checkout_url();
748 }
749
750 $min_max_settings = [
751 'calendar_start_date_buffer' => 0,
752 'required_min_nights' => 1,
753 'required_max_nights' => 999,
754 ];
755
756 $eshb_settings = get_option('eshb_settings');
757 $search_capacity_settings = [];
758 if(!empty($eshb_settings)){
759 $search_capacity_settings = [
760 'min_adult_quantity' => !empty($eshb_settings['min-adult-capacity']) ? $eshb_settings['min-adult-capacity'] : 1,
761 'min_children_quantity' => !empty($eshb_settings['min-children-capacity']) ? $eshb_settings['min-children-capacity'] : 0,
762 'max_adult_quantity' => !empty($eshb_settings['max-adult-capacity']) ? $eshb_settings['max-adult-capacity'] : 1000,
763 'max_children_quantity' => !empty($eshb_settings['max-children-capacity']) ? $eshb_settings['max-children-capacity'] : 1000,
764 ];
765 }
766 $booking_capacity_settings = [];
767 if(!empty($eshb_settings)){
768 $booking_capacity_settings = [
769 'min_adult_quantity' => !empty($eshb_settings['booking-min-adult-capacity']) ? $eshb_settings['booking-min-adult-capacity'] : 1,
770 'min_children_quantity' => !empty($eshb_settings['booking-min-children-capacity']) ? $eshb_settings['booking-min-children-capacity'] : 0,
771 ];
772 }
773
774 $eshb_min_max_settings = apply_filters( 'eshb_min_max_global_settings_localize', $min_max_settings);
775 $calendar_start_date_buffer = !empty($eshb_min_max_settings['calendar_start_date_buffer']) ? $eshb_min_max_settings['calendar_start_date_buffer'] : 0;
776 $required_min_nights = !empty($eshb_min_max_settings['required_min_nights']) ? $eshb_min_max_settings['required_min_nights'] : 1;
777 $required_max_nights = !empty($eshb_min_max_settings['required_max_nights']) ? $eshb_min_max_settings['required_max_nights'] : 999;
778
779 $eshb_week_settings = apply_filters( 'eshb_week_settings', [] );
780 $string_check_in_day_error_msg = !empty($eshb_week_settings['string_check_in_day_error_msg']) ? $eshb_week_settings['string_check_in_day_error_msg'] : '';
781
782
783 // accomodation details metadata
784 $eshb_accomodation_metaboxes = false;
785 $is_it_single_day_booking_accomodation = false;
786
787 if(is_singular( 'eshb_accomodation' ) && ($accomodation_id == null || empty($accomodation_id))){
788 $accomodation_id = get_the_ID();
789 }
790
791
792 $eshb_booking = new ESHB_Booking();
793 $start_date = '';
794 $end_date = '';
795
796 if (isset($_GET['nonce']) && wp_verify_nonce( sanitize_text_field(wp_unslash($_GET['nonce'])), ESHB_Helper::generate_secure_nonce_action('eshb_global_nonce_action'))) {
797 $start_date = !empty($_POST['start_date']) ? sanitize_text_field( wp_unslash($_POST['start_date']) ) : '';
798 $end_date = !empty($_POST['end_date']) ? sanitize_text_field( wp_unslash($_POST['end_date']) ) : '';
799 }
800
801 if($accomodation_id) {
802 //error_log('$accomodation_id' . $accomodation_id);
803 $eshb_accomodation_metaboxes = get_post_meta($accomodation_id, 'eshb_accomodation_metaboxes', true);
804 $available_rooms = $eshb_booking->get_available_room_count_by_date_range($accomodation_id, $start_date, $end_date);
805 $available_rooms = $available_rooms > 0 ? $available_rooms : 0;
806 $eshb_accomodation_metaboxes['available_rooms'] = $available_rooms;
807
808
809 $is_global_source_for_min_max = !empty($eshb_accomodation_metaboxes['is_global_source_for_min_max']) ? true : false;
810 if($is_global_source_for_min_max != true) {
811 $required_min_nights = !empty($eshb_accomodation_metaboxes['required_min_nights']) ? $eshb_accomodation_metaboxes['required_min_nights'] : 1;
812 $required_max_nights = !empty($eshb_accomodation_metaboxes['required_max_nights']) ? $eshb_accomodation_metaboxes['required_max_nights'] : 999;
813 }
814 }
815
816 $eshb_translations = [
817 'maximumCapacity' => __('Maximum Capacity', 'easy-hotel'),
818 'availableCapacity' => __('Available Capacity', 'easy-hotel'),
819 'availableRoom' => __('Available Room', 'easy-hotel'),
820 'maximumAdultAndChildrenCapacity' => __('Maximum Adult and Children Capacity', 'easy-hotel'),
821 'maximumTimeSlot' => __('Allowed max time for this slot is', 'easy-hotel'),
822 'minimumTimeSlot' => __('Allowed min time for this slot is', 'easy-hotel'),
823 'minNightsErrorMsg' => __('Ops! This Reservation has been failed. Requried Minimum', 'easy-hotel'),
824 'maxNightsErrorMsg' => __('Ops! This Reservation has been failed. Requried Maximum', 'easy-hotel'),
825 'minNightsErrorMsgAvCal' => __('Requried Minimum Nights:', 'easy-hotel'),
826 'maxNightsErrorMsgAvCal' => __('Requried Maximum Nights:', 'easy-hotel'),
827 ];
828
829 $nonce_action = ESHB_Helper::generate_secure_nonce_action('eshb_global_nonce_action');
830 wp_localize_script(
831 'eshb-public-script',
832 'eshb_ajax',
833 [
834 'root' => esc_url(rest_url()),
835 'ajaxurl' => admin_url( 'admin-ajax.php' ),
836 'adminURL' => admin_url(),
837 'wooCartUrl' => $cart_url,
838 'wooCheckoutUrl' => $checkout_url,
839 'direct_booking' => !empty($eshb_settings['direct-booking']),
840 'is_admin' => is_admin(),
841 'nonce' => wp_create_nonce($nonce_action),
842 'eshb_add_to_cart_reservation_nonce' => wp_create_nonce('eshb_eshb_add_to_cart_reservation_nonce'),
843 'reservation_request_nonce' => wp_create_nonce('eshb_reservation_request_nonce'),
844 'version' => ESHB_VERSION,
845 'pluginURL' => ESHB_DIR_URL,
846 'dateFormat' => get_option( 'date_format' ),
847 'requiredMinNights' => $required_min_nights,
848 'requiredMaxNights' => $required_max_nights,
849 'calendar_start_date_buffer' => $calendar_start_date_buffer,
850 'checkInDayErrorMsg' => $string_check_in_day_error_msg,
851 'currentAccomodationMeta' => $eshb_accomodation_metaboxes,
852 'search_capacities' => $search_capacity_settings,
853 'booking_capacities' => $booking_capacity_settings,
854 'translations' => $eshb_translations,
855 'cart_blocking_enabled' => ! empty( $eshb_settings['cart-blocking-switcher'] ),
856 'cart_blocking_time' => ! empty( $eshb_settings['cart-blocking-time'] ) ? (int) $eshb_settings['cart-blocking-time'] : 5,
857 'cart_blocking_color' => ! empty( $eshb_settings['cart-blocking-color'] ) ? esc_attr( $eshb_settings['cart-blocking-color'] ) : '#720eec',
858 'cart_blocking_notice_msg' => ! empty( $eshb_settings['cart-blocking-notice-msg'] ) ? esc_html( $eshb_settings['cart-blocking-notice-msg'] ) : esc_html__( 'Your reservation is held for', 'easy-hotel' ),
859 ]
860 );
861 }
862
863 public static function get_eshb_min_stay_night_by_session($accomodation_id, $start_date, $end_date) {
864 $metaboxes = get_post_meta($accomodation_id, 'eshb_accomodation_metaboxes', true);
865 $min_stay_night = 0;
866
867
868 // Get all sessions
869 $qargs = array(
870 'post_type' => 'eshb_session',
871 'post_status' => 'publish',
872 'posts_per_page' => -1,
873 );
874 $query = new WP_Query($qargs);
875
876 $sessions = [];
877
878 if ($query->have_posts()) {
879 while ($query->have_posts()) {
880 $query->the_post();
881 $post_id = get_the_ID();
882 $metaboxes = maybe_unserialize(get_post_meta($post_id, 'eshb_session_metaboxes', true));
883
884 if (empty($metaboxes['start_date']) || empty($metaboxes['end_date'])) continue;
885
886 $sessions[] = [
887 'id' => $post_id,
888 'start_date' => $metaboxes['start_date'],
889 'end_date' => $metaboxes['end_date'],
890 'min_nights' => $metaboxes['min_nights'] ?? 0,
891 'accomodation_ids' => $metaboxes['accomodation_ids'] ?? [],
892 'days' => $metaboxes['days'] ?? [],
893 ];
894 }
895 wp_reset_postdata();
896 }
897
898 // Create date range (exclude checkout date if multi-day)
899 $period = new DatePeriod(
900 new DateTime($start_date),
901 new DateInterval('P1D'),
902 (new DateTime($start_date) == new DateTime($end_date))
903 ? (new DateTime($end_date))->modify('+1 day') // same-day booking
904 : new DateTime($end_date) // exclude checkout
905 );
906
907 $total_nights = iterator_count($period);
908 $per_day_prices = [];
909
910
911 foreach ($period as $day) {
912 $current_date = $day->format('Y-m-d');
913 $current_day_name = strtolower($day->format('l'));
914
915 foreach ($sessions as $session) {
916
917
918 if(empty($session['accomodation_ids'])){
919 continue;
920 }
921
922 if (!empty($session['accomodation_ids']) && !in_array($accomodation_id, $session['accomodation_ids'])) {
923 continue;
924 }
925
926 $session_days = !empty($session['days']) ? $session['days'] : ['saturday', 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday'];
927 if(in_array('all', $session_days)){
928 $session_days = ['saturday', 'sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday'];
929 }
930
931 if ($current_date >= $session['start_date'] && $current_date <= $session['end_date'] && in_array($current_day_name, $session_days)) {
932
933 // 1. Get min night
934 $min_stay_night = $session['min_nights'];
935 break;
936 }
937 }
938 }
939 return $min_stay_night;
940 }
941 }