PluginProbe ʕ •ᴥ•ʔ
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress / 5.6.11
Appointment Booking Plugin – LatePoint | Calendar & Scheduling for WordPress v5.6.11
5.6.11 5.6.10 5.6.9 5.6.8 5.6.7 5.6.6 5.6.5 5.6.4 5.6.3 5.6.2 5.6.1 5.6.0 5.5.2 5.5.1 5.5.0 5.4.2 trunk 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.1.91 5.1.92 5.1.93 5.1.94 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1
latepoint / lib / models / booking_model.php
latepoint / lib / models Last commit date
activity_model.php 5 months ago agent_meta_model.php 5 months ago agent_model.php 5 months ago booking_meta_model.php 5 months ago booking_model.php 2 days ago bundle_meta_model.php 5 months ago bundle_model.php 2 months ago cart_item_model.php 5 months ago cart_meta_model.php 5 months ago cart_model.php 1 month ago connector_model.php 5 months ago customer_meta_model.php 5 months ago customer_model.php 3 months ago invoice_model.php 1 month ago join_bundles_services_model.php 5 months ago location_category_model.php 5 months ago location_model.php 5 months ago meta_model.php 5 months ago model.php 2 months ago off_period_model.php 5 months ago order_intent_meta_model.php 5 months ago order_intent_model.php 2 days ago order_item_model.php 1 month ago order_meta_model.php 5 months ago order_model.php 3 months ago otp_model.php 5 months ago payment_request_model.php 5 months ago process_job_model.php 5 months ago process_model.php 3 months ago recurrence_model.php 5 months ago service_category_model.php 5 months ago service_meta_model.php 5 months ago service_model.php 5 months ago session_model.php 5 months ago settings_model.php 5 months ago step_settings_model.php 5 months ago transaction_intent_model.php 5 months ago transaction_model.php 5 months ago transaction_refund_model.php 5 months ago work_period_model.php 5 months ago
booking_model.php
1345 lines
1 <?php
2
3 /**
4 * @property OsCustomerModel $customer
5 * @property OsAgentModel $agent
6 * @property OsServiceModel $service
7 * @property string $service_category_id
8 * @property OsLocationModel $location
9 */
10 class OsBookingModel extends OsModel {
11 public $id,
12 $booking_code,
13 $service_id,
14 $customer_id,
15 $agent_id,
16 $location_id,
17 $recurrence_id,
18 $buffer_before = 0,
19 $buffer_after = 0,
20 $status,
21 $start_date,
22 $end_date,
23 $start_time,
24 $end_time,
25 $start_datetime_utc,
26 $end_datetime_utc,
27 $duration,
28 $total_attendees = 1,
29 $total_attendees_sum = 1,
30 $total_customers = 1,
31 $cart_item_id = null,
32 $order_item_id,
33 $server_timezone,
34 $customer_timezone,
35 $meta_class = 'OsBookingMetaModel',
36 $keys_to_manage = [],
37 $generate_recurrent_sequence = [],
38 $updated_at,
39 $created_at;
40
41 function __construct( $id = false ) {
42 parent::__construct();
43 $this->table_name = LATEPOINT_TABLE_BOOKINGS;
44 $this->nice_names = array(
45 'service_id' => __( 'Service', 'latepoint' ),
46 'agent_id' => __( 'Agent', 'latepoint' ),
47 );
48
49 if ( $id ) {
50 $this->load_by_id( $id );
51 }
52 }
53
54
55 /**
56 * @return mixed|void
57 *
58 * Returns full amount to charge in database format 1999.0000
59 *
60 */
61 public function full_amount_to_charge() {
62 return OsBookingHelper::calculate_full_amount_for_booking( $this );
63 }
64
65 /**
66 * @return mixed|void
67 *
68 * Returns deposit amount to charge in database format 1999.0000
69 *
70 */
71 public function deposit_amount_to_charge() {
72 return OsBookingHelper::calculate_deposit_amount_to_charge( $this );
73 }
74
75
76 public function get_key_to_manage_for( string $for ): string {
77 if ( $this->is_new_record() ) {
78 return '';
79 }
80 if ( ! empty( $this->keys_to_manage[ $for ] ) ) {
81 return $this->keys_to_manage[ $for ];
82 }
83 $key = OsMetaHelper::get_booking_meta_by_key( 'key_to_manage_for_' . $for, $this->id );
84 if ( empty( $key ) ) {
85 $key = OsUtilHelper::generate_key_to_manage();
86 OsMetaHelper::save_booking_meta_by_key( 'key_to_manage_for_' . $for, $key, $this->id );
87 }
88 $this->keys_to_manage[ $for ] = $key;
89 return $key;
90 }
91
92 public function manage_by_key_url( string $for = 'customer' ): string {
93 return OsBookingHelper::generate_direct_manage_booking_url( $this, $for );
94 }
95
96 public function get_service_name_for_summary() {
97 $service_name = $this->service_id ? $this->service->name : '';
98
99 /**
100 * Get service name to be displayed on a booking summary
101 *
102 * @param {string} $service_name Service name to be filtered
103 * @param {OsBookingModel} $booking Booking model which service name is requested
104 *
105 * @returns {string} Filtered service name
106 * @since 5.0.0
107 * @hook latepoint_booking_get_service_name_for_summary
108 *
109 */
110 return apply_filters( 'latepoint_booking_get_service_name_for_summary', $service_name, $this );
111 }
112
113 public function get_order() {
114 if ( $this->order_item_id ) {
115 if ( ! isset( $this->order_item ) || ( $this->order_item->id != $this->order_item_id ) ) {
116 $this->order_item = new OsOrderItemModel( $this->order_item_id );
117 if ( ! isset( $this->order ) || ( $this->order->id != $this->order_item->order_id ) ) {
118 $this->order = new OsOrderModel( $this->order_item->order_id );
119 }
120 }
121 } else {
122 $this->order = new OsOrderModel();
123 }
124
125 return $this->order;
126 }
127
128 public function get_order_item() {
129 if ( ! isset( $this->order_item ) ) {
130 $this->order_item = new OsOrderItemModel( $this->order_item_id );
131 }
132 return $this->order_item;
133 }
134
135 public function filter_allowed_records(): OsModel {
136 if ( ! OsRolesHelper::are_all_records_allowed() ) {
137 if ( ! OsRolesHelper::are_all_records_allowed( 'agent' ) ) {
138 $this->filter_where_conditions( [ 'agent_id' => OsRolesHelper::get_allowed_records( 'agent' ) ] );
139 }
140 if ( ! OsRolesHelper::are_all_records_allowed( 'location' ) ) {
141 $this->filter_where_conditions( [ 'location_id' => OsRolesHelper::get_allowed_records( 'location' ) ] );
142 }
143 if ( ! OsRolesHelper::are_all_records_allowed( 'service' ) ) {
144 $this->filter_where_conditions( [ 'service_id' => OsRolesHelper::get_allowed_records( 'service' ) ] );
145 }
146 }
147
148 return $this;
149 }
150
151 public function properties_to_query(): array {
152 return [
153 'service_id' => __( 'Service', 'latepoint' ),
154 'service_category_id' => __( 'Service Category', 'latepoint' ),
155 'agent_id' => __( 'Agent', 'latepoint' ),
156 'status' => __( 'Status', 'latepoint' ),
157 'start_datetime_utc' => __( 'Start Time', 'latepoint' ),
158 'order_item_counts' => __( 'Order Item Counts', 'latepoint' ),
159 ];
160 }
161
162 public function generate_item_data() {
163 return wp_json_encode( $this->generate_params_for_booking_form() );
164 }
165
166
167 public function generate_params_for_booking_form() {
168 $params = [
169 'id' => $this->id,
170 'customer_id' => $this->customer_id,
171 'agent_id' => $this->agent_id,
172 'location_id' => $this->location_id,
173 'service_id' => $this->service_id,
174 'recurrence_id' => $this->recurrence_id,
175 'start_date' => $this->start_date,
176 'start_time' => $this->start_time,
177 'end_date' => $this->end_date,
178 'end_time' => $this->end_time,
179 'status' => $this->status,
180 'buffer_before' => $this->buffer_before,
181 'buffer_after' => $this->buffer_after,
182 'duration' => $this->duration,
183 'generate_recurrent_sequence' => $this->generate_recurrent_sequence,
184 ];
185
186 /**
187 * Returns an array of params generated from OsBookingModel to be used in a booking form
188 *
189 * @param {array} $params Array of booking params
190 * @param {OsBookingModel} $booking Instance of <code>OsBookingModel</code> that params are being generated for
191 *
192 * @returns {array} Filtered array of booking params
193 * @since 5.0.0
194 * @hook latepoint_generated_params_for_booking_form
195 *
196 */
197 return apply_filters( 'latepoint_generated_params_for_booking_form', $params, $this );
198 }
199
200 public function get_formatted_price() {
201 $order_item = new OsOrderItemModel( $this->order_item_id );
202 return OsMoneyHelper::format_price( $order_item->get_total() );
203 }
204
205 public function generate_first_level_data_vars(): array {
206 $vars = [
207 'id' => $this->id,
208 'booking_code' => $this->booking_code,
209 'start_datetime' => $this->format_start_date_and_time_rfc3339(),
210 'end_datetime' => $this->format_end_date_and_time_rfc3339(),
211 'service_id' => $this->service_id,
212 'service_name' => $this->service->name,
213 'duration' => $this->duration,
214 'customer_comment' => $this->order->customer_comment,
215 'status' => $this->status,
216 'start_date' => $this->format_start_date(),
217 'start_time' => OsTimeHelper::minutes_to_hours_and_minutes( $this->start_time ),
218 'end_time' => OsTimeHelper::minutes_to_hours_and_minutes( $this->end_time ),
219 'timezone' => OsTimeHelper::get_wp_timezone_name(),
220 'agent' => $this->agent->get_data_vars(),
221 'created_datetime' => $this->format_created_datetime_rfc3339(),
222 'manage_booking_for_agent' => OsBookingHelper::generate_direct_manage_booking_url( $this, 'agent' ),
223 'manage_booking_for_customer' => OsBookingHelper::generate_direct_manage_booking_url( $this, 'customer' ),
224 ];
225 return $vars;
226 }
227
228 public function generate_data_vars(): array {
229 $vars = $this->get_first_level_data_vars();
230
231 $vars['customer'] = $this->customer->get_data_vars();
232 $vars['transactions'] = [];
233 $vars['order'] = $this->order->get_first_level_data_vars();
234
235 $transactions = $this->order->get_transactions();
236 if ( $transactions ) {
237 foreach ( $transactions as $transaction ) {
238 $vars['transactions'][] = $transaction->get_data_vars();
239 }
240 }
241
242 return $vars;
243 }
244
245
246 public function is_ready_for_summary() {
247 return ( $this->agent_id && $this->agent_id != LATEPOINT_ANY_AGENT && OsAgentHelper::count_agents() > 1 ) || $this->service_id;
248 }
249
250 public function is_part_of_bundle(): bool {
251 if ( $this->order_item_id ) {
252 $order_item = new OsOrderItemModel( $this->order_item_id );
253
254 return $order_item->is_bundle();
255 }
256
257 return false;
258 }
259
260 public function is_upcoming(): bool {
261 if ( empty( $this->start_datetime_utc ) ) {
262 return false;
263 }
264 $start_time_utc = new OsWpDateTime( $this->start_datetime_utc, new DateTimeZone( 'UTC' ) );
265 $now_time_utc = new OsWpDateTime( 'now', new DateTimeZone( 'UTC' ) );
266
267 return ( $start_time_utc > $now_time_utc );
268 }
269
270 public function set_utc_datetimes( bool $save = false ) {
271 if ( empty( $this->start_date ) || empty( $this->end_date ) || empty( $this->start_time ) || empty( $this->end_time ) ) {
272 return;
273 }
274 $this->start_datetime_utc = $this->get_start_datetime( 'UTC' )->format( LATEPOINT_DATETIME_DB_FORMAT );
275 $this->end_datetime_utc = $this->get_end_datetime( 'UTC' )->format( LATEPOINT_DATETIME_DB_FORMAT );
276 if ( $save ) {
277 $this->update_attributes(
278 [
279 'start_datetime_utc' => $this->start_datetime_utc,
280 'end_datetime_utc' => $this->end_datetime_utc,
281 ]
282 );
283 }
284 }
285
286
287 public function delete( $id = false ) {
288 if ( ! $id && isset( $this->id ) ) {
289 $id = $this->id;
290 }
291
292 $booking_metas = new OsBookingMetaModel();
293 $booking_metas->delete_where( [ 'object_id' => $id ] );
294 $process_jobs = new OsProcessJobModel();
295 $process_jobs->delete_where(
296 [
297 'object_id' => $id,
298 'object_model_type' => 'booking',
299 ]
300 );
301
302
303 return parent::delete( $id );
304 }
305
306 public function delete_meta_by_key( $meta_key ) {
307 if ( $this->is_new_record() ) {
308 return true;
309 }
310
311 $meta = new OsBookingMetaModel();
312
313 return $meta->delete_by_key( $meta_key, $this->id );
314 }
315
316 public function get_url_for_add_to_calendar_button( string $calendar_type ): string {
317 switch ( $calendar_type ) {
318 case 'google':
319 $url = 'https://calendar.google.com/calendar/render';
320 $params = [
321 'action' => 'TEMPLATE',
322 'text' => $this->service->name,
323 'dates' => $this->get_start_datetime_object( new DateTimeZone( 'UTC' ) )->format( 'Ymd\THis\Z' ) . '/' . $this->get_end_datetime_object( new DateTimeZone( 'UTC' ) )->format( 'Ymd\THis\Z' ),
324 ];
325 if ( ! empty( $this->location->full_address ) ) {
326 $params['location'] = $this->location->full_address;
327 }
328 break;
329 case 'outlook':
330 $url = 'https://outlook.office.com/calendar/0/deeplink/compose';
331 $params = [
332 'path' => '/calendar/action/compose',
333 'rru' => 'addevent',
334 'startdt' => $this->get_start_datetime_object( new DateTimeZone( 'UTC' ) )->format( 'Y-m-d\TH:i:s\Z' ),
335 'enddt' => $this->get_end_datetime_object( new DateTimeZone( 'UTC' ) )->format( 'Y-m-d\TH:i:s\Z' ),
336 'subject' => $this->service->name,
337 ];
338 break;
339 }
340 /**
341 * Generate params for the add to calendar link
342 *
343 * @param {array} $params Array of parameters that will be converted into a param query
344 * @param {string} $calendar_type Type of calendar the link is requested for
345 * @param {OsBookingModel} $booking A booking object
346 * @returns {array} The filtered array of appointment attributes
347 *
348 * @since 4.8.1
349 * @hook latepoint_build_add_to_calendar_link_params
350 *
351 */
352 $params = apply_filters( 'latepoint_build_add_to_calendar_link_params', $params, $calendar_type, $this );
353
354 $url = $url . '?' . http_build_query( $params );
355
356 /**
357 * URL for the link for a button to add appointment to calendar
358 *
359 * @param {array} $params Array of parameters that will be converted into a param query
360 * @param {string} $calendar_type Type of calendar the link is requested for
361 * @param {OsBookingModel} $booking A booking object
362 * @returns {string} The filtered url of adding appointment to calendar
363 *
364 * @since 4.8.1
365 * @hook latepoint_build_add_to_calendar_link_url
366 *
367 */
368 return apply_filters( 'latepoint_build_add_to_calendar_link_url', $url, $calendar_type, $this );
369 }
370
371 public function get_ical_download_link( $key = false ) {
372 return ( $key ) ? OsRouterHelper::build_admin_post_link(
373 [
374 'manage_booking_by_key',
375 'ical_download',
376 ],
377 [ 'key' => $key ]
378 ) : OsRouterHelper::build_admin_post_link(
379 [
380 'customer_cabinet',
381 'ical_download',
382 ],
383 [ 'latepoint_booking_id' => $this->id ]
384 );
385 }
386
387 public function get_print_link( $key = false ) {
388 return ( $key ) ? OsRouterHelper::build_admin_post_link( [ 'manage_booking_by_key', 'print' ], [ 'key' => $key ] ) : OsRouterHelper::build_admin_post_link( [ 'customer_cabinet', 'print_booking_info' ], [ 'latepoint_booking_id' => $this->id ] );
389 }
390
391 public function get_meta_by_key( $meta_key, $default = false ) {
392 if ( $this->is_new_record() ) {
393 return $default;
394 }
395
396 $meta = new OsBookingMetaModel();
397
398 return $meta->get_by_key( $meta_key, $this->id, $default );
399 }
400
401 public function get_coupon_code() {
402 $order = $this->get_order();
403 return $order->coupon_code;
404 }
405
406 public function get_coupon_discount(): string {
407 $order_item = $this->get_order_item();
408 $coupon_discount = $order_item->get_coupon_discount();
409 return $coupon_discount > 0 ? OsMoneyHelper::format_price( $order_item->get_coupon_discount() ) : '';
410 }
411
412 public function save_meta_by_key( $meta_key, $meta_value ) {
413 if ( $this->is_new_record() ) {
414 return false;
415 }
416
417 $meta = new OsBookingMetaModel();
418
419 return $meta->save_by_key( $meta_key, $meta_value, $this->id );
420 }
421
422 public function calculate_end_date() {
423 if ( empty( $this->start_time ) || empty( $this->start_date ) ) {
424 return $this->start_date;
425 }
426 if ( ( $this->start_time + $this->get_total_duration() ) > ( 24 * 60 ) ) {
427 $date_obj = new OsWpDateTime( $this->start_date );
428 $end_date = $date_obj->modify( '+1 day' )->format( 'Y-m-d' );
429 } else {
430 $end_date = $this->start_date;
431 }
432
433 return $end_date;
434 }
435
436
437 public function calculate_end_time() {
438 $end_time = (int) $this->start_time + (int) $this->get_total_duration();
439 // continues to next day?
440 if ( $end_time > ( 24 * 60 ) ) {
441 $end_time = $end_time - ( 24 * 60 );
442 }
443
444 return $end_time;
445 }
446
447 public function calculate_end_date_and_time() {
448 $this->end_time = $this->calculate_end_time();
449 $this->end_date = $this->calculate_end_date();
450 }
451
452 public function after_data_was_set( $data ) {
453 if ( empty( $this->end_time ) ) {
454 $this->calculate_end_date_and_time();
455 }
456 if ( empty( $this->end_date ) ) {
457 $this->calculate_end_date();
458 }
459 }
460
461 public function set_buffers() {
462 if ( $this->service_id ) {
463 $service = new OsServiceModel( $this->service_id );
464 if ( $service ) {
465 $this->buffer_before = $service->buffer_before;
466 $this->buffer_after = $service->buffer_after;
467 }
468 }
469 }
470
471 public function get_total_duration( $calculate_from_start_and_end = false ) {
472 if ( $calculate_from_start_and_end ) {
473 if ( $this->start_date == $this->end_date ) {
474 // same day
475 $total_duration = $this->end_time - $this->start_time;
476 } else {
477 // TODO calculate how many days difference there is, if difference is more than 1 day - account for that
478 $total_duration = 60 * 24 - $this->start_time + $this->end_time;
479 }
480 } else {
481 if ( $this->duration ) {
482 $total_duration = $this->duration;
483 } else {
484 $total_duration = ( $this->service_id ) ? $this->service->duration : 60;
485 }
486 $total_duration = apply_filters( 'latepoint_calculated_total_duration', $total_duration, $this );
487 }
488
489 return (int) $total_duration;
490 }
491
492
493 public function get_nice_created_at( $include_time = true ) {
494 $format = $include_time ? OsSettingsHelper::get_readable_date_format() . ' ' . OsSettingsHelper::get_readable_time_format() : OsSettingsHelper::get_readable_date_format();
495 $utc_date = date_create_from_format( LATEPOINT_DATETIME_DB_FORMAT, $this->created_at );
496 $wp_timezone_date = $utc_date->setTimezone( OsTimeHelper::get_wp_timezone() );
497
498 return date_format( $wp_timezone_date, $format );
499 }
500
501 public function is_bookable( array $settings = [] ): bool {
502
503 $defaults = [
504 'skip_customer_check' => false,
505 'log_errors' => true,
506 ];
507
508 $settings = OsUtilHelper::merge_default_atts( $defaults, $settings );
509
510 $customer = $this->customer_id ? new OsCustomerModel( $this->customer_id ) : false;
511 // check if customer has to be assigned to a booking, or a guest booking is fine at this point
512 if ( $settings['skip_customer_check'] ) {
513 $customer_requirement_satisfied = true;
514 } else {
515 $customer_requirement_satisfied = ( $this->customer_id && $customer && $customer->id && ( $this->customer_id == $customer->id ) );
516 }
517
518 // A hidden service is never bookable through the public booking flow, regardless of how
519 // the booking object was populated. Admins/agents book hidden services via the backend panel.
520 if ( $this->service_id ) {
521 $service = ( $this->service instanceof OsServiceModel && $this->service->id )
522 ? $this->service
523 : new OsServiceModel( $this->service_id );
524 if ( $service->is_hidden() ) {
525 $this->add_error( 'send_to_step', __( 'Selected service is not available.', 'latepoint' ), 'booking__service' );
526 return false;
527 }
528 }
529
530 // agent, service and customer should be set
531 if ( $this->service_id && $this->agent_id && $customer_requirement_satisfied ) {
532
533 if ( $this->agent_id == LATEPOINT_ANY_AGENT && $this->location_id == LATEPOINT_ANY_LOCATION ) {
534 // both location and agent are set to any
535 $connections = new OsConnectorModel();
536 $connection_groups = $connections->select( LATEPOINT_TABLE_AGENTS_SERVICES . '.agent_id, ' . LATEPOINT_TABLE_AGENTS_SERVICES . '.location_id' )
537 ->where(
538 [
539 'service_id' => $this->service_id,
540 LATEPOINT_TABLE_AGENTS . '.status' => LATEPOINT_AGENT_STATUS_ACTIVE,
541 LATEPOINT_TABLE_LOCATIONS . '.status' => LATEPOINT_LOCATION_STATUS_ACTIVE,
542 ]
543 )
544 ->join( LATEPOINT_TABLE_AGENTS, [ 'id' => LATEPOINT_TABLE_AGENTS_SERVICES . '.agent_id' ] )
545 ->join( LATEPOINT_TABLE_LOCATIONS, [ 'id' => LATEPOINT_TABLE_AGENTS_SERVICES . '.location_id' ] )
546 ->get_results( ARRAY_A );
547 if ( empty( $connection_groups ) ) {
548 // no active locations and agents are connected to this service
549 $this->add_error( 'send_to_step', __( 'Unfortunately there are no active resources that can offer selected service, please select another service.', 'latepoint' ), 'booking__service' );
550
551 return false;
552 } else {
553 foreach ( $connection_groups as $connection ) {
554 $this->location_id = $connection['location_id'];
555 $this->agent_id = OsBookingHelper::get_any_agent_for_booking_by_rule( $this );
556 // available agent found in this location - break the loop
557 if ( $this->agent_id ) {
558 break;
559 }
560 }
561 if ( ! $this->agent_id ) {
562 $this->add_error( 'send_to_step', __( 'Unfortunately the selected time slot is not available anymore, please select another timeslot.', 'latepoint' ), 'booking__datepicker' );
563
564 return false;
565 }
566 }
567 } elseif ( $this->agent_id == LATEPOINT_ANY_AGENT ) {
568 $this->agent_id = OsBookingHelper::get_any_agent_for_booking_by_rule( $this );
569 if ( ! $this->agent_id ) {
570 $this->add_error( 'send_to_step', __( 'Unfortunately the selected time slot is not available anymore, please select another timeslot.', 'latepoint' ), 'booking__datepicker' );
571
572 return false;
573 }
574 } elseif ( $this->location_id == LATEPOINT_ANY_LOCATION ) {
575 $this->location_id = OsBookingHelper::get_any_location_for_booking_by_rule( $this );
576 if ( ! $this->location_id ) {
577 $this->add_error( 'send_to_step', __( 'Unfortunately the selected time slot is not available anymore, please select another timeslot.', 'latepoint' ), 'booking__datepicker' );
578
579 return false;
580 }
581 } else {
582 // check if booking time is still available
583 if ( ! OsBookingHelper::is_booking_request_available( \LatePoint\Misc\BookingRequest::create_from_booking_model( $this ) ) ) {
584 // translators: %1$s is the timeslot date and time
585 // translators: %2$s is the service name
586 $error_message = sprintf( __( 'Unfortunately the selected time slot "%1$s" for "%2$s" is not available anymore, please select another timeslot.', 'latepoint' ), $this->get_nice_start_datetime_for_customer(), $this->service->name );
587 $this->add_error( 'send_to_step', $error_message, 'booking__datepicker' );
588
589 return false;
590 }
591 }
592
593 if ( ! $this->validate( false, [ 'order_item_id', 'status', 'customer_id' ] ) ) {
594 return false;
595 }
596
597 return true;
598 } else {
599 if ( ! $this->service_id ) {
600 $this->add_error( 'missing_service', __( 'You have to select a service', 'latepoint' ) );
601 }
602 if ( ! $this->agent_id ) {
603 $this->add_error( 'missing_agent', __( 'You have to select an agent', 'latepoint' ) );
604 }
605 if ( ! $this->customer_id && ! $settings['skip_customer_check'] ) {
606 $this->add_error( 'missing_customer', __( 'Customer Not Found', 'latepoint' ) );
607 if ( $settings['log_errors'] ) {
608 OsDebugHelper::log( 'Customer not found', 'customer_error', print_r( $customer, true ) );
609 }
610 }
611 if ( ! $customer && ! $settings['skip_customer_check'] ) {
612 $this->add_error( 'missing_customer', __( 'You have to be logged in', 'latepoint' ) );
613 if ( $settings['log_errors'] ) {
614 OsDebugHelper::log( 'Customer not logged in', 'customer_error', print_r( $customer, true ) );
615 }
616 }
617 if ( $settings['log_errors'] ) {
618 OsDebugHelper::log( 'Error saving booking', 'booking_error', 'Agent: ' . $this->agent_id . ', Service: ' . $this->service_id . ', Booking Customer: ' . $this->customer_id );
619 }
620
621 return false;
622 }
623 }
624
625
626 public function get_nice_status() {
627 return OsBookingHelper::get_nice_status_name( $this->status );
628 }
629
630 public function get_latest_bookings_sorted_by_status( $args = array() ) {
631 $args = array_merge(
632 array(
633 'service_id' => false,
634 'customer_id' => false,
635 'agent_id' => false,
636 'location_id' => false,
637 'limit' => false,
638 'offset' => false,
639 ),
640 $args
641 );
642
643 $bookings = new OsBookingModel();
644 $query_args = array();
645 if ( $args['service_id'] ) {
646 $query_args['service_id'] = $args['service_id'];
647 }
648 if ( $args['customer_id'] ) {
649 $query_args['customer_id'] = $args['customer_id'];
650 }
651 if ( $args['agent_id'] ) {
652 $query_args['agent_id'] = $args['agent_id'];
653 }
654 if ( $args['location_id'] ) {
655 $query_args['location_id'] = $args['location_id'];
656 }
657 if ( $args['limit'] ) {
658 $bookings->set_limit( $args['limit'] );
659 }
660 if ( $args['offset'] ) {
661 $bookings->set_offset( $args['offset'] );
662 }
663
664 return $bookings->where( $query_args )->should_not_be_cancelled()->order_by( "status != '" . LATEPOINT_BOOKING_STATUS_PENDING . "' asc, start_date asc, start_time asc" )->get_results_as_models();
665 }
666
667
668 public function should_not_be_cancelled() {
669 return $this->where( [ $this->table_name . '.status !=' => LATEPOINT_BOOKING_STATUS_CANCELLED ] );
670 }
671
672 public function should_be_cancelled() {
673 return $this->where( [ $this->table_name . '.status' => LATEPOINT_BOOKING_STATUS_CANCELLED ] );
674 }
675
676 public function should_be_approved() {
677 return $this->where( [ $this->table_name . '.status' => LATEPOINT_BOOKING_STATUS_APPROVED ] );
678 }
679
680 public function should_be_in_future() {
681 return $this->where(
682 [
683 'OR' => [
684 'start_date >' => OsTimeHelper::today_date( 'Y-m-d' ),
685 'AND' => [
686 'start_date' => OsTimeHelper::today_date( 'Y-m-d' ),
687 'start_time >' => OsTimeHelper::get_current_minutes(),
688 ],
689 ],
690 ]
691 );
692 }
693
694
695 public function get_upcoming_bookings( $agent_id = false, $customer_id = false, $service_id = false, $location_id = false, int $limit = 3 ) {
696 $bookings = new OsBookingModel();
697 $args = array(
698 'OR' => array(
699 'start_date >' => OsTimeHelper::today_date( 'Y-m-d' ),
700 'AND' => array(
701 'start_date' => OsTimeHelper::today_date( 'Y-m-d' ),
702 'start_time >' => OsTimeHelper::get_current_minutes(),
703 ),
704 ),
705 );
706 if ( $service_id ) {
707 $args['service_id'] = $service_id;
708 }
709 if ( $customer_id ) {
710 $args['customer_id'] = $customer_id;
711 }
712 if ( $agent_id ) {
713 $args['agent_id'] = $agent_id;
714 }
715 if ( $location_id ) {
716 $args['location_id'] = $location_id;
717 }
718
719 $args = OsAuthHelper::get_current_user()->clean_query_args( $args );
720 $allowed_statuses = OsCalendarHelper::get_booking_statuses_to_display_on_calendar();
721 if ( empty( $allowed_statuses ) ) {
722 return [];
723 }
724
725 return $bookings->select( '*, count(id) as total_customers, sum(total_attendees) as total_attendees_sum' )
726 ->where_in( 'status', $allowed_statuses )
727 ->group_by( 'start_datetime_utc, agent_id, service_id, location_id' )
728 ->where( $args )
729 ->set_limit( $limit )
730 ->order_by( 'start_datetime_utc asc' )
731 ->get_results_as_models();
732 }
733
734 public function get_nice_start_time_for_customer() {
735 return $this->format_start_date_and_time( OsTimeHelper::get_time_format(), false, $this->get_customer_timezone() );
736 }
737
738 public function get_nice_end_time_for_customer() {
739 return $this->format_end_date_and_time( OsTimeHelper::get_time_format(), false, $this->get_customer_timezone() );
740 }
741
742 public function get_nice_start_date_for_customer( $customer_timezone = false, $hide_year = false ) {
743 if ( ! $customer_timezone ) {
744 $customer_timezone = $this->get_customer_timezone();
745 }
746 return OsUtilHelper::translate_months( $this->format_start_date_and_time( OsSettingsHelper::get_readable_date_format( $hide_year ), false, $customer_timezone ) );
747 }
748
749 public function get_nice_start_datetime_for_customer( $customer_timezone = false ) {
750 if ( ! $customer_timezone ) {
751 $customer_timezone = $this->get_customer_timezone();
752 }
753 return OsUtilHelper::translate_months( $this->format_start_date_and_time( OsSettingsHelper::get_readable_datetime_format(), false, $customer_timezone ) );
754 }
755
756 public function get_start_datetime_for_customer(): OsWpDateTime {
757 return $this->get_start_datetime( $this->get_customer_timezone_name() );
758 }
759 public function get_end_datetime_for_customer(): OsWpDateTime {
760 return $this->get_end_datetime( $this->get_customer_timezone_name() );
761 }
762
763 public function get_customer_timezone(): DateTimeZone {
764 if ( OsSettingsHelper::is_on( 'steps_show_timezone_selector' ) ) {
765 return ( $this->customer_id ) ? $this->customer->get_selected_timezone_obj() : OsTimeHelper::get_timezone_from_session();
766 } else {
767 return OsTimeHelper::get_wp_timezone();
768 }
769 }
770 public function get_customer_timezone_name(): string {
771 if ( OsSettingsHelper::is_on( 'steps_show_timezone_selector' ) ) {
772 return ( $this->customer_id ) ? $this->customer->get_selected_timezone_name() : OsTimeHelper::get_timezone_name_from_session();
773 } else {
774 return OsTimeHelper::get_wp_timezone_name();
775 }
776 }
777
778 /**
779 *
780 * Returns time in WP timezone, because start_time is stored in WP timezone, do not use it for customer facing outputs
781 *
782 * @return string|null
783 */
784 public function get_nice_start_time() {
785 return OsTimeHelper::minutes_to_hours_and_minutes( $this->start_time );
786 }
787
788 /**
789 *
790 * Returns time in WP timezone, because end_time is stored in WP timezone, do not use it for customer facing outputs
791 *
792 * @return string|null
793 */
794 public function get_nice_end_time() {
795 return OsTimeHelper::minutes_to_hours_and_minutes( $this->end_time );
796 }
797
798 /**
799 *
800 * Returns time in WP timezone, because start_date is stored in WP timezone, do not use it for customer facing outputs
801 *
802 * @return string|null
803 */
804 public function get_nice_end_date( $hide_year_if_current = false ) {
805 $datetime = OsWpDateTime::os_createFromFormat( 'Y-m-d', $this->end_date );
806 OsTimeHelper::format_to_nice_date( $datetime, $hide_year_if_current );
807 }
808
809 /**
810 *
811 * Returns time in WP timezone, because end_date is stored in WP timezone, do not use it for customer facing outputs
812 *
813 * @return string|null
814 */
815 public function get_nice_start_date( $hide_year_if_current = false ): string {
816 $datetime = OsWpDateTime::os_createFromFormat( 'Y-m-d', $this->start_date );
817 return OsTimeHelper::format_to_nice_date( $datetime, $hide_year_if_current );
818 }
819
820
821
822 /**
823 *
824 * Returns time in WP timezone, because start_date is stored in WP timezone, do not use it for customer facing outputs
825 *
826 * @param $hide_if_today bool
827 * @param $hide_year_if_current bool
828 *
829 * @return string
830 */
831 public function get_nice_start_datetime( bool $hide_if_today = true, bool $hide_year_if_current = true ): string {
832 if ( $hide_if_today && $this->start_date == OsTimeHelper::today_date( 'Y-m-d' ) ) {
833 $date = __( 'Today', 'latepoint' );
834 } else {
835 $date = $this->get_nice_start_date( $hide_year_if_current );
836 }
837
838 return implode( ', ', array_filter( [ $date, $this->get_nice_start_time() ] ) );
839 }
840
841
842 public function is_bundle_scheduling(): bool {
843 return ! empty( $this->order_item_id );
844 }
845
846 public function get_connected_recurring_bookings(): array {
847 if ( empty( $this->recurrence_id ) || $this->is_new_record() ) {
848 return [];
849 }
850 $bookings = new OsBookingModel();
851 return $bookings->where(
852 [
853 'recurrence_id' => $this->recurrence_id,
854 'id !=' => $this->id,
855 ]
856 )->order_by( 'start_datetime_utc asc' )->get_results_as_models();
857 }
858
859 public function get_nice_datetime_for_summary( string $viewer = 'customer' ) {
860 $nice_datetime = '';
861 if ( $this->start_date ) {
862 $nice_datetime = $this->get_nice_start_datetime( false );
863 if ( OsSettingsHelper::is_on( 'show_booking_end_time' ) && ! empty( $this->end_time ) && ! empty( $this->start_time ) ) {
864 $nice_datetime = $nice_datetime . ' - ' . $this->get_nice_end_time();
865 }
866 }
867 /**
868 * Get a formatted start and end time (if needed)
869 *
870 * @since 5.1.0
871 * @hook latepoint_get_nice_datetime_for_summary
872 *
873 * @param {string} $statuses Nice datetime
874 * @param {OsBookingModel} $booking An object of booking model
875 *
876 * @returns {string} Filtered nice datetime
877 */
878 $nice_datetime = apply_filters( 'latepoint_get_nice_datetime_for_summary', $nice_datetime, $this, $viewer );
879 return $nice_datetime;
880 }
881
882
883 public function format_end_date_and_time( $format = LATEPOINT_DATETIME_DB_FORMAT, $input_timezone = false, $output_timezone = false ) {
884 if ( ! $input_timezone ) {
885 $input_timezone = OsTimeHelper::get_wp_timezone();
886 }
887 if ( ! $output_timezone ) {
888 $output_timezone = OsTimeHelper::get_wp_timezone();
889 }
890
891 $date = OsWpDateTime::os_createFromFormat( LATEPOINT_DATETIME_DB_FORMAT, $this->end_date . ' ' . OsTimeHelper::minutes_to_army_hours_and_minutes( $this->end_time ) . ':00', $input_timezone );
892 $date->setTimeZone( $output_timezone );
893
894 return OsUtilHelper::translate_months( $date->format( $format ) );
895 }
896
897 public function format_start_date() {
898 if ( empty( $this->start_date ) ) {
899 $date = new OsWpDateTime();
900 $this->start_date = $date->format( 'Y-m-d' );
901 } else {
902 $date = OsWpDateTime::os_createFromFormat( 'Y-m-d', $this->start_date );
903 }
904
905 return $date->format( OsSettingsHelper::get_date_format() );
906 }
907
908 public function format_start_date_and_time( $format = LATEPOINT_DATETIME_DB_FORMAT, $input_timezone = false, $output_timezone = false ) {
909 if ( ! $input_timezone ) {
910 $input_timezone = OsTimeHelper::get_wp_timezone();
911 }
912 if ( ! $output_timezone ) {
913 $output_timezone = OsTimeHelper::get_wp_timezone();
914 }
915
916 if ( is_null( $this->start_time ) || $this->start_time === '' ) {
917 // no time set yet (could be because summary is reloaded when date is picked, before the time is picked)
918 $date = OsWpDateTime::os_createFromFormat( 'Y-m-d', $this->start_date );
919 if ( $date ) {
920 return OsUtilHelper::translate_months( $date->format( OsSettingsHelper::get_readable_date_format() ) );
921 } else {
922 return __( 'Invalid Date/Time', 'latepoint' );
923 }
924 } else {
925 // both date & time are set, update timezone and translate
926 $date = OsWpDateTime::os_createFromFormat( LATEPOINT_DATETIME_DB_FORMAT, $this->start_date . ' ' . OsTimeHelper::minutes_to_army_hours_and_minutes( $this->start_time ) . ':00', $input_timezone );
927 if ( $date ) {
928 $date->setTimeZone( $output_timezone );
929
930 return OsUtilHelper::translate_months( $date->format( $format ) );
931 } else {
932 return __( 'Invalid Date/Time', 'latepoint' );
933 }
934 }
935 }
936
937 public function format_start_date_and_time_rfc3339() {
938 return $this->format_start_date_and_time( \DateTime::RFC3339 );
939 }
940
941 public function format_end_date_and_time_rfc3339() {
942 return $this->format_end_date_and_time( \DateTime::RFC3339 );
943 }
944
945 public function format_start_date_and_time_for_google() {
946 return $this->format_start_date_and_time( \DateTime::RFC3339 );
947 }
948
949 public function format_end_date_and_time_for_google() {
950 return $this->format_end_date_and_time( \DateTime::RFC3339 );
951 }
952
953 /*
954 * Checks if the booking has passed
955 */
956 public function time_status() {
957 try {
958 $now_datetime = OsTimeHelper::now_datetime_utc();
959 if ( empty( $this->start_datetime_utc ) || empty( $this->end_datetime_utc ) ) {
960 $this->set_utc_datetimes( true );
961 }
962 $booking_start = new OsWpDateTime( $this->start_datetime_utc, new DateTimeZone( 'UTC' ) );
963 $booking_end = new OsWpDateTime( $this->end_datetime_utc, new DateTimeZone( 'UTC' ) );
964 if ( ( $now_datetime <= $booking_end ) && ( $now_datetime >= $booking_start ) ) {
965 return 'now';
966 } elseif ( $now_datetime <= $booking_start ) {
967 return 'upcoming';
968 } else {
969 return 'past';
970 }
971 } catch ( Exception $e ) {
972 return 'past';
973 }
974 }
975
976 public function start_datetime_in_format( string $format, string $output_in_timezone_name ): string {
977 if ( empty( $this->start_datetime_utc ) ) {
978 return '';
979 }
980 $booking_start_datetime = OsTimeHelper::date_from_db( $this->start_datetime_utc );
981 $booking_start_datetime->setTimezone( new DateTimeZone( $output_in_timezone_name ) );
982 return $booking_start_datetime->format( $format );
983 }
984
985 public function is_start_date_and_time_set(): bool {
986 return ( $this->start_date != '' && $this->start_time != '' );
987 }
988
989 protected function get_time_left() {
990 $now_datetime = new OsWpDateTime( 'now' );
991 $booking_datetime = OsWpDateTime::os_createFromFormat( LATEPOINT_DATETIME_DB_FORMAT, $this->format_start_date_and_time() );
992 $css_class = 'left-days';
993
994 if ( $booking_datetime ) {
995 $diff = $now_datetime->diff( $booking_datetime );
996 if ( $diff->d > 0 || $diff->m > 0 || $diff->y > 0 ) {
997 $left = $diff->format( '%a ' . __( 'days', 'latepoint' ) );
998 } else {
999 if ( $diff->h > 0 ) {
1000 $css_class = 'left-hours';
1001 $left = $diff->format( '%h ' . __( 'hours', 'latepoint' ) );
1002 } else {
1003 $css_class = 'left-minutes';
1004 $left = $diff->format( '%i ' . __( 'minutes', 'latepoint' ) );
1005 }
1006 }
1007 } else {
1008 $left = 'n/a';
1009 }
1010
1011 return '<span class="time-left ' . esc_attr( $css_class ) . '">' . esc_html( $left ) . '</span>';
1012 }
1013
1014
1015 protected function get_agent() {
1016 if ( $this->agent_id ) {
1017 if ( ! isset( $this->agent ) || ( isset( $this->agent ) && ( $this->agent->id != $this->agent_id ) ) ) {
1018 $this->agent = new OsAgentModel( $this->agent_id );
1019 }
1020 } else {
1021 $this->agent = new OsAgentModel();
1022 }
1023
1024 return $this->agent;
1025 }
1026
1027 public function get_agent_full_name() {
1028 if ( $this->agent_id == LATEPOINT_ANY_AGENT ) {
1029 return __( 'Any Available Agent', 'latepoint' );
1030 } else {
1031 return $this->agent->full_name;
1032 }
1033 }
1034
1035
1036 public function get_location() {
1037 if ( $this->location_id ) {
1038 // if location has not been initialized yet, or location_id is different from the one initialized - init again
1039 if ( empty( $this->location ) || ( $this->location->id != $this->location_id ) ) {
1040 $this->location = new OsLocationModel( $this->location_id );
1041 }
1042 } else {
1043 $this->location = new OsLocationModel();
1044 }
1045
1046 return $this->location;
1047 }
1048
1049 protected function get_customer() {
1050 if ( $this->customer_id ) {
1051 if ( ! isset( $this->customer ) || ( isset( $this->customer ) && ( $this->customer->id != $this->customer_id ) ) ) {
1052 $this->customer = new OsCustomerModel( $this->customer_id );
1053 }
1054 } else {
1055 $this->customer = new OsCustomerModel();
1056 }
1057
1058 return $this->customer;
1059 }
1060
1061
1062 /**
1063 * Category of the service this booking is for. Not a column on the bookings
1064 * table - resolved through the service, and exposed as a queryable property
1065 * so processes can be conditioned on a service category instead of having to
1066 * list every service in it.
1067 */
1068 protected function get_service_category_id(): string {
1069 return $this->service_id ? (string) $this->service->category_id : '';
1070 }
1071
1072 protected function get_service() {
1073 if ( $this->service_id ) {
1074 if ( ! isset( $this->service ) || ( isset( $this->service ) && ( $this->service->id != $this->service_id ) ) ) {
1075 $this->service = new OsServiceModel( $this->service_id );
1076 }
1077 } else {
1078 $this->service = new OsServiceModel();
1079 }
1080
1081 return $this->service;
1082 }
1083
1084 public function get_nice_start_date_in_timezone( string $timezone_name = '', $hide_year_if_current = false ): string {
1085 $datetime = $this->get_start_datetime( $timezone_name );
1086 return OsTimeHelper::format_to_nice_date( $datetime, $hide_year_if_current );
1087 }
1088
1089 public function get_nice_end_date_in_timezone( string $timezone_name = '', $hide_year_if_current = false ): string {
1090 $datetime = $this->get_end_datetime( $timezone_name );
1091 return OsTimeHelper::format_to_nice_date( $datetime, $hide_year_if_current );
1092 }
1093
1094 public function get_nice_start_time_in_timezone( string $timezone_name = '' ): string {
1095 $datetime = $this->get_start_datetime( $timezone_name );
1096 return OsTimeHelper::format_to_nice_time( $datetime );
1097 }
1098
1099 public function get_nice_end_time_in_timezone( string $timezone_name = '' ): string {
1100 $datetime = $this->get_end_datetime( $timezone_name );
1101 return OsTimeHelper::format_to_nice_time( $datetime );
1102 }
1103
1104 public function get_start_datetime_object( ?DateTimeZone $timezone = null ) {
1105 if ( empty( $timezone ) ) {
1106 $timezone = OsTimeHelper::get_wp_timezone();
1107 }
1108 if ( empty( $this->start_datetime_utc ) ) {
1109 // fix data, probably an older booking from the time when we didn't store UTC date
1110 $this->start_datetime_utc = $this->generate_start_datetime_in_db_format();
1111 }
1112 $booking_start_datetime = OsTimeHelper::date_from_db( $this->start_datetime_utc );
1113 if ( $booking_start_datetime ) {
1114 $booking_start_datetime->setTimezone( $timezone );
1115 } else {
1116 OsDebugHelper::log( 'Error generating start date and time for booking ID: ' . $this->id, 'corrupt_booking_data' );
1117 }
1118
1119 return $booking_start_datetime;
1120 }
1121
1122 public function get_end_datetime_object( ?DateTimeZone $timezone = null ) {
1123 if ( empty( $timezone ) ) {
1124 $timezone = OsTimeHelper::get_wp_timezone();
1125 }
1126 if ( empty( $this->end_datetime_utc ) ) {
1127 // fix data, probably an older booking from the time when we didn't store UTC date
1128 $this->end_datetime_utc = $this->generate_end_datetime_in_db_format();
1129 }
1130 $booking_end_datetime = OsTimeHelper::date_from_db( $this->end_datetime_utc );
1131 if ( $booking_end_datetime ) {
1132 $booking_end_datetime->setTimezone( $timezone );
1133 } else {
1134 OsDebugHelper::log( 'Error generating end date and time for booking ID: ' . $this->id, 'corrupt_booking_data' );
1135 }
1136
1137 return $booking_end_datetime;
1138 }
1139
1140 public function get_start_datetime( string $set_timezone = 'UTC' ): OsWpDateTime {
1141 try {
1142 // start_time and start_date is legacy stored in wordpress timezone
1143 $dateTime = new OsWpDateTime( $this->start_date . ' 00:00:00', OsTimeHelper::get_wp_timezone() );
1144 if ( $this->start_time > 0 ) {
1145 $dateTime->modify( '+' . $this->start_time . ' minutes' );
1146 }
1147 if ( $set_timezone ) {
1148 $dateTime->setTimezone( new DateTimeZone( $set_timezone ) );
1149 }
1150 return $dateTime;
1151 } catch ( Exception $e ) {
1152 return new OsWpDateTime( 'now' );
1153 }
1154 }
1155
1156 public function get_end_datetime( string $set_timezone = 'UTC' ): OsWpDateTime {
1157 try {
1158 // start_time and start_date is legacy stored in wordpress timezone
1159 $dateTime = new OsWpDateTime( $this->end_date . ' 00:00:00', OsTimeHelper::get_wp_timezone() );
1160 if ( $this->end_time > 0 ) {
1161 $dateTime->modify( '+' . $this->end_time . ' minutes' );
1162 }
1163 if ( $set_timezone ) {
1164 $dateTime->setTimezone( new DateTimeZone( $set_timezone ) );
1165 }
1166 return $dateTime;
1167 } catch ( Exception $e ) {
1168 return new OsWpDateTime( 'now' );
1169 }
1170 }
1171
1172 public function generate_start_datetime_in_db_format( string $timezone = 'UTC' ): string {
1173 $dateTime = $this->get_start_datetime( $timezone );
1174
1175 return $dateTime->format( LATEPOINT_DATETIME_DB_FORMAT );
1176 }
1177
1178
1179 public function generate_end_datetime_in_db_format( string $timezone = 'UTC' ): string {
1180 $dateTime = $this->get_end_datetime( $timezone );
1181
1182 return $dateTime->format( LATEPOINT_DATETIME_DB_FORMAT );
1183 }
1184
1185
1186 protected function before_save() {
1187 // TODO check for uniqueness
1188 if ( empty( $this->booking_code ) ) {
1189 $this->booking_code = strtoupper( OsUtilHelper::random_text( 'distinct', 7 ) );
1190 }
1191 if ( empty( $this->end_date ) ) {
1192 $this->end_date = $this->calculate_end_date();
1193 }
1194 if ( empty( $this->status ) ) {
1195 $this->status = $this->get_default_booking_status();
1196 }
1197 if ( empty( $this->total_attendees ) ) {
1198 $this->total_attendees = 1;
1199 }
1200 if ( empty( $this->duration ) && $this->service_id ) {
1201 $service = new OsServiceModel( $this->service_id );
1202 $this->duration = $service->duration;
1203 }
1204 // buffers can arrive as NULL from a service with NULL buffer_before/after, and the bookings table declares them NOT NULL
1205 $this->buffer_before = (int) $this->buffer_before;
1206 $this->buffer_after = (int) $this->buffer_after;
1207 }
1208
1209 public function get_default_booking_status() {
1210 return OsBookingHelper::get_default_booking_status( $this->service_id );
1211 }
1212
1213 public function update_status( $new_status ) {
1214 if ( $new_status == $this->status ) {
1215 return true;
1216 } else {
1217 if ( ! in_array( $new_status, array_keys( OsBookingHelper::get_statuses_list() ) ) ) {
1218 $this->add_error( 'invalid_booking_status', 'Invalid booking status' );
1219
1220 return false;
1221 }
1222 $old_booking = clone $this;
1223 $this->status = $new_status;
1224 $result = $this->update_attributes( [ 'status' => $new_status ] );
1225 if ( $result ) {
1226 do_action( 'latepoint_booking_updated', $this, $old_booking );
1227
1228 return true;
1229 } else {
1230 return false;
1231 }
1232 }
1233 }
1234
1235 public function convert_start_datetime_into_server_timezone( string $input_timezone, bool $set_as_customer_timezone = true ) {
1236 $this->server_timezone = OsTimeHelper::get_wp_timezone_name();
1237 if ( $set_as_customer_timezone ) {
1238 $this->customer_timezone = $input_timezone;
1239 }
1240 if ( $this->is_start_date_and_time_set() && $this->server_timezone != $input_timezone ) {
1241
1242 try {
1243 // convert from submitted customer timezone into WP timezone
1244 $start_datetime = new OsWpDateTime( $this->start_date . ' 00:00:00', new DateTimeZone( $input_timezone ) );
1245 if ( $this->start_time > 0 ) {
1246 $start_datetime->modify( '+' . $this->start_time . ' minutes' );
1247 }
1248 $start_datetime->setTimezone( OsTimeHelper::get_wp_timezone() );
1249 $this->start_date = $start_datetime->format( 'Y-m-d' );
1250 $this->start_time = OsTimeHelper::convert_datetime_to_minutes( $start_datetime );
1251
1252 } catch ( Exception $e ) {
1253 }
1254 }
1255 }
1256
1257 public function save_avatar( $image_id = false ) {
1258 if ( ( false === $image_id ) && $this->image_id ) {
1259 $image_id = $this->image_id;
1260 }
1261 if ( $image_id && $this->post_id ) {
1262 set_post_thumbnail( $this->post_id, $image_id );
1263 $this->image_id = $image_id;
1264 }
1265
1266 return $this->image_id;
1267 }
1268
1269 protected function allowed_params( $role = 'admin' ) {
1270 $allowed_params = array(
1271 'service_id',
1272 'booking_code',
1273 'agent_id',
1274 'customer_id',
1275 'location_id',
1276 'start_date',
1277 'end_date',
1278 'start_time',
1279 'end_time',
1280 'start_datetime_utc',
1281 'end_datetime_utc',
1282 'buffer_before',
1283 'duration',
1284 'buffer_after',
1285 'total_attendees',
1286 'total_attendees_sum',
1287 'total_customers',
1288 'cart_item_id',
1289 'order_item_id',
1290 'status',
1291 'form_id',
1292 'server_timezone',
1293 'customer_timezone',
1294 'generate_recurrent_sequence',
1295 'recurrence_id',
1296 );
1297
1298 return $allowed_params;
1299 }
1300
1301
1302 protected function params_to_save( $role = 'admin' ) {
1303 $params_to_save = array(
1304 'service_id',
1305 'booking_code',
1306 'agent_id',
1307 'customer_id',
1308 'location_id',
1309 'start_date',
1310 'end_date',
1311 'start_time',
1312 'end_time',
1313 'start_datetime_utc',
1314 'end_datetime_utc',
1315 'duration',
1316 'buffer_before',
1317 'buffer_after',
1318 'total_attendees',
1319 'status',
1320 'order_item_id',
1321 'server_timezone',
1322 'customer_timezone',
1323 'recurrence_id',
1324 );
1325
1326 return $params_to_save;
1327 }
1328
1329
1330 protected function properties_to_validate() {
1331 $validations = array(
1332 'order_item_id' => array( 'presence' ),
1333 'service_id' => array( 'presence' ),
1334 'agent_id' => array( 'presence' ),
1335 'location_id' => array( 'presence' ),
1336 'customer_id' => array( 'presence' ),
1337 'start_date' => array( 'presence' ),
1338 'end_date' => array( 'presence' ),
1339 'status' => array( 'presence' ),
1340 );
1341
1342 return $validations;
1343 }
1344 }
1345