[Back] <?php
/**
* @property OsCustomerModel $customer
* @property OsAgentModel $agent
* @property OsServiceModel $service
* @property OsLocationModel $location
*/
class OsBookingModel extends OsModel {
public $id,
$booking_code,
$service_id,
$customer_id,
$agent_id,
$location_id,
$recurrence_id,
$buffer_before = 0,
$buffer_after = 0,
$status,
$start_date,
$end_date,
$start_time,
$end_time,
$start_datetime_utc,
$end_datetime_utc,
$duration,
$total_attendees = 1,
$total_attendees_sum = 1,
$total_customers = 1,
$cart_item_id = null,
$order_item_id,
$server_timezone,
$customer_timezone,
$meta_class = 'OsBookingMetaModel',
$keys_to_manage = [],
$generate_recurrent_sequence = [],
$updated_at,
$created_at;
function __construct( $id = false ) {
parent::__construct();
$this->table_name = LATEPOINT_TABLE_BOOKINGS;
$this->nice_names = array(
'service_id' => __( 'Service', 'latepoint' ),
'agent_id' => __( 'Agent', 'latepoint' )
);
if ( $id ) {
$this->load_by_id( $id );
}
}
/**
* @return mixed|void
*
* Returns full amount to charge in database format 1999.0000
*
*/
public function full_amount_to_charge() {
return OsBookingHelper::calculate_full_amount_for_booking( $this );
}
/**
* @return mixed|void
*
* Returns deposit amount to charge in database format 1999.0000
*
*/
public function deposit_amount_to_charge() {
return OsBookingHelper::calculate_deposit_amount_to_charge( $this );
}
public function get_key_to_manage_for(string $for): string {
if($this->is_new_record()) return '';
if(!empty($this->keys_to_manage[$for])) return $this->keys_to_manage[$for];
$key = OsMetaHelper::get_booking_meta_by_key( 'key_to_manage_for_' . $for, $this->id );
if ( empty( $key ) ) {
$key = OsUtilHelper::generate_key_to_manage();
OsMetaHelper::save_booking_meta_by_key( 'key_to_manage_for_' . $for, $key, $this->id );
}
$this->keys_to_manage[$for] = $key;
return $key;
}
public function manage_by_key_url(string $for = 'customer'): string{
return OsBookingHelper::generate_direct_manage_booking_url($this, $for);
}
public function get_service_name_for_summary() {
$service_name = $this->service_id ? $this->service->name : '';
/**
* Get service name to be displayed on a booking summary
*
* @param {string} $service_name Service name to be filtered
* @param {OsBookingModel} $booking Booking model which service name is requested
*
* @returns {string} Filtered service name
* @since 5.0.0
* @hook latepoint_booking_get_service_name_for_summary
*
*/
return apply_filters( 'latepoint_booking_get_service_name_for_summary', $service_name, $this );
}
public function get_order() {
if ( $this->order_item_id ) {
if ( ! isset( $this->order_item ) || ( $this->order_item->id != $this->order_item_id ) ) {
$this->order_item = new OsOrderItemModel( $this->order_item_id );
if ( ! isset( $this->order ) || ( $this->order->id != $this->order_item->order_id ) ) {
$this->order = new OsOrderModel( $this->order_item->order_id );
}
}
} else {
$this->order = new OsOrderModel();
}
return $this->order;
}
public function get_order_item( ) {
if (!isset( $this->order_item )) {
$this->order_item = new OsOrderItemModel( $this->order_item_id );
}
return $this->order_item;
}
public function filter_allowed_records(): OsModel {
if ( ! OsRolesHelper::are_all_records_allowed() ) {
if ( ! OsRolesHelper::are_all_records_allowed( 'agent' ) ) {
$this->filter_where_conditions( [ 'agent_id' => OsRolesHelper::get_allowed_records( 'agent' ) ] );
}
if ( ! OsRolesHelper::are_all_records_allowed( 'location' ) ) {
$this->filter_where_conditions( [ 'location_id' => OsRolesHelper::get_allowed_records( 'location' ) ] );
}
if ( ! OsRolesHelper::are_all_records_allowed( 'service' ) ) {
$this->filter_where_conditions( [ 'service_id' => OsRolesHelper::get_allowed_records( 'service' ) ] );
}
}
return $this;
}
public function properties_to_query(): array {
return [
'service_id' => __( 'Service', 'latepoint' ),
'agent_id' => __( 'Agent', 'latepoint' ),
'status' => __( 'Status', 'latepoint' ),
'start_datetime_utc' => __( 'Start Time', 'latepoint' ),
];
}
public function generate_item_data() {
return wp_json_encode( $this->generate_params_for_booking_form() );
}
public function generate_params_for_booking_form() {
$params = [
"id" => $this->id,
"customer_id" => $this->customer_id,
"agent_id" => $this->agent_id,
"location_id" => $this->location_id,
"service_id" => $this->service_id,
"recurrence_id" => $this->recurrence_id,
"start_date" => $this->start_date,
"start_time" => $this->start_time,
"end_date" => $this->end_date,
"end_time" => $this->end_time,
"status" => $this->status,
"buffer_before" => $this->buffer_before,
"buffer_after" => $this->buffer_after,
"duration" => $this->duration,
"generate_recurrent_sequence" => $this->generate_recurrent_sequence,
];
/**
* Returns an array of params generated from OsBookingModel to be used in a booking form
*
* @param {array} $params Array of booking params
* @param {OsBookingModel} $booking Instance of <code>OsBookingModel</code> that params are being generated for
*
* @returns {array} Filtered array of booking params
* @since 5.0.0
* @hook latepoint_generated_params_for_booking_form
*
*/
return apply_filters( 'latepoint_generated_params_for_booking_form', $params, $this );
}
public function get_formatted_price(){
$order_item = new OsOrderItemModel( $this->order_item_id );
return OsMoneyHelper::format_price($order_item->get_total());
}
public function generate_first_level_data_vars() : array{
$vars = [
'id' => $this->id,
'booking_code' => $this->booking_code,
'start_datetime' => $this->format_start_date_and_time_rfc3339(),
'end_datetime' => $this->format_end_date_and_time_rfc3339(),
'service_name' => $this->service->name,
'duration' => $this->duration,
'customer_comment' => $this->order->customer_comment,
'status' => $this->status,
'start_date' => $this->format_start_date(),
'start_time' => OsTimeHelper::minutes_to_hours_and_minutes( $this->start_time ),
'timezone' => OsTimeHelper::get_wp_timezone_name(),
'agent' => $this->agent->get_data_vars(),
'created_datetime' => $this->format_created_datetime_rfc3339(),
'manage_booking_for_agent' => OsBookingHelper::generate_direct_manage_booking_url( $this, 'agent' ),
'manage_booking_for_customer' => OsBookingHelper::generate_direct_manage_booking_url( $this, 'customer' ),
];
return $vars;
}
public function generate_data_vars(): array {
$vars = $this->get_first_level_data_vars();
$vars['customer'] = $this->customer->get_data_vars();
$vars['transactions'] = [];
$vars['order'] = $this->order->get_first_level_data_vars();
$transactions = $this->order->get_transactions();
if ( $transactions ) {
foreach ( $transactions as $transaction ) {
$vars['transactions'][] = $transaction->get_data_vars();
}
}
return $vars;
}
public function is_ready_for_summary() {
return ( $this->agent_id && $this->agent_id != LATEPOINT_ANY_AGENT && OsAgentHelper::count_agents() > 1 ) || $this->service_id;
}
public function is_part_of_bundle(): bool {
if ( $this->order_item_id ) {
$order_item = new OsOrderItemModel( $this->order_item_id );
return $order_item->is_bundle();
}
return false;
}
public function is_upcoming(): bool {
if ( empty( $this->start_datetime_utc ) ) {
return false;
}
$start_time_utc = new OsWpDateTime( $this->start_datetime_utc, new DateTimeZone( 'UTC' ) );
$now_time_utc = new OsWpDateTime( 'now', new DateTimeZone( 'UTC' ) );
return ( $start_time_utc > $now_time_utc );
}
public function set_utc_datetimes(bool $save = false) {
if ( empty( $this->start_date ) || empty( $this->end_date ) || empty( $this->start_time ) || empty( $this->end_time ) ) {
return;
}
$this->start_datetime_utc = $this->get_start_datetime('UTC')->format(LATEPOINT_DATETIME_DB_FORMAT);
$this->end_datetime_utc = $this->get_end_datetime('UTC')->format(LATEPOINT_DATETIME_DB_FORMAT);
if ( $save ) {
$this->update_attributes(['start_datetime_utc' => $this->start_datetime_utc, 'end_datetime_utc' => $this->end_datetime_utc]);
}
}
public function delete( $id = false ) {
if ( ! $id && isset( $this->id ) ) {
$id = $this->id;
}
$booking_metas = new OsBookingMetaModel();
$booking_metas->delete_where( [ 'object_id' => $id ] );
$process_jobs = new OsProcessJobModel();
$process_jobs->delete_where( [ 'object_id' => $id, 'object_model_type' => 'booking' ] );
return parent::delete( $id );
}
public function delete_meta_by_key( $meta_key ) {
if ( $this->is_new_record() ) {
return true;
}
$meta = new OsBookingMetaModel();
return $meta->delete_by_key( $meta_key, $this->id );
}
public function get_url_for_add_to_calendar_button( string $calendar_type ): string {
switch ( $calendar_type ) {
case 'google':
$url = 'https://calendar.google.com/calendar/render';
$params = [
'action' => 'TEMPLATE',
'text' => $this->service->name,
'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' )
];
if ( ! empty( $this->location->full_address ) ) {
$params['location'] = $this->location->full_address;
}
break;
case 'outlook':
$url = 'https://outlook.office.com/calendar/0/deeplink/compose';
$params = [
'path' => '/calendar/action/compose',
'rru' => 'addevent',
'startdt' => $this->get_start_datetime_object( new DateTimeZone( 'UTC' ) )->format( 'Y-m-d\TH:i:s\Z' ),
'enddt' => $this->get_end_datetime_object( new DateTimeZone( 'UTC' ) )->format( 'Y-m-d\TH:i:s\Z' ),
'subject' => $this->service->name,
];
break;
}
/**
* Generate params for the add to calendar link
*
* @param {array} $params Array of parameters that will be converted into a param query
* @param {string} $calendar_type Type of calendar the link is requested for
* @param {OsBookingModel} $booking A booking object
* @returns {array} The filtered array of appointment attributes
*
* @since 4.8.1
* @hook latepoint_build_add_to_calendar_link_params
*
*/
$params = apply_filters( 'latepoint_build_add_to_calendar_link_params', $params, $calendar_type, $this );
$url = $url . '?' . http_build_query( $params );
/**
* URL for the link for a button to add appointment to calendar
*
* @param {array} $params Array of parameters that will be converted into a param query
* @param {string} $calendar_type Type of calendar the link is requested for
* @param {OsBookingModel} $booking A booking object
* @returns {string} The filtered url of adding appointment to calendar
*
* @since 4.8.1
* @hook latepoint_build_add_to_calendar_link_url
*
*/
return apply_filters( 'latepoint_build_add_to_calendar_link_url', $url, $calendar_type, $this );
}
public function get_ical_download_link( $key = false ) {
return ( $key ) ? OsRouterHelper::build_admin_post_link( [
'manage_booking_by_key',
'ical_download'
], [ 'key' => $key ] ) : OsRouterHelper::build_admin_post_link( [
'customer_cabinet',
'ical_download'
], [ 'latepoint_booking_id' => $this->id ] );
}
public function get_print_link( $key = false ) {
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 ] );
}
public function get_meta_by_key( $meta_key, $default = false ) {
if ( $this->is_new_record() ) {
return $default;
}
$meta = new OsBookingMetaModel();
return $meta->get_by_key( $meta_key, $this->id, $default );
}
public function get_coupon_code( ) {
$order = $this->get_order();
return $order->coupon_code;
}
public function get_coupon_discount( ): string {
$order_item = $this->get_order_item();
$coupon_discount = $order_item->get_coupon_discount();
return $coupon_discount > 0 ? OsMoneyHelper::format_price($order_item->get_coupon_discount()) : '';
}
public function save_meta_by_key( $meta_key, $meta_value ) {
if ( $this->is_new_record() ) {
return false;
}
$meta = new OsBookingMetaModel();
return $meta->save_by_key( $meta_key, $meta_value, $this->id );
}
public function calculate_end_date() {
if ( empty( $this->start_time ) || empty( $this->start_date ) ) {
return $this->start_date;
}
if ( ( $this->start_time + $this->get_total_duration() ) >= ( 24 * 60 ) ) {
$date_obj = new OsWpDateTime( $this->start_date );
$end_date = $date_obj->modify( '+1 day' )->format( 'Y-m-d' );
} else {
$end_date = $this->start_date;
}
return $end_date;
}
public function calculate_end_time() {
$end_time = (int) $this->start_time + (int) $this->get_total_duration();
// continues to next day?
if ( $end_time > ( 24 * 60 ) ) {
$end_time = $end_time - ( 24 * 60 );
}
return $end_time;
}
public function calculate_end_date_and_time() {
$this->end_time = $this->calculate_end_time();
$this->end_date = $this->calculate_end_date();
}
public function after_data_was_set( $data ) {
if ( empty( $this->end_time ) ) {
$this->calculate_end_date_and_time();
}
if ( empty( $this->end_date ) ) {
$this->calculate_end_date();
}
}
public function set_buffers() {
if ( $this->service_id ) {
$service = new OsServiceModel( $this->service_id );
if ( $service ) {
$this->buffer_before = $service->buffer_before;
$this->buffer_after = $service->buffer_after;
}
}
}
public function get_total_duration( $calculate_from_start_and_end = false ) {
if ( $calculate_from_start_and_end ) {
if ( $this->start_date == $this->end_date ) {
// same day
$total_duration = $this->end_time - $this->start_time;
} else {
// TODO calculate how many days difference there is, if difference is more than 1 day - account for that
$total_duration = 60 * 24 - $this->start_time + $this->end_time;
}
} else {
if ( $this->duration ) {
$total_duration = $this->duration;
} else {
$total_duration = ( $this->service_id ) ? $this->service->duration : 60;
}
$total_duration = apply_filters( 'latepoint_calculated_total_duration', $total_duration, $this );
}
return (int) $total_duration;
}
public function get_nice_created_at( $include_time = true ) {
$format = $include_time ? OsSettingsHelper::get_readable_date_format() . ' ' . OsSettingsHelper::get_readable_time_format() : OsSettingsHelper::get_readable_date_format();
$utc_date = date_create_from_format( LATEPOINT_DATETIME_DB_FORMAT, $this->created_at );
$wp_timezone_date = $utc_date->setTimezone(OsTimeHelper::get_wp_timezone());
return date_format( $wp_timezone_date, $format );
}
public function is_bookable( array $settings = [] ): bool {
$defaults = [
'skip_customer_check' => false,
'log_errors' => true
];
$settings = OsUtilHelper::merge_default_atts( $defaults, $settings );
$customer = $this->customer_id ? new OsCustomerModel( $this->customer_id ) : false;
// check if customer has to be assigned to a booking, or a guest booking is fine at this point
if($settings['skip_customer_check']){
$customer_requirement_satisfied = true;
}else{
$customer_requirement_satisfied = ($this->customer_id && $customer && $customer->id && ( $this->customer_id == $customer->id ));
}
// agent, service and customer should be set
if ( $this->service_id && $this->agent_id && $customer_requirement_satisfied) {
if ( $this->agent_id == LATEPOINT_ANY_AGENT && $this->location_id == LATEPOINT_ANY_LOCATION ) {
// both location and agent are set to any
$connections = new OsConnectorModel();
$connection_groups = $connections->select( LATEPOINT_TABLE_AGENTS_SERVICES . '.agent_id, ' . LATEPOINT_TABLE_AGENTS_SERVICES . '.location_id' )
->where( [
'service_id' => $this->service_id,
LATEPOINT_TABLE_AGENTS . '.status' => LATEPOINT_AGENT_STATUS_ACTIVE,
LATEPOINT_TABLE_LOCATIONS . '.status' => LATEPOINT_LOCATION_STATUS_ACTIVE
] )
->join( LATEPOINT_TABLE_AGENTS, [ 'id' => LATEPOINT_TABLE_AGENTS_SERVICES . '.agent_id' ] )
->join( LATEPOINT_TABLE_LOCATIONS, [ 'id' => LATEPOINT_TABLE_AGENTS_SERVICES . '.location_id' ] )
->get_results( ARRAY_A );
if ( empty( $connection_groups ) ) {
// no active locations and agents are connected to this service
$this->add_error( 'send_to_step', __( 'Unfortunately there are no active resources that can offer selected service, please select another service.', 'latepoint' ), 'booking__service' );
return false;
} else {
foreach ( $connection_groups as $connection ) {
$this->location_id = $connection['location_id'];
$this->agent_id = OsBookingHelper::get_any_agent_for_booking_by_rule( $this );
// available agent found in this location - break the loop
if ( $this->agent_id ) {
break;
}
}
if ( ! $this->agent_id ) {
$this->add_error( 'send_to_step', __( 'Unfortunately the selected time slot is not available anymore, please select another timeslot.', 'latepoint' ), 'booking__datepicker' );
return false;
}
}
} elseif ( $this->agent_id == LATEPOINT_ANY_AGENT ) {
$this->agent_id = OsBookingHelper::get_any_agent_for_booking_by_rule( $this );
if ( ! $this->agent_id ) {
$this->add_error( 'send_to_step', __( 'Unfortunately the selected time slot is not available anymore, please select another timeslot.', 'latepoint' ), 'booking__datepicker' );
return false;
}
} elseif ( $this->location_id == LATEPOINT_ANY_LOCATION ) {
$this->location_id = OsBookingHelper::get_any_location_for_booking_by_rule( $this );
if ( ! $this->location_id ) {
$this->add_error( 'send_to_step', __( 'Unfortunately the selected time slot is not available anymore, please select another timeslot.', 'latepoint' ), 'booking__datepicker' );
return false;
}
} else {
// check if booking time is still available
if ( ! OsBookingHelper::is_booking_request_available( \LatePoint\Misc\BookingRequest::create_from_booking_model( $this ) ) ) {
// translators: %1$s is the timeslot date and time
// translators: %2$s is the service name
$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 );
$this->add_error( 'send_to_step', $error_message, 'booking__datepicker' );
return false;
}
}
if(!$this->validate(false, ['order_item_id', 'status', 'customer_id'])){
return false;
}
return true;
} else {
if ( ! $this->service_id ) {
$this->add_error( 'missing_service', __( 'You have to select a service', 'latepoint' ) );
}
if ( ! $this->agent_id ) {
$this->add_error( 'missing_agent', __( 'You have to select an agent', 'latepoint' ) );
}
if ( ! $this->customer_id && !$settings['skip_customer_check'] ) {
$this->add_error( 'missing_customer', __( 'Customer Not Found', 'latepoint' ) );
if($settings['log_errors']){
OsDebugHelper::log( 'Customer not found', 'customer_error', print_r( $customer, true ) );
}
}
if ( ! $customer && !$settings['skip_customer_check'] ) {
$this->add_error( 'missing_customer', __( 'You have to be logged in', 'latepoint' ) );
if($settings['log_errors']){
OsDebugHelper::log( 'Customer not logged in', 'customer_error', print_r( $customer, true ) );
}
}
if($settings['log_errors']){
OsDebugHelper::log( 'Error saving booking', 'booking_error', 'Agent: ' . $this->agent_id . ', Service: ' . $this->service_id . ', Booking Customer: ' . $this->customer_id );
}
return false;
}
}
public function get_nice_status() {
return OsBookingHelper::get_nice_status_name( $this->status );
}
public function get_latest_bookings_sorted_by_status( $args = array() ) {
$args = array_merge( array(
'service_id' => false,
'customer_id' => false,
'agent_id' => false,
'location_id' => false,
'limit' => false,
'offset' => false
), $args );
$bookings = new OsBookingModel();
$query_args = array();
if ( $args['service_id'] ) {
$query_args['service_id'] = $args['service_id'];
}
if ( $args['customer_id'] ) {
$query_args['customer_id'] = $args['customer_id'];
}
if ( $args['agent_id'] ) {
$query_args['agent_id'] = $args['agent_id'];
}
if ( $args['location_id'] ) {
$query_args['location_id'] = $args['location_id'];
}
if ( $args['limit'] ) {
$bookings->set_limit( $args['limit'] );
}
if ( $args['offset'] ) {
$bookings->set_offset( $args['offset'] );
}
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();
}
public function should_not_be_cancelled() {
return $this->where( [ $this->table_name . '.status !=' => LATEPOINT_BOOKING_STATUS_CANCELLED ] );
}
public function should_be_cancelled() {
return $this->where( [ $this->table_name . '.status' => LATEPOINT_BOOKING_STATUS_CANCELLED ] );
}
public function should_be_approved() {
return $this->where( [ $this->table_name . '.status' => LATEPOINT_BOOKING_STATUS_APPROVED ] );
}
public function should_be_in_future() {
return $this->where( [
'OR' => [
'start_date >' => OsTimeHelper::today_date( 'Y-m-d' ),
'AND' => [
'start_date' => OsTimeHelper::today_date( 'Y-m-d' ),
'start_time >' => OsTimeHelper::get_current_minutes()
]
]
] );
}
public function get_upcoming_bookings( $agent_id = false, $customer_id = false, $service_id = false, $location_id = false, int $limit = 3 ) {
$bookings = new OsBookingModel();
$args = array(
'OR' => array(
'start_date >' => OsTimeHelper::today_date( 'Y-m-d' ),
'AND' => array(
'start_date' => OsTimeHelper::today_date( 'Y-m-d' ),
'start_time >' => OsTimeHelper::get_current_minutes()
)
)
);
if ( $service_id ) {
$args['service_id'] = $service_id;
}
if ( $customer_id ) {
$args['customer_id'] = $customer_id;
}
if ( $agent_id ) {
$args['agent_id'] = $agent_id;
}
if ( $location_id ) {
$args['location_id'] = $location_id;
}
$args = OsAuthHelper::get_current_user()->clean_query_args( $args );
$allowed_statuses = OsCalendarHelper::get_booking_statuses_to_display_on_calendar();
if (empty($allowed_statuses)) {
return [];
}
return $bookings->select( '*, count(id) as total_customers, sum(total_attendees) as total_attendees_sum' )
->where_in('status', $allowed_statuses )
->group_by( 'start_datetime_utc, agent_id, service_id, location_id' )
->where( $args )
->set_limit( $limit )
->order_by( 'start_datetime_utc asc' )
->get_results_as_models();
}
public function get_nice_start_time_for_customer() {
return $this->format_start_date_and_time( OsTimeHelper::get_time_format(), false, $this->get_customer_timezone() );
}
public function get_nice_end_time_for_customer() {
return $this->format_end_date_and_time( OsTimeHelper::get_time_format(), false, $this->get_customer_timezone() );
}
public function get_nice_start_date_for_customer($customer_timezone = false, $hide_year = false) {
if(!$customer_timezone) $customer_timezone = $this->get_customer_timezone();
return OsUtilHelper::translate_months($this->format_start_date_and_time( OsSettingsHelper::get_readable_date_format($hide_year), false, $customer_timezone ));
}
public function get_nice_start_datetime_for_customer($customer_timezone = false) {
if(!$customer_timezone) $customer_timezone = $this->get_customer_timezone();
return OsUtilHelper::translate_months($this->format_start_date_and_time( OsSettingsHelper::get_readable_datetime_format(), false, $customer_timezone ));
}
public function get_start_datetime_for_customer() : OsWpDateTime{
return $this->get_start_datetime($this->get_customer_timezone_name());
}
public function get_end_datetime_for_customer() : OsWpDateTime{
return $this->get_end_datetime($this->get_customer_timezone_name());
}
public function get_customer_timezone() : DateTimeZone{
if(OsSettingsHelper::is_on('steps_show_timezone_selector')){
return ($this->customer_id) ? $this->customer->get_selected_timezone_obj() : OsTimeHelper::get_timezone_from_session();
}else{
return OsTimeHelper::get_wp_timezone();
}
}
public function get_customer_timezone_name() : string{
if(OsSettingsHelper::is_on('steps_show_timezone_selector')) {
return ( $this->customer_id ) ? $this->customer->get_selected_timezone_name() : OsTimeHelper::get_timezone_name_from_session();
}else{
return OsTimeHelper::get_wp_timezone_name();
}
}
/**
*
* Returns time in WP timezone, because start_time is stored in WP timezone, do not use it for customer facing outputs
*
* @return string|null
*/
public function get_nice_start_time() {
return OsTimeHelper::minutes_to_hours_and_minutes( $this->start_time );
}
/**
*
* Returns time in WP timezone, because end_time is stored in WP timezone, do not use it for customer facing outputs
*
* @return string|null
*/
public function get_nice_end_time() {
return OsTimeHelper::minutes_to_hours_and_minutes( $this->end_time );
}
/**
*
* Returns time in WP timezone, because start_date is stored in WP timezone, do not use it for customer facing outputs
*
* @return string|null
*/
public function get_nice_end_date( $hide_year_if_current = false ) {
$datetime = OsWpDateTime::os_createFromFormat( "Y-m-d", $this->end_date );
OsTimeHelper::format_to_nice_date($datetime, $hide_year_if_current);
}
/**
*
* Returns time in WP timezone, because end_date is stored in WP timezone, do not use it for customer facing outputs
*
* @return string|null
*/
public function get_nice_start_date( $hide_year_if_current = false ) : string {
$datetime = OsWpDateTime::os_createFromFormat( "Y-m-d", $this->start_date );
return OsTimeHelper::format_to_nice_date($datetime, $hide_year_if_current);
}
/**
*
* Returns time in WP timezone, because start_date is stored in WP timezone, do not use it for customer facing outputs
*
* @param $hide_if_today bool
* @param $hide_year_if_current bool
*
* @return string
*/
public function get_nice_start_datetime( bool $hide_if_today = true, bool $hide_year_if_current = true ): string {
if ( $hide_if_today && $this->start_date == OsTimeHelper::today_date( 'Y-m-d' ) ) {
$date = __( 'Today', 'latepoint' );
} else {
$date = $this->get_nice_start_date( $hide_year_if_current );
}
return implode( ', ', array_filter( [ $date, $this->get_nice_start_time() ] ) );
}
public function is_bundle_scheduling() : bool {
return ! empty( $this->order_item_id );
}
public function get_connected_recurring_bookings() : array{
if(empty($this->recurrence_id) || $this->is_new_record()) return [];
$bookings = new OsBookingModel();
return $bookings->where(['recurrence_id' => $this->recurrence_id, 'id !=' => $this->id])->order_by('start_datetime_utc asc')->get_results_as_models();
}
public function get_nice_datetime_for_summary(string $viewer = 'customer'){
$nice_datetime = '';
if($this->start_date){
$nice_datetime = $this->get_nice_start_datetime(false);
if(OsSettingsHelper::is_on( 'show_booking_end_time') && !empty($this->end_time) && !empty($this->start_time)){
$nice_datetime = $nice_datetime.' - '.$this->get_nice_end_time();
}
}
/**
* Get a formatted start and end time (if needed)
*
* @since 5.1.0
* @hook latepoint_get_nice_datetime_for_summary
*
* @param {string} $statuses Nice datetime
* @param {OsBookingModel} $booking An object of booking model
*
* @returns {string} Filtered nice datetime
*/
$nice_datetime = apply_filters('latepoint_get_nice_datetime_for_summary', $nice_datetime, $this, $viewer);
return $nice_datetime;
}
public function format_end_date_and_time( $format = LATEPOINT_DATETIME_DB_FORMAT, $input_timezone = false, $output_timezone = false ) {
if ( ! $input_timezone ) {
$input_timezone = OsTimeHelper::get_wp_timezone();
}
if ( ! $output_timezone ) {
$output_timezone = OsTimeHelper::get_wp_timezone();
}
$date = OsWpDateTime::os_createFromFormat( LATEPOINT_DATETIME_DB_FORMAT, $this->end_date . ' ' . OsTimeHelper::minutes_to_army_hours_and_minutes( $this->end_time ) . ':00', $input_timezone );
$date->setTimeZone( $output_timezone );
return OsUtilHelper::translate_months( $date->format( $format ) );
}
public function format_start_date() {
if ( empty( $this->start_date ) ) {
$date = new OsWpDateTime();
$this->start_date = $date->format( 'Y-m-d' );
} else {
$date = OsWpDateTime::os_createFromFormat( "Y-m-d", $this->start_date );
}
return $date->format( OsSettingsHelper::get_date_format() );
}
public function format_start_date_and_time( $format = LATEPOINT_DATETIME_DB_FORMAT, $input_timezone = false, $output_timezone = false ) {
if ( ! $input_timezone ) {
$input_timezone = OsTimeHelper::get_wp_timezone();
}
if ( ! $output_timezone ) {
$output_timezone = OsTimeHelper::get_wp_timezone();
}
if ( is_null( $this->start_time ) || $this->start_time === '' ) {
// no time set yet (could be because summary is reloaded when date is picked, before the time is picked)
$date = OsWpDateTime::os_createFromFormat( "Y-m-d", $this->start_date );
if ( $date ) {
return OsUtilHelper::translate_months( $date->format( OsSettingsHelper::get_readable_date_format() ) );
} else {
return __( 'Invalid Date/Time', 'latepoint' );
}
} else {
// both date & time are set, update timezone and translate
$date = OsWpDateTime::os_createFromFormat( LATEPOINT_DATETIME_DB_FORMAT, $this->start_date . ' ' . OsTimeHelper::minutes_to_army_hours_and_minutes( $this->start_time ) . ':00', $input_timezone );
if ( $date ) {
$date->setTimeZone( $output_timezone );
return OsUtilHelper::translate_months( $date->format( $format ) );
} else {
return __( 'Invalid Date/Time', 'latepoint' );
}
}
}
public function format_start_date_and_time_rfc3339() {
return $this->format_start_date_and_time( \DateTime::RFC3339 );
}
public function format_end_date_and_time_rfc3339() {
return $this->format_end_date_and_time( \DateTime::RFC3339 );
}
public function format_start_date_and_time_for_google() {
return $this->format_start_date_and_time( \DateTime::RFC3339 );
}
public function format_end_date_and_time_for_google() {
return $this->format_end_date_and_time( \DateTime::RFC3339 );
}
/*
* Checks if the booking has passed
*/
public function time_status() {
try {
$now_datetime = OsTimeHelper::now_datetime_utc();
if(empty($this->start_datetime_utc) || empty($this->end_datetime_utc)){
$this->set_utc_datetimes(true);
}
$booking_start = new OsWpDateTime( $this->start_datetime_utc, new DateTimeZone( 'UTC' ) );
$booking_end = new OsWpDateTime( $this->end_datetime_utc, new DateTimeZone( 'UTC' ) );
if ( ( $now_datetime <= $booking_end ) && ( $now_datetime >= $booking_start ) ) {
return 'now';
} elseif ( $now_datetime <= $booking_start ) {
return 'upcoming';
} else {
return 'past';
}
} catch ( Exception $e ) {
return 'past';
}
}
public function start_datetime_in_format( string $format, string $output_in_timezone_name ) : string {
if(empty($this->start_datetime_utc)) return '';
$booking_start_datetime = OsTimeHelper::date_from_db( $this->start_datetime_utc );
$booking_start_datetime->setTimezone( new DateTimeZone($output_in_timezone_name) );
return $booking_start_datetime->format( $format );
}
public function is_start_date_and_time_set() : bool {
return ($this->start_date != '' && $this->start_time != '');
}
protected function get_time_left() {
$now_datetime = new OsWpDateTime( 'now' );
$booking_datetime = OsWpDateTime::os_createFromFormat( LATEPOINT_DATETIME_DB_FORMAT, $this->format_start_date_and_time() );
$css_class = 'left-days';
if ( $booking_datetime ) {
$diff = $now_datetime->diff( $booking_datetime );
if ( $diff->d > 0 || $diff->m > 0 || $diff->y > 0 ) {
$left = $diff->format( '%a ' . __( 'days', 'latepoint' ) );
} else {
if ( $diff->h > 0 ) {
$css_class = 'left-hours';
$left = $diff->format( '%h ' . __( 'hours', 'latepoint' ) );
} else {
$css_class = 'left-minutes';
$left = $diff->format( '%i ' . __( 'minutes', 'latepoint' ) );
}
}
} else {
$left = 'n/a';
}
return '<span class="time-left ' . esc_attr($css_class) . '">' . esc_html($left) . '</span>';
}
protected function get_agent() {
if ( $this->agent_id ) {
if ( ! isset( $this->agent ) || ( isset( $this->agent ) && ( $this->agent->id != $this->agent_id ) ) ) {
$this->agent = new OsAgentModel( $this->agent_id );
}
} else {
$this->agent = new OsAgentModel();
}
return $this->agent;
}
public function get_agent_full_name() {
if ( $this->agent_id == LATEPOINT_ANY_AGENT ) {
return __( 'Any Available Agent', 'latepoint' );
} else {
return $this->agent->full_name;
}
}
public function get_location() {
if ( $this->location_id ) {
// if location has not been initialized yet, or location_id is different from the one initialized - init again
if ( empty( $this->location ) || ( $this->location->id != $this->location_id ) ) {
$this->location = new OsLocationModel( $this->location_id );
}
} else {
$this->location = new OsLocationModel();
}
return $this->location;
}
protected function get_customer() {
if ( $this->customer_id ) {
if ( ! isset( $this->customer ) || ( isset( $this->customer ) && ( $this->customer->id != $this->customer_id ) ) ) {
$this->customer = new OsCustomerModel( $this->customer_id );
}
} else {
$this->customer = new OsCustomerModel();
}
return $this->customer;
}
protected function get_service() {
if ( $this->service_id ) {
if ( ! isset( $this->service ) || ( isset( $this->service ) && ( $this->service->id != $this->service_id ) ) ) {
$this->service = new OsServiceModel( $this->service_id );
}
} else {
$this->service = new OsServiceModel();
}
return $this->service;
}
public function get_nice_start_date_in_timezone(string $timezone_name = '', $hide_year_if_current = false) : string{
$datetime = $this->get_start_datetime($timezone_name);
return OsTimeHelper::format_to_nice_date($datetime, $hide_year_if_current);
}
public function get_nice_end_date_in_timezone(string $timezone_name = '', $hide_year_if_current = false) : string{
$datetime = $this->get_end_datetime($timezone_name);
return OsTimeHelper::format_to_nice_date($datetime, $hide_year_if_current);
}
public function get_nice_start_time_in_timezone(string $timezone_name = '') : string{
$datetime = $this->get_start_datetime($timezone_name);
return OsTimeHelper::format_to_nice_time($datetime);
}
public function get_nice_end_time_in_timezone(string $timezone_name = '') : string{
$datetime = $this->get_end_datetime($timezone_name);
return OsTimeHelper::format_to_nice_time($datetime);
}
public function get_start_datetime_object( ?DateTimeZone $timezone = null ) {
if ( empty( $timezone ) ) {
$timezone = OsTimeHelper::get_wp_timezone();
}
if ( empty( $this->start_datetime_utc ) ) {
// fix data, probably an older booking from the time when we didn't store UTC date
$this->start_datetime_utc = $this->generate_start_datetime_in_db_format();
}
$booking_start_datetime = OsTimeHelper::date_from_db( $this->start_datetime_utc );
if ( $booking_start_datetime ) {
$booking_start_datetime->setTimezone( $timezone );
} else {
OsDebugHelper::log( 'Error generating start date and time for booking ID: ' . $this->id, 'corrupt_booking_data' );
}
return $booking_start_datetime;
}
public function get_end_datetime_object( ?DateTimeZone $timezone = null ) {
if ( empty( $timezone ) ) {
$timezone = OsTimeHelper::get_wp_timezone();
}
if ( empty( $this->end_datetime_utc ) ) {
// fix data, probably an older booking from the time when we didn't store UTC date
$this->end_datetime_utc = $this->generate_end_datetime_in_db_format();
}
$booking_end_datetime = OsTimeHelper::date_from_db( $this->end_datetime_utc );
if ( $booking_end_datetime ) {
$booking_end_datetime->setTimezone( $timezone );
} else {
OsDebugHelper::log( 'Error generating end date and time for booking ID: ' . $this->id, 'corrupt_booking_data' );
}
return $booking_end_datetime;
}
public function get_start_datetime( string $set_timezone = 'UTC') : OsWpDateTime{
try{
// start_time and start_date is legacy stored in wordpress timezone
$dateTime = new OsWpDateTime( $this->start_date . ' 00:00:00', OsTimeHelper::get_wp_timezone() );
if($this->start_time > 0){
$dateTime->modify( '+' . $this->start_time . ' minutes' );
}
if($set_timezone) $dateTime->setTimezone( new DateTimeZone( $set_timezone ) );
return $dateTime;
}catch(Exception $e){
return new OsWpDateTime('now');
}
}
public function get_end_datetime( string $set_timezone = 'UTC') : OsWpDateTime{
try{
// start_time and start_date is legacy stored in wordpress timezone
$dateTime = new OsWpDateTime( $this->end_date . ' 00:00:00', OsTimeHelper::get_wp_timezone() );
if($this->end_time > 0){
$dateTime->modify( '+' . $this->end_time . ' minutes' );
}
if($set_timezone) $dateTime->setTimezone( new DateTimeZone( $set_timezone ) );
return $dateTime;
}catch(Exception $e){
return new OsWpDateTime('now');
}
}
public function generate_start_datetime_in_db_format( string $timezone = 'UTC' ): string {
$dateTime = $this->get_start_datetime($timezone);
return $dateTime->format( LATEPOINT_DATETIME_DB_FORMAT );
}
public function generate_end_datetime_in_db_format( string $timezone = 'UTC' ): string {
$dateTime = $this->get_end_datetime($timezone);
return $dateTime->format( LATEPOINT_DATETIME_DB_FORMAT );
}
protected function before_save() {
// TODO check for uniqueness
if ( empty( $this->booking_code ) ) {
$this->booking_code = strtoupper( OsUtilHelper::random_text( 'distinct', 7 ) );
}
if ( empty( $this->end_date ) ) {
$this->end_date = $this->calculate_end_date();
}
if ( empty( $this->status ) ) {
$this->status = $this->get_default_booking_status();
}
if ( empty( $this->total_attendees ) ) {
$this->total_attendees = 1;
}
if ( empty( $this->duration ) && $this->service_id ) {
$service = new OsServiceModel( $this->service_id );
$this->duration = $service->duration;
}
}
public function get_default_booking_status() {
return OsBookingHelper::get_default_booking_status( $this->service_id );
}
public function update_status( $new_status ) {
if ( $new_status == $this->status ) {
return true;
} else {
if ( ! in_array( $new_status, array_keys( OsBookingHelper::get_statuses_list() ) ) ) {
$this->add_error( 'invalid_booking_status', 'Invalid booking status' );
return false;
}
$old_booking = clone $this;
$this->status = $new_status;
$result = $this->update_attributes( [ 'status' => $new_status ] );
if ( $result ) {
do_action( 'latepoint_booking_updated', $this, $old_booking );
return true;
} else {
return false;
}
}
}
public function convert_start_datetime_into_server_timezone(string $input_timezone, bool $set_as_customer_timezone = true){
$this->server_timezone = OsTimeHelper::get_wp_timezone_name();
if($set_as_customer_timezone) $this->customer_timezone = $input_timezone;
if ( $this->is_start_date_and_time_set() && $this->server_timezone != $input_timezone ) {
try {
// convert from submitted customer timezone into WP timezone
$start_datetime = new OsWpDateTime( $this->start_date . ' 00:00:00', new DateTimeZone( $input_timezone ) );
if ( $this->start_time > 0 ) {
$start_datetime->modify( '+' . $this->start_time . ' minutes' );
}
$start_datetime->setTimezone( OsTimeHelper::get_wp_timezone() );
$this->start_date = $start_datetime->format( 'Y-m-d' );
$this->start_time = OsTimeHelper::convert_datetime_to_minutes( $start_datetime );
} catch ( Exception $e ) {
}
}
}
public function save_avatar( $image_id = false ) {
if ( ( false === $image_id ) && $this->image_id ) {
$image_id = $this->image_id;
}
if ( $image_id && $this->post_id ) {
set_post_thumbnail( $this->post_id, $image_id );
$this->image_id = $image_id;
}
return $this->image_id;
}
protected function allowed_params( $role = 'admin' ) {
$allowed_params = array(
'service_id',
'booking_code',
'agent_id',
'customer_id',
'location_id',
'start_date',
'end_date',
'start_time',
'end_time',
'start_datetime_utc',
'end_datetime_utc',
'buffer_before',
'duration',
'buffer_after',
'total_attendees',
'total_attendees_sum',
'total_customers',
'cart_item_id',
'order_item_id',
'status',
'form_id',
'server_timezone',
'customer_timezone',
'generate_recurrent_sequence',
'recurrence_id'
);
return $allowed_params;
}
protected function params_to_save( $role = 'admin' ) {
$params_to_save = array(
'service_id',
'booking_code',
'agent_id',
'customer_id',
'location_id',
'start_date',
'end_date',
'start_time',
'end_time',
'start_datetime_utc',
'end_datetime_utc',
'duration',
'buffer_before',
'buffer_after',
'total_attendees',
'status',
'order_item_id',
'server_timezone',
'customer_timezone',
'recurrence_id'
);
return $params_to_save;
}
protected function properties_to_validate() {
$validations = array(
'order_item_id' => array( 'presence' ),
'service_id' => array( 'presence' ),
'agent_id' => array( 'presence' ),
'location_id' => array( 'presence' ),
'customer_id' => array( 'presence' ),
'start_date' => array( 'presence' ),
'end_date' => array( 'presence' ),
'status' => array( 'presence' ),
);
return $validations;
}
}