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