PluginProbe ʕ •ᴥ•ʔ
LatePoint – Calendar Booking Plugin for Appointments and Events / 5.1.1
LatePoint – Calendar Booking Plugin for Appointments and Events v5.1.1
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 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
1131 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() {
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 }
259
260
261 public function delete( $id = false ) {
262 if ( ! $id && isset( $this->id ) ) {
263 $id = $this->id;
264 }
265
266 $booking_metas = new OsBookingMetaModel();
267 $booking_metas->delete_where( [ 'object_id' => $id ] );
268 $process_jobs = new OsProcessJobModel();
269 $process_jobs->delete_where( [ 'object_id' => $id, 'object_model_type' => 'booking' ] );
270
271
272 return parent::delete( $id );
273 }
274
275 public function delete_meta_by_key( $meta_key ) {
276 if ( $this->is_new_record() ) {
277 return true;
278 }
279
280 $meta = new OsBookingMetaModel();
281
282 return $meta->delete_by_key( $meta_key, $this->id );
283 }
284
285 public function get_url_for_add_to_calendar_button( string $calendar_type ): string {
286 switch ( $calendar_type ) {
287 case 'google':
288 $url = 'https://calendar.google.com/calendar/render';
289 $params = [
290 'action' => 'TEMPLATE',
291 'text' => $this->service->name,
292 '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' )
293 ];
294 if ( ! empty( $this->location->full_address ) ) {
295 $params['location'] = $this->location->full_address;
296 }
297 break;
298 case 'outlook':
299 $url = 'https://outlook.office.com/calendar/0/deeplink/compose';
300 $params = [
301 'path' => '/calendar/action/compose',
302 'rru' => 'addevent',
303 'startdt' => $this->get_start_datetime_object( new DateTimeZone( 'UTC' ) )->format( 'Y-m-d\TH:i:s\Z' ),
304 'enddt' => $this->get_end_datetime_object( new DateTimeZone( 'UTC' ) )->format( 'Y-m-d\TH:i:s\Z' ),
305 'subject' => $this->service->name,
306 ];
307 break;
308 }
309 /**
310 * Generate params for the add to calendar link
311 *
312 * @param {array} $params Array of parameters that will be converted into a param query
313 * @param {string} $calendar_type Type of calendar the link is requested for
314 * @param {OsBookingModel} $booking A booking object
315 * @returns {array} The filtered array of appointment attributes
316 *
317 * @since 4.8.1
318 * @hook latepoint_build_add_to_calendar_link_params
319 *
320 */
321 $params = apply_filters( 'latepoint_build_add_to_calendar_link_params', $params, $calendar_type, $this );
322
323 $url = $url . '?' . http_build_query( $params );
324
325 /**
326 * URL for the link for a button to add appointment to calendar
327 *
328 * @param {array} $params Array of parameters that will be converted into a param query
329 * @param {string} $calendar_type Type of calendar the link is requested for
330 * @param {OsBookingModel} $booking A booking object
331 * @returns {string} The filtered url of adding appointment to calendar
332 *
333 * @since 4.8.1
334 * @hook latepoint_build_add_to_calendar_link_url
335 *
336 */
337 return apply_filters( 'latepoint_build_add_to_calendar_link_url', $url, $calendar_type, $this );
338 }
339
340 public function get_ical_download_link( $key = false ) {
341 return ( $key ) ? OsRouterHelper::build_admin_post_link( [
342 'manage_booking_by_key',
343 'ical_download'
344 ], [ 'key' => $key ] ) : OsRouterHelper::build_admin_post_link( [
345 'customer_cabinet',
346 'ical_download'
347 ], [ 'latepoint_booking_id' => $this->id ] );
348 }
349
350 public function get_print_link( $key = false ) {
351 return ( $key ) ? OsRouterHelper::build_admin_post_link( [
352 'manage_booking_by_key',
353 'print_booking_info'
354 ], [ 'key' => $key ] ) : OsRouterHelper::build_admin_post_link( [
355 'customer_cabinet',
356 'print_booking_info'
357 ], [ 'latepoint_booking_id' => $this->id ] );
358 }
359
360 public function get_meta_by_key( $meta_key, $default = false ) {
361 if ( $this->is_new_record() ) {
362 return $default;
363 }
364
365 $meta = new OsBookingMetaModel();
366
367 return $meta->get_by_key( $meta_key, $this->id, $default );
368 }
369
370 public function save_meta_by_key( $meta_key, $meta_value ) {
371 if ( $this->is_new_record() ) {
372 return false;
373 }
374
375 $meta = new OsBookingMetaModel();
376
377 return $meta->save_by_key( $meta_key, $meta_value, $this->id );
378 }
379
380 public function calculate_end_date() {
381 if ( empty( $this->start_time ) || empty( $this->start_date ) ) {
382 return $this->start_date;
383 }
384 if ( ( $this->start_time + $this->get_total_duration() ) >= ( 24 * 60 ) ) {
385 $date_obj = new OsWpDateTime( $this->start_date );
386 $end_date = $date_obj->modify( '+1 day' )->format( 'Y-m-d' );
387 } else {
388 $end_date = $this->start_date;
389 }
390
391 return $end_date;
392 }
393
394
395 public function calculate_end_time() {
396 $end_time = (int) $this->start_time + (int) $this->get_total_duration();
397 // continues to next day?
398 if ( $end_time > ( 24 * 60 ) ) {
399 $end_time = $end_time - ( 24 * 60 );
400 }
401
402 return $end_time;
403 }
404
405 public function calculate_end_date_and_time() {
406 $this->end_time = $this->calculate_end_time();
407 $this->end_date = $this->calculate_end_date();
408 }
409
410 public function after_data_was_set( $data ) {
411 if ( empty( $this->end_time ) ) {
412 $this->calculate_end_date_and_time();
413 }
414 if ( empty( $this->end_date ) ) {
415 $this->calculate_end_date();
416 }
417 }
418
419 public function set_buffers() {
420 if ( $this->service_id ) {
421 $service = new OsServiceModel( $this->service_id );
422 if ( $service ) {
423 $this->buffer_before = $service->buffer_before;
424 $this->buffer_after = $service->buffer_after;
425 }
426 }
427 }
428
429 public function get_total_duration( $calculate_from_start_and_end = false ) {
430 if ( $calculate_from_start_and_end ) {
431 if ( $this->start_date == $this->end_date ) {
432 // same day
433 $total_duration = $this->end_time - $this->start_time;
434 } else {
435 // TODO calculate how many days difference there is, if difference is more than 1 day - account for that
436 $total_duration = 60 * 24 - $this->start_time + $this->end_time;
437 }
438 } else {
439 if ( $this->duration ) {
440 $total_duration = $this->duration;
441 } else {
442 $total_duration = ( $this->service_id ) ? $this->service->duration : 60;
443 }
444 $total_duration = apply_filters( 'latepoint_calculated_total_duration', $total_duration, $this );
445 }
446
447 return (int) $total_duration;
448 }
449
450
451 public function get_start_time_shifted_for_customer() {
452 $start_time = OsTimeHelper::shift_time_by_minutes( $this->start_time, $this->customer->get_timeshift_in_minutes() );
453
454 return $start_time;
455 }
456
457 public function get_end_time_shifted_for_customer() {
458 $end_time = OsTimeHelper::shift_time_by_minutes( $this->end_time, $this->customer->get_timeshift_in_minutes() );
459
460 return $end_time;
461 }
462
463 public function get_nice_created_at( $include_time = true ) {
464 $format = $include_time ? OsSettingsHelper::get_readable_date_format() . ' ' . OsSettingsHelper::get_readable_time_format() : OsSettingsHelper::get_readable_date_format();
465
466 return date_format( date_create_from_format( LATEPOINT_DATETIME_DB_FORMAT, $this->created_at ), $format );
467 }
468
469 public function is_bookable( $load_customer_from_session = false, $allow_guest_customer = false ): bool {
470 if ( $load_customer_from_session ) {
471 $customer = OsAuthHelper::get_logged_in_customer();
472 $this->customer_id = $customer->id;
473 } else {
474 $customer = new OsCustomerModel( $this->customer_id );
475 }
476
477 // check if customer has to be assigned to a booking, or a guest booking is fine at this point
478 $customer_requirement_satisfied = $allow_guest_customer ? true : ($this->customer_id && $customer && $customer->id && ( $this->customer_id == $customer->id ));
479
480 // agent, service and customer should be set
481 if ( $this->service_id && $this->agent_id && $customer_requirement_satisfied) {
482
483 if ( $this->agent_id == LATEPOINT_ANY_AGENT && $this->location_id == LATEPOINT_ANY_LOCATION ) {
484 // both location and agent are set to any
485 $connections = new OsConnectorModel();
486 $connection_groups = $connections->select( LATEPOINT_TABLE_AGENTS_SERVICES . '.agent_id, ' . LATEPOINT_TABLE_AGENTS_SERVICES . '.location_id' )
487 ->where( [
488 'service_id' => $this->service_id,
489 LATEPOINT_TABLE_AGENTS . '.status' => LATEPOINT_AGENT_STATUS_ACTIVE,
490 LATEPOINT_TABLE_LOCATIONS . '.status' => LATEPOINT_LOCATION_STATUS_ACTIVE
491 ] )
492 ->join( LATEPOINT_TABLE_AGENTS, [ 'id' => LATEPOINT_TABLE_AGENTS_SERVICES . '.agent_id' ] )
493 ->join( LATEPOINT_TABLE_LOCATIONS, [ 'id' => LATEPOINT_TABLE_AGENTS_SERVICES . '.location_id' ] )
494 ->get_results( ARRAY_A );
495 if ( empty( $connection_groups ) ) {
496 // no active locations and agents are connected to this service
497 $this->add_error( 'send_to_step', __( 'Unfortunately there are no active resources that can offer selected service, please select another service.', 'latepoint' ), 'booking__service' );
498
499 return false;
500 } else {
501 foreach ( $connection_groups as $connection ) {
502 $this->location_id = $connection['location_id'];
503 $this->agent_id = OsBookingHelper::get_any_agent_for_booking_by_rule( $this );
504 // available agent found in this location - break the loop
505 if ( $this->agent_id ) {
506 break;
507 }
508 }
509 if ( ! $this->agent_id ) {
510 $this->add_error( 'send_to_step', __( 'Unfortunately the selected time slot is not available anymore, please select another timeslot.', 'latepoint' ), 'booking__datepicker' );
511
512 return false;
513 }
514 }
515
516
517 } elseif ( $this->agent_id == LATEPOINT_ANY_AGENT ) {
518 $this->agent_id = OsBookingHelper::get_any_agent_for_booking_by_rule( $this );
519 if ( ! $this->agent_id ) {
520 $this->add_error( 'send_to_step', __( 'Unfortunately the selected time slot is not available anymore, please select another timeslot.', 'latepoint' ), 'booking__datepicker' );
521
522 return false;
523 }
524 } elseif ( $this->location_id == LATEPOINT_ANY_LOCATION ) {
525 $this->location_id = OsBookingHelper::get_any_location_for_booking_by_rule( $this );
526 if ( ! $this->location_id ) {
527 $this->add_error( 'send_to_step', __( 'Unfortunately the selected time slot is not available anymore, please select another timeslot.', 'latepoint' ), 'booking__datepicker' );
528
529 return false;
530 }
531 } else {
532 // check if booking time is still available
533 if ( ! OsBookingHelper::is_booking_request_available( \LatePoint\Misc\BookingRequest::create_from_booking_model( $this ) ) ) {
534 // translators: %1$s is the timeslot date and time
535 // translators: %2$s is the service name
536 $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 );
537 $this->add_error( 'send_to_step', $error_message, 'booking__datepicker' );
538
539 return false;
540 }
541 }
542
543 if(!$this->validate(false, ['order_item_id', 'status', 'customer_id'])){
544 return false;
545 }
546
547 return true;
548 } else {
549 if ( ! $this->service_id ) {
550 $this->add_error( 'missing_service', __( 'You have to select a service', 'latepoint' ) );
551 }
552 if ( ! $this->agent_id ) {
553 $this->add_error( 'missing_agent', __( 'You have to select an agent', 'latepoint' ) );
554 }
555 if ( ! $this->customer_id && !$allow_guest_customer ) {
556 $this->add_error( 'missing_customer', __( 'Customer Not Found', 'latepoint' ) );
557 OsDebugHelper::log( 'Customer not found', 'customer_error', print_r( $customer, true ) );
558 }
559 if ( ! $customer && !$allow_guest_customer ) {
560 $this->add_error( 'missing_customer', __( 'You have to be logged in', 'latepoint' ) );
561 OsDebugHelper::log( 'Customer not logged in', 'customer_error', print_r( $customer, true ) );
562 }
563 OsDebugHelper::log( 'Error saving booking', 'booking_error', 'Agent: ' . $this->agent_id . ', Service: ' . $this->service_id . ', Booking Customer: ' . $this->customer_id );
564
565 return false;
566 }
567 }
568
569
570 public function get_nice_status() {
571 return OsBookingHelper::get_nice_status_name( $this->status );
572 }
573
574 public function get_latest_bookings_sorted_by_status( $args = array() ) {
575 $args = array_merge( array(
576 'service_id' => false,
577 'customer_id' => false,
578 'agent_id' => false,
579 'location_id' => false,
580 'limit' => false,
581 'offset' => false
582 ), $args );
583
584 $bookings = new OsBookingModel();
585 $query_args = array();
586 if ( $args['service_id'] ) {
587 $query_args['service_id'] = $args['service_id'];
588 }
589 if ( $args['customer_id'] ) {
590 $query_args['customer_id'] = $args['customer_id'];
591 }
592 if ( $args['agent_id'] ) {
593 $query_args['agent_id'] = $args['agent_id'];
594 }
595 if ( $args['location_id'] ) {
596 $query_args['location_id'] = $args['location_id'];
597 }
598 if ( $args['limit'] ) {
599 $bookings->set_limit( $args['limit'] );
600 }
601 if ( $args['offset'] ) {
602 $bookings->set_offset( $args['offset'] );
603 }
604
605 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();
606
607 }
608
609
610 public function should_not_be_cancelled() {
611 return $this->where( [ $this->table_name . '.status !=' => LATEPOINT_BOOKING_STATUS_CANCELLED ] );
612 }
613
614 public function should_be_cancelled() {
615 return $this->where( [ $this->table_name . '.status' => LATEPOINT_BOOKING_STATUS_CANCELLED ] );
616 }
617
618 public function should_be_approved() {
619 return $this->where( [ $this->table_name . '.status' => LATEPOINT_BOOKING_STATUS_APPROVED ] );
620 }
621
622 public function should_be_in_future() {
623 return $this->where( [
624 'OR' => [
625 'start_date >' => OsTimeHelper::today_date( 'Y-m-d' ),
626 'AND' => [
627 'start_date' => OsTimeHelper::today_date( 'Y-m-d' ),
628 'start_time >' => OsTimeHelper::get_current_minutes()
629 ]
630 ]
631 ] );
632 }
633
634
635 public function get_upcoming_bookings( $agent_id = false, $customer_id = false, $service_id = false, $location_id = false, int $limit = 3 ) {
636 $bookings = new OsBookingModel();
637 $args = array(
638 'OR' => array(
639 'start_date >' => OsTimeHelper::today_date( 'Y-m-d' ),
640 'AND' => array(
641 'start_date' => OsTimeHelper::today_date( 'Y-m-d' ),
642 'start_time >' => OsTimeHelper::get_current_minutes()
643 )
644 )
645 );
646 if ( $service_id ) {
647 $args['service_id'] = $service_id;
648 }
649 if ( $customer_id ) {
650 $args['customer_id'] = $customer_id;
651 }
652 if ( $agent_id ) {
653 $args['agent_id'] = $agent_id;
654 }
655 if ( $location_id ) {
656 $args['location_id'] = $location_id;
657 }
658
659 $args = OsAuthHelper::get_current_user()->clean_query_args( $args );
660
661 return $bookings->should_be_approved()
662 ->select( '*, count(id) as total_customers, sum(total_attendees) as total_attendees_sum' )
663 ->group_by( 'start_datetime_utc, agent_id, service_id, location_id' )
664 ->where( $args )
665 ->set_limit( $limit )
666 ->order_by( 'start_datetime_utc asc' )
667 ->get_results_as_models();
668
669 }
670
671 public function get_nice_start_time_for_customer() {
672 return $this->format_start_date_and_time( OsTimeHelper::get_time_format(), false, $this->customer->get_selected_timezone_obj() );
673 }
674
675 public function get_nice_end_time_for_customer() {
676 return $this->format_end_date_and_time( OsTimeHelper::get_time_format(), false, $this->customer->get_selected_timezone_obj() );
677 }
678
679 public function get_nice_start_date_for_customer() {
680 return $this->format_start_date_and_time( OsSettingsHelper::get_readable_date_format(), false, $this->customer->get_selected_timezone_obj() );
681 }
682
683 public function get_nice_start_datetime_for_customer() {
684 return $this->format_start_date_and_time( OsSettingsHelper::get_readable_datetime_format(), false, $this->customer->get_selected_timezone_obj() );
685 }
686
687 public function get_nice_start_time() {
688 return OsTimeHelper::minutes_to_hours_and_minutes( $this->start_time );
689 }
690
691 public function get_nice_end_time() {
692 return OsTimeHelper::minutes_to_hours_and_minutes( $this->end_time );
693 }
694
695 public function get_nice_end_date( $hide_year_if_current = false ) {
696 $d = OsWpDateTime::os_createFromFormat( "Y-m-d", $this->end_date );
697 if ( ! $d ) {
698 return 'n/a';
699 }
700 if ( $hide_year_if_current && ( $d->format( 'Y' ) == OsTimeHelper::today_date( 'Y' ) ) ) {
701 $format = OsSettingsHelper::get_readable_date_format( true );
702 } else {
703 $format = OsSettingsHelper::get_readable_date_format();
704 }
705
706 return OsUtilHelper::translate_months( $d->format( $format ) );
707 }
708
709 public function get_nice_start_date( $hide_year_if_current = false ) {
710 $d = OsWpDateTime::os_createFromFormat( "Y-m-d", $this->start_date );
711 if ( ! $d ) {
712 return 'n/a';
713 }
714 if ( $hide_year_if_current && ( $d->format( 'Y' ) == OsTimeHelper::today_date( 'Y' ) ) ) {
715 $format = OsSettingsHelper::get_readable_date_format( true );
716 } else {
717 $format = OsSettingsHelper::get_readable_date_format();
718 }
719
720 return OsUtilHelper::translate_months( $d->format( $format ) );
721 }
722
723
724 /**
725 * @param $hide_if_today bool
726 * @param $hide_year_if_current bool
727 *
728 * @return string
729 */
730 public function get_nice_start_datetime( bool $hide_if_today = true, bool $hide_year_if_current = true ): string {
731 if ( $hide_if_today && $this->start_date == OsTimeHelper::today_date( 'Y-m-d' ) ) {
732 $date = __( 'Today', 'latepoint' );
733 } else {
734 $date = $this->get_nice_start_date( $hide_year_if_current );
735 }
736
737 return implode( ', ', array_filter( [ $date, $this->get_nice_start_time() ] ) );
738 }
739
740 public function get_nice_datetime_for_summary(string $viewer = 'customer'){
741 $nice_datetime = '';
742 if($this->start_date){
743 $nice_datetime = $this->get_nice_start_datetime(false);
744 if(OsSettingsHelper::is_on( 'show_booking_end_time') && !empty($this->end_time) && !empty($this->start_time)){
745 $nice_datetime = $nice_datetime.' - '.$this->get_nice_end_time();
746 }
747 }
748 /**
749 * Get a formatted start and end time (if needed)
750 *
751 * @since 5.1.0
752 * @hook latepoint_get_nice_datetime_for_summary
753 *
754 * @param {string} $statuses Nice datetime
755 * @param {OsBookingModel} $booking An object of booking model
756 *
757 * @returns {string} Filtered nice datetime
758 */
759 $nice_datetime = apply_filters('latepoint_get_nice_datetime_for_summary', $nice_datetime, $this, $viewer);
760 return $nice_datetime;
761 }
762
763
764 public function format_end_date_and_time( $format = LATEPOINT_DATETIME_DB_FORMAT, $input_timezone = false, $output_timezone = false ) {
765 if ( ! $input_timezone ) {
766 $input_timezone = OsTimeHelper::get_wp_timezone();
767 }
768 if ( ! $output_timezone ) {
769 $output_timezone = OsTimeHelper::get_wp_timezone();
770 }
771
772 $date = OsWpDateTime::os_createFromFormat( LATEPOINT_DATETIME_DB_FORMAT, $this->end_date . ' ' . OsTimeHelper::minutes_to_army_hours_and_minutes( $this->end_time ) . ':00', $input_timezone );
773 $date->setTimeZone( $output_timezone );
774
775 return OsUtilHelper::translate_months( $date->format( $format ) );
776 }
777
778 public function format_start_date() {
779 if ( empty( $this->start_date ) ) {
780 $date = new OsWpDateTime();
781 $this->start_date = $date->format( 'Y-m-d' );
782 } else {
783 $date = OsWpDateTime::os_createFromFormat( "Y-m-d", $this->start_date );
784 }
785
786 return $date->format( OsSettingsHelper::get_date_format() );
787 }
788
789 public function format_start_date_and_time( $format = LATEPOINT_DATETIME_DB_FORMAT, $input_timezone = false, $output_timezone = false ) {
790 if ( ! $input_timezone ) {
791 $input_timezone = OsTimeHelper::get_wp_timezone();
792 }
793 if ( ! $output_timezone ) {
794 $output_timezone = OsTimeHelper::get_wp_timezone();
795 }
796
797 if ( is_null( $this->start_time ) || $this->start_time === '' ) {
798 // no time set yet (could be because summary is reloaded when date is picked, before the time is picked)
799 $date = OsWpDateTime::os_createFromFormat( "Y-m-d", $this->start_date );
800 if ( $date ) {
801 return OsUtilHelper::translate_months( $date->format( OsSettingsHelper::get_readable_date_format() ) );
802 } else {
803 return __( 'Invalid Date/Time', 'latepoint' );
804 }
805 } else {
806 // both date & time are set, update timezone and translate
807 $date = OsWpDateTime::os_createFromFormat( LATEPOINT_DATETIME_DB_FORMAT, $this->start_date . ' ' . OsTimeHelper::minutes_to_army_hours_and_minutes( $this->start_time ) . ':00', $input_timezone );
808 if ( $date ) {
809 $date->setTimeZone( $output_timezone );
810
811 return OsUtilHelper::translate_months( $date->format( $format ) );
812 } else {
813 return __( 'Invalid Date/Time', 'latepoint' );
814 }
815 }
816 }
817
818 public function format_start_date_and_time_rfc3339() {
819 return $this->format_start_date_and_time( \DateTime::RFC3339 );
820 }
821
822 public function format_end_date_and_time_rfc3339() {
823 return $this->format_end_date_and_time( \DateTime::RFC3339 );
824 }
825
826 public function format_start_date_and_time_for_google() {
827 return $this->format_start_date_and_time( \DateTime::RFC3339 );
828 }
829
830 public function format_end_date_and_time_for_google() {
831 return $this->format_end_date_and_time( \DateTime::RFC3339 );
832 }
833
834 /*
835 * Checks if the booking has passed
836 */
837 public function time_status() {
838 try {
839 $now_datetime = OsTimeHelper::now_datetime_utc();
840 $booking_start = new OsWpDateTime( $this->start_datetime_utc, new DateTimeZone( 'UTC' ) );
841 $booking_end = new OsWpDateTime( $this->end_datetime_utc, new DateTimeZone( 'UTC' ) );
842 if ( ( $now_datetime <= $booking_end ) && ( $now_datetime >= $booking_start ) ) {
843 return 'now';
844 } elseif ( $now_datetime <= $booking_start ) {
845 return 'upcoming';
846 } else {
847 return 'past';
848 }
849 } catch ( Exception $e ) {
850 return 'past';
851 }
852
853 }
854
855 public function start_datetime_in_format( string $format, string $output_in_timezone_name ) : string {
856 if(empty($this->start_datetime_utc)) return '';
857 $booking_start_datetime = OsTimeHelper::date_from_db( $this->start_datetime_utc );
858 $booking_start_datetime->setTimezone( new DateTimeZone($output_in_timezone_name) );
859 return $booking_start_datetime->format( $format );
860 }
861
862 protected function get_time_left() {
863 $now_datetime = new OsWpDateTime( 'now' );
864 $booking_datetime = OsWpDateTime::os_createFromFormat( LATEPOINT_DATETIME_DB_FORMAT, $this->format_start_date_and_time() );
865 $css_class = 'left-days';
866
867 if ( $booking_datetime ) {
868 $diff = $now_datetime->diff( $booking_datetime );
869 if ( $diff->d > 0 ) {
870 $left = $diff->format( '%a ' . __( 'days', 'latepoint' ) );
871 } else {
872 if ( $diff->h > 0 ) {
873 $css_class = 'left-hours';
874 $left = $diff->format( '%h ' . __( 'hours', 'latepoint' ) );
875 } else {
876 $css_class = 'left-minutes';
877 $left = $diff->format( '%i ' . __( 'minutes', 'latepoint' ) );
878 }
879 }
880 } else {
881 $left = 'n/a';
882 }
883
884 return '<span class="time-left ' . esc_attr($css_class) . '">' . esc_html($left) . '</span>';
885 }
886
887
888 protected function get_agent() {
889 if ( $this->agent_id ) {
890 if ( ! isset( $this->agent ) || ( isset( $this->agent ) && ( $this->agent->id != $this->agent_id ) ) ) {
891 $this->agent = new OsAgentModel( $this->agent_id );
892 }
893 } else {
894 $this->agent = new OsAgentModel();
895 }
896
897 return $this->agent;
898 }
899
900 public function get_agent_full_name() {
901 if ( $this->agent_id == LATEPOINT_ANY_AGENT ) {
902 return __( 'Any Available Agent', 'latepoint' );
903 } else {
904 return $this->agent->full_name;
905 }
906 }
907
908
909 public function get_location() {
910 if ( $this->location_id ) {
911 // if location has not been initialized yet, or location_id is different from the one initialized - init again
912 if ( empty( $this->location ) || ( $this->location->id != $this->location_id ) ) {
913 $this->location = new OsLocationModel( $this->location_id );
914 }
915 } else {
916 $this->location = new OsLocationModel();
917 }
918
919 return $this->location;
920 }
921
922 protected function get_customer() {
923 if ( $this->customer_id ) {
924 if ( ! isset( $this->customer ) || ( isset( $this->customer ) && ( $this->customer->id != $this->customer_id ) ) ) {
925 $this->customer = new OsCustomerModel( $this->customer_id );
926 }
927 } else {
928 $this->customer = new OsCustomerModel();
929 }
930
931 return $this->customer;
932 }
933
934
935 protected function get_service() {
936 if ( $this->service_id ) {
937 if ( ! isset( $this->service ) || ( isset( $this->service ) && ( $this->service->id != $this->service_id ) ) ) {
938 $this->service = new OsServiceModel( $this->service_id );
939 }
940 } else {
941 $this->service = new OsServiceModel();
942 }
943
944 return $this->service;
945 }
946
947 public function get_start_datetime_object( DateTimeZone $timezone = null ) {
948 if ( empty( $timezone ) ) {
949 $timezone = OsTimeHelper::get_wp_timezone();
950 }
951 if ( empty( $this->start_datetime_utc ) ) {
952 // fix data, probably an older booking from the time when we didn't store UTC date
953 $this->start_datetime_utc = $this->generate_start_datetime_in_db_format();
954 }
955 $booking_start_datetime = OsTimeHelper::date_from_db( $this->start_datetime_utc );
956 if ( $booking_start_datetime ) {
957 $booking_start_datetime->setTimezone( $timezone );
958 } else {
959 OsDebugHelper::log( 'Error generating start date and time for booking ID: ' . $this->id, 'corrupt_booking_data' );
960 }
961
962 return $booking_start_datetime;
963 }
964
965 public function get_end_datetime_object( DateTimeZone $timezone = null ) {
966 if ( empty( $timezone ) ) {
967 $timezone = OsTimeHelper::get_wp_timezone();
968 }
969 if ( empty( $this->end_datetime_utc ) ) {
970 // fix data, probably an older booking from the time when we didn't store UTC date
971 $this->end_datetime_utc = $this->generate_end_datetime_in_db_format();
972 }
973 $booking_end_datetime = OsTimeHelper::date_from_db( $this->end_datetime_utc );
974 if ( $booking_end_datetime ) {
975 $booking_end_datetime->setTimezone( $timezone );
976 } else {
977 OsDebugHelper::log( 'Error generating end date and time for booking ID: ' . $this->id, 'corrupt_booking_data' );
978 }
979
980 return $booking_end_datetime;
981 }
982
983
984 public function generate_start_datetime_in_db_format( string $timezone = 'UTC' ): string {
985 // start_time and start_date is legacy stored in wordpress timezone
986 $dateTime = new OsWpDateTime( $this->start_date . ' 00:00:00', OsTimeHelper::get_wp_timezone() );
987 $dateTime->modify( '+' . $this->start_time . ' minutes' );
988 $dateTime->setTimezone( new DateTimeZone( $timezone ) );
989
990 return $dateTime->format( LATEPOINT_DATETIME_DB_FORMAT );
991 }
992
993
994 public function generate_end_datetime_in_db_format( string $timezone = 'UTC' ): string {
995 // end_time and end_date is legacy stored in wordpress timezone
996 $dateTime = new OsWpDateTime( $this->end_date . ' 00:00:00', OsTimeHelper::get_wp_timezone() );
997 $dateTime->modify( '+' . $this->end_time . ' minutes' );
998 $dateTime->setTimezone( new DateTimeZone( $timezone ) );
999
1000 return $dateTime->format( LATEPOINT_DATETIME_DB_FORMAT );
1001 }
1002
1003
1004 protected function before_save() {
1005 // TODO check for uniqueness
1006 if ( empty( $this->booking_code ) ) {
1007 $this->booking_code = strtoupper( OsUtilHelper::random_text( 'distinct', 7 ) );
1008 }
1009 if ( empty( $this->end_date ) ) {
1010 $this->end_date = $this->calculate_end_date();
1011 }
1012 if ( empty( $this->status ) ) {
1013 $this->status = $this->get_default_booking_status();
1014 }
1015 if ( empty( $this->total_attendees ) ) {
1016 $this->total_attendees = 1;
1017 }
1018 if ( empty( $this->duration ) && $this->service_id ) {
1019 $service = new OsServiceModel( $this->service_id );
1020 $this->duration = $service->duration;
1021 }
1022 }
1023
1024 public function get_default_booking_status() {
1025 return OsBookingHelper::get_default_booking_status( $this->service_id );
1026 }
1027
1028 public function update_status( $new_status ) {
1029 if ( $new_status == $this->status ) {
1030 return true;
1031 } else {
1032 if ( ! in_array( $new_status, array_keys( OsBookingHelper::get_statuses_list() ) ) ) {
1033 $this->add_error( 'invalid_booking_status', 'Invalid booking status' );
1034
1035 return false;
1036 }
1037 $old_booking = clone $this;
1038 $this->status = $new_status;
1039 $result = $this->update_attributes( [ 'status' => $new_status ] );
1040 if ( $result ) {
1041 do_action( 'latepoint_booking_updated', $this, $old_booking );
1042
1043 return true;
1044 } else {
1045 return false;
1046 }
1047 }
1048 }
1049
1050 public function save_avatar( $image_id = false ) {
1051 if ( ( false === $image_id ) && $this->image_id ) {
1052 $image_id = $this->image_id;
1053 }
1054 if ( $image_id && $this->post_id ) {
1055 set_post_thumbnail( $this->post_id, $image_id );
1056 $this->image_id = $image_id;
1057 }
1058
1059 return $this->image_id;
1060 }
1061
1062
1063 protected function allowed_params( $role = 'admin' ) {
1064 $allowed_params = array(
1065 'service_id',
1066 'booking_code',
1067 'agent_id',
1068 'customer_id',
1069 'location_id',
1070 'start_date',
1071 'end_date',
1072 'start_time',
1073 'end_time',
1074 'start_datetime_utc',
1075 'end_datetime_utc',
1076 'buffer_before',
1077 'duration',
1078 'buffer_after',
1079 'total_attendees',
1080 'total_attendees_sum',
1081 'total_customers',
1082 'cart_item_id',
1083 'order_item_id',
1084 'status',
1085 'form_id'
1086 );
1087
1088 return $allowed_params;
1089 }
1090
1091
1092 protected function params_to_save( $role = 'admin' ) {
1093 $params_to_save = array(
1094 'service_id',
1095 'booking_code',
1096 'agent_id',
1097 'customer_id',
1098 'location_id',
1099 'start_date',
1100 'end_date',
1101 'start_time',
1102 'end_time',
1103 'start_datetime_utc',
1104 'end_datetime_utc',
1105 'duration',
1106 'buffer_before',
1107 'buffer_after',
1108 'total_attendees',
1109 'status',
1110 'order_item_id'
1111 );
1112
1113 return $params_to_save;
1114 }
1115
1116
1117 protected function properties_to_validate() {
1118 $validations = array(
1119 'order_item_id' => array( 'presence' ),
1120 'service_id' => array( 'presence' ),
1121 'agent_id' => array( 'presence' ),
1122 'location_id' => array( 'presence' ),
1123 'customer_id' => array( 'presence' ),
1124 'start_date' => array( 'presence' ),
1125 'end_date' => array( 'presence' ),
1126 'status' => array( 'presence' ),
1127 );
1128
1129 return $validations;
1130 }
1131 }