activities_controller.php
1 month ago
auth_controller.php
3 months ago
booking_form_settings_controller.php
3 months ago
bookings_controller.php
18 hours ago
calendars_controller.php
3 months ago
carts_controller.php
18 hours ago
controller.php
3 months ago
customer_cabinet_controller.php
2 months ago
customers_controller.php
18 hours ago
dashboard_controller.php
2 months ago
default_agent_controller.php
3 months ago
events_controller.php
3 months ago
form_fields_controller.php
1 week ago
integrations_controller.php
3 months ago
invoices_controller.php
18 hours ago
manage_booking_by_key_controller.php
3 months ago
manage_order_by_key_controller.php
3 months ago
notifications_controller.php
3 months ago
orders_controller.php
18 hours ago
pro_controller.php
2 weeks ago
process_jobs_controller.php
3 months ago
processes_controller.php
1 month ago
razorpay_connect_controller.php
1 week ago
search_controller.php
3 months ago
services_controller.php
3 months ago
settings_controller.php
2 months ago
steps_controller.php
2 weeks ago
stripe_connect_controller.php
1 week ago
support_topics_controller.php
3 months ago
todos_controller.php
3 months ago
transactions_controller.php
18 hours ago
wizard_controller.php
1 week ago
customers_controller.php
624 lines
| 1 | <?php |
| 2 | if ( ! defined( 'ABSPATH' ) ) { |
| 3 | exit; // Exit if accessed directly. |
| 4 | } |
| 5 | |
| 6 | |
| 7 | if ( ! class_exists( 'OsCustomersController' ) ) : |
| 8 | |
| 9 | |
| 10 | class OsCustomersController extends OsController { |
| 11 | |
| 12 | function __construct() { |
| 13 | parent::__construct(); |
| 14 | |
| 15 | |
| 16 | $this->views_folder = LATEPOINT_VIEWS_ABSPATH . 'customers/'; |
| 17 | $this->vars['page_header'] = OsMenuHelper::get_menu_items_by_id( 'customers' ); |
| 18 | $this->vars['breadcrumbs'][] = array( |
| 19 | 'label' => __( 'Customers', 'latepoint' ), |
| 20 | 'link' => OsRouterHelper::build_link( OsRouterHelper::build_route_name( 'customers', 'index' ) ), |
| 21 | ); |
| 22 | } |
| 23 | |
| 24 | public function destroy() { |
| 25 | if ( filter_var( $this->params['id'], FILTER_VALIDATE_INT ) ) { |
| 26 | $this->check_nonce( 'destroy_customer_' . $this->params['id'] ); |
| 27 | $customer = new OsCustomerModel( $this->params['id'] ); |
| 28 | if ( ! OsRolesHelper::can_user_make_action_on_model_record( $customer, 'delete' ) ) { |
| 29 | $status = LATEPOINT_STATUS_ERROR; |
| 30 | $response_html = __( 'Not Allowed', 'latepoint' ); |
| 31 | } elseif ( $customer->delete() ) { |
| 32 | $status = LATEPOINT_STATUS_SUCCESS; |
| 33 | $response_html = __( 'Customer Removed', 'latepoint' ); |
| 34 | } else { |
| 35 | $status = LATEPOINT_STATUS_ERROR; |
| 36 | $response_html = __( 'Error Removing Customer', 'latepoint' ); |
| 37 | } |
| 38 | } else { |
| 39 | $status = LATEPOINT_STATUS_ERROR; |
| 40 | $response_html = __( 'Error Removing Customer', 'latepoint' ); |
| 41 | } |
| 42 | |
| 43 | if ( $this->get_return_format() == 'json' ) { |
| 44 | $this->send_json( |
| 45 | array( |
| 46 | 'status' => $status, |
| 47 | 'message' => $response_html, |
| 48 | ) |
| 49 | ); |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | |
| 54 | public function view_customer_log() { |
| 55 | |
| 56 | $customer = new OsCustomerModel( $this->params['customer_id'] ); |
| 57 | if ( ! OsRolesHelper::can_user_make_action_on_model_record( $customer, 'view' ) ) { |
| 58 | $this->access_not_allowed(); |
| 59 | return; |
| 60 | } |
| 61 | |
| 62 | $activities = new OsActivityModel(); |
| 63 | $activities = $activities->where( [ 'customer_id' => absint( $this->params['customer_id'] ) ] )->order_by( 'id desc' )->get_results_as_models(); |
| 64 | |
| 65 | $this->vars['customer'] = $customer; |
| 66 | $this->vars['activities'] = $activities; |
| 67 | |
| 68 | $this->format_render( __FUNCTION__ ); |
| 69 | } |
| 70 | |
| 71 | |
| 72 | public function quick_new() { |
| 73 | $customer = new OsCustomerModel(); |
| 74 | |
| 75 | $this->vars['customer'] = $customer; |
| 76 | |
| 77 | $this->format_render( 'quick_edit' ); |
| 78 | } |
| 79 | |
| 80 | public function quick_edit() { |
| 81 | if ( ! filter_var( $this->params['customer_id'], FILTER_VALIDATE_INT ) ) { |
| 82 | $this->access_not_allowed(); |
| 83 | } |
| 84 | $customer = new OsCustomerModel( $this->params['customer_id'] ); |
| 85 | if ( ! OsRolesHelper::can_user_make_action_on_model_record( $customer, 'edit' ) ) { |
| 86 | $this->send_json( |
| 87 | array( |
| 88 | 'status' => LATEPOINT_STATUS_ERROR, |
| 89 | 'message' => __( 'Not Allowed', 'latepoint' ), |
| 90 | ) |
| 91 | ); |
| 92 | } |
| 93 | |
| 94 | $this->vars['customer'] = $customer; |
| 95 | |
| 96 | $this->format_render( __FUNCTION__ ); |
| 97 | } |
| 98 | |
| 99 | |
| 100 | public function inline_edit_form() { |
| 101 | $selected_customer = new OsCustomerModel(); |
| 102 | if ( isset( $this->params['customer_id'] ) ) { |
| 103 | $selected_customer->load_by_id( $this->params['customer_id'] ); |
| 104 | } |
| 105 | $this->vars['default_fields_for_customer'] = OsSettingsHelper::get_default_fields_for_customer(); |
| 106 | $this->vars['selected_customer'] = $selected_customer; |
| 107 | $this->format_render( __FUNCTION__ ); |
| 108 | } |
| 109 | |
| 110 | public function set_as_guest() { |
| 111 | // CSRF protection |
| 112 | $this->check_nonce( 'set_customer_as_guest_' . $this->params['id'] ); |
| 113 | |
| 114 | if ( filter_var( $this->params['id'], FILTER_VALIDATE_INT ) ) { |
| 115 | $customer = new OsCustomerModel( $this->params['id'] ); |
| 116 | if ( ! OsRolesHelper::can_user_make_action_on_model_record( $customer, 'edit' ) ) { |
| 117 | $status = LATEPOINT_STATUS_ERROR; |
| 118 | $response_html = __( 'Not Allowed', 'latepoint' ); |
| 119 | } elseif ( $customer->update_attributes( [ 'is_guest' => true ] ) ) { |
| 120 | $status = LATEPOINT_STATUS_SUCCESS; |
| 121 | $response_html = __( 'Customer is now allowed to book without password', 'latepoint' ); |
| 122 | } else { |
| 123 | $status = LATEPOINT_STATUS_ERROR; |
| 124 | $response_html = $customer->get_error_messages(); |
| 125 | } |
| 126 | } else { |
| 127 | $status = LATEPOINT_STATUS_ERROR; |
| 128 | $response_html = __( 'Error setting customer as guest', 'latepoint' ); |
| 129 | } |
| 130 | |
| 131 | if ( $this->get_return_format() == 'json' ) { |
| 132 | $this->send_json( |
| 133 | array( |
| 134 | 'status' => $status, |
| 135 | 'message' => $response_html, |
| 136 | ) |
| 137 | ); |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | /* |
| 142 | Edit customer |
| 143 | */ |
| 144 | |
| 145 | public function edit_form() { |
| 146 | $this->vars['page_header'] = __( 'Edit Customer', 'latepoint' ); |
| 147 | $this->vars['breadcrumbs'][] = array( |
| 148 | 'label' => __( 'Edit Customer', 'latepoint' ), |
| 149 | 'link' => false, |
| 150 | ); |
| 151 | |
| 152 | if ( filter_var( $this->params['id'], FILTER_VALIDATE_INT ) ) { |
| 153 | // check if allowed to access |
| 154 | $customer = new OsCustomerModel(); |
| 155 | $customer = $customer->where( [ LATEPOINT_TABLE_CUSTOMERS . '.id' => absint( $this->params['id'] ) ] )->filter_allowed_records()->set_limit( 1 )->get_results_as_models(); |
| 156 | $this->vars['customer'] = $customer; |
| 157 | $this->vars['wp_users_for_select'] = OsWpUserHelper::get_wp_users_for_select(); |
| 158 | } |
| 159 | |
| 160 | $this->format_render( __FUNCTION__ ); |
| 161 | } |
| 162 | |
| 163 | |
| 164 | public function query_for_booking_form() { |
| 165 | $query = trim( $this->params['query'] ); |
| 166 | $sql_query = '%' . $query . '%'; |
| 167 | $query = $this->params['query']; |
| 168 | $customers = new OsCustomerModel(); |
| 169 | $this->vars['query'] = $query; |
| 170 | $this->vars['customers'] = $customers->where( |
| 171 | array( |
| 172 | 'OR' => array( |
| 173 | 'CONCAT (first_name, " ", last_name) LIKE ' => $sql_query, |
| 174 | 'email LIKE' => $sql_query, |
| 175 | 'phone LIKE' => $sql_query, |
| 176 | ), |
| 177 | ) |
| 178 | )->set_limit( 20 )->order_by( 'first_name asc, last_name asc' )->get_results_as_models(); |
| 179 | |
| 180 | $this->format_render( __FUNCTION__ ); |
| 181 | } |
| 182 | |
| 183 | |
| 184 | /* |
| 185 | Create customer |
| 186 | */ |
| 187 | |
| 188 | public function create() { |
| 189 | $this->check_nonce( 'new_customer' ); |
| 190 | $customer = new OsCustomerModel(); |
| 191 | // Security fix: Prevent mass assignment of wordpress_user_id by non-admin users. |
| 192 | // Use admin scope for backend panel users (admin, agent, custom roles), otherwise restrict to public fields. |
| 193 | $customer->set_data( $this->params['customer'], OsAuthHelper::get_current_user()->has_backend_access() ? LATEPOINT_PARAMS_SCOPE_ADMIN : LATEPOINT_PARAMS_SCOPE_PUBLIC ); |
| 194 | if ( $customer->save() ) { |
| 195 | // translators: %s is the html of a customer edit link |
| 196 | $response_html = sprintf( __( 'Customer Created ID: %s', 'latepoint' ), '<span class="os-notification-link" ' . OsCustomerHelper::quick_customer_btn_html( $customer->id ) . '>' . $customer->id . '</span>' ); |
| 197 | $status = LATEPOINT_STATUS_SUCCESS; |
| 198 | do_action( 'latepoint_customer_created', $customer ); |
| 199 | } else { |
| 200 | $response_html = $customer->get_error_messages(); |
| 201 | $status = LATEPOINT_STATUS_ERROR; |
| 202 | } |
| 203 | if ( $this->get_return_format() == 'json' ) { |
| 204 | $this->send_json( |
| 205 | array( |
| 206 | 'status' => $status, |
| 207 | 'message' => $response_html, |
| 208 | ) |
| 209 | ); |
| 210 | } |
| 211 | } |
| 212 | |
| 213 | |
| 214 | /* |
| 215 | Update customer |
| 216 | */ |
| 217 | |
| 218 | public function update() { |
| 219 | if ( isset( $this->params['customer']['id'] ) && filter_var( $this->params['customer']['id'], FILTER_VALIDATE_INT ) ) { |
| 220 | $this->check_nonce( 'edit_customer_' . $this->params['customer']['id'] ); |
| 221 | $customer = new OsCustomerModel( $this->params['customer']['id'] ); |
| 222 | if ( ! $customer || ! OsRolesHelper::can_user_make_action_on_model_record( $customer, 'edit' ) ) { |
| 223 | $response_html = __( 'Access Restricted', 'latepoint' ); |
| 224 | $status = LATEPOINT_STATUS_ERROR; |
| 225 | } else { |
| 226 | $old_customer_data = $customer->get_data_vars(); |
| 227 | // Security fix: Prevent mass assignment of wordpress_user_id by non-admin users. |
| 228 | // Use admin scope for backend panel users (admin, agent, custom roles), otherwise restrict to public fields. |
| 229 | $customer->set_data( $this->params['customer'], OsAuthHelper::get_current_user()->has_backend_access() ? LATEPOINT_PARAMS_SCOPE_ADMIN : LATEPOINT_PARAMS_SCOPE_PUBLIC ); |
| 230 | if ( $customer->save() ) { |
| 231 | // translators: %s is the html of a customer edit link |
| 232 | $response_html = sprintf( __( 'Customer Updated ID: %s', 'latepoint' ), '<span class="os-notification-link" ' . OsCustomerHelper::quick_customer_btn_html( $customer->id ) . '>' . $customer->id . '</span>' ); |
| 233 | $status = LATEPOINT_STATUS_SUCCESS; |
| 234 | do_action( 'latepoint_customer_updated', $customer, $old_customer_data ); |
| 235 | } else { |
| 236 | $response_html = $customer->get_error_messages(); |
| 237 | $status = LATEPOINT_STATUS_ERROR; |
| 238 | } |
| 239 | } |
| 240 | } else { |
| 241 | $response_html = __( 'Invalid customer ID', 'latepoint' ); |
| 242 | $status = LATEPOINT_STATUS_ERROR; |
| 243 | } |
| 244 | if ( $this->get_return_format() == 'json' ) { |
| 245 | $this->send_json( |
| 246 | array( |
| 247 | 'status' => $status, |
| 248 | 'message' => $response_html, |
| 249 | ) |
| 250 | ); |
| 251 | } |
| 252 | } |
| 253 | |
| 254 | public function mini_profile() { |
| 255 | if ( filter_var( $this->params['customer_id'], FILTER_VALIDATE_INT ) ) { |
| 256 | $customer = new OsCustomerModel( $this->params['customer_id'] ); |
| 257 | $this->vars['upcoming_booking'] = $customer->get_future_bookings( 1, true ); |
| 258 | $this->vars['customer'] = $customer; |
| 259 | |
| 260 | |
| 261 | $pie_labels = []; |
| 262 | $pie_colors = []; |
| 263 | $pie_values = []; |
| 264 | $pie_chart_data = OsBookingHelper::get_stat( |
| 265 | 'bookings', |
| 266 | [ |
| 267 | 'group_by' => 'status', |
| 268 | 'customer_id' => $customer->id, |
| 269 | ] |
| 270 | ); |
| 271 | $colors = [ '#2752E4', '#C066F1', '#26B7DD', '#E8C634', '#19CED6', '#2FEAA3', '#252a3e', '#8d87a5', '#b9b784' ]; |
| 272 | $status_colors = [ |
| 273 | LATEPOINT_BOOKING_STATUS_APPROVED => '#35d893', |
| 274 | LATEPOINT_BOOKING_STATUS_PENDING => '#e6b935', |
| 275 | LATEPOINT_BOOKING_STATUS_PAYMENT_PENDING => '#4ca4ef', |
| 276 | LATEPOINT_BOOKING_STATUS_CANCELLED => '#f1585d', |
| 277 | ]; |
| 278 | $i = 0; |
| 279 | foreach ( $pie_chart_data as $pie_data ) { |
| 280 | $pie_labels[] = $pie_data['status']; |
| 281 | $pie_colors[] = isset( $status_colors[ $pie_data['status'] ] ) ? $status_colors[ $pie_data['status'] ] : $colors[ $i ]; |
| 282 | $pie_values[] = $pie_data['stat']; |
| 283 | $i++; |
| 284 | } |
| 285 | |
| 286 | $this->vars['pie_chart_data'] = [ |
| 287 | 'labels' => $pie_labels, |
| 288 | 'colors' => $pie_colors, |
| 289 | 'values' => $pie_values, |
| 290 | ]; |
| 291 | |
| 292 | |
| 293 | $this->set_layout( 'none' ); |
| 294 | $response_html = $this->format_render_return( __FUNCTION__ ); |
| 295 | } else { |
| 296 | $status = LATEPOINT_STATUS_ERROR; |
| 297 | $response_html = __( 'Error Accessing Customer', 'latepoint' ); |
| 298 | } |
| 299 | |
| 300 | if ( $this->get_return_format() == 'json' ) { |
| 301 | $this->send_json( |
| 302 | array( |
| 303 | 'status' => $status, |
| 304 | 'message' => $response_html, |
| 305 | ) |
| 306 | ); |
| 307 | } |
| 308 | } |
| 309 | |
| 310 | public function connect_all_to_wp_users() { |
| 311 | // CSRF protection |
| 312 | $this->check_nonce( 'connect_all_customers_to_wp_users' ); |
| 313 | |
| 314 | $customers = new OsCustomerModel(); |
| 315 | $customers = $customers->where( [ 'wordpress_user_id' => [ 'OR' => [ 0, 'IS NULL' ] ] ] )->get_results_as_models(); |
| 316 | if ( $customers ) { |
| 317 | foreach ( $customers as $customer ) { |
| 318 | $wp_user_id = OsCustomerHelper::create_wp_user_for_customer( $customer ); |
| 319 | if ( $wp_user_id ) { |
| 320 | //check if wp user already connected to another customer |
| 321 | $connected_customer = new OsCustomerModel(); |
| 322 | $connected_customer = $connected_customer->where( [ 'wordpress_user_id' => $wp_user_id ] )->set_limit( 1 )->get_results_as_models(); |
| 323 | if ( ! $connected_customer ) { |
| 324 | $customer->update_attributes( [ 'wordpress_user_id' => $wp_user_id ] ); |
| 325 | } |
| 326 | } |
| 327 | } |
| 328 | } |
| 329 | |
| 330 | if ( $this->get_return_format() == 'json' ) { |
| 331 | $this->send_json( |
| 332 | array( |
| 333 | 'status' => LATEPOINT_STATUS_SUCCESS, |
| 334 | 'message' => __( 'Customers Connected', 'latepoint' ), |
| 335 | ) |
| 336 | ); |
| 337 | } |
| 338 | } |
| 339 | |
| 340 | public function disconnect_from_wp_user() { |
| 341 | // CSRF protection |
| 342 | $this->check_nonce( 'disconnect_customer_from_wp_user_' . $this->params['customer_id'] ); |
| 343 | |
| 344 | $customer_id = $this->params['customer_id']; |
| 345 | $customer = new OsCustomerModel(); |
| 346 | $customer = $customer->where( [ 'id' => $customer_id ] )->set_limit( 1 )->get_results_as_models(); |
| 347 | if ( $customer ) { |
| 348 | if ( ! OsRolesHelper::can_user_make_action_on_model_record( $customer, 'edit' ) ) { |
| 349 | $this->send_json( |
| 350 | array( |
| 351 | 'status' => LATEPOINT_STATUS_ERROR, |
| 352 | 'message' => __( 'Not Allowed', 'latepoint' ), |
| 353 | ) |
| 354 | ); |
| 355 | } |
| 356 | $customer->update_attributes( [ 'wordpress_user_id' => null ] ); |
| 357 | } |
| 358 | if ( $this->get_return_format() == 'json' ) { |
| 359 | $this->send_json( |
| 360 | array( |
| 361 | 'status' => LATEPOINT_STATUS_SUCCESS, |
| 362 | 'message' => __( 'Customer Disconnected', 'latepoint' ), |
| 363 | ) |
| 364 | ); |
| 365 | } |
| 366 | } |
| 367 | |
| 368 | public function connect_to_wp_user() { |
| 369 | // CSRF protection |
| 370 | $this->check_nonce( 'connect_customer_to_wp_user_' . $this->params['customer_id'] ); |
| 371 | |
| 372 | $customer_id = $this->params['customer_id']; |
| 373 | $customer = new OsCustomerModel(); |
| 374 | $customer = $customer->where( [ 'id' => $customer_id ] )->set_limit( 1 )->get_results_as_models(); |
| 375 | if ( $customer ) { |
| 376 | if ( ! OsRolesHelper::can_user_make_action_on_model_record( $customer, 'edit' ) ) { |
| 377 | $this->send_json( |
| 378 | array( |
| 379 | 'status' => LATEPOINT_STATUS_ERROR, |
| 380 | 'message' => __( 'Not Allowed', 'latepoint' ), |
| 381 | ) |
| 382 | ); |
| 383 | } |
| 384 | if ( ! $customer->wordpress_user_id ) { |
| 385 | $wp_user = OsCustomerHelper::create_wp_user_for_customer( $customer ); |
| 386 | } |
| 387 | } |
| 388 | if ( $this->get_return_format() == 'json' ) { |
| 389 | $this->send_json( |
| 390 | array( |
| 391 | 'status' => LATEPOINT_STATUS_SUCCESS, |
| 392 | 'message' => __( 'Customer Connected', 'latepoint' ), |
| 393 | ) |
| 394 | ); |
| 395 | } |
| 396 | } |
| 397 | |
| 398 | |
| 399 | public function index() { |
| 400 | $this->vars['page_header'] = false; |
| 401 | $page_number = isset( $this->params['page_number'] ) ? $this->params['page_number'] : 1; |
| 402 | $per_page = OsSettingsHelper::get_number_of_records_per_page(); |
| 403 | $offset = ( $page_number > 1 ) ? ( ( $page_number - 1 ) * $per_page ) : 0; |
| 404 | |
| 405 | |
| 406 | $customers = new OsCustomerModel(); |
| 407 | $query_args = []; |
| 408 | |
| 409 | $filter = isset( $this->params['filter'] ) ? $this->params['filter'] : false; |
| 410 | |
| 411 | // TABLE SEARCH FILTERS |
| 412 | if ( $filter ) { |
| 413 | if ( $filter['id'] ) { |
| 414 | $query_args['id'] = $filter['id']; |
| 415 | } |
| 416 | if ( $filter['registration_date_from'] && $filter['registration_date_to'] ) { |
| 417 | $query_args[ LATEPOINT_TABLE_CUSTOMERS . '.created_at >=' ] = $filter['registration_date_from']; |
| 418 | $query_args[ LATEPOINT_TABLE_CUSTOMERS . '.created_at <=' ] = $filter['registration_date_to']; |
| 419 | } |
| 420 | if ( $filter['customer'] ) { |
| 421 | $query_args[ 'concat_ws(" ", ' . LATEPOINT_TABLE_CUSTOMERS . '.first_name,' . LATEPOINT_TABLE_CUSTOMERS . '.last_name) LIKE' ] = '%' . $filter['customer'] . '%'; |
| 422 | $this->vars['customer_name_query'] = $filter['customer']; |
| 423 | } |
| 424 | if ( $filter['phone'] ) { |
| 425 | $query_args['phone LIKE'] = '%' . $filter['phone'] . '%'; |
| 426 | $this->vars['phone_query'] = $filter['phone']; |
| 427 | } |
| 428 | if ( $filter['email'] ) { |
| 429 | $query_args['email LIKE'] = '%' . $filter['email'] . '%'; |
| 430 | $this->vars['email_query'] = $filter['email']; |
| 431 | } |
| 432 | } |
| 433 | |
| 434 | |
| 435 | // OUTPUT CSV IF REQUESTED |
| 436 | if ( isset( $this->params['download'] ) && $this->params['download'] == 'csv' ) { |
| 437 | // CSRF protection |
| 438 | $this->check_nonce( 'customers_csv_export' ); |
| 439 | |
| 440 | $csv_filename = 'customers_' . OsUtilHelper::random_text(); |
| 441 | |
| 442 | header( 'Content-Type: text/csv' ); |
| 443 | header( "Content-Disposition: attachment; filename={$csv_filename}.csv" ); |
| 444 | |
| 445 | $labels_row = [ |
| 446 | __( 'ID', 'latepoint' ), |
| 447 | __( 'Name', 'latepoint' ), |
| 448 | __( 'Phone', 'latepoint' ), |
| 449 | __( 'Email', 'latepoint' ), |
| 450 | __( 'Total Appointments', 'latepoint' ), |
| 451 | __( 'Next Appointment', 'latepoint' ), |
| 452 | __( 'Registered On', 'latepoint' ), |
| 453 | ]; |
| 454 | |
| 455 | |
| 456 | $customers_data = []; |
| 457 | $customers_data[] = $labels_row; |
| 458 | |
| 459 | |
| 460 | $customers_arr = $customers->where( $query_args )->filter_allowed_records()->order_by( 'id desc' )->get_results_as_models(); |
| 461 | if ( $customers_arr ) { |
| 462 | foreach ( $customers_arr as $customer ) { |
| 463 | $next_booking = $customer->get_future_bookings( 1, true ); |
| 464 | $values_row = [ |
| 465 | $customer->id, |
| 466 | $customer->full_name, |
| 467 | $customer->phone, |
| 468 | $customer->email, |
| 469 | $customer->total_bookings_count, |
| 470 | $next_booking ? $next_booking->nice_start_datetime : 'n/a', |
| 471 | $customer->formatted_created_date(), |
| 472 | ]; |
| 473 | $values_row = apply_filters( 'latepoint_customer_row_for_csv_export', $values_row, $customer, $this->params ); |
| 474 | $customers_data[] = $values_row; |
| 475 | } |
| 476 | } |
| 477 | $customers_data = apply_filters( 'latepoint_customers_data_for_csv_export', $customers_data, $this->params ); |
| 478 | OsCSVHelper::array_to_csv( $customers_data ); |
| 479 | |
| 480 | return; |
| 481 | } |
| 482 | |
| 483 | $customers->where( $query_args )->filter_allowed_records(); |
| 484 | $count_total_customers = clone $customers; |
| 485 | |
| 486 | $total_customers = $count_total_customers->count(); |
| 487 | $total_pages = ceil( $total_customers / $per_page ); |
| 488 | |
| 489 | |
| 490 | $this->vars['customers_violating_auth_rules'] = OsAuthHelper::count_total_customers_violating_auth_rules(); |
| 491 | |
| 492 | $this->vars['customers'] = $customers->set_limit( $per_page )->set_offset( $offset )->order_by( 'id desc' )->get_results_as_models(); |
| 493 | $this->vars['total_customers'] = $total_customers; |
| 494 | |
| 495 | $this->vars['total_pages'] = ceil( $total_customers / $per_page ); |
| 496 | $this->vars['per_page'] = $per_page; |
| 497 | $this->vars['current_page_number'] = $page_number; |
| 498 | |
| 499 | $this->vars['showing_from'] = ( ( $page_number - 1 ) * $per_page ) ? ( ( $page_number - 1 ) * $per_page ) : 1; |
| 500 | $this->vars['showing_to'] = min( $page_number * $per_page, $this->vars['total_customers'] ); |
| 501 | |
| 502 | $this->format_render( |
| 503 | [ |
| 504 | 'json_view_name' => '_table_body', |
| 505 | 'html_view_name' => __FUNCTION__, |
| 506 | ], |
| 507 | [], |
| 508 | [ |
| 509 | 'total_pages' => $total_pages, |
| 510 | 'showing_from' => $this->vars['showing_from'], |
| 511 | 'showing_to' => $this->vars['showing_to'], |
| 512 | 'total_records' => $total_customers, |
| 513 | ] |
| 514 | ); |
| 515 | } |
| 516 | |
| 517 | |
| 518 | public function import_csv_modal() { |
| 519 | $current_step = $this->params['step'] ?? 'upload_csv'; |
| 520 | $steps = [ |
| 521 | 'upload_csv' => [ 'next_step' => 'mapping' ], |
| 522 | 'mapping' => [ 'next_step' => 'importing' ], |
| 523 | ]; |
| 524 | $this->vars['current_step'] = $current_step; |
| 525 | $this->vars['next_step'] = array_key_exists( $current_step, $steps ) ? $steps[ $current_step ]['next_step'] : 'upload_csv'; |
| 526 | $this->format_render( __FUNCTION__ ); |
| 527 | } |
| 528 | |
| 529 | public function import_load_step() { |
| 530 | $this->check_nonce( 'import_customers_csv' ); |
| 531 | $current_step = $this->params['step'] ?? 'upload_csv'; |
| 532 | $status = LATEPOINT_STATUS_SUCCESS; |
| 533 | |
| 534 | try { |
| 535 | switch ( $current_step ) { |
| 536 | case 'upload_csv': |
| 537 | $response_html = $this->_handle_upload_step(); |
| 538 | break; |
| 539 | |
| 540 | case 'mapping': |
| 541 | $response_html = $this->_handle_mapping_step(); |
| 542 | break; |
| 543 | |
| 544 | case 'confirmation': |
| 545 | $response_html = $this->_handleConfirmationStep(); |
| 546 | break; |
| 547 | |
| 548 | default: |
| 549 | throw new Exception( 'Invalid step provided' ); |
| 550 | } |
| 551 | } catch ( Exception $e ) { |
| 552 | $response_html = $e->getMessage(); |
| 553 | $status = LATEPOINT_STATUS_ERROR; |
| 554 | } |
| 555 | |
| 556 | $this->send_json( |
| 557 | array( |
| 558 | 'status' => $status, |
| 559 | 'message' => $response_html, |
| 560 | ) |
| 561 | ); |
| 562 | } |
| 563 | |
| 564 | private function _handle_upload_step(): string { |
| 565 | $file_path = OsCSVHelper::upload_csv_file( $this->files, 'latepoint_customers_csv' ); |
| 566 | $csv_data = OsCSVHelper::get_csv_data( $file_path, 1 ); |
| 567 | |
| 568 | return $this->render( |
| 569 | $this->views_folder . 'import_steps/step_mapping.php', |
| 570 | 'none', |
| 571 | [ |
| 572 | 'csv_data' => $csv_data, |
| 573 | ] |
| 574 | ); |
| 575 | } |
| 576 | |
| 577 | private function _handle_mapping_step(): string { |
| 578 | $columnMapping = $this->params['latepoint_column_mapping'] ?? []; |
| 579 | |
| 580 | if ( ! OsCustomerImportHelper::validate_import_mapping( $columnMapping ) ) { |
| 581 | throw new Exception( esc_html__( 'Email field is required', 'latepoint' ) ); |
| 582 | } |
| 583 | |
| 584 | $csv_data = OsCSVHelper::get_csv_data( OsCustomerImportHelper::get_import_tmp_filepath() ); |
| 585 | $validation_result = OsCustomerImportHelper::validate_csv_data( $csv_data, $columnMapping ); |
| 586 | |
| 587 | return $this->render( |
| 588 | $this->views_folder . 'import_steps/step_confirmation.php', |
| 589 | 'none', |
| 590 | [ |
| 591 | 'conflicts' => $validation_result['conflicts'], |
| 592 | 'can_be_imported' => $validation_result['importable_count'], |
| 593 | 'latepoint_column_mapping' => $columnMapping, |
| 594 | ] |
| 595 | ); |
| 596 | } |
| 597 | |
| 598 | |
| 599 | private function _handleConfirmationStep(): string { |
| 600 | $update_existing_customers = ! empty( $this->params['latepoint_update_customers_acknowledgement'] ) && OsUtilHelper::is_on( $this->params['latepoint_update_customers_acknowledgement'] ); |
| 601 | $column_mapping = ! empty( $this->params['latepoint_column_mapping'] ) ? json_decode( $this->params['latepoint_column_mapping'], true ) : []; |
| 602 | |
| 603 | $file_path = OsCustomerImportHelper::get_import_tmp_filepath(); |
| 604 | $csv_data = OsCSVHelper::get_csv_data( $file_path ); |
| 605 | |
| 606 | $importResult = OsCustomerImportHelper::import_customers( $csv_data, $column_mapping, $update_existing_customers ); |
| 607 | |
| 608 | // Cleanup after successful import |
| 609 | OsCustomerImportHelper::cleanup_stored_file(); |
| 610 | |
| 611 | return $this->render( |
| 612 | $this->views_folder . 'import_steps/step_done.php', |
| 613 | 'none', |
| 614 | [ |
| 615 | 'skipped_records' => $importResult['skipped_count'], |
| 616 | 'updated_records' => $importResult['updated_count'], |
| 617 | ] |
| 618 | ); |
| 619 | } |
| 620 | } |
| 621 | |
| 622 | |
| 623 | endif; |
| 624 |