| @@ -554,9 +554,11 @@ | ||
| 554 | 554 | 'as_single_resource' => false, // get dates as for 'single resource' or 'parent' resource including bookings in all 'child booking resources'. |
| 555 | 555 | 'max_days_count' => wpbc_get_max_visible_days_in_calendar(), // 365. |
| 556 | 556 | 'timeslots_to_check_intersect' => array(), // arr: '12:20 - 12:55', '13:00 - 14:00' ) // TODO: ? do we really need it, because below we get it from function. |
| 557 | 557 | '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. |
| 558 | + 'allow_past' => false, | |
| 558 | 559 | 'custom_form' => '', // Required for checking all available time-slots and compare with booked time slots. |
| 560 | + 'force_check_from_today_unavailable' => false, // Optional admin tools can force relative availability checks inside explicit date ranges. | |
| 559 | 561 | ); |
| 560 | 562 | |
| 561 | 563 | $params = wp_parse_args( $params, $defaults ); |
| 562 | 564 | |
| @@ -561,11 +563,12 @@ | ||
| 561 | 563 | $params = wp_parse_args( $params, $defaults ); |
| 562 | 564 | |
| 563 | 565 | // FixIn: 10.7.1.2. //FixIn: 10.10.3.2. |
| 564 | 566 | if ( |
| 567 | + ( ! empty( $params['allow_past'] ) ) || | |
| 565 | 568 | ( false !== strpos( $params['request_uri'], 'allow_past' ) ) || |
| 566 | 569 | ( |
| 567 | - ( false !== strpos( $params['request_uri'], 'page=wpbc-new' ) ) && | |
| 570 | + wpbc_is_new_booking_page_url( $params['request_uri'] ) && | |
| 568 | 571 | ( false !== strpos( $params['request_uri'], 'booking_hash' ) ) |
| 569 | 572 | ) |
| 570 | 573 | ) { |
| 571 | 574 | $params['dates_to_check'] = 'ALL'; |
| @@ -620,10 +623,15 @@ | ||
| 620 | 623 | // ----------------------------------------------------------------------------------------------------------------- |
| 621 | 624 | // Main function to get "Bookings": ['2023-08-30'][ > resource_id < ][ > time_seconds_range < ] => booking_date_obj[ booking_id:45, approved:1, ... |
| 622 | 625 | $bookings__in_dates = wpbc_get__booked_dates__per_resources__arr( $params ); |
| 623 | 626 | |
| 627 | + // ----------------------------------------------------------------------------------------------------------------- | |
| 628 | + // Time slots availability blocks. | |
| 629 | + // ----------------------------------------------------------------------------------------------------------------- | |
| 630 | + $availability_timeslots__in_dates = array(); // FixIn: 10.16.1. | |
| 624 | 631 | |
| 625 | 632 | |
| 633 | + | |
| 626 | 634 | // ----------------------------------------------------------------------------------------------------------------- |
| 627 | 635 | // Booking > Availability page |
| 628 | 636 | // ----------------------------------------------------------------------------------------------------------------- |
| 629 | 637 | $search_dates = $params['dates_to_check']; // 'CURDATE' | 'ALL' | [ "2023-08-03" , "2023-08-04" , "2023-08-05" ] |
| @@ -642,9 +650,28 @@ | ||
| 642 | 650 | } |
| 643 | 651 | $unavailable_dates__per_resources__arr = wpbc_aggregate_merge_availability( $unavailable_dates__per_resources__arr, |
| 644 | 652 | $resource_id__with_children__arr, |
| 645 | 653 | $aggregate_resource_id_arr ); |
| 654 | + // FixIn: 10.16.1. | |
| 655 | + if ( function_exists( 'wpbc_availability_timeslots__get_intervals_for_resources' ) ) { | |
| 656 | + $availability_timeslots__resource_ids = wpbc_get_unique_array_of_resources_id( $resource_id__with_children__arr, $aggregate_resource_id_arr ); | |
| 657 | + $availability_timeslots__in_dates = wpbc_availability_timeslots__get_intervals_for_resources( | |
| 658 | + array( | |
| 659 | + 'resource_id' => $availability_timeslots__resource_ids, | |
| 660 | + 'dates_to_check' => $search_dates, | |
| 661 | + 'max_days_count' => $params['max_days_count'], | |
| 662 | + ) | |
| 663 | + ); | |
| 646 | 664 | |
| 665 | + if ( function_exists( 'wpbc_availability_timeslots__aggregate_merge_intervals' ) ) { | |
| 666 | + $availability_timeslots__in_dates = wpbc_availability_timeslots__aggregate_merge_intervals( | |
| 667 | + $availability_timeslots__in_dates, | |
| 668 | + $resource_id__with_children__arr, | |
| 669 | + $aggregate_resource_id_arr | |
| 670 | + ); | |
| 671 | + } | |
| 672 | + } | |
| 673 | + | |
| 647 | 674 | // ----------------------------------------------------------------------------------------------------------------- |
| 648 | 675 | // Booking > Resources > Availability page |
| 649 | 676 | // ----------------------------------------------------------------------------------------------------------------- |
| 650 | 677 | $season_filters_availability = array(); |
| @@ -661,14 +688,14 @@ | ||
| 661 | 688 | // P a r a m e t e r s f o r L O O P |
| 662 | 689 | // ----------------------------------------------------------------------------------------------------------------- |
| 663 | 690 | |
| 664 | 691 | // - Is this Booking > Add booking page ? -------------------------------------------------------------------------- |
| 665 | - $is_this_bap_page = ( false !== strpos( $params['request_uri'], 'page=wpbc-new' ) ) ? true : false; | |
| 692 | + $is_this_bap_page = wpbc_is_new_booking_page_url( $params['request_uri'] ); | |
| 666 | 693 | $is_this_hash_page = ( false !== strpos( $params['request_uri'], 'booking_hash' ) ) ? true : false; // Set it to TRUE for adding booking in past at Booking > Add booking page |
| 667 | 694 | |
| 668 | 695 | // FixIn: 10.7.1.2. |
| 669 | 696 | if ( ! $is_this_hash_page ) { |
| 670 | - $is_this_hash_page = ( false !== strpos( $params['request_uri'], 'allow_past' ) ) ? true : false; | |
| 697 | + $is_this_hash_page = ( ( ! empty( $params['allow_past'] ) ) || ( false !== strpos( $params['request_uri'], 'allow_past' ) ) ) ? true : false; | |
| 671 | 698 | } |
| 672 | 699 | |
| 673 | 700 | if ( ( $is_this_bap_page ) && ( $is_this_hash_page ) ) { // Start days in calendar from = PAST |
| 674 | 701 | $params['dates_to_check'] = 'ALL'; |
| @@ -697,12 +724,12 @@ | ||
| 697 | 724 | } else if ( is_array( $params['dates_to_check'] ) ) { |
| 698 | 725 | |
| 699 | 726 | $start_day_number = 1; |
| 700 | 727 | |
| 701 | - $today_inix_timestamp = strtotime( $params['dates_to_check'][0] . ' 00:00:00' ); // '2023-09-03 00:00:00' | |
| 728 | + $today_inix_timestamp = strtotime( $params['dates_to_check'][0] . ' 00:00:00 UTC' ); // '2023-09-03 00:00:00' | |
| 702 | 729 | |
| 703 | - $dif_in_days = strtotime( $params['dates_to_check'][ ( count( $params['dates_to_check'] ) - 1 ) ] . ' 00:00:00' ) | |
| 704 | - - strtotime( $params['dates_to_check'][0] . ' 00:00:00' ); | |
| 730 | + $dif_in_days = strtotime( $params['dates_to_check'][ ( count( $params['dates_to_check'] ) - 1 ) ] . ' 00:00:00 UTC' ) | |
| 731 | + - strtotime( $params['dates_to_check'][0] . ' 00:00:00 UTC' ); | |
| 705 | 732 | |
| 706 | 733 | $dif_in_days = $dif_in_days / ( 24 * 60 * 60 ); |
| 707 | 734 | $params['max_days_count'] = ceil( $dif_in_days ) + 1; |
| 708 | 735 | } |
| @@ -757,8 +784,9 @@ | ||
| 757 | 784 | $availability_per_day[ $my_day_tag ]['statuses']['bookings_status'] = array(); // per each child booking resource |
| 758 | 785 | $availability_per_day[ $my_day_tag ]['summary'] = array(); |
| 759 | 786 | $availability_per_day[ $my_day_tag ]['summary']['status_for_day'] = ''; // available | time_slots_booking | full_day_booking | ... |
| 760 | 787 | $availability_per_day[ $my_day_tag ]['summary']['status_for_bookings'] = ''; // pending | approved | CO: pending_pending | pending_approved | approved_pending | approved_approved |
| 788 | + $availability_per_day[ $my_day_tag ]['summary']['status_for_bookings_arr'] = array(); // Structured statuses array, e.g. array( 'approved', 'approved_approved' ). | |
| 761 | 789 | $availability_per_day[ $my_day_tag ]['summary']['tooltip_times'] = ''; |
| 762 | 790 | $availability_per_day[ $my_day_tag ]['summary']['tooltip_availability'] = ''; |
| 763 | 791 | $availability_per_day[ $my_day_tag ]['summary']['tooltip_day_cost'] = ''; |
| 764 | 792 | $availability_per_day[ $my_day_tag ]['summary']['tooltip_booking_details'] = ''; |
| @@ -931,11 +959,20 @@ | ||
| 931 | 959 | |
| 932 | 960 | // ===================================================================================================== |
| 933 | 961 | // Change over days = |
| 934 | 962 | // ===================================================================================================== |
| 963 | + $is_booking_used_check_in_out_time = null; | |
| 964 | + if ( function_exists( 'wpbc_settings_calendar__preview_is_changeover_enabled' ) ) { | |
| 965 | + $is_booking_used_check_in_out_time = wpbc_settings_calendar__preview_is_changeover_enabled( $resource_id, $params['request_uri'] ); | |
| 966 | + } | |
| 967 | + if ( null === $is_booking_used_check_in_out_time ) { | |
| 968 | + $is_booking_used_check_in_out_time = ( | |
| 969 | + ( function_exists( 'wpbc_is_booking_used_check_in_out_time' ) ) | |
| 970 | + && ( wpbc_is_booking_used_check_in_out_time( $params['request_uri'], $resource_id ) ) | |
| 971 | + ); | |
| 972 | + } | |
| 935 | 973 | if ( |
| 936 | - ( function_exists( 'wpbc_is_booking_used_check_in_out_time' ) ) | |
| 937 | - && ( wpbc_is_booking_used_check_in_out_time( $params[ 'request_uri' ] ) ) | |
| 974 | + $is_booking_used_check_in_out_time | |
| 938 | 975 | && ( ! $availability_per_day[ $my_day_tag ][ $resource_id ]->is_day_unavailable ) // And this date still free false |
| 939 | 976 | ) { |
| 940 | 977 | |
| 941 | 978 | /** |
| @@ -1020,9 +1057,48 @@ | ||
| 1020 | 1057 | } |
| 1021 | 1058 | // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| 1022 | 1059 | // B O O K I N G S - E n d |
| 1023 | 1060 | // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| 1061 | + // Working Time applies only to time-based booking forms: rangetime, start/end, or start/duration. | |
| 1062 | + $working_time__timeslots_to_check_intersect = $as_seconds_timeslots_arr__to_check_intersect; | |
| 1063 | + if ( empty( $working_time__timeslots_to_check_intersect ) && ! empty( $params['timeslots_to_check_intersect'] ) ) { | |
| 1064 | + if ( ! is_array( $params['timeslots_to_check_intersect'] ) ) { | |
| 1065 | + $params['timeslots_to_check_intersect'] = array( $params['timeslots_to_check_intersect'] ); | |
| 1066 | + } | |
| 1067 | + $working_time__timeslots_to_check_intersect = array(); | |
| 1068 | + foreach ( $params['timeslots_to_check_intersect'] as $time_slot_readable ) { | |
| 1069 | + $working_time__timeslots_to_check_intersect[] = wpbc_convert__readable_time_slot__to__time_range_in_seconds( $time_slot_readable ); | |
| 1070 | + } | |
| 1071 | + } | |
| 1024 | 1072 | |
| 1073 | + if ( | |
| 1074 | + ! empty( $working_time__timeslots_to_check_intersect ) | |
| 1075 | + && function_exists( 'wpbc_working_time__get_non_working_intervals_for_date' ) | |
| 1076 | + && function_exists( 'wpbc_availability_timeslots__apply_blocks_to_availability_obj' ) | |
| 1077 | + ) { | |
| 1078 | + $working_time__blocked_intervals = wpbc_working_time__get_non_working_intervals_for_date( $resource_id, $my_day_tag ); | |
| 1079 | + | |
| 1080 | + if ( ! empty( $working_time__blocked_intervals ) ) { | |
| 1081 | + $availability_per_day[ $my_day_tag ][ $resource_id ] = wpbc_availability_timeslots__apply_blocks_to_availability_obj( | |
| 1082 | + $availability_per_day[ $my_day_tag ][ $resource_id ], | |
| 1083 | + $working_time__blocked_intervals, | |
| 1084 | + $working_time__timeslots_to_check_intersect | |
| 1085 | + ); | |
| 1086 | + } | |
| 1087 | + } | |
| 1088 | + | |
| 1089 | + // FixIn: 10.16.1. | |
| 1090 | + if ( | |
| 1091 | + function_exists( 'wpbc_availability_timeslots__apply_blocks_to_availability_obj' ) | |
| 1092 | + && isset( $availability_timeslots__in_dates[ $my_day_tag ][ $resource_id ] ) | |
| 1093 | + ) { | |
| 1094 | + $availability_per_day[ $my_day_tag ][ $resource_id ] = wpbc_availability_timeslots__apply_blocks_to_availability_obj( | |
| 1095 | + $availability_per_day[ $my_day_tag ][ $resource_id ], | |
| 1096 | + $availability_timeslots__in_dates[ $my_day_tag ][ $resource_id ], | |
| 1097 | + $as_seconds_timeslots_arr__to_check_intersect | |
| 1098 | + ); | |
| 1099 | + } | |
| 1100 | + | |
| 1025 | 1101 | //========================================================================================================== |
| 1026 | 1102 | // $availability_per_day[..]->_day_status = 'season_filter' | ... |
| 1027 | 1103 | //========================================================================================================== |
| 1028 | 1104 | // Booking > Resources > Availability page >>> 'season_filter' |
| @@ -1030,18 +1106,20 @@ | ||
| 1030 | 1106 | |
| 1031 | 1107 | // Booking > Availability page >>> 'resource_availability' |
| 1032 | 1108 | $availability_per_day[ $my_day_tag ][ $resource_id ] = wpbc_support_capacity__day_status__resource_availability( $availability_per_day, $my_day_tag, $resource_id, $unavailable_dates__per_resources__arr ); |
| 1033 | 1109 | |
| 1034 | - // Week Days unavailable >>> 'weekday_unavailable' | |
| 1035 | - $availability_per_day[ $my_day_tag ][ $resource_id ] = wpbc_support_capacity__day_status__weekday_unavailable( $availability_per_day, $my_day_tag, $resource_id ); | |
| 1110 | + if ( empty( $params['skip_general_availability'] ) ) { | |
| 1111 | + // Week Days unavailable >>> 'weekday_unavailable' | |
| 1112 | + $availability_per_day[ $my_day_tag ][ $resource_id ] = wpbc_support_capacity__day_status__weekday_unavailable( $availability_per_day, $my_day_tag, $resource_id ); | |
| 1113 | + } | |
| 1036 | 1114 | |
| 1037 | 1115 | // if ( ( ! $is_this_bap_page ) || ( ! $is_this_hash_page ) ) { |
| 1038 | - if ( ( ! $is_this_bap_page ) && ( ! $is_this_hash_page ) ) { | |
| 1116 | + if ( empty( $params['skip_general_availability'] ) && ( ! $is_this_bap_page ) && ( ! $is_this_hash_page ) ) { | |
| 1039 | 1117 | // Do not apply these settings at Booking > Add booking page, when we edit booking - e.g. exist 'booking_hash' |
| 1040 | 1118 | |
| 1041 | 1119 | // Skip checking unavailable from today, if defined the calendar_dates_start='2025-02-10' calendar_dates_end='2025-12-31' in shortcode. // FixIn: 10.13.1.4. |
| 1042 | 1120 | $is_skip_check__from_today_unavailable = false; |
| 1043 | - if ( is_array( $params['dates_to_check'] ) ) { | |
| 1121 | + if ( is_array( $params['dates_to_check'] ) && empty( $params['force_check_from_today_unavailable'] ) ) { | |
| 1044 | 1122 | if ( ( ( count( $params['dates_to_check'] ) ) > 0 ) && ( wpbc_is_date_less_than( $params['dates_to_check'][0], $my_day_tag ) ) ) { |
| 1045 | 1123 | $is_skip_check__from_today_unavailable = true; |
| 1046 | 1124 | } |
| 1047 | 1125 | if ( ( ( count( $params['dates_to_check'] ) ) > 1 ) && ( wpbc_is_date_higher_than( $params['dates_to_check'][1], $my_day_tag ) ) ) { |
| @@ -1179,8 +1257,9 @@ | ||
| 1179 | 1257 | $temp_status[] = $bookings_status_element; |
| 1180 | 1258 | } |
| 1181 | 1259 | } |
| 1182 | 1260 | $temp_status = array_filter( $temp_status ); // All entries of array equal to FALSE (0, '', '0' ) will be removed. |
| 1261 | + $temp_status_counts = array_count_values( $temp_status ); | |
| 1183 | 1262 | $temp_status = array_unique( $temp_status ); // Erase duplicates |
| 1184 | 1263 | $availability_per_this_day['summary']['status_for_day'] = $temp_status; |
| 1185 | 1264 | |
| 1186 | 1265 | /** |
| @@ -1205,10 +1284,34 @@ | ||
| 1205 | 1284 | // Priority: available > time_slots_booking > full_day_booking |
| 1206 | 1285 | // > season_filter > resource_availability |
| 1207 | 1286 | // > weekday_unavailable > from_today_unavailable > limit_available_from_today |
| 1208 | 1287 | |
| 1209 | - // if at least one booking "available" then day has this status | |
| 1288 | + // Capacity resources can be available even when every child has a check-in/out or full-day status. | |
| 1289 | + $max_capacity = isset( $availability_per_this_day['max_capacity'] ) ? intval( $availability_per_this_day['max_capacity'] ) : 1; | |
| 1290 | + $blocking_statuses = array( | |
| 1291 | + 'full_day_booking', | |
| 1292 | + 'season_filter', | |
| 1293 | + 'resource_availability', | |
| 1294 | + 'weekday_unavailable', | |
| 1295 | + 'from_today_unavailable', | |
| 1296 | + 'limit_available_from_today', | |
| 1297 | + 'change_over', | |
| 1298 | + ); | |
| 1299 | + $blocked_capacity = 0; | |
| 1300 | + foreach ( $blocking_statuses as $blocking_status ) { | |
| 1301 | + $blocked_capacity += isset( $temp_status_counts[ $blocking_status ] ) ? intval( $temp_status_counts[ $blocking_status ] ) : 0; | |
| 1302 | + } | |
| 1303 | + $booked_capacity__before_check_in = $blocked_capacity + ( isset( $temp_status_counts['check_out'] ) ? intval( $temp_status_counts['check_out'] ) : 0 ); | |
| 1304 | + $booked_capacity__after_check_in = $blocked_capacity + ( isset( $temp_status_counts['check_in'] ) ? intval( $temp_status_counts['check_in'] ) : 0 ); | |
| 1305 | + $is_capacity_available_during_change_over = ( | |
| 1306 | + ( $max_capacity > 1 ) | |
| 1307 | + && ( $max_capacity > max( $booked_capacity__before_check_in, $booked_capacity__after_check_in ) ) | |
| 1308 | + && ( ! in_array( 'time_slots_booking', $availability_per_this_day['summary']['status_for_day'] ) ) | |
| 1309 | + ); | |
| 1310 | + | |
| 1311 | + // if at least one resource is available then day has this status | |
| 1210 | 1312 | if ( in_array( 'available', $availability_per_this_day['summary']['status_for_day'] ) ) { $availability_per_this_day['summary']['status_for_day'] = 'available'; |
| 1313 | + } else if ( $is_capacity_available_during_change_over ) { $availability_per_this_day['summary']['status_for_day'] = 'available'; | |
| 1211 | 1314 | } else if ( in_array( 'time_slots_booking', $availability_per_this_day['summary']['status_for_day'] ) ) { $availability_per_this_day['summary']['status_for_day'] = 'time_slots_booking'; |
| 1212 | 1315 | } else if ( ( ! in_array( 'check_out', $availability_per_this_day['summary']['status_for_day'] ) ) |
| 1213 | 1316 | && ( in_array( 'check_in', $availability_per_this_day['summary']['status_for_day'] ) ) ) { $availability_per_this_day['summary']['status_for_day'] = 'check_in'; |
| 1214 | 1317 | } else if ( ( ! in_array( 'check_in', $availability_per_this_day['summary']['status_for_day'] ) ) |
| @@ -1234,19 +1337,21 @@ | ||
| 1234 | 1337 | $temp_status[] = $bookings_status_element; |
| 1235 | 1338 | } |
| 1236 | 1339 | } |
| 1237 | 1340 | $temp_status = array_filter( $temp_status ); // All entries of array equal to FALSE (0, '', '0' ) will be removed. |
| 1238 | - $temp_status = array_unique( $temp_status ); | |
| 1239 | - $availability_per_this_day['summary']['status_for_bookings'] = $temp_status; | |
| 1341 | + $temp_status = array_values( array_unique( $temp_status ) ); | |
| 1240 | 1342 | |
| 1343 | + // Structured booking statuses contract. Keep numeric keys sequential so JSON encodes this as a JS array. | |
| 1344 | + $availability_per_this_day['summary']['status_for_bookings_arr'] = $temp_status; | |
| 1345 | + | |
| 1241 | 1346 | // Priority: pending > approved > '' |
| 1242 | 1347 | |
| 1243 | 1348 | // if at least one booking pending then day is pending |
| 1244 | - if ( in_array( 'pending', $availability_per_this_day['summary']['status_for_bookings'] ) ) { $availability_per_this_day['summary']['status_for_bookings'] = 'pending'; | |
| 1245 | - } else if ( in_array( 'pending_pending', $availability_per_this_day['summary']['status_for_bookings'] ) ) { $availability_per_this_day['summary']['status_for_bookings'] = 'pending_pending'; | |
| 1349 | + if ( in_array( 'pending', $temp_status ) ) { $availability_per_this_day['summary']['status_for_bookings'] = 'pending'; | |
| 1350 | + } else if ( in_array( 'pending_pending', $temp_status ) ) { $availability_per_this_day['summary']['status_for_bookings'] = 'pending_pending'; | |
| 1246 | 1351 | // Usual situations for change over days: pending_pending | pending_approved | approved_pending | approved_approved |
| 1247 | 1352 | } else { |
| 1248 | - $availability_per_this_day['summary']['status_for_bookings'] = implode( ' ', $availability_per_this_day['summary']['status_for_bookings'] ); | |
| 1353 | + $availability_per_this_day['summary']['status_for_bookings'] = implode( ' ', $temp_status ); | |
| 1249 | 1354 | } |
| 1250 | 1355 | |
| 1251 | 1356 | |
| 1252 | 1357 | // ------------------------------------------------------------------------------------------------------------- |
| @@ -1939,5 +2044,5 @@ | ||
| 1939 | 2044 | // * // , 'wpdev_active_locale' => 'en_US' |
| 1940 | 2045 | // * ) ); |
| 1941 | 2046 | // . |
| 1942 | 2047 | // add_bk_filter('wpbc_add_new_booking_filter' , 'wpbc_add_new_booking' ); |
| 1943 | -// TODO: recheck hooks in wpbc-dev-api.php | |
| 2048 | +// TODO: recheck hooks in wpbc-dev-api.php | |