activities_helper.php
3 months ago
agent_helper.php
3 months ago
analytics_helper.php
4 months ago
auth_helper.php
3 months ago
blocks_helper.php
3 months ago
booking_helper.php
3 months ago
bricks_helper.php
3 months ago
bundles_helper.php
3 months ago
calendar_helper.php
3 months ago
carts_helper.php
3 months ago
connector_helper.php
3 months ago
csv_helper.php
3 months ago
customer_helper.php
3 months ago
customer_import_helper.php
3 months ago
database_helper.php
3 months ago
debug_helper.php
3 months ago
defaults_helper.php
3 months ago
elementor_helper.php
3 months ago
email_helper.php
3 months ago
encrypt_helper.php
3 months ago
events_helper.php
3 months ago
form_helper.php
3 months ago
icalendar_helper.php
3 months ago
image_helper.php
3 months ago
invoices_helper.php
3 months ago
license_helper.php
3 months ago
location_helper.php
3 months ago
marketing_systems_helper.php
3 months ago
meeting_systems_helper.php
3 months ago
menu_helper.php
3 months ago
meta_helper.php
3 months ago
migrations_helper.php
3 months ago
money_helper.php
3 months ago
notifications_helper.php
3 months ago
nps_survey_helper.php
3 months ago
order_intent_helper.php
3 months ago
orders_helper.php
3 months ago
otp_helper.php
3 months ago
pages_helper.php
3 months ago
params_helper.php
3 months ago
payments_helper.php
3 months ago
price_breakdown_helper.php
3 months ago
process_jobs_helper.php
3 months ago
processes_helper.php
3 months ago
replacer_helper.php
3 months ago
resource_helper.php
3 months ago
roles_helper.php
3 months ago
router_helper.php
3 months ago
service_helper.php
3 months ago
sessions_helper.php
3 months ago
settings_helper.php
3 months ago
short_links_systems_helper.php
3 months ago
shortcodes_helper.php
3 months ago
sms_helper.php
3 months ago
steps_helper.php
3 months ago
stripe_connect_helper.php
3 months ago
styles_helper.php
3 months ago
support_topics_helper.php
3 months ago
time_helper.php
3 months ago
timeline_helper.php
3 months ago
transaction_helper.php
3 months ago
transaction_intent_helper.php
3 months ago
util_helper.php
3 months ago
version_specific_updates_helper.php
3 months ago
whatsapp_helper.php
3 months ago
work_periods_helper.php
3 months ago
wp_datetime.php
3 months ago
wp_user_helper.php
3 months ago
booking_helper.php
1847 lines
| 1 | <?php |
| 2 | |
| 3 | class OsBookingHelper { |
| 4 | |
| 5 | |
| 6 | /** |
| 7 | * @param OsBookingModel $booking |
| 8 | * |
| 9 | * @return mixed|void |
| 10 | * |
| 11 | * Returns full amount to charge in database format 1999.0000 |
| 12 | * |
| 13 | */ |
| 14 | public static function calculate_full_amount_for_booking( OsBookingModel $booking ) { |
| 15 | if ( ! $booking->service_id ) { |
| 16 | return 0; |
| 17 | } |
| 18 | $amount = self::calculate_full_amount_for_service( $booking ); |
| 19 | $amount = apply_filters( 'latepoint_calculate_full_amount_for_booking', $amount, $booking ); |
| 20 | $amount = OsMoneyHelper::pad_to_db_format( $amount ); |
| 21 | |
| 22 | return $amount; |
| 23 | } |
| 24 | |
| 25 | |
| 26 | /** |
| 27 | * @param OsBookingModel $booking |
| 28 | * |
| 29 | * @return mixed|void |
| 30 | * |
| 31 | */ |
| 32 | public static function calculate_full_amount_for_service( OsBookingModel $booking ) { |
| 33 | if ( ! $booking->service_id ) { |
| 34 | return 0; |
| 35 | } |
| 36 | $service = new OsServiceModel( $booking->service_id ); |
| 37 | $amount_for_service = $service->get_full_amount_for_duration( $booking->duration ); |
| 38 | $amount_for_service = apply_filters( 'latepoint_full_amount_for_service', $amount_for_service, $booking ); |
| 39 | |
| 40 | return $amount_for_service; |
| 41 | } |
| 42 | |
| 43 | |
| 44 | /** |
| 45 | * @param OsBookingModel $booking |
| 46 | * |
| 47 | * @return mixed|void |
| 48 | * |
| 49 | * Returns deposit amount to charge in database format 1999.0000 |
| 50 | * |
| 51 | */ |
| 52 | public static function calculate_deposit_amount_to_charge( OsBookingModel $booking ) { |
| 53 | if ( ! $booking->service_id ) { |
| 54 | return 0; |
| 55 | } |
| 56 | $service = new OsServiceModel( $booking->service_id ); |
| 57 | $amount = $service->get_deposit_amount_for_duration( $booking->duration ); |
| 58 | $amount = apply_filters( 'latepoint_deposit_amount_for_service', $amount, $booking ); |
| 59 | $amount = OsMoneyHelper::pad_to_db_format( $amount ); |
| 60 | |
| 61 | return $amount; |
| 62 | } |
| 63 | |
| 64 | |
| 65 | /** |
| 66 | * @param array $item_data |
| 67 | * |
| 68 | * @return OsBookingModel |
| 69 | */ |
| 70 | public static function build_booking_model_from_item_data( array $item_data ): OsBookingModel { |
| 71 | $booking = new OsBookingModel(); |
| 72 | if ( $item_data['id'] ) { |
| 73 | $booking = $booking->load_by_id( $item_data['id'] ); |
| 74 | if ( ! $booking ) { |
| 75 | $booking = new OsBookingModel(); |
| 76 | } |
| 77 | } |
| 78 | $booking->set_data( $item_data ); |
| 79 | // get buffers from service and set to booking object |
| 80 | if ( ! isset( $item_data['buffer_before'] ) && ! isset( $item_data['buffer_after'] ) ) { |
| 81 | $booking->set_buffers(); |
| 82 | } |
| 83 | if ( empty( $booking->end_time ) ) { |
| 84 | $booking->calculate_end_date_and_time(); |
| 85 | } |
| 86 | if ( empty( $booking->end_date ) ) { |
| 87 | $booking->calculate_end_date(); |
| 88 | } |
| 89 | $booking->set_utc_datetimes(); |
| 90 | |
| 91 | return $booking; |
| 92 | } |
| 93 | |
| 94 | public static function get_booking_id_and_manage_ability_by_key( string $key ) { |
| 95 | $booking_id = OsMetaHelper::get_booking_id_by_meta_value( 'key_to_manage_for_agent', $key ); |
| 96 | if ( $booking_id ) { |
| 97 | return [ |
| 98 | 'booking_id' => $booking_id, |
| 99 | 'for' => 'agent', |
| 100 | ]; |
| 101 | } |
| 102 | |
| 103 | $booking_id = OsMetaHelper::get_booking_id_by_meta_value( 'key_to_manage_for_customer', $key ); |
| 104 | if ( $booking_id ) { |
| 105 | return [ |
| 106 | 'booking_id' => $booking_id, |
| 107 | 'for' => 'customer', |
| 108 | ]; |
| 109 | } |
| 110 | |
| 111 | return false; |
| 112 | } |
| 113 | |
| 114 | public static function is_action_allowed( string $action, OsBookingModel $booking, string $key = '' ) { |
| 115 | $is_allowed = false; |
| 116 | if ( empty( $booking->id ) ) { |
| 117 | return false; |
| 118 | } |
| 119 | if ( ! in_array( $action, [ 'cancel', 'reschedule' ] ) ) { |
| 120 | return false; |
| 121 | } |
| 122 | $action_result = false; |
| 123 | switch ( $action ) { |
| 124 | case 'cancel': |
| 125 | $action_result = OsCustomerHelper::can_cancel_booking( $booking ); |
| 126 | break; |
| 127 | case 'reschedule': |
| 128 | $action_result = OsCustomerHelper::can_reschedule_booking( $booking ); |
| 129 | break; |
| 130 | } |
| 131 | if ( ! empty( $key ) ) { |
| 132 | // key is passed, check if allowed through key |
| 133 | $agent_key_meta = OsMetaHelper::get_booking_meta_by_key( 'key_to_manage_for_agent', $booking->id ); |
| 134 | $customer_key_meta = OsMetaHelper::get_booking_meta_by_key( 'key_to_manage_for_customer', $booking->id ); |
| 135 | if ( $key == $agent_key_meta ) { |
| 136 | // agent can do everything, no need to check for action |
| 137 | $is_allowed = true; |
| 138 | } elseif ( $key == $customer_key_meta ) { |
| 139 | // customer |
| 140 | $is_allowed = $action_result; |
| 141 | } |
| 142 | } elseif ( OsAuthHelper::get_logged_in_customer_id() == $booking->customer_id ) { |
| 143 | $is_allowed = $action_result; |
| 144 | } |
| 145 | |
| 146 | return $is_allowed; |
| 147 | } |
| 148 | |
| 149 | public static function generate_add_to_calendar_links( OsBookingModel $booking, $key = false ): string { |
| 150 | $html = '<div class="add-to-calendar-types"> |
| 151 | <div class="atc-heading-wrapper"> |
| 152 | <div class="atc-heading">' . esc_html__( 'Calendar Type', 'latepoint' ) . '</div> |
| 153 | <div class="close-calendar-types"></div> |
| 154 | </div> |
| 155 | <a href="' . esc_url( $booking->get_ical_download_link( $key ) ) . '" target="_blank" class="atc-type atc-type-apple"> |
| 156 | <div class="atc-type-image"></div> |
| 157 | <div class="atc-type-name">' . esc_html__( 'Apple Calendar', 'latepoint' ) . '</div> |
| 158 | </a> |
| 159 | <a href="' . esc_url( $booking->get_url_for_add_to_calendar_button( 'google' ) ) . '" target="_blank" class="atc-type atc-type-google"> |
| 160 | <div class="atc-type-image"></div> |
| 161 | <div class="atc-type-name">' . esc_html__( 'Google Calendar', 'latepoint' ) . '</div> |
| 162 | </a> |
| 163 | <a href="' . esc_url( $booking->get_url_for_add_to_calendar_button( 'outlook' ) ) . '" target="_blank" class="atc-type atc-type-outlook"> |
| 164 | <div class="atc-type-image"></div> |
| 165 | <div class="atc-type-name">' . esc_html__( 'Outlook.com', 'latepoint' ) . '</div> |
| 166 | </a> |
| 167 | <a href="' . esc_url( $booking->get_url_for_add_to_calendar_button( 'outlook' ) ) . '" target="_blank" class="atc-type atc-type-office-365"> |
| 168 | <div class="atc-type-image"></div> |
| 169 | <div class="atc-type-name">' . esc_html__( 'Microsoft 365', 'latepoint' ) . '</div> |
| 170 | </a> |
| 171 | </div>'; |
| 172 | |
| 173 | return $html; |
| 174 | } |
| 175 | |
| 176 | |
| 177 | public static function get_bookings_for_select( $should_be_in_future = false ) { |
| 178 | $bookings = new OsBookingModel(); |
| 179 | if ( $should_be_in_future ) { |
| 180 | $bookings = $bookings->should_be_in_future(); |
| 181 | } |
| 182 | $bookings = $bookings->order_by( 'id desc' )->set_limit( 100 )->get_results_as_models(); |
| 183 | $bookings_options = []; |
| 184 | foreach ( $bookings as $booking ) { |
| 185 | $name = $booking->service->name . ', ' . $booking->agent->full_name . ', ' . $booking->customer->full_name . ' [' . $booking->booking_code . ' : ' . $booking->id . ']'; |
| 186 | $bookings_options[] = [ |
| 187 | 'value' => $booking->id, |
| 188 | 'label' => esc_html( $name ), |
| 189 | ]; |
| 190 | } |
| 191 | |
| 192 | return $bookings_options; |
| 193 | } |
| 194 | |
| 195 | /** |
| 196 | * |
| 197 | * Determine whether to show |
| 198 | * |
| 199 | * @param $rows |
| 200 | * |
| 201 | * @return bool |
| 202 | */ |
| 203 | public static function is_breakdown_free( $rows ) { |
| 204 | return ( ( empty( $rows['subtotal']['raw_value'] ) || ( (float) $rows['subtotal']['raw_value'] <= 0 ) ) && ( empty( $rows['total']['raw_value'] ) || ( (float) $rows['total']['raw_value'] <= 0 ) ) ); |
| 205 | } |
| 206 | |
| 207 | public static function output_price_breakdown( $rows ) { |
| 208 | foreach ( $rows['before_subtotal'] as $row ) { |
| 209 | self::output_price_breakdown_row( $row ); |
| 210 | } |
| 211 | // if there is nothing between subtotal and total - don't show subtotal as it will be identical to total |
| 212 | if ( ! empty( $rows['after_subtotal'] ) ) { |
| 213 | if ( ! empty( $rows['subtotal'] ) ) { |
| 214 | echo '<div class="subtotal-separator"></div>'; |
| 215 | self::output_price_breakdown_row( $rows['subtotal'] ); |
| 216 | } |
| 217 | foreach ( $rows['after_subtotal'] as $row ) { |
| 218 | self::output_price_breakdown_row( $row ); |
| 219 | } |
| 220 | } |
| 221 | if ( ! empty( $rows['total'] ) ) { |
| 222 | self::output_price_breakdown_row( $rows['total'] ); |
| 223 | } |
| 224 | if ( ! empty( $rows['payments'] ) ) { |
| 225 | foreach ( $rows['payments'] as $row ) { |
| 226 | self::output_price_breakdown_row( $row ); |
| 227 | } |
| 228 | } |
| 229 | if ( ! empty( $rows['balance'] ) ) { |
| 230 | self::output_price_breakdown_row( $rows['balance'] ); |
| 231 | } |
| 232 | } |
| 233 | |
| 234 | public static function output_price_breakdown_row( $row ) { |
| 235 | if ( ! empty( $row['items'] ) ) { |
| 236 | if ( ! empty( $row['heading'] ) ) { |
| 237 | echo '<div class="summary-box-heading"><div class="sbh-item">' . esc_html( $row['heading'] ) . '</div><div class="sbh-line"></div></div>'; |
| 238 | } |
| 239 | foreach ( $row['items'] as $row_item ) { |
| 240 | self::output_price_breakdown_row( $row_item ); |
| 241 | } |
| 242 | } else { |
| 243 | $extra_class = ''; |
| 244 | if ( isset( $row['style'] ) && $row['style'] == 'strong' ) { |
| 245 | $extra_class .= ' spi-strong'; |
| 246 | } |
| 247 | if ( isset( $row['style'] ) && $row['style'] == 'total' ) { |
| 248 | $extra_class .= ' spi-total'; |
| 249 | } |
| 250 | if ( isset( $row['type'] ) && $row['type'] == 'credit' ) { |
| 251 | $extra_class .= ' spi-positive'; |
| 252 | } |
| 253 | ?> |
| 254 | <div class="summary-price-item-w <?php echo esc_attr( $extra_class ); ?>"> |
| 255 | <div class="spi-name"> |
| 256 | <?php echo $row['label']; ?> |
| 257 | <?php if ( ! empty( $row['note'] ) ) { |
| 258 | echo '<span class="pi-note">' . esc_html( $row['note'] ) . '</span>'; |
| 259 | } ?> |
| 260 | <?php if ( ! empty( $row['badge'] ) ) { |
| 261 | echo '<span class="pi-badge">' . esc_html( $row['badge'] ) . '</span>'; |
| 262 | } ?> |
| 263 | </div> |
| 264 | <div class="spi-price"><?php echo esc_html( $row['value'] ); ?></div> |
| 265 | </div> |
| 266 | <?php |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | public static function output_price_breakdown_row_as_input_field( $row, $base_name ) { |
| 271 | $field_name = $base_name . '[' . OsUtilHelper::random_text( 'alnum', 8 ) . ']'; |
| 272 | if ( ! empty( $row['items'] ) ) { |
| 273 | echo OsFormHelper::hidden_field( $field_name . '[heading]', $row['heading'] ?? '' ); |
| 274 | foreach ( $row['items'] as $row_item ) { |
| 275 | self::output_price_breakdown_row_as_input_field( $row_item, $field_name . '[items]' ); |
| 276 | } |
| 277 | } else { |
| 278 | $wrapper_class = ( $row['raw_value'] < 0 ) ? [ 'class' => 'green-value-input' ] : []; |
| 279 | $label = $row['label'] ?? ''; |
| 280 | if ( ! empty( $row['note'] ) ) { |
| 281 | $label .= ' ' . $row['note']; |
| 282 | } |
| 283 | echo OsFormHelper::money_field( $field_name . '[value]', $label, $row['raw_value'], [ 'theme' => 'right-aligned' ], [], $wrapper_class ); |
| 284 | echo OsFormHelper::hidden_field( $field_name . '[label]', $row['label'] ?? '' ); |
| 285 | echo OsFormHelper::hidden_field( $field_name . '[style]', $row['style'] ?? '' ); |
| 286 | echo OsFormHelper::hidden_field( $field_name . '[type]', $row['type'] ?? '' ); |
| 287 | echo OsFormHelper::hidden_field( $field_name . '[note]', $row['note'] ?? '' ); |
| 288 | echo OsFormHelper::hidden_field( $field_name . '[badge]', $row['badge'] ?? '' ); |
| 289 | } |
| 290 | if ( ! empty( $row['sub_items'] ) ) { |
| 291 | foreach ( $row['sub_items'] as $row_item ) { |
| 292 | self::output_price_breakdown_row_as_input_field( $row_item, $field_name . '[sub_items]' ); |
| 293 | } |
| 294 | } |
| 295 | } |
| 296 | |
| 297 | /** |
| 298 | * @param \LatePoint\Misc\Filter $filter |
| 299 | * @param bool $accessed_from_backend |
| 300 | * |
| 301 | * @return array |
| 302 | */ |
| 303 | public static function get_blocked_periods_grouped_by_day( \LatePoint\Misc\Filter $filter, bool $accessed_from_backend = false ): array { |
| 304 | $grouped_blocked_periods = []; |
| 305 | |
| 306 | if ( $filter->date_from ) { |
| 307 | $date_from = OsWpDateTime::os_createFromFormat( 'Y-m-d', $filter->date_from ); |
| 308 | $date_to = ( $filter->date_to ) ? OsWpDateTime::os_createFromFormat( 'Y-m-d', $filter->date_to ) : OsWpDateTime::os_createFromFormat( 'Y-m-d', $filter->date_from ); |
| 309 | |
| 310 | # Loop through days to fill in days that might have no bookings |
| 311 | for ( $day = clone $date_from; $day->format( 'Y-m-d' ) <= $date_to->format( 'Y-m-d' ); $day->modify( '+1 day' ) ) { |
| 312 | $grouped_blocked_periods[ $day->format( 'Y-m-d' ) ] = []; |
| 313 | } |
| 314 | } |
| 315 | if ( ! $accessed_from_backend ) { |
| 316 | $today = new OsWpDateTime( 'today' ); |
| 317 | $earliest_possible_booking = OsSettingsHelper::get_earliest_possible_booking_restriction( $filter->service_id ); |
| 318 | |
| 319 | $block_end_datetime = OsTimeHelper::now_datetime_object(); |
| 320 | if ( $earliest_possible_booking ) { |
| 321 | try { |
| 322 | $block_end_datetime->modify( $earliest_possible_booking ); |
| 323 | } catch ( Exception $e ) { |
| 324 | $block_end_datetime = OsTimeHelper::now_datetime_object(); |
| 325 | } |
| 326 | } |
| 327 | for ( $day = clone $today; $day->format( 'Y-m-d' ) <= $block_end_datetime->format( 'Y-m-d' ); $day->modify( '+1 day' ) ) { |
| 328 | // loop days from now to the earliest possible booking and block timeslots if these days were actually requested |
| 329 | if ( isset( $grouped_blocked_periods[ $day->format( 'Y-m-d' ) ] ) ) { |
| 330 | $grouped_blocked_periods[ $day->format( 'Y-m-d' ) ][] = new \LatePoint\Misc\BlockedPeriod( |
| 331 | [ |
| 332 | 'start_time' => 0, |
| 333 | 'end_time' => ( $day->format( 'Y-m-d' ) < $block_end_datetime->format( 'Y-m-d' ) ) ? 24 * 60 : OsTimeHelper::convert_datetime_to_minutes( $block_end_datetime ), |
| 334 | 'start_date' => $day->format( 'Y-m-d' ), |
| 335 | 'end_date' => $day->format( 'Y-m-d' ), |
| 336 | ] |
| 337 | ); |
| 338 | } |
| 339 | } |
| 340 | $latest_possible_booking = OsSettingsHelper::get_latest_possible_booking_restriction( $filter->service_id ); |
| 341 | if ( $latest_possible_booking ) { |
| 342 | try { |
| 343 | $latest_booking_datetime = OsTimeHelper::now_datetime_object(); |
| 344 | $latest_booking_datetime->modify( $latest_possible_booking ); |
| 345 | } catch ( Exception $e ) { |
| 346 | $latest_booking_datetime = null; |
| 347 | } |
| 348 | if ( $latest_booking_datetime && $filter->date_from ) { |
| 349 | $date_to = ( $filter->date_to ) ? OsWpDateTime::os_createFromFormat( 'Y-m-d', $filter->date_to ) : OsWpDateTime::os_createFromFormat( 'Y-m-d', $filter->date_from ); |
| 350 | // Start from the latest_booking_datetime day |
| 351 | for ( $day = clone $latest_booking_datetime; $day->format( 'Y-m-d' ) <= $date_to->format( 'Y-m-d' ); $day->modify( '+1 day' ) ) { |
| 352 | if ( isset( $grouped_blocked_periods[ $day->format( 'Y-m-d' ) ] ) ) { |
| 353 | $grouped_blocked_periods[ $day->format( 'Y-m-d' ) ][] = new \LatePoint\Misc\BlockedPeriod( |
| 354 | [ |
| 355 | 'start_time' => ( $day->format( 'Y-m-d' ) == $latest_booking_datetime->format( 'Y-m-d' ) ) ? OsTimeHelper::convert_datetime_to_minutes( $latest_booking_datetime ) : 0, |
| 356 | 'end_time' => 24 * 60, |
| 357 | 'start_date' => $day->format( 'Y-m-d' ), |
| 358 | 'end_date' => $day->format( 'Y-m-d' ), |
| 359 | ] |
| 360 | ); |
| 361 | } |
| 362 | } |
| 363 | } |
| 364 | } |
| 365 | } |
| 366 | |
| 367 | $grouped_blocked_periods = apply_filters( 'latepoint_blocked_periods_for_range', $grouped_blocked_periods, $filter ); |
| 368 | |
| 369 | return $grouped_blocked_periods; |
| 370 | } |
| 371 | |
| 372 | /** |
| 373 | * @param \LatePoint\Misc\Filter $filter |
| 374 | * |
| 375 | * @return array |
| 376 | */ |
| 377 | public static function get_booked_periods_grouped_by_day( \LatePoint\Misc\Filter $filter ): array { |
| 378 | $booked_periods = self::get_booked_periods( $filter ); |
| 379 | |
| 380 | $grouped_booked_periods = []; |
| 381 | if ( $filter->date_from ) { |
| 382 | $date_from = OsWpDateTime::os_createFromFormat( 'Y-m-d', $filter->date_from ); |
| 383 | $date_to = ( $filter->date_to ) ? OsWpDateTime::os_createFromFormat( 'Y-m-d', $filter->date_to ) : OsWpDateTime::os_createFromFormat( 'Y-m-d', $filter->date_from ); |
| 384 | |
| 385 | # Loop through days to fill in days that might have no bookings |
| 386 | for ( $day = clone $date_from; $day->format( 'Y-m-d' ) <= $date_to->format( 'Y-m-d' ); $day->modify( '+1 day' ) ) { |
| 387 | $grouped_booked_periods[ $day->format( 'Y-m-d' ) ] = []; |
| 388 | } |
| 389 | foreach ( $booked_periods as $booked_period ) { |
| 390 | $grouped_booked_periods[ $booked_period->start_date ][] = $booked_period; |
| 391 | // if event spans multiple days - add to other days as well |
| 392 | if ( $booked_period->end_date && ( $booked_period->start_date != $booked_period->end_date ) ) { |
| 393 | $grouped_booked_periods[ $booked_period->end_date ][] = $booked_period; |
| 394 | } |
| 395 | } |
| 396 | } |
| 397 | |
| 398 | return $grouped_booked_periods; |
| 399 | } |
| 400 | |
| 401 | /** |
| 402 | * @param \LatePoint\Misc\Filter $filter |
| 403 | * |
| 404 | * @return \LatePoint\Misc\BookedPeriod[] |
| 405 | */ |
| 406 | public static function get_booked_periods( \LatePoint\Misc\Filter $filter ): array { |
| 407 | |
| 408 | |
| 409 | $bookings = self::get_bookings( $filter, true ); |
| 410 | $booked_periods = []; |
| 411 | |
| 412 | foreach ( $bookings as $booking ) { |
| 413 | $booked_periods[] = \LatePoint\Misc\BookedPeriod::create_from_booking_model( $booking ); |
| 414 | } |
| 415 | |
| 416 | |
| 417 | if ( $filter->consider_cart_items ) { |
| 418 | $cart = OsCartsHelper::get_or_create_cart(); |
| 419 | $bookings_in_cart = $cart->get_bookings_from_cart_items(); |
| 420 | |
| 421 | foreach ( $bookings_in_cart as $cart_booking ) { |
| 422 | $booked_periods[] = \LatePoint\Misc\BookedPeriod::create_from_booking_model( $cart_booking ); |
| 423 | } |
| 424 | } |
| 425 | |
| 426 | // TODO Update all filters to accept new "filter" variable (In Google Calendar addon) |
| 427 | $booked_periods = apply_filters( 'latepoint_get_booked_periods', $booked_periods, $filter ); |
| 428 | |
| 429 | return $booked_periods; |
| 430 | } |
| 431 | |
| 432 | |
| 433 | /** |
| 434 | * @param \LatePoint\Misc\Filter $filter |
| 435 | * @param bool $as_models |
| 436 | * |
| 437 | * @return array |
| 438 | */ |
| 439 | public static function get_bookings( \LatePoint\Misc\Filter $filter, bool $as_models = false ): array { |
| 440 | $bookings = new OsBookingModel(); |
| 441 | if ( $filter->date_from ) { |
| 442 | if ( $filter->date_from && $filter->date_to ) { |
| 443 | # both start and end date provided - means it's a range |
| 444 | $bookings->where( |
| 445 | [ |
| 446 | 'start_date >=' => $filter->date_from, |
| 447 | 'start_date <=' => $filter->date_to, |
| 448 | ] |
| 449 | ); |
| 450 | } else { |
| 451 | # only start_date provided - means it's a specific date requested |
| 452 | $bookings->where( [ 'start_date' => $filter->date_from ] ); |
| 453 | } |
| 454 | } |
| 455 | |
| 456 | |
| 457 | if ( $filter->connections ) { |
| 458 | $connection_conditions = []; |
| 459 | foreach ( $filter->connections as $connection ) { |
| 460 | $connection_conditions[] = [ |
| 461 | 'AND' => |
| 462 | [ |
| 463 | 'agent_id' => $connection->agent_id, |
| 464 | 'service_id' => $connection->service_id, |
| 465 | 'location_id' => $connection->location_id, |
| 466 | ], |
| 467 | ]; |
| 468 | } |
| 469 | $bookings->where( [ 'OR' => $connection_conditions ] ); |
| 470 | } else { |
| 471 | if ( $filter->agent_id ) { |
| 472 | $bookings->where( [ 'agent_id' => $filter->agent_id ] ); |
| 473 | } |
| 474 | if ( $filter->location_id ) { |
| 475 | $bookings->where( [ 'location_id' => $filter->location_id ] ); |
| 476 | } |
| 477 | if ( $filter->service_id ) { |
| 478 | $bookings->where( [ 'service_id' => $filter->service_id ] ); |
| 479 | } |
| 480 | } |
| 481 | if ( $filter->statuses ) { |
| 482 | $bookings->where( [ 'status' => $filter->statuses ] ); |
| 483 | } |
| 484 | if ( $filter->exclude_booking_ids ) { |
| 485 | $bookings->where( [ 'id NOT IN' => $filter->exclude_booking_ids ] ); |
| 486 | } |
| 487 | $bookings->order_by( 'start_time asc, end_time asc, service_id asc' ); |
| 488 | $bookings = ( $as_models ) ? $bookings->get_results_as_models() : $bookings->get_results(); |
| 489 | |
| 490 | // make sure to return empty array if nothing is found |
| 491 | if ( empty( $bookings ) ) { |
| 492 | $bookings = []; |
| 493 | } |
| 494 | |
| 495 | return $bookings; |
| 496 | } |
| 497 | |
| 498 | public static function generate_ical_event_string( $booking ) { |
| 499 | // translators: %1$s is agent name, %2$s is service name |
| 500 | $booking_description = sprintf( __( 'Appointment with %1$s for %2$s', 'latepoint' ), $booking->agent->full_name, $booking->service->name ); |
| 501 | |
| 502 | $ics = new ICS( |
| 503 | array( |
| 504 | 'location' => $booking->location->full_address, |
| 505 | 'description' => '', |
| 506 | 'dtstart' => $booking->format_start_date_and_time_for_google(), |
| 507 | 'dtend' => $booking->format_end_date_and_time_for_google(), |
| 508 | 'summary' => $booking_description, |
| 509 | 'url' => get_site_url(), |
| 510 | ) |
| 511 | ); |
| 512 | |
| 513 | return $ics->to_string(); |
| 514 | } |
| 515 | |
| 516 | /** |
| 517 | * @param \LatePoint\Misc\BookingRequest $booking_request |
| 518 | * |
| 519 | * @return bool |
| 520 | * |
| 521 | * Checks if requested booking slot is available, loads work periods and booked periods from database and checks availability against them |
| 522 | */ |
| 523 | public static function is_booking_request_available( \LatePoint\Misc\BookingRequest $booking_request, $settings = [] ): bool { |
| 524 | try { |
| 525 | $requested_date = new OsWpDateTime( $booking_request->start_date ); |
| 526 | } catch ( Exception $e ) { |
| 527 | return false; |
| 528 | } |
| 529 | $resources = OsResourceHelper::get_resources_grouped_by_day( $booking_request, $requested_date, $requested_date, $settings ); |
| 530 | if ( empty( $resources[ $requested_date->format( 'Y-m-d' ) ] ) ) { |
| 531 | return false; |
| 532 | } |
| 533 | $is_available = false; |
| 534 | |
| 535 | |
| 536 | // check if satisfies earliest and latest bookings - check per-service settings first, then global |
| 537 | $earliest_possible_booking = OsSettingsHelper::get_earliest_possible_booking_restriction( $booking_request->service_id ); |
| 538 | $latest_possible_booking = OsSettingsHelper::get_latest_possible_booking_restriction( $booking_request->service_id ); |
| 539 | |
| 540 | |
| 541 | if ( $earliest_possible_booking || $latest_possible_booking ) { |
| 542 | // check earliest |
| 543 | if ( ! empty( $earliest_possible_booking ) ) { |
| 544 | try { |
| 545 | $earliest_possible_booking_date = new OsWpDateTime( $earliest_possible_booking ); |
| 546 | if ( $earliest_possible_booking_date > $booking_request->get_start_datetime() ) { |
| 547 | return false; |
| 548 | } |
| 549 | } catch ( Exception $e ) { |
| 550 | |
| 551 | } |
| 552 | } |
| 553 | if ( ! empty( $latest_possible_booking ) ) { |
| 554 | // check latest |
| 555 | try { |
| 556 | $latest_possible_booking_date = new OsWpDateTime( $latest_possible_booking ); |
| 557 | if ( $latest_possible_booking_date < $booking_request->get_start_datetime() ) { |
| 558 | return false; |
| 559 | } |
| 560 | } catch ( Exception $e ) { |
| 561 | |
| 562 | } |
| 563 | } |
| 564 | } |
| 565 | |
| 566 | foreach ( $resources[ $requested_date->format( 'Y-m-d' ) ] as $resource ) { |
| 567 | foreach ( $resource->slots as $slot ) { |
| 568 | if ( $slot->start_time == $booking_request->start_time && $slot->can_accomodate( $booking_request->total_attendees ) ) { |
| 569 | $is_available = true; |
| 570 | } |
| 571 | if ( $is_available ) { |
| 572 | break; |
| 573 | } |
| 574 | } |
| 575 | if ( $is_available ) { |
| 576 | break; |
| 577 | } |
| 578 | } |
| 579 | |
| 580 | return $is_available; |
| 581 | } |
| 582 | |
| 583 | /** |
| 584 | * |
| 585 | * Checks if two bookings are part of the same group appointment |
| 586 | * |
| 587 | * @param bool|OsBookingModel $booking |
| 588 | * @param bool|OsBookingModel $compare_booking |
| 589 | * |
| 590 | * @return bool |
| 591 | */ |
| 592 | public static function check_if_group_bookings( $booking, $compare_booking ): bool { |
| 593 | if ( $booking && $compare_booking && ( $compare_booking->start_time == $booking->start_time ) && ( $compare_booking->end_time == $booking->end_time ) && ( $compare_booking->service_id == $booking->service_id ) && ( $compare_booking->location_id == $booking->location_id ) ) { |
| 594 | return true; |
| 595 | } else { |
| 596 | return false; |
| 597 | } |
| 598 | } |
| 599 | |
| 600 | |
| 601 | public static function process_actions_after_save( $booking_id ) { |
| 602 | } |
| 603 | |
| 604 | /** |
| 605 | * @param DateTime $start_date |
| 606 | * @param DateTime $end_date |
| 607 | * @param \LatePoint\Misc\BookingRequest $booking_request |
| 608 | * @param \LatePoint\Misc\BookingResource[] $resources |
| 609 | * @param array $settings |
| 610 | * |
| 611 | * @return string |
| 612 | * @throws Exception |
| 613 | */ |
| 614 | public static function get_quick_availability_days( DateTime $start_date, DateTime $end_date, \LatePoint\Misc\BookingRequest $booking_request, array $resources = [], array $settings = [] ) { |
| 615 | $default_settings = [ |
| 616 | 'work_boundaries' => false, |
| 617 | 'exclude_booking_ids' => [], |
| 618 | ]; |
| 619 | $settings = array_merge( $default_settings, $settings ); |
| 620 | |
| 621 | $html = ''; |
| 622 | |
| 623 | if ( ! $resources ) { |
| 624 | $resources = OsResourceHelper::get_resources_grouped_by_day( $booking_request, $start_date, $end_date, $settings ); |
| 625 | } |
| 626 | if ( ! $settings['work_boundaries'] ) { |
| 627 | $settings['work_boundaries'] = OsResourceHelper::get_work_boundaries_for_groups_of_resources( $resources ); |
| 628 | } |
| 629 | |
| 630 | if ( $start_date->format( 'j' ) != '1' ) { |
| 631 | $html .= '<div class="ma-month-label">' . OsUtilHelper::get_month_name_by_number( $start_date->format( 'n' ) ) . '</div>'; |
| 632 | } |
| 633 | |
| 634 | for ( $day_date = clone $start_date; $day_date <= $end_date; $day_date->modify( '+1 day' ) ) { |
| 635 | // first day of month, output month name |
| 636 | if ( $day_date->format( 'j' ) == '1' ) { |
| 637 | $html .= '<div class="ma-month-label">' . OsUtilHelper::get_month_name_by_number( $day_date->format( 'n' ) ) . '</div>'; |
| 638 | } |
| 639 | $html .= '<div class="ma-day ma-day-number-' . $day_date->format( 'N' ) . '">'; |
| 640 | $html .= '<div class="ma-day-info">'; |
| 641 | $html .= '<span class="ma-day-number">' . $day_date->format( 'j' ) . '</span>'; |
| 642 | $html .= '<span class="ma-day-weekday">' . OsUtilHelper::get_weekday_name_by_number( $day_date->format( 'N' ), true ) . '</span>'; |
| 643 | $html .= '</div>'; |
| 644 | $html .= OsTimelineHelper::availability_timeline( $booking_request, $settings['work_boundaries'], $resources[ $day_date->format( 'Y-m-d' ) ], [ 'book_on_click' => false ] ); |
| 645 | $html .= '</div>'; |
| 646 | } |
| 647 | |
| 648 | return $html; |
| 649 | } |
| 650 | |
| 651 | public static function count_pending_bookings() { |
| 652 | $bookings = new OsBookingModel(); |
| 653 | |
| 654 | return $bookings->filter_allowed_records()->where( [ 'status IN' => OsBookingHelper::get_booking_statuses_for_pending_page() ] )->count(); |
| 655 | } |
| 656 | |
| 657 | |
| 658 | public static function generate_bundles_folder(): void { |
| 659 | $bundles_model = new OsBundleModel(); |
| 660 | $bundles = $bundles_model->should_be_active()->should_not_be_hidden()->get_results_as_models(); |
| 661 | |
| 662 | if ( $bundles ) { |
| 663 | ?> |
| 664 | <div class="os-item-category-w os-items os-as-rows os-animated-child"> |
| 665 | <div class="os-item-category-info-w os-item os-animated-self with-plus"> |
| 666 | <div class="os-item-category-info os-item-i" tabindex="0"> |
| 667 | <div class="os-item-img-w"><i class="latepoint-icon latepoint-icon-shopping-bag"></i></div> |
| 668 | <div class="os-item-name-w"> |
| 669 | <div class="os-item-name"><?php echo esc_html__( 'Bundle & Save', 'latepoint' ); ?></div> |
| 670 | </div> |
| 671 | <?php if ( OsSettingsHelper::is_on( 'show_service_categories_count' ) && count( $bundles ) ) { ?> |
| 672 | <div class="os-item-child-count"> |
| 673 | <span><?php echo count( $bundles ); ?></span> <?php esc_html_e( 'Bundles', 'latepoint' ); ?> |
| 674 | </div> |
| 675 | <?php } ?> |
| 676 | </div> |
| 677 | </div> |
| 678 | <div class="os-bundles os-animated-parent os-items os-as-rows os-selectable-items"> |
| 679 | <?php |
| 680 | foreach ( $bundles as $bundle ) { ?> |
| 681 | <div class="os-animated-child os-item os-selectable-item <?php echo ( $bundle->charge_amount ) ? 'os-priced-item' : ''; ?> <?php if ( $bundle->short_description ) { |
| 682 | echo 'with-description'; } ?>" |
| 683 | tabindex="0" |
| 684 | data-item-price="<?php echo esc_attr( $bundle->charge_amount ); ?>" |
| 685 | data-priced-item-type="bundle" |
| 686 | data-summary-field-name="bundle" |
| 687 | data-summary-value="<?php echo esc_attr( $bundle->name ); ?>" |
| 688 | data-item-id="<?php echo esc_attr( $bundle->id ); ?>" |
| 689 | data-cart-item-item-data-key="bundle_id" |
| 690 | data-os-call-func="latepoint_bundle_selected"> |
| 691 | <div class="os-service-selector os-item-i os-animated-self" |
| 692 | data-bundle-id="<?php echo esc_attr( $bundle->id ); ?>"> |
| 693 | <span class="os-item-img-w"><i class="latepoint-icon latepoint-icon-shopping-bag"></i></span> |
| 694 | <span class="os-item-name-w"> |
| 695 | <span class="os-item-name"><?php echo esc_html( $bundle->name ); ?></span> |
| 696 | <?php if ( $bundle->short_description ) { ?> |
| 697 | <span class="os-item-desc"><?php echo wp_kses_post( $bundle->short_description ); ?></span> |
| 698 | <?php } ?> |
| 699 | </span> |
| 700 | |
| 701 | <?php if ( $bundle->charge_amount > 0 ) { ?> |
| 702 | <span class="os-item-price-w"> |
| 703 | <span class="os-item-price"> |
| 704 | <?php echo esc_html( OsMoneyHelper::format_price( $bundle->charge_amount ) ); ?> |
| 705 | </span> |
| 706 | </span> |
| 707 | <?php } ?> |
| 708 | </div> |
| 709 | </div> |
| 710 | <?php |
| 711 | } |
| 712 | ?> |
| 713 | </div> |
| 714 | </div> |
| 715 | <?php |
| 716 | } |
| 717 | } |
| 718 | |
| 719 | public static function generate_services_list( $services = false, $preselected_service = false ) { |
| 720 | if ( $services && is_array( $services ) && ! empty( $services ) ) { ?> |
| 721 | <div class="os-services os-animated-parent os-items os-as-rows os-selectable-items"> |
| 722 | <?php foreach ( $services as $service ) { |
| 723 | // if service is preselected - only output that service, skip the rest |
| 724 | if ( $preselected_service && $service->id != $preselected_service->id ) { |
| 725 | continue; |
| 726 | } |
| 727 | $service_durations = $service->get_all_durations_arr(); |
| 728 | $is_priced = ( ! ( count( $service_durations ) > 1 ) && $service->charge_amount ) ? true : false; |
| 729 | ?> |
| 730 | <div class="os-animated-child os-item os-selectable-item <?php echo ( $preselected_service && $service->id == $preselected_service->id ) ? 'selected is-preselected' : ''; ?> <?php echo ( $is_priced ) ? 'os-priced-item' : ''; ?> <?php if ( $service->short_description ) { |
| 731 | echo 'with-description'; } ?>" |
| 732 | tabindex="0" |
| 733 | data-item-price="<?php echo esc_attr( $service->charge_amount ); ?>" |
| 734 | data-priced-item-type="service" |
| 735 | data-summary-field-name="service" |
| 736 | data-summary-value="<?php echo esc_attr( $service->name ); ?>" |
| 737 | data-item-id="<?php echo esc_attr( $service->id ); ?>" |
| 738 | data-cart-item-item-data-key="service_id" |
| 739 | data-os-call-func="latepoint_service_selected" |
| 740 | data-id-holder=".latepoint_service_id"> |
| 741 | <div class="os-service-selector os-item-i os-animated-self" data-service-id="<?php echo esc_attr( $service->id ); ?>"> |
| 742 | <?php if ( $service->selection_image_id ) { ?> |
| 743 | <span class="os-item-img-w" style="background-image: url(<?php echo esc_url( $service->selection_image_url ); ?>);"></span> |
| 744 | <?php } ?> |
| 745 | <span class="os-item-name-w"> |
| 746 | <span class="os-item-name"><?php echo esc_html( $service->name ); ?></span> |
| 747 | <?php if ( $service->short_description ) { ?> |
| 748 | <span class="os-item-desc"><?php echo wp_kses_post( $service->short_description ); ?></span> |
| 749 | <?php } ?> |
| 750 | </span> |
| 751 | <?php if ( $service->price_min > 0 ) { ?> |
| 752 | <span class="os-item-price-w"> |
| 753 | <span class="os-item-price"> |
| 754 | <?php |
| 755 | /** |
| 756 | * Filters the display price value shown on the service tile on a booking form |
| 757 | * |
| 758 | * @since 5.1.94 |
| 759 | * @hook latepoint_booking_form_display_service_price |
| 760 | * |
| 761 | * @param {string} $price displayed price that will be outputted |
| 762 | * @param {OsServiceModel} $service Service that the price is displayed for |
| 763 | * |
| 764 | * @returns {string} Filtered displayed price |
| 765 | */ |
| 766 | $display_price = apply_filters( 'latepoint_booking_form_display_service_price', $service->price_min_formatted, $service ); |
| 767 | echo esc_html( $display_price ) ?> |
| 768 | </span> |
| 769 | <?php if ( $service->price_min != $service->price_max ) { ?> |
| 770 | <span class="os-item-price-label"><?php esc_html_e( 'Starts From', 'latepoint' ); ?></span> |
| 771 | <?php } ?> |
| 772 | </span> |
| 773 | <?php } ?> |
| 774 | </div> |
| 775 | </div> |
| 776 | <?php } ?> |
| 777 | </div> |
| 778 | <?php } |
| 779 | } |
| 780 | |
| 781 | public static function generate_services_bundles_and_categories_list( $parent_id = false, array $settings = [] ) { |
| 782 | $default_settings = [ |
| 783 | 'show_service_categories_arr' => false, |
| 784 | 'show_services_arr' => false, |
| 785 | 'preselected_service' => false, |
| 786 | 'preselected_category' => false, |
| 787 | ]; |
| 788 | $settings = array_merge( $default_settings, $settings ); |
| 789 | |
| 790 | if ( $settings['preselected_service'] ) { |
| 791 | OsBookingHelper::generate_services_list( [ $settings['preselected_service'] ], $settings['preselected_service'] ); |
| 792 | |
| 793 | return; |
| 794 | } |
| 795 | |
| 796 | $service_categories = new OsServiceCategoryModel(); |
| 797 | $args = array(); |
| 798 | if ( $settings['show_service_categories_arr'] && is_array( $settings['show_service_categories_arr'] ) ) { |
| 799 | if ( $parent_id ) { |
| 800 | $service_categories->where( [ 'parent_id' => $parent_id ] ); |
| 801 | } else { |
| 802 | if ( $settings['preselected_category'] ) { |
| 803 | $service_categories->where( [ 'id' => $settings['preselected_category'] ] ); |
| 804 | } else { |
| 805 | $service_categories->where_in( 'id', $settings['show_service_categories_arr'] ); |
| 806 | $service_categories->where( |
| 807 | [ |
| 808 | 'parent_id' => [ |
| 809 | 'OR' => [ |
| 810 | 'IS NULL', |
| 811 | ' NOT IN' => $settings['show_service_categories_arr'], |
| 812 | ], |
| 813 | ], |
| 814 | ] |
| 815 | ); |
| 816 | } |
| 817 | } |
| 818 | } else { |
| 819 | if ( $settings['preselected_category'] ) { |
| 820 | $service_categories->where( [ 'id' => $settings['preselected_category'] ] ); |
| 821 | } else { |
| 822 | $args['parent_id'] = $parent_id ? $parent_id : 'IS NULL'; |
| 823 | } |
| 824 | } |
| 825 | $service_categories = $service_categories->where( $args )->order_by( 'order_number asc' )->get_results_as_models(); |
| 826 | |
| 827 | $main_parent_class = ( $parent_id ) ? 'os-animated-parent' : 'os-item-categories-main-parent os-animated-parent'; |
| 828 | if ( ! $settings['preselected_category'] ) { |
| 829 | echo '<div class="os-item-categories-holder ' . esc_attr( $main_parent_class ) . '">'; |
| 830 | } |
| 831 | |
| 832 | // generate services that have no category |
| 833 | if ( $parent_id == false && $settings['preselected_category'] == false ) { ?> |
| 834 | <?php |
| 835 | $services_without_category = new OsServiceModel(); |
| 836 | if ( $settings['show_services_arr'] ) { |
| 837 | $services_without_category->where_in( 'id', $settings['show_services_arr'] ); |
| 838 | } |
| 839 | $services_without_category = $services_without_category->where( [ 'category_id' => 0 ] )->should_be_active()->get_results_as_models(); |
| 840 | if ( $services_without_category ) { |
| 841 | OsBookingHelper::generate_services_list( $services_without_category, false ); |
| 842 | } |
| 843 | } |
| 844 | |
| 845 | if ( is_array( $service_categories ) ) { |
| 846 | foreach ( $service_categories as $service_category ) { ?> |
| 847 | <?php |
| 848 | $services = []; |
| 849 | $category_services = $service_category->get_active_services(); |
| 850 | if ( is_array( $category_services ) ) { |
| 851 | // if show selected services restriction is set - filter |
| 852 | if ( $settings['show_services_arr'] ) { |
| 853 | foreach ( $category_services as $category_service ) { |
| 854 | if ( in_array( $category_service->id, $settings['show_services_arr'] ) ) { |
| 855 | $services[] = $category_service; |
| 856 | } |
| 857 | } |
| 858 | } else { |
| 859 | $services = $category_services; |
| 860 | } |
| 861 | } |
| 862 | $child_categories = new OsServiceCategoryModel(); |
| 863 | $count_child_categories = $child_categories->where( [ 'parent_id' => $service_category->id ] )->count(); |
| 864 | // show only if it has either at least one child category or service |
| 865 | if ( $count_child_categories || count( $services ) ) { |
| 866 | // preselected category, just show contents, not the wrapper |
| 867 | if ( $service_category->id == $settings['preselected_category'] ) { |
| 868 | OsBookingHelper::generate_services_list( $services, false ); |
| 869 | OsBookingHelper::generate_services_bundles_and_categories_list( $service_category->id, array_merge( $settings, [ 'preselected_category' => false ] ) ); |
| 870 | } else { ?> |
| 871 | <div class="os-item-category-w os-items os-as-rows os-animated-child" |
| 872 | data-id="<?php echo esc_attr( $service_category->id ); ?>"> |
| 873 | <div class="os-item-category-info-w os-item os-animated-self with-plus"> |
| 874 | <div class="os-item-category-info os-item-i"> |
| 875 | <div class="os-item-img-w" |
| 876 | style="background-image: url(<?php echo esc_url( $service_category->selection_image_url ); ?>);"></div> |
| 877 | <div class="os-item-name-w"> |
| 878 | <div class="os-item-name"><?php echo esc_html( $service_category->name ); ?></div> |
| 879 | <?php if ( ! empty( $service_category->short_description ) ) { ?> |
| 880 | <div class="os-item-desc"><?php echo $service_category->short_description; ?></div> |
| 881 | <?php } ?> |
| 882 | </div> |
| 883 | <?php if ( OsSettingsHelper::is_on( 'show_service_categories_count' ) && count( $services ) ) { ?> |
| 884 | <div class="os-item-child-count"> |
| 885 | <span><?php echo count( $services ); ?></span> <?php esc_html_e( 'Services', 'latepoint' ); ?> |
| 886 | </div> |
| 887 | <?php } ?> |
| 888 | </div> |
| 889 | </div> |
| 890 | <?php OsBookingHelper::generate_services_list( $services, false ); ?> |
| 891 | <?php OsBookingHelper::generate_services_bundles_and_categories_list( $service_category->id, array_merge( $settings, [ 'preselected_category' => false ] ) ); ?> |
| 892 | </div><?php |
| 893 | } |
| 894 | } |
| 895 | } |
| 896 | } |
| 897 | if ( ! $settings['preselected_category'] && ! $parent_id ) { |
| 898 | OsBookingHelper::generate_bundles_folder(); |
| 899 | } |
| 900 | if ( ! $settings['preselected_category'] ) { |
| 901 | echo '</div>'; |
| 902 | } |
| 903 | } |
| 904 | |
| 905 | public static function group_booking_btn_html( $booking_id = false ) { |
| 906 | $html = 'data-os-params="' . esc_attr( http_build_query( [ 'booking_id' => $booking_id ] ) ) . '" |
| 907 | data-os-action="' . esc_attr( OsRouterHelper::build_route_name( 'bookings', 'grouped_bookings_quick_view' ) ) . '" |
| 908 | data-os-output-target="lightbox" |
| 909 | data-os-lightbox-classes="width-500" |
| 910 | data-os-after-call="latepoint_init_grouped_bookings_form"'; |
| 911 | |
| 912 | return $html; |
| 913 | } |
| 914 | |
| 915 | public static function quick_booking_btn_html( $booking_id = false, $params = array() ) { |
| 916 | $html = ''; |
| 917 | if ( $booking_id ) { |
| 918 | $params['booking_id'] = $booking_id; |
| 919 | } |
| 920 | $route = OsRouterHelper::build_route_name( 'orders', 'quick_edit' ); |
| 921 | |
| 922 | $params_str = http_build_query( $params ); |
| 923 | $html = 'data-os-params="' . esc_attr( $params_str ) . '" |
| 924 | data-os-action="' . esc_attr( $route ) . '" |
| 925 | data-os-output-target="side-panel" |
| 926 | data-os-after-call="latepoint_init_quick_order_form"'; |
| 927 | |
| 928 | return $html; |
| 929 | } |
| 930 | |
| 931 | |
| 932 | /** |
| 933 | * @param OsBookingModel $booking |
| 934 | * |
| 935 | * @return false|mixed |
| 936 | * |
| 937 | * Search for available location based on booking requirements. Will return false if no available location found. |
| 938 | */ |
| 939 | public static function get_any_location_for_booking_by_rule( OsBookingModel $booking ) { |
| 940 | // ANY LOCATION SELECTED |
| 941 | // get available locations |
| 942 | $connected_ids = OsLocationHelper::get_location_ids_for_service_and_agent( $booking->service_id, $booking->agent_id ); |
| 943 | |
| 944 | // If date/time is selected - filter locations who are available at that time |
| 945 | if ( $booking->start_date && $booking->start_time ) { |
| 946 | $available_location_ids = []; |
| 947 | $booking_request = \LatePoint\Misc\BookingRequest::create_from_booking_model( $booking ); |
| 948 | foreach ( $connected_ids as $location_id ) { |
| 949 | $booking_request->location_id = $location_id; |
| 950 | if ( OsBookingHelper::is_booking_request_available( $booking_request ) ) { |
| 951 | $available_location_ids[] = $location_id; |
| 952 | } |
| 953 | } |
| 954 | $connected_ids = array_intersect( $available_location_ids, $connected_ids ); |
| 955 | } |
| 956 | |
| 957 | |
| 958 | $locations_model = new OsLocationModel(); |
| 959 | if ( ! empty( $connected_ids ) ) { |
| 960 | $locations_model->where_in( 'id', $connected_ids ); |
| 961 | $locations = $locations_model->should_be_active()->get_results_as_models(); |
| 962 | } else { |
| 963 | $locations = []; |
| 964 | } |
| 965 | |
| 966 | if ( empty( $locations ) ) { |
| 967 | return false; |
| 968 | } |
| 969 | |
| 970 | $selected_location_id = $connected_ids[ wp_rand( 0, count( $connected_ids ) - 1 ) ]; |
| 971 | $booking->location_id = $selected_location_id; |
| 972 | |
| 973 | return $selected_location_id; |
| 974 | } |
| 975 | |
| 976 | /** |
| 977 | * @param OsBookingModel $booking |
| 978 | * |
| 979 | * @return false|mixed |
| 980 | * |
| 981 | * Search for available agent based on booking requirements and agent picking preferences. Will return false if no available agent found. |
| 982 | */ |
| 983 | public static function get_any_agent_for_booking_by_rule( OsBookingModel $booking ) { |
| 984 | // ANY AGENT SELECTED |
| 985 | // get available agents |
| 986 | $connected_ids = OsAgentHelper::get_agent_ids_for_service_and_location( $booking->service_id, $booking->location_id ); |
| 987 | |
| 988 | // If date/time is selected - filter agents who are available at that time |
| 989 | if ( $booking->start_date && $booking->start_time ) { |
| 990 | $available_agent_ids = []; |
| 991 | $booking_request = \LatePoint\Misc\BookingRequest::create_from_booking_model( $booking ); |
| 992 | foreach ( $connected_ids as $agent_id ) { |
| 993 | $booking_request->agent_id = $agent_id; |
| 994 | if ( OsBookingHelper::is_booking_request_available( $booking_request ) ) { |
| 995 | $available_agent_ids[] = $agent_id; |
| 996 | } |
| 997 | } |
| 998 | $connected_ids = array_intersect( $available_agent_ids, $connected_ids ); |
| 999 | } |
| 1000 | |
| 1001 | |
| 1002 | /** |
| 1003 | * Get IDs of agents that are eligible to be assigned a booking that has "ANY" agent pre-selected |
| 1004 | * |
| 1005 | * @param {array} $connected_ids Array of eligible Agent IDs |
| 1006 | * @param {OsBookingModel} $booking Booking that needs agent ID |
| 1007 | * |
| 1008 | * @returns {array} Filtered array of IDs of eligible agents |
| 1009 | * @since 4.7.6 |
| 1010 | * @hook latepoint_agent_ids_assignable_to_any_agent_booking |
| 1011 | * |
| 1012 | */ |
| 1013 | $connected_ids = apply_filters( 'latepoint_agent_ids_assignable_to_any_agent_booking', $connected_ids, $booking ); |
| 1014 | |
| 1015 | if ( ! empty( $connected_ids ) ) { |
| 1016 | $agents_model = new OsAgentModel(); |
| 1017 | $agents_model->where_in( 'id', $connected_ids ); |
| 1018 | $agents = $agents_model->should_be_active()->get_results_as_models(); |
| 1019 | } else { |
| 1020 | $agents = []; |
| 1021 | } |
| 1022 | |
| 1023 | if ( empty( $agents ) ) { |
| 1024 | return false; |
| 1025 | } |
| 1026 | |
| 1027 | |
| 1028 | $selected_agent_id = false; |
| 1029 | $agent_order_rule = OsSettingsHelper::get_any_agent_order(); |
| 1030 | switch ( $agent_order_rule ) { |
| 1031 | case LATEPOINT_ANY_AGENT_ORDER_RANDOM: |
| 1032 | $selected_agent_id = $connected_ids[ wp_rand( 0, count( $connected_ids ) - 1 ) ]; |
| 1033 | break; |
| 1034 | case LATEPOINT_ANY_AGENT_ORDER_PRICE_HIGH: |
| 1035 | $highest_price = false; |
| 1036 | foreach ( $agents as $agent ) { |
| 1037 | $booking->agent_id = $agent->id; |
| 1038 | $price = OsBookingHelper::calculate_full_amount_for_booking( $booking ); |
| 1039 | if ( $highest_price === false && $selected_agent_id === false ) { |
| 1040 | $highest_price = $price; |
| 1041 | $selected_agent_id = $agent->id; |
| 1042 | } else { |
| 1043 | if ( $highest_price < $price ) { |
| 1044 | $highest_price = $price; |
| 1045 | $selected_agent_id = $agent->id; |
| 1046 | } |
| 1047 | } |
| 1048 | } |
| 1049 | break; |
| 1050 | case LATEPOINT_ANY_AGENT_ORDER_PRICE_LOW: |
| 1051 | $lowest_price = false; |
| 1052 | foreach ( $agents as $agent ) { |
| 1053 | $booking->agent_id = $agent->id; |
| 1054 | $price = OsBookingHelper::calculate_full_amount_for_booking( $booking ); |
| 1055 | if ( $lowest_price === false && $selected_agent_id === false ) { |
| 1056 | $lowest_price = $price; |
| 1057 | $selected_agent_id = $agent->id; |
| 1058 | } else { |
| 1059 | if ( $lowest_price > $price ) { |
| 1060 | $lowest_price = $price; |
| 1061 | $selected_agent_id = $agent->id; |
| 1062 | } |
| 1063 | } |
| 1064 | } |
| 1065 | break; |
| 1066 | case LATEPOINT_ANY_AGENT_ORDER_BUSY_HIGH: |
| 1067 | $max_bookings = false; |
| 1068 | foreach ( $agents as $agent ) { |
| 1069 | $agent_total_bookings = OsBookingHelper::get_total_bookings_for_date( $booking->start_date, [ 'agent_id' => $agent->id ] ); |
| 1070 | if ( $max_bookings === false && $selected_agent_id === false ) { |
| 1071 | $max_bookings = $agent_total_bookings; |
| 1072 | $selected_agent_id = $agent->id; |
| 1073 | } else { |
| 1074 | if ( $max_bookings < $agent_total_bookings ) { |
| 1075 | $max_bookings = $agent_total_bookings; |
| 1076 | $selected_agent_id = $agent->id; |
| 1077 | } |
| 1078 | } |
| 1079 | } |
| 1080 | break; |
| 1081 | case LATEPOINT_ANY_AGENT_ORDER_BUSY_LOW: |
| 1082 | $min_bookings = false; |
| 1083 | foreach ( $agents as $agent ) { |
| 1084 | $agent_total_bookings = OsBookingHelper::get_total_bookings_for_date( $booking->start_date, [ 'agent_id' => $agent->id ] ); |
| 1085 | if ( $min_bookings === false && $selected_agent_id === false ) { |
| 1086 | $min_bookings = $agent_total_bookings; |
| 1087 | $selected_agent_id = $agent->id; |
| 1088 | } else { |
| 1089 | if ( $min_bookings > $agent_total_bookings ) { |
| 1090 | $min_bookings = $agent_total_bookings; |
| 1091 | $selected_agent_id = $agent->id; |
| 1092 | } |
| 1093 | } |
| 1094 | } |
| 1095 | break; |
| 1096 | } |
| 1097 | /** |
| 1098 | * Get ID of agent that will be assigned to a booking, depending on order rules, where agent is set to ANY |
| 1099 | * |
| 1100 | * @param {integer} $selected_agent_id Currently selected agent ID |
| 1101 | * @param {OsAgentModel[]} $agents Array of eligible agent models to pick from |
| 1102 | * @param {OsBookingModel} $booking Booking that needs agent ID |
| 1103 | * @param {string} $agent_order_rule Rule of agent ordering |
| 1104 | * |
| 1105 | * @returns {integer} ID of the agent that will be assigned to this booking |
| 1106 | * @since 4.7.6 |
| 1107 | * @hook latepoint_get_any_agent_id_for_booking_by_rule |
| 1108 | * |
| 1109 | */ |
| 1110 | $selected_agent_id = apply_filters( 'latepoint_get_any_agent_id_for_booking_by_rule', $selected_agent_id, $agents, $booking, $agent_order_rule ); |
| 1111 | $booking->agent_id = $selected_agent_id; |
| 1112 | |
| 1113 | return $selected_agent_id; |
| 1114 | } |
| 1115 | |
| 1116 | |
| 1117 | public static function get_total_bookings_for_date( $date, $conditions = [], $grouped = false ) { |
| 1118 | $args = [ 'start_date' => $date ]; |
| 1119 | if ( isset( $conditions['agent_id'] ) && $conditions['agent_id'] ) { |
| 1120 | $args['agent_id'] = $conditions['agent_id']; |
| 1121 | } |
| 1122 | if ( isset( $conditions['service_id'] ) && $conditions['service_id'] ) { |
| 1123 | $args['service_id'] = $conditions['service_id']; |
| 1124 | } |
| 1125 | if ( isset( $conditions['location_id'] ) && $conditions['location_id'] ) { |
| 1126 | $args['location_id'] = $conditions['location_id']; |
| 1127 | } |
| 1128 | |
| 1129 | |
| 1130 | $bookings = new OsBookingModel(); |
| 1131 | if ( $grouped ) { |
| 1132 | $bookings->group_by( 'start_date, start_time, end_time, service_id, location_id' ); |
| 1133 | } |
| 1134 | $bookings = $bookings->where( $args ); |
| 1135 | |
| 1136 | return $bookings->count(); |
| 1137 | } |
| 1138 | |
| 1139 | |
| 1140 | /** |
| 1141 | * |
| 1142 | * Get list of statuses that block timeslot availability |
| 1143 | * |
| 1144 | * @return array |
| 1145 | */ |
| 1146 | public static function get_timeslot_blocking_statuses(): array { |
| 1147 | $statuses = explode( ',', OsSettingsHelper::get_settings_value( 'timeslot_blocking_statuses', '' ) ); |
| 1148 | |
| 1149 | /** |
| 1150 | * Get list of statuses that block timeslot availability |
| 1151 | * |
| 1152 | * @param {array} $statuses array of status codes that block timeslot availability |
| 1153 | * @returns {array} The filtered array of status codes |
| 1154 | * |
| 1155 | * @since 4.7.0 |
| 1156 | * @hook latepoint_get_timeslot_blocking_statuses |
| 1157 | * |
| 1158 | */ |
| 1159 | return apply_filters( 'latepoint_get_timeslot_blocking_statuses', $statuses ); |
| 1160 | } |
| 1161 | |
| 1162 | |
| 1163 | /** |
| 1164 | * |
| 1165 | * Get list of statuses that appear on pending page |
| 1166 | * |
| 1167 | * @return array |
| 1168 | */ |
| 1169 | public static function get_booking_statuses_for_pending_page(): array { |
| 1170 | $statuses = explode( ',', OsSettingsHelper::get_settings_value( 'need_action_statuses', '' ) ); |
| 1171 | |
| 1172 | /** |
| 1173 | * Get list of statuses that appear on pending page |
| 1174 | * |
| 1175 | * @param {array} $statuses array of status codes that appear on pending page |
| 1176 | * @returns {array} The filtered array of status codes |
| 1177 | * |
| 1178 | * @since 4.7.0 |
| 1179 | * @hook latepoint_get_booking_statuses_for_pending_page |
| 1180 | * |
| 1181 | */ |
| 1182 | return apply_filters( 'latepoint_get_booking_statuses_for_pending_page', $statuses ); |
| 1183 | } |
| 1184 | |
| 1185 | /** |
| 1186 | * |
| 1187 | * Get list of statuses that are not cancelled |
| 1188 | * |
| 1189 | * @return array |
| 1190 | */ |
| 1191 | public static function get_non_cancelled_booking_statuses(): array { |
| 1192 | $statuses = self::get_statuses_list(); |
| 1193 | if ( isset( $statuses[ LATEPOINT_BOOKING_STATUS_CANCELLED ] ) ) { |
| 1194 | unset( $statuses[ LATEPOINT_BOOKING_STATUS_CANCELLED ] ); |
| 1195 | } |
| 1196 | $statuses = array_keys( $statuses ); |
| 1197 | |
| 1198 | /** |
| 1199 | * Get list of statuses that are not cancelled |
| 1200 | * |
| 1201 | * @param {array} $statuses array of status codes that are not cancelled |
| 1202 | * @returns {array} The filtered array of status codes |
| 1203 | * |
| 1204 | * @since 5.0.5 |
| 1205 | * @hook get_non_cancelled_booking_statuses |
| 1206 | * |
| 1207 | */ |
| 1208 | return apply_filters( 'get_non_cancelled_booking_statuses', $statuses ); |
| 1209 | } |
| 1210 | |
| 1211 | |
| 1212 | public static function get_default_booking_status( $service_id = false ) { |
| 1213 | if ( $service_id ) { |
| 1214 | $service = new OsServiceModel( $service_id ); |
| 1215 | if ( $service && ! empty( $service->id ) ) { |
| 1216 | return $service->get_default_booking_status(); |
| 1217 | } |
| 1218 | } |
| 1219 | $default_status = OsSettingsHelper::get_settings_value( 'default_booking_status' ); |
| 1220 | if ( $default_status ) { |
| 1221 | return $default_status; |
| 1222 | } else { |
| 1223 | return LATEPOINT_BOOKING_STATUS_APPROVED; |
| 1224 | } |
| 1225 | } |
| 1226 | |
| 1227 | |
| 1228 | public static function change_booking_status( $booking_id, $new_status ) { |
| 1229 | $booking = new OsBookingModel( $booking_id ); |
| 1230 | if ( ! $booking_id || ! $booking ) { |
| 1231 | return false; |
| 1232 | } |
| 1233 | |
| 1234 | if ( $new_status == $booking->status ) { |
| 1235 | return true; |
| 1236 | } else { |
| 1237 | $old_booking = clone $booking; |
| 1238 | if ( $booking->update_status( $new_status ) ) { |
| 1239 | do_action( 'latepoint_booking_updated', $booking, $old_booking ); |
| 1240 | |
| 1241 | return true; |
| 1242 | } else { |
| 1243 | return false; |
| 1244 | } |
| 1245 | } |
| 1246 | } |
| 1247 | |
| 1248 | |
| 1249 | /** |
| 1250 | * @param \LatePoint\Misc\BookingRequest $booking_request |
| 1251 | * @param \LatePoint\Misc\BookedPeriod[] |
| 1252 | * @param int $capacity |
| 1253 | * |
| 1254 | * @return bool |
| 1255 | */ |
| 1256 | public static function is_timeframe_in_booked_periods( \LatePoint\Misc\BookingRequest $booking_request, array $booked_periods, OsServiceModel $service ): bool { |
| 1257 | if ( empty( $booked_periods ) ) { |
| 1258 | return false; |
| 1259 | } |
| 1260 | $count_existing_attendees = 0; |
| 1261 | foreach ( $booked_periods as $period ) { |
| 1262 | if ( self::is_period_overlapping( $booking_request->get_start_time_with_buffer(), $booking_request->get_end_time_with_buffer(), $period->start_time_with_buffer(), $period->end_time_with_buffer() ) ) { |
| 1263 | // if it's the same service overlapping - count how many times |
| 1264 | // TODO maybe add an option to toggle on/off ability to share a timeslot capacity between two different services |
| 1265 | if ( $booking_request->service_id == $period->service_id ) { |
| 1266 | $count_existing_attendees += $period->total_attendees; |
| 1267 | } else { |
| 1268 | return true; |
| 1269 | } |
| 1270 | } |
| 1271 | } |
| 1272 | if ( $count_existing_attendees > 0 ) { |
| 1273 | // if there are attendees, check if they are below minimum need for timeslot to be blocked, if they are - then the slot is considered booked |
| 1274 | if ( ( $count_existing_attendees + $booking_request->total_attendees ) <= $service->get_capacity_needed_before_slot_is_blocked() ) { |
| 1275 | return false; |
| 1276 | } else { |
| 1277 | return true; |
| 1278 | } |
| 1279 | } else { |
| 1280 | // no attendees in the overlapping booked periods yet, just check if the requested number of attendees is within the service capacity |
| 1281 | if ( $booking_request->total_attendees <= $service->capacity_max ) { |
| 1282 | return false; |
| 1283 | } else { |
| 1284 | return true; |
| 1285 | } |
| 1286 | } |
| 1287 | } |
| 1288 | |
| 1289 | |
| 1290 | public static function is_period_overlapping( $period_one_start, $period_one_end, $period_two_start, $period_two_end ) { |
| 1291 | // https://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap/ |
| 1292 | return ( ( $period_one_start < $period_two_end ) && ( $period_two_start < $period_one_end ) ); |
| 1293 | } |
| 1294 | |
| 1295 | public static function is_period_inside_another( $period_one_start, $period_one_end, $period_two_start, $period_two_end ) { |
| 1296 | return ( ( $period_one_start >= $period_two_start ) && ( $period_one_end <= $period_two_end ) ); |
| 1297 | } |
| 1298 | |
| 1299 | // args = [agent_id, 'service_id', 'location_id'] |
| 1300 | public static function get_bookings_for_date( $date, $args = [] ) { |
| 1301 | $bookings = new OsBookingModel(); |
| 1302 | $args['start_date'] = $date; |
| 1303 | // if any of these are false or 0 - remove it from arguments list |
| 1304 | if ( isset( $args['location_id'] ) && empty( $args['location_id'] ) ) { |
| 1305 | unset( $args['location_id'] ); |
| 1306 | } |
| 1307 | if ( isset( $args['agent_id'] ) && empty( $args['agent_id'] ) ) { |
| 1308 | unset( $args['agent_id'] ); |
| 1309 | } |
| 1310 | if ( isset( $args['service_id'] ) && empty( $args['service_id'] ) ) { |
| 1311 | unset( $args['service_id'] ); |
| 1312 | } |
| 1313 | |
| 1314 | $bookings->where( $args )->order_by( 'start_time asc, end_time asc, service_id asc' ); |
| 1315 | |
| 1316 | return $bookings->get_results_as_models(); |
| 1317 | } |
| 1318 | |
| 1319 | /** |
| 1320 | * @param \LatePoint\Misc\Filter $filter |
| 1321 | * |
| 1322 | * @return int |
| 1323 | */ |
| 1324 | public static function count_bookings( \LatePoint\Misc\Filter $filter ) { |
| 1325 | $bookings = new OsBookingModel(); |
| 1326 | $query_args = []; |
| 1327 | if ( $filter->date_from ) { |
| 1328 | $query_args['start_date'] = $filter->date_from; |
| 1329 | } |
| 1330 | if ( $filter->location_id ) { |
| 1331 | $query_args['location_id'] = $filter->location_id; |
| 1332 | } |
| 1333 | if ( $filter->agent_id ) { |
| 1334 | $query_args['agent_id'] = $filter->agent_id; |
| 1335 | } |
| 1336 | if ( $filter->service_id ) { |
| 1337 | $query_args['service_id'] = $filter->service_id; |
| 1338 | } |
| 1339 | |
| 1340 | return $bookings->should_not_be_cancelled()->where( $query_args )->count(); |
| 1341 | } |
| 1342 | |
| 1343 | |
| 1344 | public static function get_nice_status_name( $status ) { |
| 1345 | $statuses_list = OsBookingHelper::get_statuses_list(); |
| 1346 | if ( $status && isset( $statuses_list[ $status ] ) ) { |
| 1347 | return $statuses_list[ $status ]; |
| 1348 | } else { |
| 1349 | return __( 'Undefined Status', 'latepoint' ); |
| 1350 | } |
| 1351 | } |
| 1352 | |
| 1353 | |
| 1354 | public static function get_statuses_list() { |
| 1355 | $statuses = [ |
| 1356 | LATEPOINT_BOOKING_STATUS_APPROVED => __( 'Approved', 'latepoint' ), |
| 1357 | LATEPOINT_BOOKING_STATUS_PENDING => __( 'Pending Approval', 'latepoint' ), |
| 1358 | LATEPOINT_BOOKING_STATUS_CANCELLED => __( 'Cancelled', 'latepoint' ), |
| 1359 | LATEPOINT_BOOKING_STATUS_NO_SHOW => __( 'No Show', 'latepoint' ), |
| 1360 | LATEPOINT_BOOKING_STATUS_COMPLETED => __( 'Completed', 'latepoint' ), |
| 1361 | ]; |
| 1362 | $additional_statuses = array_map( 'trim', explode( ',', OsSettingsHelper::get_settings_value( 'additional_booking_statuses', '' ) ) ); |
| 1363 | if ( ! empty( $additional_statuses ) ) { |
| 1364 | foreach ( $additional_statuses as $status ) { |
| 1365 | if ( ! empty( $status ) ) { |
| 1366 | $statuses[ str_replace( ' ', '_', strtolower( $status ) ) ] = $status; |
| 1367 | } |
| 1368 | } |
| 1369 | } |
| 1370 | $statuses = apply_filters( 'latepoint_booking_statuses', $statuses ); |
| 1371 | |
| 1372 | return $statuses; |
| 1373 | } |
| 1374 | |
| 1375 | |
| 1376 | public static function get_weekdays_arr( $full_name = false ) { |
| 1377 | if ( $full_name ) { |
| 1378 | $weekdays = array( |
| 1379 | __( 'Monday', 'latepoint' ), |
| 1380 | __( 'Tuesday', 'latepoint' ), |
| 1381 | __( 'Wednesday', 'latepoint' ), |
| 1382 | __( 'Thursday', 'latepoint' ), |
| 1383 | __( 'Friday', 'latepoint' ), |
| 1384 | __( 'Saturday', 'latepoint' ), |
| 1385 | __( 'Sunday', 'latepoint' ), |
| 1386 | ); |
| 1387 | } else { |
| 1388 | $weekdays = array( |
| 1389 | __( 'Mon', 'latepoint' ), |
| 1390 | __( 'Tue', 'latepoint' ), |
| 1391 | __( 'Wed', 'latepoint' ), |
| 1392 | __( 'Thu', 'latepoint' ), |
| 1393 | __( 'Fri', 'latepoint' ), |
| 1394 | __( 'Sat', 'latepoint' ), |
| 1395 | __( 'Sun', 'latepoint' ), |
| 1396 | ); |
| 1397 | } |
| 1398 | |
| 1399 | return $weekdays; |
| 1400 | } |
| 1401 | |
| 1402 | public static function get_weekday_name_by_number( $weekday_number, $full_name = false ) { |
| 1403 | $weekdays = OsBookingHelper::get_weekdays_arr( $full_name ); |
| 1404 | if ( ! isset( $weekday_number ) || $weekday_number < 1 || $weekday_number > 7 ) { |
| 1405 | return ''; |
| 1406 | } else { |
| 1407 | return $weekdays[ $weekday_number - 1 ]; |
| 1408 | } |
| 1409 | } |
| 1410 | |
| 1411 | public static function get_stat( $stat, $args = [] ) { |
| 1412 | if ( ! in_array( $stat, [ 'duration', 'price', 'bookings' ] ) ) { |
| 1413 | return false; |
| 1414 | } |
| 1415 | $defaults = [ |
| 1416 | 'customer_id' => false, |
| 1417 | 'agent_id' => false, |
| 1418 | 'service_id' => false, |
| 1419 | 'location_id' => false, |
| 1420 | 'date_from' => false, |
| 1421 | 'date_to' => false, |
| 1422 | 'group_by' => false, |
| 1423 | 'exclude_status' => false, |
| 1424 | ]; |
| 1425 | $args = array_merge( $defaults, $args ); |
| 1426 | $bookings = new OsBookingModel(); |
| 1427 | $query_args = array( $args['date_from'], $args['date_to'] ); |
| 1428 | switch ( $stat ) { |
| 1429 | case 'duration': |
| 1430 | $stat_query = 'SUM(end_time - start_time)'; |
| 1431 | break; |
| 1432 | case 'price': |
| 1433 | $stat_query = 'sum(total)'; |
| 1434 | break; |
| 1435 | case 'bookings': |
| 1436 | $stat_query = 'count(id)'; |
| 1437 | break; |
| 1438 | } |
| 1439 | $select_query = $stat_query . ' as stat'; |
| 1440 | if ( $args['group_by'] ) { |
| 1441 | $select_query .= ',' . $args['group_by']; |
| 1442 | } |
| 1443 | $bookings->select( $select_query ); |
| 1444 | |
| 1445 | |
| 1446 | if ( $args['date_from'] ) { |
| 1447 | $bookings->where( [ 'start_date >=' => $args['date_from'] ] ); |
| 1448 | } |
| 1449 | if ( $args['date_to'] ) { |
| 1450 | $bookings->where( [ 'start_date <=' => $args['date_to'] ] ); |
| 1451 | } |
| 1452 | if ( $args['service_id'] ) { |
| 1453 | $bookings->where( [ 'service_id' => $args['service_id'] ] ); |
| 1454 | } |
| 1455 | if ( $args['agent_id'] ) { |
| 1456 | $bookings->where( [ 'agent_id' => $args['agent_id'] ] ); |
| 1457 | } |
| 1458 | if ( $args['location_id'] ) { |
| 1459 | $bookings->where( [ 'location_id' => $args['location_id'] ] ); |
| 1460 | } |
| 1461 | if ( $args['customer_id'] ) { |
| 1462 | $bookings->where( [ 'customer_id' => $args['customer_id'] ] ); |
| 1463 | } |
| 1464 | if ( $args['group_by'] ) { |
| 1465 | $bookings->group_by( $args['group_by'] ); |
| 1466 | } |
| 1467 | // TODO, need to support custom status exclusions |
| 1468 | if ( $args['exclude_status'] == LATEPOINT_BOOKING_STATUS_CANCELLED ) { |
| 1469 | $bookings->should_not_be_cancelled(); |
| 1470 | } |
| 1471 | |
| 1472 | $stat_total = $bookings->get_results( ARRAY_A ); |
| 1473 | if ( $args['group_by'] ) { |
| 1474 | return $stat_total; |
| 1475 | } else { |
| 1476 | return isset( $stat_total[0]['stat'] ) ? $stat_total[0]['stat'] : 0; |
| 1477 | } |
| 1478 | } |
| 1479 | |
| 1480 | public static function get_new_customer_stat_for_period( DateTime $date_from, DateTime $date_to, \LatePoint\Misc\Filter $filter ) { |
| 1481 | // TODO make sure filter is respected |
| 1482 | $customers = new OsCustomerModel(); |
| 1483 | |
| 1484 | return $customers->filter_allowed_records()->where( |
| 1485 | [ |
| 1486 | 'created_at >=' => $date_from->format( 'Y-m-d' ), |
| 1487 | 'created_at <=' => $date_to->format( 'Y-m-d' ), |
| 1488 | ] |
| 1489 | )->count(); |
| 1490 | } |
| 1491 | |
| 1492 | public static function get_stat_for_period( $stat, $date_from, $date_to, \LatePoint\Misc\Filter $filter, $group_by = false ) { |
| 1493 | if ( ! in_array( $stat, [ 'duration', 'price', 'bookings' ] ) ) { |
| 1494 | return false; |
| 1495 | } |
| 1496 | if ( ! in_array( $group_by, [ false, 'agent_id', 'service_id', 'location_id' ] ) ) { |
| 1497 | return false; |
| 1498 | } |
| 1499 | $bookings = new OsBookingModel(); |
| 1500 | switch ( $stat ) { |
| 1501 | case 'duration': |
| 1502 | $stat_query = 'SUM(end_time - start_time)'; |
| 1503 | break; |
| 1504 | case 'price': |
| 1505 | $stat_query = 'sum(' . LATEPOINT_TABLE_ORDER_ITEMS . '.subtotal)'; |
| 1506 | $bookings->join( LATEPOINT_TABLE_ORDER_ITEMS, [ 'id' => $bookings->table_name . '.order_item_id' ] ); |
| 1507 | $bookings->join( LATEPOINT_TABLE_ORDERS, [ 'id' => LATEPOINT_TABLE_ORDER_ITEMS . '.order_id' ] ); |
| 1508 | break; |
| 1509 | case 'bookings': |
| 1510 | $stat_query = 'count(id)'; |
| 1511 | break; |
| 1512 | } |
| 1513 | $select_query = $stat_query . ' as stat'; |
| 1514 | if ( $group_by ) { |
| 1515 | $select_query .= ',' . $group_by; |
| 1516 | } |
| 1517 | $bookings->select( $select_query )->where( |
| 1518 | [ |
| 1519 | 'start_date >=' => $date_from, |
| 1520 | 'start_date <= ' => $date_to, |
| 1521 | ] |
| 1522 | ); |
| 1523 | |
| 1524 | if ( $filter->service_id ) { |
| 1525 | $bookings->where( [ 'service_id' => $filter->service_id ] ); |
| 1526 | } |
| 1527 | if ( $filter->agent_id ) { |
| 1528 | $bookings->where( [ 'agent_id' => $filter->agent_id ] ); |
| 1529 | } |
| 1530 | if ( $filter->location_id ) { |
| 1531 | $bookings->where( [ 'location_id' => $filter->location_id ] ); |
| 1532 | } |
| 1533 | |
| 1534 | $bookings->should_not_be_cancelled(); |
| 1535 | |
| 1536 | if ( $group_by ) { |
| 1537 | $bookings->group_by( $group_by ); |
| 1538 | } |
| 1539 | |
| 1540 | $stat_total = $bookings->get_results( ARRAY_A ); |
| 1541 | if ( $group_by ) { |
| 1542 | return $stat_total; |
| 1543 | } else { |
| 1544 | return isset( $stat_total[0]['stat'] ) ? $stat_total[0]['stat'] : 0; |
| 1545 | } |
| 1546 | } |
| 1547 | |
| 1548 | public static function get_total_bookings_per_day_for_period( $date_from, $date_to, \LatePoint\Misc\Filter $filter ) { |
| 1549 | $bookings = new OsBookingModel(); |
| 1550 | $bookings->select( 'count(id) as bookings_per_day, start_date' ) |
| 1551 | ->where( |
| 1552 | [ |
| 1553 | 'start_date >=' => $date_from, |
| 1554 | 'start_date <=' => $date_to, |
| 1555 | ] |
| 1556 | ) |
| 1557 | ->where( [ 'status NOT IN' => OsCalendarHelper::get_booking_statuses_hidden_from_calendar() ] ); |
| 1558 | if ( $filter->service_id ) { |
| 1559 | $bookings->where( [ 'service_id' => $filter->service_id ] ); |
| 1560 | } |
| 1561 | if ( $filter->agent_id ) { |
| 1562 | $bookings->where( [ 'agent_id' => $filter->agent_id ] ); |
| 1563 | } |
| 1564 | if ( $filter->location_id ) { |
| 1565 | $bookings->where( [ 'location_id' => $filter->location_id ] ); |
| 1566 | } |
| 1567 | $bookings->group_by( 'start_date' ); |
| 1568 | |
| 1569 | return $bookings->get_results(); |
| 1570 | } |
| 1571 | |
| 1572 | |
| 1573 | public static function get_min_max_work_periods( $specific_weekdays = false, $service_id = false, $agent_id = false ) { |
| 1574 | $select_string = 'MIN(start_time) as start_time, MAX(end_time) as end_time'; |
| 1575 | $work_periods = new OsWorkPeriodModel(); |
| 1576 | $work_periods = $work_periods->select( $select_string ); |
| 1577 | $query_args = array( |
| 1578 | 'service_id' => 0, |
| 1579 | 'agent_id' => 0, |
| 1580 | ); |
| 1581 | if ( $service_id ) { |
| 1582 | $query_args['service_id'] = $service_id; |
| 1583 | } |
| 1584 | if ( $agent_id ) { |
| 1585 | $query_args['agent_id'] = $agent_id; |
| 1586 | } |
| 1587 | if ( $specific_weekdays && ! empty( $specific_weekdays ) ) { |
| 1588 | $query_args['week_day'] = $specific_weekdays; |
| 1589 | } |
| 1590 | $results = $work_periods->set_limit( 1 )->where( $query_args )->get_results( ARRAY_A ); |
| 1591 | if ( ( $service_id || $agent_id ) && empty( $results['min_start_time'] ) ) { |
| 1592 | if ( $service_id && empty( $results['min_start_time'] ) ) { |
| 1593 | $query_args['service_id'] = 0; |
| 1594 | $work_periods = new OsWorkPeriodModel(); |
| 1595 | $work_periods = $work_periods->select( $select_string ); |
| 1596 | $results = $work_periods->set_limit( 1 )->where( $query_args )->get_results( ARRAY_A ); |
| 1597 | } |
| 1598 | if ( $agent_id && empty( $results['min_start_time'] ) ) { |
| 1599 | $query_args['agent_id'] = 0; |
| 1600 | $work_periods = new OsWorkPeriodModel(); |
| 1601 | $work_periods = $work_periods->select( $select_string ); |
| 1602 | $results = $work_periods->set_limit( 1 )->where( $query_args )->get_results( ARRAY_A ); |
| 1603 | } |
| 1604 | } |
| 1605 | if ( $results ) { |
| 1606 | return array( $results['start_time'], $results['end_time'] ); |
| 1607 | } else { |
| 1608 | return false; |
| 1609 | } |
| 1610 | } |
| 1611 | |
| 1612 | |
| 1613 | public static function get_work_start_end_time_for_multiple_dates( $dates = false, $service_id = false, $agent_id = false ) { |
| 1614 | $specific_weekdays = array(); |
| 1615 | if ( $dates ) { |
| 1616 | foreach ( $dates as $date ) { |
| 1617 | $target_date = new OsWpDateTime( $date ); |
| 1618 | $weekday = $target_date->format( 'N' ); |
| 1619 | if ( ! in_array( $weekday, $specific_weekdays ) ) { |
| 1620 | $specific_weekdays[] = $weekday; |
| 1621 | } |
| 1622 | } |
| 1623 | } |
| 1624 | $work_minmax_start_end = self::get_min_max_work_periods( $specific_weekdays, $service_id, $agent_id ); |
| 1625 | |
| 1626 | return $work_minmax_start_end; |
| 1627 | } |
| 1628 | |
| 1629 | /** |
| 1630 | * @param int $minute |
| 1631 | * @param \LatePoint\Misc\WorkPeriod[] $work_periods_arr |
| 1632 | * |
| 1633 | * @return bool |
| 1634 | */ |
| 1635 | public static function is_minute_in_work_periods( int $minute, array $work_periods_arr ): bool { |
| 1636 | // print_r($work_periods_arr); |
| 1637 | if ( empty( $work_periods_arr ) ) { |
| 1638 | return false; |
| 1639 | } |
| 1640 | foreach ( $work_periods_arr as $work_period ) { |
| 1641 | // end of period does not count because we cant make appointment with 0 duration |
| 1642 | if ( $work_period->start_time <= $minute && $work_period->end_time > $minute ) { |
| 1643 | return true; |
| 1644 | } |
| 1645 | } |
| 1646 | |
| 1647 | return false; |
| 1648 | } |
| 1649 | |
| 1650 | public static function get_calendar_start_end_time( $bookings, $work_start_minutes, $work_end_minutes ) { |
| 1651 | $calendar_start_minutes = $work_start_minutes; |
| 1652 | $calendar_end_minutes = $work_end_minutes; |
| 1653 | if ( $bookings ) { |
| 1654 | foreach ( $bookings as $bookings_for_agent ) { |
| 1655 | if ( $bookings_for_agent ) { |
| 1656 | foreach ( $bookings_for_agent as $booking ) { |
| 1657 | if ( $booking->start_time < $calendar_start_minutes ) { |
| 1658 | $calendar_start_minutes = $booking->start_time; |
| 1659 | } |
| 1660 | if ( $booking->end_time > $calendar_end_minutes ) { |
| 1661 | $calendar_end_minutes = $booking->end_time; |
| 1662 | } |
| 1663 | } |
| 1664 | } |
| 1665 | } |
| 1666 | } |
| 1667 | |
| 1668 | return [ $calendar_start_minutes, $calendar_end_minutes ]; |
| 1669 | } |
| 1670 | |
| 1671 | public static function generate_direct_manage_booking_url( OsBookingModel $booking, string $for ): string { |
| 1672 | if ( ! in_array( $for, [ 'agent', 'customer' ] ) ) { |
| 1673 | return ''; |
| 1674 | } |
| 1675 | $key = $booking->get_key_to_manage_for( $for ); |
| 1676 | $url = OsRouterHelper::build_admin_post_link( [ 'manage_booking_by_key', 'show' ], [ 'key' => $key ] ); |
| 1677 | |
| 1678 | return $url; |
| 1679 | } |
| 1680 | |
| 1681 | public static function generate_summary_actions_for_booking( OsBookingModel $booking, ?string $key = null ) { |
| 1682 | ?> |
| 1683 | <div class="booking-full-summary-actions"> |
| 1684 | <div class="add-to-calendar-wrapper"> |
| 1685 | <a href="#" class="open-calendar-types booking-summary-action-btn"><i class="latepoint-icon latepoint-icon-calendar"></i><span><?php esc_html_e( 'Add to Calendar', 'latepoint' ); ?></span></a> |
| 1686 | <?php echo OsBookingHelper::generate_add_to_calendar_links( $booking, $key ?? $booking->get_key_to_manage_for( 'customer' ) ); ?> |
| 1687 | </div> |
| 1688 | <a href="<?php echo esc_url( $booking->get_print_link( $key ?? $booking->get_key_to_manage_for( 'customer' ) ) ); ?>" class="print-booking-btn booking-summary-action-btn" target="_blank"><i class="latepoint-icon latepoint-icon-printer"></i><span><?php esc_html_e( 'Print', 'latepoint' ); ?></span></a> |
| 1689 | <?php |
| 1690 | if ( $booking->is_upcoming() ) { |
| 1691 | if ( OsCustomerHelper::can_reschedule_booking( $booking ) ) { ?> |
| 1692 | <a href="#" class="latepoint-request-booking-reschedule booking-summary-action-btn" data-os-after-call="latepoint_init_reschedule" data-os-lightbox-classes="width-450 reschedule-calendar-wrapper" data-os-action="<?php echo esc_attr( OsRouterHelper::build_route_name( 'manage_booking_by_key', 'request_reschedule_calendar' ) ); ?>" data-os-params="<?php echo esc_attr( OsUtilHelper::build_os_params( [ 'key' => $key ?? $booking->get_key_to_manage_for( 'customer' ) ] ) ); ?>" data-os-output-target="lightbox"> |
| 1693 | <i class="latepoint-icon latepoint-icon-calendar"></i> |
| 1694 | <span><?php esc_html_e( 'Reschedule', 'latepoint' ); ?></span> |
| 1695 | </a> |
| 1696 | <?php |
| 1697 | } |
| 1698 | if ( OsCustomerHelper::can_cancel_booking( $booking ) ) { ?> |
| 1699 | <a href="#" class="booking-summary-action-btn cancel-appointment-btn" |
| 1700 | data-os-prompt="<?php esc_attr_e( 'Are you sure you want to cancel this appointment?', 'latepoint' ); ?>" |
| 1701 | data-os-success-action="reload" |
| 1702 | data-os-action="<?php echo esc_attr( OsRouterHelper::build_route_name( 'manage_booking_by_key', 'request_cancellation' ) ); ?>" |
| 1703 | data-os-params="<?php echo esc_attr( OsUtilHelper::build_os_params( [ 'key' => $key ?? $booking->get_key_to_manage_for( 'customer' ) ], 'cancel_booking_' . $booking->id ) ); ?>"> |
| 1704 | <i class="latepoint-icon latepoint-icon-ui-24"></i> |
| 1705 | <span><?php esc_html_e( 'Cancel', 'latepoint' ); ?></span> |
| 1706 | </a> |
| 1707 | <?php |
| 1708 | } |
| 1709 | } |
| 1710 | do_action( 'latepoint_booking_summary_after_booking_actions', $booking ); |
| 1711 | ?> |
| 1712 | </div> |
| 1713 | <?php |
| 1714 | } |
| 1715 | |
| 1716 | public static function generate_summary_for_booking( OsBookingModel $booking, $cart_item_id = false, ?string $viewer = 'customer' ): string { |
| 1717 | $summary_html = ''; |
| 1718 | $summary_html .= apply_filters( 'latepoint_booking_summary_before_summary_box', '', $booking ); |
| 1719 | $summary_html .= '<div class="summary-box main-box" ' . ( ( $cart_item_id ) ? 'data-cart-item-id="' . $cart_item_id . '"' : '' ) . '>'; |
| 1720 | $output_timezone_name = $viewer == 'customer' ? $booking->get_customer_timezone_name() : OsTimeHelper::get_wp_timezone_name(); |
| 1721 | if ( ! empty( $booking->start_datetime_utc ) ) { |
| 1722 | $summary_html .= '<div class="summary-box-booking-date-box">'; |
| 1723 | $summary_html .= '<div class="summary-box-booking-date-day">' . $booking->start_datetime_in_format( 'j', $output_timezone_name ) . '</div>'; |
| 1724 | $summary_html .= '<div class="summary-box-booking-date-month">' . OsUtilHelper::get_month_name_by_number( $booking->start_datetime_in_format( 'n', $output_timezone_name ), true ) . '</div>'; |
| 1725 | $summary_html .= '</div>'; |
| 1726 | } |
| 1727 | $summary_html .= '<div class="summary-box-inner">'; |
| 1728 | $service_headings = []; |
| 1729 | $service_headings = apply_filters( 'latepoint_booking_summary_service_headings', $service_headings, $booking ); |
| 1730 | if ( $service_headings ) { |
| 1731 | $summary_html .= '<div class="summary-box-heading">'; |
| 1732 | foreach ( $service_headings as $heading ) { |
| 1733 | $summary_html .= '<div class="sbh-item">' . $heading . '</div>'; |
| 1734 | } |
| 1735 | $summary_html .= '<div class="sbh-line"></div>'; |
| 1736 | $summary_html .= '</div>'; |
| 1737 | } |
| 1738 | $summary_html .= '<div class="summary-box-content os-cart-item">'; |
| 1739 | if ( $cart_item_id && OsCartsHelper::can_checkout_multiple_items() ) { |
| 1740 | $summary_html .= '<div class="os-remove-item-from-cart" role="button" tabindex="0" data-confirm-text="' . __( 'Are you sure you want to remove this item from your cart?', 'latepoint' ) . '" data-cart-item-id="' . $cart_item_id . '" data-route="' . OsRouterHelper::build_route_name( 'carts', 'remove_item_from_cart' ) . '"> |
| 1741 | <div class="os-remove-from-cart-icon"></div> |
| 1742 | </div>'; |
| 1743 | } |
| 1744 | $summary_html .= '<div class="sbc-big-item">' . $booking->get_service_name_for_summary() . '</div>'; |
| 1745 | if ( $booking->start_date ) { |
| 1746 | $summary_html .= '<div class="sbc-highlighted-item">' . $booking->get_nice_datetime_for_summary( $viewer ) . '</div>'; |
| 1747 | } |
| 1748 | /** |
| 1749 | * Output summary of the booking data after a start date and time |
| 1750 | * |
| 1751 | * @since 5.2.0 |
| 1752 | * @hook latepoint_summary_booking_info_after_start_date |
| 1753 | * |
| 1754 | * @param {string} $summary_html HTML of the summary |
| 1755 | * @param {OsBookingModel} $booking Booking object that is being outputted |
| 1756 | * @param {string} $cart_item_id ID of a cart item this booking belongs to |
| 1757 | * @param {string} $viewer determines who is viewing this summary, can be customer or agent |
| 1758 | * |
| 1759 | * @returns {string} Filtered HTML |
| 1760 | */ |
| 1761 | $summary_html = apply_filters( 'latepoint_summary_booking_info_after_start_date', $summary_html, $booking, $cart_item_id, $viewer ); |
| 1762 | $summary_html .= '</div>'; |
| 1763 | |
| 1764 | $service_attributes = []; |
| 1765 | $service_attributes = apply_filters( 'latepoint_booking_summary_service_attributes', $service_attributes, $booking ); |
| 1766 | if ( $service_attributes ) { |
| 1767 | $summary_html .= '<div class="summary-attributes sa-clean">'; |
| 1768 | foreach ( $service_attributes as $attribute ) { |
| 1769 | $summary_html .= '<span>' . $attribute['label'] . ': <strong>' . $attribute['value'] . '</strong></span>'; |
| 1770 | } |
| 1771 | $summary_html .= '</div>'; |
| 1772 | } |
| 1773 | $summary_html .= '</div>'; |
| 1774 | $summary_html .= apply_filters( 'latepoint_booking_summary_after_summary_box_inner', '', $booking ); |
| 1775 | $summary_html .= '</div>'; |
| 1776 | $summary_html .= apply_filters( 'latepoint_booking_summary_after_summary_box', '', $booking ); |
| 1777 | |
| 1778 | return $summary_html; |
| 1779 | } |
| 1780 | |
| 1781 | |
| 1782 | /** |
| 1783 | * @param OsBookingModel[] $bookings |
| 1784 | * |
| 1785 | * @return bool |
| 1786 | */ |
| 1787 | public static function bookings_have_same_agent( array $bookings ): bool { |
| 1788 | return ( count( array_unique( array_column( $bookings, 'agent_id' ) ) ) == 1 ); |
| 1789 | } |
| 1790 | |
| 1791 | /** |
| 1792 | * @param OsBookingModel[] $bookings |
| 1793 | * |
| 1794 | * @return bool |
| 1795 | */ |
| 1796 | public static function bookings_have_same_location( array $bookings ): bool { |
| 1797 | return ( count( array_unique( array_column( $bookings, 'location_id' ) ) ) == 1 ); |
| 1798 | } |
| 1799 | |
| 1800 | /** |
| 1801 | * @param OsBookingModel[] $bookings |
| 1802 | * |
| 1803 | * @return bool |
| 1804 | */ |
| 1805 | public static function bookings_have_same_service( array $bookings ): bool { |
| 1806 | return ( count( array_unique( array_column( $bookings, 'service_id' ) ) ) == 1 ); |
| 1807 | } |
| 1808 | |
| 1809 | public static function prepare_new_from_params( array $params ): OsBookingModel { |
| 1810 | $booking = new OsBookingModel(); |
| 1811 | |
| 1812 | $services = OsServiceHelper::get_allowed_active_services(); |
| 1813 | $agents = OsAgentHelper::get_allowed_active_agents(); |
| 1814 | |
| 1815 | // LOAD FROM PASSED PARAMS |
| 1816 | $booking->order_item_id = $params['order_item_id'] ?? ''; |
| 1817 | $booking->service_id = ! empty( $params['service_id'] ) ? OsUtilHelper::first_value_if_array( $params['service_id'] ) : ''; |
| 1818 | if ( empty( $booking->service_id ) && ! empty( $services ) ) { |
| 1819 | $booking->service_id = $services[0]->id; |
| 1820 | } |
| 1821 | |
| 1822 | $booking->agent_id = ! empty( $params['agent_id'] ) ? OsUtilHelper::first_value_if_array( $params['agent_id'] ) : ''; |
| 1823 | if ( empty( $booking->agent_id ) && ! empty( $agents ) ) { |
| 1824 | $booking->agent_id = $agents[0]->id; |
| 1825 | } |
| 1826 | |
| 1827 | if ( ! empty( $params['order_id'] ) ) { |
| 1828 | $order = new OsOrderModel( $params['order_id'] ); |
| 1829 | $booking->customer_id = $order->customer_id; |
| 1830 | } else { |
| 1831 | $booking->customer_id = ! empty( $params['customer_id'] ) ? OsUtilHelper::first_value_if_array( $params['customer_id'] ) : ''; |
| 1832 | } |
| 1833 | |
| 1834 | $booking->location_id = ! empty( $params['location_id'] ) ? OsUtilHelper::first_value_if_array( $params['location_id'] ) : OsLocationHelper::get_default_location_id( true ); |
| 1835 | $booking->start_date = $params['start_date'] ?? OsTimeHelper::today_date( 'Y-m-d' ); |
| 1836 | $booking->start_time = $params['start_time'] ?? 600; |
| 1837 | |
| 1838 | $booking->end_time = ( $booking->service_id ) ? $booking->calculate_end_time() : $booking->start_time + 60; |
| 1839 | $booking->end_date = $booking->calculate_end_date(); |
| 1840 | $booking->buffer_before = ( $booking->service_id ) ? $booking->service->buffer_before : 0; |
| 1841 | $booking->buffer_after = ( $booking->service_id ) ? $booking->service->buffer_after : 0; |
| 1842 | $booking->status = LATEPOINT_BOOKING_STATUS_APPROVED; |
| 1843 | |
| 1844 | return $booking; |
| 1845 | } |
| 1846 | } |
| 1847 |