PluginProbe
Booking Calendar / 11.0
Booking Calendar v11.0
11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 10.11 10.11.2 All 203 releases
booking / includes / _capacity / where_to_save.php

where_to_save.php in Booking Calendar 11.0, at includes/_capacity/where_to_save.php

434 lines 23.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly // FixIn: 9.8.0.4.
4
5 // ---------------------------------------------------------------------------------------------------------------------
6 // Check where to save the booking
7 // ---------------------------------------------------------------------------------------------------------------------
8
9 /**
10 * Check if these dates contain marker '2981-01-13', which is means that this booking was submited without dates - as Contact Form.
11 *
12 * @param array|string $dates_sql_arr - array of SQL dates.
13 *
14 * @return bool
15 */
16 function wpbc_is_these_dates__for__no_dates( $dates_sql_arr ){
17
18 // FixIn: 2981-01-13 13 Jan 2981.
19
20 if ( is_array( $dates_sql_arr ) ) {
21 foreach ( $dates_sql_arr as $date_ymd ) {
22 if ( '2981-01-13' === gmdate( 'Y-m-d', mysql2date( 'U', $date_ymd ) ) ) {
23 return true;
24 }
25 }
26 } elseif ( '2981-01-13' === gmdate( 'Y-m-d', mysql2date( 'U', $dates_sql_arr ) ) ) {
27 return true;
28 }
29
30 return false;
31 }
32
33
34 /**
35 * Get booking resources for each selected date, where we can save the booking, or false if we can not save it
36 *
37 * @param array $params = [
38 * 'resource_id' => 2
39 * 'skip_booking_id' => '', | 125 if edit booking
40 * 'dates_only_sql_arr' => [ "2023-10-18", "2023-10-25", "2023-11-25" ]
41 * 'time_as_seconds_arr' => [ 36000, 39600 ]
42 * 'how_many_items_to_book' => 1
43 * 'request_uri' => 'http://beta/resource-id2/' // Optional default -> DOING_AJAX ? $_SERVER['HTTP_REFERER'] : $_SERVER['REQUEST_URI']
44 * 'as_single_resource' => false, // Optional default false
45 * 'is_use_booking_recurrent_time' => false // Optional default ( 'On' === get_bk_option( 'booking_recurrent_time' ) )
46 * ]
47 * @return array = [ 'result' => 'ok', 'main__resource_id' => 2, 'resources_in_dates' => [ '2023-09-23':[2,10,11], '2023-09-24':[2,10,11] ], 'time_to_book' => [ "00:00:00", "24:00:00" ] ]
48 * Note. 'main__resource_id' - it's first booking resource in the list, basically needed for saving in wp_booking DB table.
49 * OR
50 * = [ 'result' => 'error', 'message' => 'Booking can not be saved ...' ]
51 */
52 function wpbc__where_to_save_booking( $local_params ){
53
54 $server_request_uri = ( ( isset( $_SERVER['REQUEST_URI'] ) ) ? sanitize_text_field( $_SERVER['REQUEST_URI'] ) : '' ); /* phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash */ /* FixIn: sanitize_unslash */
55 $server_http_referer_uri = ( ( isset( $_SERVER['HTTP_REFERER'] ) ) ? sanitize_text_field( $_SERVER['HTTP_REFERER'] ) : '' ); /* phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash */ /* FixIn: sanitize_unslash */
56 $defaults = array(
57 'request_uri' => ( ( ( defined( 'DOING_AJAX' ) ) && ( DOING_AJAX ) ) ? $server_http_referer_uri : $server_request_uri ), // front-end: $server_request_uri | ajax: $server_http_referer_uri // It different in Ajax requests. It's used for change-over days to detect for exception at specific pages
58 'as_single_resource' => false,
59 'is_use_booking_recurrent_time' => ( 'On' === get_bk_option( 'booking_recurrent_time' ) ),
60 'aggregate_resource_id_arr' => array(),
61 'custom_form' => '' //FixIn: 10.0.0.10 // Required for checking all available time-slots and compare with booked time slots
62 );
63 $local_params = wp_parse_args( $local_params, $defaults );
64
65 $time_as_seconds_arr = $local_params['time_as_seconds_arr'];
66 $time_as_seconds_arr[0] = ( 0 != $time_as_seconds_arr[0] ) ? $time_as_seconds_arr[0] + 1 : $time_as_seconds_arr[0]; // +1 s. -- set check in time with ended 1 second
67 $time_as_seconds_arr[1] = ( ( 24 * 60 * 60 ) != $time_as_seconds_arr[1] ) ? $time_as_seconds_arr[1] + 2 : $time_as_seconds_arr[1]; // +2 s. -- set check out time with ended 2 seconds
68 if ( ( 0 != $time_as_seconds_arr[0] ) && ( ( 24 * 60 * 60 ) == $time_as_seconds_arr[1] ) ) {
69 //FixIn: 10.0.0.49 - in case if we have start time != 00:00 and end time as 24:00 then set end time as 23:59:52
70 $time_as_seconds_arr[1] += - 8;
71 }
72 $local_params['time_as_his_arr'] = array(
73 wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[0] ),
74 wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[1] )
75 );
76 // FixIn: 2981-01-13 13 Jan 2981
77 $availability_per_days_arr__params = array();
78 if ( ( ! empty( $local_params['dates_only_sql_arr'] ) ) && ( is_array( $local_params['dates_only_sql_arr'] ) ) && ( '2981-01-13' === $local_params['dates_only_sql_arr'][0] ) ) {
79 $availability_per_days_arr__params['is_days_always_available'] = true;
80 }
81 // If we are using the unavailable before/after booking dates, then we need to extend "$local_params['dates_only_sql_arr']" to such dates.
82 if ( function_exists( 'wpbc__extend_checking_dates_range__if_used__extra_seconds__before_after' ) ) {
83 $maybe_extended__dates_to_check_arr = wpbc__extend_checking_dates_range__if_used__extra_seconds__before_after( $local_params['dates_only_sql_arr'] );
84 } else {
85 $maybe_extended__dates_to_check_arr = $local_params['dates_only_sql_arr'];
86 }
87 // [ "dates": [ "2023-11-05": [ "day_availability":4, ... "2": [ "is_day_unavailable":false, ...]], '2023-11-06':[...] ] ,"resources_id_arr__in_dates":[2,12,10,11]}
88
89 $availability_per_days = wpbc_get_availability_per_days_arr( wp_parse_args( array(
90 'resource_id' => $local_params['resource_id'],
91 'skip_booking_id' => $local_params['skip_booking_id'],
92 'dates_to_check' => $maybe_extended__dates_to_check_arr,
93 'request_uri' => $local_params['request_uri'],
94 'as_single_resource' => $local_params['as_single_resource'], // default FALSE :: get dates as for 'single resource' or 'parent' resource including bookings in all 'child booking resources' .
95 'additional_bk_types' => $local_params['aggregate_resource_id_arr'],
96 'aggregate_type' => empty( $local_params['aggregate_type'] ) ? 'bookings_only' : $local_params['aggregate_type'], //TODO: this parameter does not transfer during saving, so here will be always default value 'bookings_only' // FixIn: 10.0.0.7.
97 'custom_form' => $local_params['custom_form'], // FixIn: 10.0.0.10.
98 ) , $availability_per_days_arr__params ) );
99
100 // Get value of how many booking resources to book
101 $how_many_items_to_book = $local_params['how_many_items_to_book'];
102
103 // FixIn: 2981-01-13 13 Jan 2981.
104 if ( ( ! empty( $local_params['dates_only_sql_arr'] ) ) && ( wpbc_is_these_dates__for__no_dates( $local_params['dates_only_sql_arr'] ) ) ) {
105 $how_many_items_to_book = 0;
106 }
107
108 // -------------------------------------------------------------------------------------------------------------
109 // [ 2023-10-18: [ 0:[ resource_id: 2, is_available: true, booked__seconds: [], booked__readable: [], time_to_book__seconds: [ 36030, 39570 ], time_to_book__readable: "10:00:00", "11:00:00" ] ], 1: [...], 2: [...], 3: [...] ], 2023-10-25: [...], 2023-11-25: [...] ]
110 $available_slots = wpbc__get_available_slots__for_selected_dates_times__bl( array(
111 'dates_sql__arr' => $local_params['dates_only_sql_arr'],
112 'time_as_seconds_arr' => $local_params['time_as_seconds_arr'],
113 'is_use_booking_recurrent_time' => $local_params['is_use_booking_recurrent_time'],
114 'availability_per_days' => $availability_per_days
115 ) );
116
117 $main__resource_id = 0;
118 // == In SAME 'child' booking resources ============================================================
119 if ( 'On' === get_bk_option( 'booking_is_dissbale_booking_for_different_sub_resources' ) ) {
120
121 $availability_in_same_child_resources = $availability_per_days['resources_id_arr__in_dates'];
122
123 foreach ( $availability_per_days['resources_id_arr__in_dates'] as $child_resource_id ) {
124 foreach ( $local_params['dates_only_sql_arr'] as $day_sql_key ) {
125 foreach ( $available_slots[ $day_sql_key ] as $available_slot ) {
126 if (
127 ( $child_resource_id == $available_slot['resource_id'] )
128 && ( ! $available_slot['is_available'] )
129 ){
130 $availability_in_same_child_resources = array_diff( $availability_in_same_child_resources, array( $child_resource_id ) ); // Remove $child_resource_id from array
131 break;
132 }
133 }
134 }
135 }
136
137 // Reset indexes in this array for ability to get first [0] element in this arr.
138 $availability_in_same_child_resources = array_values( $availability_in_same_child_resources );
139 $main__resource_id = ( ! empty( $availability_in_same_child_resources ) ) ? $availability_in_same_child_resources[0] : 0;
140
141 // [ 2, 12, 11 ] <- ID of child booking resources, where we can save our booking in the same child booking resource!
142 if ( count( $availability_in_same_child_resources ) >= $how_many_items_to_book ) {
143
144 $availability_in_same_child_resources_by_dates = array();
145 foreach ( $local_params['dates_only_sql_arr'] as $day_sql_key ) {
146 $availability_in_same_child_resources_by_dates[ $day_sql_key ] = $availability_in_same_child_resources;
147 }
148 // Good Save it
149 return array(
150 'result' => 'ok',
151 'resources_in_dates' => $availability_in_same_child_resources_by_dates, // [ 2023-09-23 = [ 2, 10, 11 ], 2023-09-24 = [ 2, 10, 11 ]
152 'time_to_book' => $local_params['time_as_his_arr'], // [ "00:00:00", "24:00:00" ]
153 'main__resource_id' => $main__resource_id
154 );
155 } else {
156 // Bad not save
157 return array(
158 'result' => 'error',
159 'message' => __( 'These dates and times in this calendar are already booked or unavailable.', 'booking' )
160 . ' <br> '
161 . __( 'It is not possible to store this sequence of the dates into the one same resource.' , 'booking' )
162 . ' <br> '
163 . __( 'Please choose alternative date(s), times, or adjust the number of slots booked.' , 'booking' )
164 . ' <a href="javascript:void(0)" onclick="'
165 .'jQuery( this ).next().toggle();'
166 .'jQuery( this).hide();'
167 .'jQuery( this ).parents(\'.wpbc_alert_message\').parent().on(\'hide\',function(even){jQuery(this).show();});'
168 .'">'
169 . __( 'Show more details', 'booking' )
170 . '</a>'
171 . ' <div style="display:none;">'
172 . '<p><hr><code>'
173 . wp_json_encode( $available_slots )
174 . '</code></p>'
175 . '</div>'
176 . ' <div style="display:none;">' . wp_json_encode( $available_slots ) . '</div>'
177 );
178 }
179
180 } else {
181
182 // == In ANY 'child' booking resources =========================================================
183
184 $availability_in_any_child_resources = array();
185 foreach ( $available_slots as $day_sql_key => $available_slot ) { // '2023-09-10': [], '2023-09-14': [],
186 $availability_in_any_child_resources[ $day_sql_key ] = array();
187 foreach ( $available_slot as $ind => $slot_value ) { // [ 0:{ resource_id: 2, is_available: false,...} , .... ]
188 if ( $slot_value['is_available'] ) {
189 if ( empty( $main__resource_id ) ) {
190 $main__resource_id = $slot_value['resource_id'];
191 }
192 $availability_in_any_child_resources[ $day_sql_key ][] = $slot_value['resource_id'];
193 }
194 }
195 }
196 /**
197 * { "2023-09-10": [ 2, 10, 11 ] <- available ID of child resources in this date
198 "2023-09-14": [ 4 ] <- available ID of child resources in this date
199 }
200 */
201
202 foreach ( $availability_in_any_child_resources as $day_sql_key2 => $available_res_in_day_arr ) {
203 if ( count( $available_res_in_day_arr ) < $how_many_items_to_book ) {
204 // Bad not save
205 // We can not store booking in this date day_sql_key2, number of available child booking resources less than required
206 return array(
207 'result' => 'error',
208 'message' => __( 'These dates and times in this calendar are already booked or unavailable.', 'booking' )
209 . ' <br> '
210 . __( 'Please choose alternative date(s), times, or adjust the number of slots booked.' , 'booking' )
211 . ' <a href="javascript:void(0)" onclick="'
212 .'jQuery( this ).next().toggle();'
213 .'jQuery( this).hide();'
214 .'jQuery( this ).parents(\'.wpbc_alert_message\').parent().on(\'hide\',function(even){jQuery(this).show();});'
215 .'">'
216 . __( 'Show more details', 'booking' )
217 . '</a>'
218 . ' <div style="display:none;">'
219 . '<p><strong>'
220 /* translators: 1: ... */
221 . sprintf( __( 'Booking can not be saved in this date %1$s, number of available (single or child) booking resource(s) (%2$d) less than required (%3$d).', 'booking' )
222 , $day_sql_key2, count( $available_res_in_day_arr ), $how_many_items_to_book )
223 . '</strong><hr><code>'
224 . wp_json_encode( $available_slots )
225 // . wp_json_encode( $local_params, $availability_per_days )
226 . '</code></p>'
227 . '</div>'
228
229 );
230 }
231 }
232
233 // Good Save it
234 return array(
235 'result' => 'ok',
236 'resources_in_dates' => $availability_in_any_child_resources, // [ 2023-09-23 = [ 2, 10, 11 ], 2023-09-24 = [ 2, 10, 11 ]
237 'time_to_book' => $local_params['time_as_his_arr'], // [ "00:00:00", "24:00:00" ]
238 'main__resource_id' => $main__resource_id
239 );
240 }
241 }
242
243
244 /**
245 * Get available slots per each date in each slot (child resource). It's checking times as well.
246 *
247 * @param $params [
248 * dates_sql__arr = [ "2023-10-18", "2023-10-25", "2023-11-25" ]
249 * time_as_seconds_arr = [ 36030, 39570 ]
250 * availability_per_days = [ dates = [...], resources_id_arr__in_dates = [...] ]
251 * ]
252 *
253 * @return array [
254 * 2023-10-18 = [
255 * 0 = [
256 * resource_id = 2
257 * is_available = true
258 * booked__seconds = []
259 * booked__readable = []
260 * time_to_book__seconds = [ 36030, 39570 ]
261 * time_to_book__readable = "10:00:00", "11:00:00" ]
262 * ]
263 * 1 = [...]
264 * 2 = [...]
265 * 3 = [...]
266 * ]
267 * 2023-10-25 = [...]
268 * 2023-11-25 = [...]
269 * ]
270 */
271 function wpbc__get_available_slots__for_selected_dates_times__bl( $params ) {
272
273 $defaults = array(
274 'dates_sql__arr' => array(),
275 'time_as_seconds_arr' => array(),
276 'availability_per_days' => array(),
277 'is_use_booking_recurrent_time' => ( 'On' === get_bk_option( 'booking_recurrent_time' ) )
278 );
279 $params = wp_parse_args( $params, $defaults );
280
281 $time_as_seconds_arr = $params['time_as_seconds_arr'];
282
283 // Shift time interval in 30 seconds
284 $shift__30sec = array( 30, 30 );
285 $time_as_seconds_arr[ 0 ] = $time_as_seconds_arr[ 0 ] + $shift__30sec[0];
286 $time_as_seconds_arr[ 1 ] = $time_as_seconds_arr[ 1 ] - $shift__30sec[1];
287
288 $available_resources_arr = array();
289
290 foreach ( $params['dates_sql__arr'] as $sql_class_day_number => $sql_class_day ) {
291 $available_resources_arr[ $sql_class_day ] = array();
292 $date_shift__30sec = $shift__30sec;
293
294 if ( isset( $params['availability_per_days']['dates'][ $sql_class_day ] ) ) {
295
296 $this_date_time_as_seconds_arr = $time_as_seconds_arr;
297
298 //TODO: debug this checking in biz_l.js 2023-09-05 16:40 --- If we are using not time slot but the check in/out times ?
299
300 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
301 // Duplicated part of code, as in ../do_search.php //FixIn: 2024-05-12_11:58
302 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
303 if (
304 ( ! $params['is_use_booking_recurrent_time'] )
305 && ( count( $params['dates_sql__arr'] ) > 1 )
306 ) {
307 if ( 0 == $sql_class_day_number ) { // check in
308 $this_date_time_as_seconds_arr[1] = 24 * 60 * 60;
309 $date_shift__30sec[1] = 0;
310 } else if ( ( count( $params['dates_sql__arr'] ) - 1 ) == $sql_class_day_number ) { // check out
311 $this_date_time_as_seconds_arr[0] = 0;
312 $date_shift__30sec[0] = 0;
313 } else {
314 $this_date_time_as_seconds_arr = array( 0, 24 * 60 * 60 );
315 $date_shift__30sec = array( 0, 0 );
316 }
317 }
318 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
319
320 $date_bookings_obj = $params['availability_per_days']['dates'][ $sql_class_day ];
321
322 foreach ( $params['availability_per_days']['resources_id_arr__in_dates'] as $child_resource_id ) {
323
324 $merged_seconds_arr = wpbc_get__booking_obj__merged_seconds_arr( $date_bookings_obj[ $child_resource_id ] );
325
326 $is_intersect = wpbc_is_intersect__merged_seconds_arr__and__seconds_arr( $merged_seconds_arr['merged_seconds'], $this_date_time_as_seconds_arr );
327
328 $this_date_available_resource = array(
329 'resource_id' => $child_resource_id,
330 'is_available' => (! $is_intersect),
331 'booked__seconds' => $merged_seconds_arr['merged_seconds'],
332 'booked__readable' => $merged_seconds_arr['merged_readable'],
333 'time_to_book__seconds' => $this_date_time_as_seconds_arr,
334 'time_to_book__readable'=> array()
335 );
336 $this_date_available_resource['time_to_book__readable'][] = wpbc_transform__seconds__in__24_hours_his(
337 (
338 ( ( $this_date_time_as_seconds_arr[0] - $date_shift__30sec[0] ) < 0 )
339 ? 0
340 : ( $this_date_time_as_seconds_arr[0] - $date_shift__30sec[0] )
341 )
342 );
343 $this_date_available_resource['time_to_book__readable'][] = wpbc_transform__seconds__in__24_hours_his(
344 (
345 ( ( $this_date_time_as_seconds_arr[1] + $date_shift__30sec[1] ) > 86400 )
346 ? 86400
347 : ( $this_date_time_as_seconds_arr[1] + $date_shift__30sec[1] )
348 )
349 );
350
351 $available_resources_arr[ $sql_class_day ][] = $this_date_available_resource;
352 }
353 }
354 }
355
356 return $available_resources_arr;
357 }
358
359
360 /**
361 * Get Merged Seconds Array from object -> $params['availability_per_days']['dates'][ $sql_class_day ][ $child_resource_id ]
362 *
363 * @param $date_booking_obj - date_bookings_obj from $params['availability_per_days']['dates']
364 *
365 * @return array - [
366 * 'merged_seconds' => $merged_seconds,
367 * 'merged_readable' => $merged_readable
368 * ]
369 */
370 function wpbc_get__booking_obj__merged_seconds_arr( $date_booking_obj ) {
371
372 if ( $date_booking_obj->is_day_unavailable ){
373 $merged_seconds = array( array( 0, 24 * 60 * 60 ) ); // Unavailable
374 $merged_readable = array( wpbc_transform_timerange__seconds_arr__in__formated_hours( array( 0, 24 * 60 * 60 ) ) );
375 } else {
376 $merged_seconds = $date_booking_obj->booked_time_slots['merged_seconds']; // [ [ 25211, 27002 ], [ 36011, 86400 ] ] // Time slots or Available
377 $merged_readable = $date_booking_obj->booked_time_slots['merged_readable'];
378 }
379
380 return array(
381 'merged_seconds' => $merged_seconds,
382 'merged_readable' => $merged_readable
383 );
384 }
385
386
387 /**
388 * Check if merged_seconds in $params['availability_per_days']['dates'][ $sql_class_day ][ $child_resource_id ] intersect with $time_as_seconds_arr
389 *
390 * @param $merged_seconds - [ [ 0, 86400 ] ]
391
392 * @param $this_date_time_as_seconds_arr - [ 36030, 39570 ] | [ 0, 24*60*60 ]
393 *
394 * @return bool
395 */
396 function wpbc_is_intersect__merged_seconds_arr__and__seconds_arr( $merged_seconds, $this_date_time_as_seconds_arr ) {
397
398 $is_intersect = wpbc_is_intersect__range_time_interval(
399 array(
400 array(
401 intval( $this_date_time_as_seconds_arr[ 0 ] ) + 20
402 , intval( $this_date_time_as_seconds_arr[ 1 ] ) - 20
403 )
404 )
405 , $merged_seconds );
406 return $is_intersect;
407 }
408
409
410 /**
411 * Is these array of intervals intersected ? - overlay of Math function
412 *
413 * @param array $time_interval_A array( array( 21600, 23400 ) )
414 * @param array $time_interval_B array( array( 25211, 27002 ), array( 36011, 86400 ) )
415 *
416 * @return bool
417 */
418 function wpbc_is_intersect__range_time_interval( $time_interval_A, $time_interval_B ){
419
420 for ( $i = 0; $i < count($time_interval_A); $i++ ){
421
422 for ( $j = 0; $j < count($time_interval_B); $j++ ){
423
424 $is_intersect = wpbc_is__intervals__intersected( $time_interval_A[ $i ], $time_interval_B[ $j ] );
425
426 if ( $is_intersect ){
427 return true;
428 }
429 }
430 }
431
432 return false;
433 }
434