<?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } if ( ! class_exists( 'OsBookingsController' ) ) : class OsBookingsController extends OsController { private $booking; function __construct() { parent::__construct(); $this->views_folder = LATEPOINT_VIEWS_ABSPATH . 'bookings/'; $this->vars['page_header'] = OsMenuHelper::get_menu_items_by_id( 'appointments' ); $this->vars['breadcrumbs'][] = array( 'label' => __( 'Appointments', 'latepoint' ), 'link' => OsRouterHelper::build_link( [ 'bookings', 'pending_approval' ] ) ); } public function view_booking_log() { $activities = new OsActivityModel(); $activities = $activities->where( [ 'booking_id' => absint( $this->params['booking_id'] ) ] )->order_by( 'id desc' )->get_results_as_models(); $booking = new OsBookingModel( $this->params['booking_id'] ); $this->vars['booking'] = $booking; $this->vars['activities'] = $activities; $this->format_render( __FUNCTION__ ); } public function grouped_bookings_quick_view() { if ( ! isset( $this->params['booking_id'] ) ) { return false; } $booking = new OsBookingModel( $this->params['booking_id'] ); $this->vars['booking'] = $booking; $group_bookings = new OsBookingModel(); $group_bookings = $group_bookings->where( [ 'start_time' => $booking->start_time, 'start_date' => $booking->start_date, 'service_id' => $booking->service_id, 'location_id' => $booking->location_id, 'agent_id' => $booking->agent_id ] )->should_not_be_cancelled()->get_results_as_models(); $total_attendees = 0; if ( $group_bookings ) { foreach ( $group_bookings as $group_booking ) { $total_attendees = $total_attendees + $group_booking->total_attendees; } } $this->vars['total_attendees'] = $total_attendees; $this->vars['group_bookings'] = $group_bookings; $this->format_render( __FUNCTION__ ); } public function pending_approval() { $this->vars['page_header'] = __( 'Pending Appointments', 'latepoint' ); $this->vars['breadcrumbs'][] = array( 'label' => __( 'Pending Appointments', 'latepoint' ), 'link' => false ); $page_number = isset( $this->params['page_number'] ) ? $this->params['page_number'] : 1; $per_page = OsSettingsHelper::get_number_of_records_per_page(); $offset = ( $page_number > 1 ) ? ( ( $page_number - 1 ) * $per_page ) : 0; $bookings = new OsBookingModel(); $query_args = [ 'status' => OsBookingHelper::get_booking_statuses_for_pending_page() ]; $bookings->where( $query_args )->filter_allowed_records(); $count_total_bookings = clone $bookings; $total_bookings = $count_total_bookings->count(); $this->vars['bookings'] = $bookings->set_limit( $per_page )->set_offset( $offset )->order_by( 'id desc' )->get_results_as_models(); $total_pages = ceil( $total_bookings / $per_page ); $this->vars['total_pages'] = $total_pages; $this->vars['total_bookings'] = $total_bookings; $this->vars['per_page'] = $per_page; $this->vars['current_page_number'] = $page_number; $this->vars['showing_from'] = ( ( $page_number - 1 ) * $per_page ) ? ( ( $page_number - 1 ) * $per_page ) : 1; $this->vars['showing_to'] = min( $page_number * $per_page, $this->vars['total_bookings'] ); $this->format_render( __FUNCTION__ ); } public function customize_table() { $this->vars['selected_columns'] = OsSettingsHelper::get_selected_columns_for_bookings_table(); $this->vars['available_columns'] = OsSettingsHelper::get_available_columns_for_bookings_table(); $this->format_render( __FUNCTION__ ); } public function index() { $this->vars['page_header'] = false; $this->vars['breadcrumbs'][] = array( 'label' => __( 'All', 'latepoint' ), 'link' => false ); $page_number = isset( $this->params['page_number'] ) ? $this->params['page_number'] : 1; $per_page = OsSettingsHelper::get_number_of_records_per_page(); $offset = ( $page_number > 1 ) ? ( ( $page_number - 1 ) * $per_page ) : 0; $customer = new OsCustomerModel(); $bookings = new OsBookingModel(); $query_args = []; $selected_columns = OsSettingsHelper::get_selected_columns_for_bookings_table(); $available_columns = OsSettingsHelper::get_available_columns_for_bookings_table(); $filter = $this->params['filter'] ?? false; $order_by = [ 'key' => 'booking_id', 'direction' => 'desc', 'column' => 'id' ]; // TABLE SEARCH FILTERS if ( $filter ) { if ( ! empty( $filter['records_ordered_by_key'] ) && ! empty( $filter['records_ordered_by_direction'] ) ) { if ( in_array( $filter['records_ordered_by_direction'], [ 'desc', 'asc' ] ) ) { $order_by['direction'] = $filter['records_ordered_by_direction']; } $order_by['key'] = $filter['records_ordered_by_key']; switch ( $filter['records_ordered_by_key'] ) { case 'booking_id': $order_by['column'] = 'id'; break; case 'booking_start_datetime': $order_by['column'] = 'start_datetime_utc'; break; case 'booking_created_on': $order_by['column'] = 'created_at'; break; case 'booking_time_left': $order_by['column'] = 'start_datetime_utc'; break; } } if ( ! empty( $filter['service_id'] ) ) { $query_args['service_id'] = $filter['service_id']; } if ( ! empty( $filter['agent_id'] ) ) { $query_args['agent_id'] = $filter['agent_id']; } if ( ! empty( $filter['location_id'] ) ) { $query_args['location_id'] = $filter['location_id']; } if ( ! empty( $filter['time_status'] ) ) { switch ( $filter['time_status'] ) { case 'now': $query_args['start_datetime_utc <='] = OsTimeHelper::now_datetime_utc_in_db_format(); $query_args['end_datetime_utc >='] = OsTimeHelper::now_datetime_utc_in_db_format(); break; case 'upcoming': $query_args['start_datetime_utc >='] = OsTimeHelper::now_datetime_utc_in_db_format(); break; case 'past': $query_args['end_datetime_utc <='] = OsTimeHelper::now_datetime_utc_in_db_format(); break; } } if ( ! empty( $filter['status'] ) ) { $query_args[ LATEPOINT_TABLE_BOOKINGS . '.status' ] = $filter['status']; } if ( ! empty( $filter['payment_status'] ) ) { $query_args[ LATEPOINT_TABLE_BOOKINGS . '.payment_status' ] = $filter['payment_status']; } if ( ! empty( $filter['id'] ) ) { $query_args[ LATEPOINT_TABLE_BOOKINGS . '.id' ] = $filter['id']; } if ( ! empty( $filter['created_date_from'] ) && ! empty( $filter['created_date_to'] ) ) { $query_args[ LATEPOINT_TABLE_BOOKINGS . '.created_at >=' ] = $filter['created_date_from'] . ' 00:00:00'; $query_args[ LATEPOINT_TABLE_BOOKINGS . '.created_at <=' ] = $filter['created_date_to'] . ' 23:59:59'; } if ( ! empty( $filter['booking_date_from'] ) && ! empty( $filter['booking_date_to'] ) ) { $query_args[ LATEPOINT_TABLE_BOOKINGS . '.start_date >=' ] = $filter['booking_date_from']; $query_args[ LATEPOINT_TABLE_BOOKINGS . '.start_date <=' ] = $filter['booking_date_to']; } $selected_columns = OsSettingsHelper::get_selected_columns_for_bookings_table(); if ( ! empty( $filter['order'] ) ) { $bookings->select( LATEPOINT_TABLE_BOOKINGS . '.*' ); $bookings->join( LATEPOINT_TABLE_ORDER_ITEMS, [ 'id' => LATEPOINT_TABLE_BOOKINGS . '.order_item_id' ] ); $bookings->join( LATEPOINT_TABLE_ORDERS, [ 'id' => LATEPOINT_TABLE_ORDER_ITEMS . '.order_id' ] ); if ( ! empty( $filter['order']['payment_status'] ) ) { $bookings->select( LATEPOINT_TABLE_ORDERS . '.payment_status' ); $query_args[ LATEPOINT_TABLE_ORDERS . '.payment_status' ] = $filter['order']['payment_status']; } } if ( ! empty( $filter['customer'] ) ) { $bookings->select( LATEPOINT_TABLE_BOOKINGS . '.*' ); if ( ! empty( $filter['customer']['full_name'] ) ) { $bookings->select( LATEPOINT_TABLE_CUSTOMERS . '.first_name, ' . LATEPOINT_TABLE_CUSTOMERS . '.last_name' ); $query_args[ 'concat_ws(" ", ' . LATEPOINT_TABLE_CUSTOMERS . '.first_name,' . LATEPOINT_TABLE_CUSTOMERS . '.last_name) LIKE' ] = '%' . $filter['customer']['full_name'] . '%'; $this->vars['customer_name_query'] = $filter['customer']['full_name']; } $bookings->join( LATEPOINT_TABLE_CUSTOMERS, [ 'id' => LATEPOINT_TABLE_BOOKINGS . '.customer_id' ] ); if ( ! empty( $selected_columns['customer'] ) ) { $meta_filter = []; foreach ( $selected_columns['customer'] as $customer_column_key ) { if ( isset( $available_columns['customer'][ $customer_column_key ] ) && ! empty( $filter['customer'][ $customer_column_key ] ) ) { if ( in_array( $customer_column_key, $customer->get_params_to_save() ) ) { // native field $bookings->select( LATEPOINT_TABLE_CUSTOMERS . '.' . $customer_column_key ); $query_args[ LATEPOINT_TABLE_CUSTOMERS . '.' . $customer_column_key . ' LIKE' ] = '%' . $filter['customer'][ $customer_column_key ] . '%'; } else { // meta field $meta_filter[ $customer_column_key ] = $filter['customer'][ $customer_column_key ]; } } } if ( count( $meta_filter ) ) { $customers_ids = OsMetaHelper::get_customers_by_filter( $meta_filter ) ?: [ - 1 ]; $query_args[ LATEPOINT_TABLE_CUSTOMERS . '.id IN' ] = $customers_ids; } } } // filters for custom selected columns, only related to booking fields if ( ! empty( $selected_columns['booking'] ) ) { foreach ( $selected_columns['booking'] as $booking_column_key ) { if ( ! empty( $available_columns['booking'][ $booking_column_key ] ) && ! empty( $filter[ $booking_column_key ] ) ) { $query_args[ $booking_column_key . ' LIKE' ] = '%' . $filter[ $booking_column_key ] . '%'; } } } } $this->vars['agents_list'] = OsAgentHelper::get_agents_list( true ); $this->vars['locations_list'] = OsLocationHelper::get_locations_list( true ); $this->vars['services_list'] = OsServiceHelper::get_services_list( true ); $this->vars['selected_columns'] = $selected_columns; $this->vars['available_columns'] = $available_columns; $this->vars['records_ordered_by_key'] = $order_by['key']; $this->vars['records_ordered_by_direction'] = $order_by['direction']; // OUTPUT CSV IF REQUESTED if ( isset( $this->params['download'] ) && $this->params['download'] == 'csv' ) { $csv_filename = 'all_bookings_' . OsUtilHelper::random_text() . '.csv'; header( "Content-Type: text/csv" ); header( "Content-Disposition: attachment; filename={$csv_filename}" ); $labels_row = [ __( 'ID', 'latepoint' ), __( 'Service', 'latepoint' ), __( 'Start Date & Time', 'latepoint' ), __( 'Duration', 'latepoint' ), __( 'Customer', 'latepoint' ), __( 'Customer Phone', 'latepoint' ), __( 'Customer Email', 'latepoint' ), __( 'Agent', 'latepoint' ), __( 'Agent Phone', 'latepoint' ), __( 'Agent Email', 'latepoint' ), __( 'Status', 'latepoint' ), __( 'Price', 'latepoint' ), __( 'Booked On', 'latepoint' ) ]; $bookings_data = []; $bookings_data[] = $labels_row; $bookings_arr = $bookings->where( $query_args )->filter_allowed_records()->order_by( $order_by['column'] . ' ' . $order_by['direction'] )->get_results_as_models(); if ( $bookings_arr ) { foreach ( $bookings_arr as $booking ) { $order_item = new OsOrderItemModel( $booking->order_item_id ); $values_row = [ $booking->id, $booking->service->name, $booking->nice_start_datetime, $booking->get_total_duration(), $booking->customer->full_name, $booking->customer->phone, $booking->customer->email, $booking->agent->full_name, $booking->agent->phone, $booking->agent->email, $booking->nice_status, OsMoneyHelper::format_price( $order_item->get_total(), true, false ), $booking->nice_created_at ]; $values_row = apply_filters( 'latepoint_booking_row_for_csv_export', $values_row, $booking, $this->params ); $bookings_data[] = $values_row; } } $bookings_data = apply_filters( 'latepoint_bookings_data_for_csv_export', $bookings_data, $this->params ); OsCSVHelper::array_to_csv( $bookings_data ); return; } $query_args = OsRolesHelper::filter_allowed_records_from_arguments_or_filter( $query_args ); $bookings->where( $query_args )->filter_allowed_records(); $count_total_bookings = clone $bookings; $total_bookings = $count_total_bookings->count(); $this->vars['bookings'] = $bookings->set_limit( $per_page )->set_offset( $offset )->order_by( $order_by['column'] . ' ' . $order_by['direction'] )->get_results_as_models(); $this->vars['total_bookings'] = $total_bookings; $total_pages = ceil( $total_bookings / $per_page ); $this->vars['total_pages'] = $total_pages; $this->vars['per_page'] = $per_page; $this->vars['current_page_number'] = $page_number; $this->vars['total_records'] = $total_bookings; $this->vars['showing_from'] = ( ( $page_number - 1 ) * $per_page ) ? ( ( $page_number - 1 ) * $per_page ) : 1; $this->vars['showing_to'] = min( $page_number * $per_page, $this->vars['total_bookings'] ); $this->format_render( [ 'json_view_name' => '_table_body', 'html_view_name' => __FUNCTION__ ], [], [ 'total_pages' => $total_pages, 'showing_from' => $this->vars['showing_from'], 'showing_to' => $this->vars['showing_to'], 'total_records' => $total_bookings ] ); } function quick_availability() { $trigger_form_booking_id = $this->params['trigger_form_booking_id']; $trigger_form_order_item_id = $this->params['trigger_form_order_item_id']; $booking = OsOrdersHelper::create_booking_object_from_booking_data_form( $this->params['order_items'][ $trigger_form_order_item_id ]['bookings'][ $trigger_form_booking_id ] ); $calendar_start_date = isset( $this->params['start_date'] ) ? new OsWpDateTime( $this->params['start_date'] ) : new OsWpDateTime( $booking->start_date ); // show one more day before so the current selection does not look weird if ( isset( $this->params['previous_days'] ) ) { $calendar_end_date = clone $calendar_start_date; $calendar_start_date->modify( '-60 days' ); } else { if ( ! isset( $this->params['show_days_only'] ) ) { $calendar_start_date->modify( '-1 day' ); } $calendar_end_date = clone $calendar_start_date; $calendar_end_date->modify( '+60 days' ); } if ( OsAuthHelper::get_current_user()->is_single_record_allowed( 'agent' ) ) { $booking->agent_id = OsRolesHelper::get_allowed_records( 'agent' )[0]; } $work_periods = OsWorkPeriodsHelper::get_work_periods( new \LatePoint\Misc\Filter( [ 'date_from' => $calendar_start_date->format( 'Y-m-d' ), 'date_to' => $calendar_end_date->format( 'Y-m-d' ), 'service_id' => $booking->service_id, 'agent_id' => $booking->agent_id, 'location_id' => $booking->location_id ] ) ); $work_start_end = OsWorkPeriodsHelper::get_work_start_end_time( $work_periods ); $booking_request = \LatePoint\Misc\BookingRequest::create_from_booking_model( $booking ); $settings = []; $settings['accessed_from_backend'] = true; if ( ! $booking->is_new_record() ) { $settings['exclude_booking_ids'] = [ $booking->id ]; } $resources = OsResourceHelper::get_resources_grouped_by_day( $booking_request, $calendar_start_date, $calendar_end_date, $settings ); $work_boundaries = OsResourceHelper::get_work_boundaries_for_groups_of_resources( $resources ); $this->vars['trigger_form_booking_id'] = $trigger_form_booking_id; $this->vars['trigger_form_order_item_id'] = $trigger_form_order_item_id; $this->vars['booking'] = $booking; $this->vars['work_boundaries'] = $work_boundaries; $this->vars['show_days_only'] = isset( $this->params['show_days_only'] ) ? true : false; $this->vars['timeblock_interval'] = $booking->service->get_timeblock_interval(); $this->vars['calendar_start_date'] = $calendar_start_date; $this->vars['calendar_end_date'] = $calendar_end_date; $this->vars['booking_request'] = $booking_request; $this->vars['resources'] = $resources; $agents = new OsAgentModel(); $this->vars['agents'] = $agents->filter_allowed_records()->get_results_as_models(); $this->format_render( __FUNCTION__ ); } function change_status() { if ( filter_var( $this->params['id'], FILTER_VALIDATE_INT ) ) { $this->check_nonce( 'change_status_booking_' . $this->params['id'] ); $booking_id = $this->params['id']; $new_status = $this->params['status']; $booking = new OsBookingModel( $booking_id ); if ( ! OsRolesHelper::can_user_make_action_on_model_record( $booking, 'edit' ) ) { exit; } if ( $booking->update_status( $new_status ) ) { $status = LATEPOINT_STATUS_SUCCESS; $response_html = __( 'Appointment Status Updated', 'latepoint' ); } else { $status = LATEPOINT_STATUS_ERROR; $response_html = __( 'Error Updating Booking Status!', 'latepoint' ) . ' ' . implode( ',', $booking->get_error_messages() ); } } else { $status = LATEPOINT_STATUS_ERROR; $response_html = __( 'Error Updating Booking Status! Invalid ID', 'latepoint' ); } if ( $this->get_return_format() == 'json' ) { $this->send_json( array( 'status' => $status, 'message' => $response_html ) ); } } } endif;