PluginProbe
Booking Calendar / 11.1
Booking Calendar v11.1
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.1, at includes/_capacity/where_to_save.php

436 lines 23.1 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 'allow_past' => false,
59 'as_single_resource' => false,
60 'is_use_booking_recurrent_time' => ( 'On' === get_bk_option( 'booking_recurrent_time' ) ),
61 'aggregate_resource_id_arr' => array(),
62 'custom_form' => '' //FixIn: 10.0.0.10 // Required for checking all available time-slots and compare with booked time slots
63 );
64 $local_params = wp_parse_args( $local_params, $defaults );
65
66 $time_as_seconds_arr = $local_params['time_as_seconds_arr'];
67 $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
68 $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
69 if ( ( 0 != $time_as_seconds_arr[0] ) && ( ( 24 * 60 * 60 ) == $time_as_seconds_arr[1] ) ) {
70 //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
71 $time_as_seconds_arr[1] += - 8;
72 }
73 $local_params['time_as_his_arr'] = array(
74 wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[0] ),
75 wpbc_transform__seconds__in__24_hours_his( $time_as_seconds_arr[1] )
76 );
77 // FixIn: 2981-01-13 13 Jan 2981
78 $availability_per_days_arr__params = array();
79 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] ) ) {
80 $availability_per_days_arr__params['is_days_always_available'] = true;
81 }
82 // If we are using the unavailable before/after booking dates, then we need to extend "$local_params['dates_only_sql_arr']" to such dates.
83 if ( function_exists( 'wpbc__extend_checking_dates_range__if_used__extra_seconds__before_after' ) ) {
84 $maybe_extended__dates_to_check_arr = wpbc__extend_checking_dates_range__if_used__extra_seconds__before_after( $local_params['dates_only_sql_arr'] );
85 } else {
86 $maybe_extended__dates_to_check_arr = $local_params['dates_only_sql_arr'];
87 }
88 // [ "dates": [ "2023-11-05": [ "day_availability":4, ... "2": [ "is_day_unavailable":false, ...]], '2023-11-06':[...] ] ,"resources_id_arr__in_dates":[2,12,10,11]}
89
90 $availability_per_days = wpbc_get_availability_per_days_arr( wp_parse_args( array(
91 'resource_id' => $local_params['resource_id'],
92 'skip_booking_id' => $local_params['skip_booking_id'],
93 'dates_to_check' => $maybe_extended__dates_to_check_arr,
94 'request_uri' => $local_params['request_uri'],
95 'allow_past' => ! empty( $local_params['allow_past'] ),
96 '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' .
97 'additional_bk_types' => $local_params['aggregate_resource_id_arr'],
98 '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.
99 'custom_form' => $local_params['custom_form'], // FixIn: 10.0.0.10.
100 ) , $availability_per_days_arr__params ) );
101
102 // Get value of how many booking resources to book
103 $how_many_items_to_book = $local_params['how_many_items_to_book'];
104
105 // FixIn: 2981-01-13 13 Jan 2981.
106 if ( ( ! empty( $local_params['dates_only_sql_arr'] ) ) && ( wpbc_is_these_dates__for__no_dates( $local_params['dates_only_sql_arr'] ) ) ) {
107 $how_many_items_to_book = 0;
108 }
109
110 // -------------------------------------------------------------------------------------------------------------
111 // [ 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: [...] ]
112 $available_slots = wpbc__get_available_slots__for_selected_dates_times__bl( array(
113 'dates_sql__arr' => $local_params['dates_only_sql_arr'],
114 'time_as_seconds_arr' => $local_params['time_as_seconds_arr'],
115 'is_use_booking_recurrent_time' => $local_params['is_use_booking_recurrent_time'],
116 'availability_per_days' => $availability_per_days
117 ) );
118
119 $main__resource_id = 0;
120 // == In SAME 'child' booking resources ============================================================
121 if ( 'On' === get_bk_option( 'booking_is_dissbale_booking_for_different_sub_resources' ) ) {
122
123 $availability_in_same_child_resources = $availability_per_days['resources_id_arr__in_dates'];
124
125 foreach ( $availability_per_days['resources_id_arr__in_dates'] as $child_resource_id ) {
126 foreach ( $local_params['dates_only_sql_arr'] as $day_sql_key ) {
127 foreach ( $available_slots[ $day_sql_key ] as $available_slot ) {
128 if (
129 ( $child_resource_id == $available_slot['resource_id'] )
130 && ( ! $available_slot['is_available'] )
131 ){
132 $availability_in_same_child_resources = array_diff( $availability_in_same_child_resources, array( $child_resource_id ) ); // Remove $child_resource_id from array
133 break;
134 }
135 }
136 }
137 }
138
139 // Reset indexes in this array for ability to get first [0] element in this arr.
140 $availability_in_same_child_resources = array_values( $availability_in_same_child_resources );
141 $main__resource_id = ( ! empty( $availability_in_same_child_resources ) ) ? $availability_in_same_child_resources[0] : 0;
142
143 // [ 2, 12, 11 ] <- ID of child booking resources, where we can save our booking in the same child booking resource!
144 if ( count( $availability_in_same_child_resources ) >= $how_many_items_to_book ) {
145
146 $availability_in_same_child_resources_by_dates = array();
147 foreach ( $local_params['dates_only_sql_arr'] as $day_sql_key ) {
148 $availability_in_same_child_resources_by_dates[ $day_sql_key ] = $availability_in_same_child_resources;
149 }
150 // Good Save it
151 return array(
152 'result' => 'ok',
153 'resources_in_dates' => $availability_in_same_child_resources_by_dates, // [ 2023-09-23 = [ 2, 10, 11 ], 2023-09-24 = [ 2, 10, 11 ]
154 'time_to_book' => $local_params['time_as_his_arr'], // [ "00:00:00", "24:00:00" ]
155 'main__resource_id' => $main__resource_id
156 );
157 } else {
158 // Bad not save
159 return array(
160 'result' => 'error',
161 'message' => __( 'These dates and times in this calendar are already booked or unavailable.', 'booking' )
162 . ' <br> '
163 . __( 'It is not possible to store this sequence of the dates into the one same resource.' , 'booking' )
164 . ' <br> '
165 . __( 'Please choose alternative date(s), times, or adjust the number of slots booked.' , 'booking' )
166 . ' <a href="javascript:void(0)" onclick="'
167 .'jQuery( this ).next().toggle();'
168 .'jQuery( this).hide();'
169 .'jQuery( this ).parents(\'.wpbc_alert_message\').parent().on(\'hide\',function(even){jQuery(this).show();});'
170 .'">'
171 . __( 'Show more details', 'booking' )
172 . '</a>'
173 . ' <div style="display:none;">'
174 . '<p><hr><code>'
175 . wp_json_encode( $available_slots )
176 . '</code></p>'
177 . '</div>'
178 . ' <div style="display:none;">' . wp_json_encode( $available_slots ) . '</div>'
179 );
180 }
181
182 } else {
183
184 // == In ANY 'child' booking resources =========================================================
185
186 $availability_in_any_child_resources = array();
187 foreach ( $available_slots as $day_sql_key => $available_slot ) { // '2023-09-10': [], '2023-09-14': [],
188 $availability_in_any_child_resources[ $day_sql_key ] = array();
189 foreach ( $available_slot as $ind => $slot_value ) { // [ 0:{ resource_id: 2, is_available: false,...} , .... ]
190 if ( $slot_value['is_available'] ) {
191 if ( empty( $main__resource_id ) ) {
192 $main__resource_id = $slot_value['resource_id'];
193 }
194 $availability_in_any_child_resources[ $day_sql_key ][] = $slot_value['resource_id'];
195 }
196 }
197 }
198 /**
199 * { "2023-09-10": [ 2, 10, 11 ] <- available ID of child resources in this date
200 "2023-09-14": [ 4 ] <- available ID of child resources in this date
201 }
202 */
203
204 foreach ( $availability_in_any_child_resources as $day_sql_key2 => $available_res_in_day_arr ) {
205 if ( count( $available_res_in_day_arr ) < $how_many_items_to_book ) {
206 // Bad not save
207 // We can not store booking in this date day_sql_key2, number of available child booking resources less than required
208 return array(
209 'result' => 'error',
210 'message' => __( 'These dates and times in this calendar are already booked or unavailable.', 'booking' )
211 . ' <br> '
212 . __( 'Please choose alternative date(s), times, or adjust the number of slots booked.' , 'booking' )
213 . ' <a href="javascript:void(0)" onclick="'
214 .'jQuery( this ).next().toggle();'
215 .'jQuery( this).hide();'
216 .'jQuery( this ).parents(\'.wpbc_alert_message\').parent().on(\'hide\',function(even){jQuery(this).show();});'
217 .'">'
218 . __( 'Show more details', 'booking' )
219 . '</a>'
220 . ' <div style="display:none;">'
221 . '<p><strong>'
222 /* translators: 1: ... */
223 . 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' )
224 , $day_sql_key2, count( $available_res_in_day_arr ), $how_many_items_to_book )
225 . '</strong><hr><code>'
226 . wp_json_encode( $available_slots )
227 // . wp_json_encode( $local_params, $availability_per_days )
228 . '</code></p>'
229 . '</div>'
230
231 );
232 }
233 }
234
235 // Good Save it
236 return array(
237 'result' => 'ok',
238 'resources_in_dates' => $availability_in_any_child_resources, // [ 2023-09-23 = [ 2, 10, 11 ], 2023-09-24 = [ 2, 10, 11 ]
239 'time_to_book' => $local_params['time_as_his_arr'], // [ "00:00:00", "24:00:00" ]
240 'main__resource_id' => $main__resource_id
241 );
242 }
243 }
244
245
246 /**
247 * Get available slots per each date in each slot (child resource). It's checking times as well.
248 *
249 * @param $params [
250 * dates_sql__arr = [ "2023-10-18", "2023-10-25", "2023-11-25" ]
251 * time_as_seconds_arr = [ 36030, 39570 ]
252 * availability_per_days = [ dates = [...], resources_id_arr__in_dates = [...] ]
253 * ]
254 *
255 * @return array [
256 * 2023-10-18 = [
257 * 0 = [
258 * resource_id = 2
259 * is_available = true
260 * booked__seconds = []
261 * booked__readable = []
262 * time_to_book__seconds = [ 36030, 39570 ]
263 * time_to_book__readable = "10:00:00", "11:00:00" ]
264 * ]
265 * 1 = [...]
266 * 2 = [...]
267 * 3 = [...]
268 * ]
269 * 2023-10-25 = [...]
270 * 2023-11-25 = [...]
271 * ]
272 */
273 function wpbc__get_available_slots__for_selected_dates_times__bl( $params ) {
274
275 $defaults = array(
276 'dates_sql__arr' => array(),
277 'time_as_seconds_arr' => array(),
278 'availability_per_days' => array(),
279 'is_use_booking_recurrent_time' => ( 'On' === get_bk_option( 'booking_recurrent_time' ) )
280 );
281 $params = wp_parse_args( $params, $defaults );
282
283 $time_as_seconds_arr = $params['time_as_seconds_arr'];
284
285 // Shift time interval in 30 seconds
286 $shift__30sec = array( 30, 30 );
287 $time_as_seconds_arr[ 0 ] = $time_as_seconds_arr[ 0 ] + $shift__30sec[0];
288 $time_as_seconds_arr[ 1 ] = $time_as_seconds_arr[ 1 ] - $shift__30sec[1];
289
290 $available_resources_arr = array();
291
292 foreach ( $params['dates_sql__arr'] as $sql_class_day_number => $sql_class_day ) {
293 $available_resources_arr[ $sql_class_day ] = array();
294 $date_shift__30sec = $shift__30sec;
295
296 if ( isset( $params['availability_per_days']['dates'][ $sql_class_day ] ) ) {
297
298 $this_date_time_as_seconds_arr = $time_as_seconds_arr;
299
300 //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 ?
301
302 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
303 // Duplicated part of code, as in ../do_search.php //FixIn: 2024-05-12_11:58
304 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
305 if (
306 ( ! $params['is_use_booking_recurrent_time'] )
307 && ( count( $params['dates_sql__arr'] ) > 1 )
308 ) {
309 if ( 0 == $sql_class_day_number ) { // check in
310 $this_date_time_as_seconds_arr[1] = 24 * 60 * 60;
311 $date_shift__30sec[1] = 0;
312 } else if ( ( count( $params['dates_sql__arr'] ) - 1 ) == $sql_class_day_number ) { // check out
313 $this_date_time_as_seconds_arr[0] = 0;
314 $date_shift__30sec[0] = 0;
315 } else {
316 $this_date_time_as_seconds_arr = array( 0, 24 * 60 * 60 );
317 $date_shift__30sec = array( 0, 0 );
318 }
319 }
320 // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
321
322 $date_bookings_obj = $params['availability_per_days']['dates'][ $sql_class_day ];
323
324 foreach ( $params['availability_per_days']['resources_id_arr__in_dates'] as $child_resource_id ) {
325
326 $merged_seconds_arr = wpbc_get__booking_obj__merged_seconds_arr( $date_bookings_obj[ $child_resource_id ] );
327
328 $is_intersect = wpbc_is_intersect__merged_seconds_arr__and__seconds_arr( $merged_seconds_arr['merged_seconds'], $this_date_time_as_seconds_arr );
329
330 $this_date_available_resource = array(
331 'resource_id' => $child_resource_id,
332 'is_available' => (! $is_intersect),
333 'booked__seconds' => $merged_seconds_arr['merged_seconds'],
334 'booked__readable' => $merged_seconds_arr['merged_readable'],
335 'time_to_book__seconds' => $this_date_time_as_seconds_arr,
336 'time_to_book__readable'=> array()
337 );
338 $this_date_available_resource['time_to_book__readable'][] = wpbc_transform__seconds__in__24_hours_his(
339 (
340 ( ( $this_date_time_as_seconds_arr[0] - $date_shift__30sec[0] ) < 0 )
341 ? 0
342 : ( $this_date_time_as_seconds_arr[0] - $date_shift__30sec[0] )
343 )
344 );
345 $this_date_available_resource['time_to_book__readable'][] = wpbc_transform__seconds__in__24_hours_his(
346 (
347 ( ( $this_date_time_as_seconds_arr[1] + $date_shift__30sec[1] ) > 86400 )
348 ? 86400
349 : ( $this_date_time_as_seconds_arr[1] + $date_shift__30sec[1] )
350 )
351 );
352
353 $available_resources_arr[ $sql_class_day ][] = $this_date_available_resource;
354 }
355 }
356 }
357
358 return $available_resources_arr;
359 }
360
361
362 /**
363 * Get Merged Seconds Array from object -> $params['availability_per_days']['dates'][ $sql_class_day ][ $child_resource_id ]
364 *
365 * @param $date_booking_obj - date_bookings_obj from $params['availability_per_days']['dates']
366 *
367 * @return array - [
368 * 'merged_seconds' => $merged_seconds,
369 * 'merged_readable' => $merged_readable
370 * ]
371 */
372 function wpbc_get__booking_obj__merged_seconds_arr( $date_booking_obj ) {
373
374 if ( $date_booking_obj->is_day_unavailable ){
375 $merged_seconds = array( array( 0, 24 * 60 * 60 ) ); // Unavailable
376 $merged_readable = array( wpbc_transform_timerange__seconds_arr__in__formated_hours( array( 0, 24 * 60 * 60 ) ) );
377 } else {
378 $merged_seconds = $date_booking_obj->booked_time_slots['merged_seconds']; // [ [ 25211, 27002 ], [ 36011, 86400 ] ] // Time slots or Available
379 $merged_readable = $date_booking_obj->booked_time_slots['merged_readable'];
380 }
381
382 return array(
383 'merged_seconds' => $merged_seconds,
384 'merged_readable' => $merged_readable
385 );
386 }
387
388
389 /**
390 * Check if merged_seconds in $params['availability_per_days']['dates'][ $sql_class_day ][ $child_resource_id ] intersect with $time_as_seconds_arr
391 *
392 * @param $merged_seconds - [ [ 0, 86400 ] ]
393
394 * @param $this_date_time_as_seconds_arr - [ 36030, 39570 ] | [ 0, 24*60*60 ]
395 *
396 * @return bool
397 */
398 function wpbc_is_intersect__merged_seconds_arr__and__seconds_arr( $merged_seconds, $this_date_time_as_seconds_arr ) {
399
400 $is_intersect = wpbc_is_intersect__range_time_interval(
401 array(
402 array(
403 intval( $this_date_time_as_seconds_arr[ 0 ] ) + 20
404 , intval( $this_date_time_as_seconds_arr[ 1 ] ) - 20
405 )
406 )
407 , $merged_seconds );
408 return $is_intersect;
409 }
410
411
412 /**
413 * Is these array of intervals intersected ? - overlay of Math function
414 *
415 * @param array $time_interval_A array( array( 21600, 23400 ) )
416 * @param array $time_interval_B array( array( 25211, 27002 ), array( 36011, 86400 ) )
417 *
418 * @return bool
419 */
420 function wpbc_is_intersect__range_time_interval( $time_interval_A, $time_interval_B ){
421
422 for ( $i = 0; $i < count($time_interval_A); $i++ ){
423
424 for ( $j = 0; $j < count($time_interval_B); $j++ ){
425
426 $is_intersect = wpbc_is__intervals__intersected( $time_interval_A[ $i ], $time_interval_B[ $j ] );
427
428 if ( $is_intersect ){
429 return true;
430 }
431 }
432 }
433
434 return false;
435 }
436