PluginProbe ʕ •ᴥ•ʔ
LatePoint – Calendar Booking Plugin for Appointments and Events / 5.2.1
LatePoint – Calendar Booking Plugin for Appointments and Events v5.2.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 / controllers / customers_controller.php
latepoint / lib / controllers Last commit date
activities_controller.php 1 year ago auth_controller.php 9 months ago booking_form_settings_controller.php 1 year ago bookings_controller.php 1 year ago calendars_controller.php 9 months ago carts_controller.php 1 year ago controller.php 9 months ago customer_cabinet_controller.php 9 months ago customers_controller.php 9 months ago dashboard_controller.php 9 months ago default_agent_controller.php 1 year ago events_controller.php 1 year ago form_fields_controller.php 9 months ago integrations_controller.php 9 months ago invoices_controller.php 1 year ago manage_booking_by_key_controller.php 1 year ago manage_order_by_key_controller.php 1 year ago notifications_controller.php 1 year ago orders_controller.php 1 year ago pro_controller.php 1 year ago process_jobs_controller.php 9 months ago processes_controller.php 1 year ago search_controller.php 1 year ago services_controller.php 9 months ago settings_controller.php 1 year ago steps_controller.php 9 months ago stripe_connect_controller.php 9 months ago support_topics_controller.php 1 year ago todos_controller.php 1 year ago transactions_controller.php 1 year ago wizard_controller.php 1 year ago
customers_controller.php
467 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('label' => __('Customers', 'latepoint'), 'link' => OsRouterHelper::build_link(OsRouterHelper::build_route_name('customers', 'index') ) );
19 }
20
21 public function destroy(){
22 if(filter_var($this->params['id'], FILTER_VALIDATE_INT)){
23 $this->check_nonce('destroy_customer_'.$this->params['id']);
24 $customer = new OsCustomerModel($this->params['id']);
25 if($customer->delete()){
26 $status = LATEPOINT_STATUS_SUCCESS;
27 $response_html = __('Customer Removed', 'latepoint');
28 }else{
29 $status = LATEPOINT_STATUS_ERROR;
30 $response_html = __('Error Removing Customer', 'latepoint');
31 }
32 }else{
33 $status = LATEPOINT_STATUS_ERROR;
34 $response_html = __('Error Removing Customer', 'latepoint');
35 }
36
37 if($this->get_return_format() == 'json'){
38 $this->send_json(array('status' => $status, 'message' => $response_html));
39 }
40 }
41
42
43 public function view_customer_log(){
44
45 $activities = new OsActivityModel();
46 $activities = $activities->where(['customer_id' => absint($this->params['customer_id'])])->order_by('id desc')->get_results_as_models();
47
48 $customer = new OsCustomerModel($this->params['customer_id']);
49
50 $this->vars['customer'] = $customer;
51 $this->vars['activities'] = $activities;
52
53 $this->format_render(__FUNCTION__);
54 }
55
56
57 public function quick_new(){
58 $customer = new OsCustomerModel();
59
60 $this->vars['customer'] = $customer;
61
62 $this->format_render('quick_edit');
63 }
64
65 public function quick_edit(){
66 if(!filter_var($this->params['customer_id'], FILTER_VALIDATE_INT)) $this->access_not_allowed();
67 $customer = new OsCustomerModel($this->params['customer_id']);
68
69 $this->vars['customer'] = $customer;
70
71 $this->format_render(__FUNCTION__);
72 }
73
74
75 public function inline_edit_form(){
76 $selected_customer = new OsCustomerModel();
77 if(isset($this->params['customer_id'])){
78 $selected_customer->load_by_id($this->params['customer_id']);
79 }
80 $this->vars['default_fields_for_customer'] = OsSettingsHelper::get_default_fields_for_customer();
81 $this->vars['selected_customer'] = $selected_customer;
82 $this->format_render(__FUNCTION__);
83 }
84
85 public function set_as_guest(){
86 if(filter_var($this->params['id'], FILTER_VALIDATE_INT)){
87 $customer = new OsCustomerModel($this->params['id']);
88 if($customer->update_attributes(['is_guest' => true])){
89 $status = LATEPOINT_STATUS_SUCCESS;
90 $response_html = __('Customer is now allowed to book without password', 'latepoint');
91 }else{
92 $status = LATEPOINT_STATUS_ERROR;
93 $response_html = $customer->get_error_messages();
94 }
95 }else{
96 $status = LATEPOINT_STATUS_ERROR;
97 $response_html = __('Error setting customer as guest', 'latepoint');
98 }
99
100 if($this->get_return_format() == 'json'){
101 $this->send_json(array('status' => $status, 'message' => $response_html));
102 }
103 }
104
105 /*
106 Edit customer
107 */
108
109 public function edit_form(){
110 $this->vars['page_header'] = __('Edit Customer', 'latepoint');
111 $this->vars['breadcrumbs'][] = array('label' => __('Edit Customer', 'latepoint'), 'link' => false );
112
113 if(filter_var($this->params['id'], FILTER_VALIDATE_INT)){
114 // check if allowed to access
115 $customer = new OsCustomerModel();
116 $customer = $customer->where([LATEPOINT_TABLE_CUSTOMERS.'.id' => absint($this->params['id'])])->filter_allowed_records()->set_limit(1)->get_results_as_models();
117 $this->vars['customer'] = $customer;
118 $this->vars['wp_users_for_select'] = OsWpUserHelper::get_wp_users_for_select();
119 }
120
121 $this->format_render(__FUNCTION__);
122 }
123
124
125 public function query_for_booking_form(){
126 $query = trim($this->params['query']);
127 $sql_query = '%'.$query.'%';
128 $query = $this->params['query'];
129 $customers = new OsCustomerModel();
130 $this->vars['query'] = $query;
131 $this->vars['customers'] = $customers->where(array('OR' => array('CONCAT (first_name, " ", last_name) LIKE ' => $sql_query, 'email LIKE' => $sql_query, 'phone LIKE' => $sql_query)))->set_limit(20)->order_by('first_name asc, last_name asc')->get_results_as_models();
132
133 $this->format_render(__FUNCTION__);
134 }
135
136
137 /*
138 Create customer
139 */
140
141 public function create(){
142 $this->check_nonce('new_customer');
143 $customer = new OsCustomerModel();
144 $customer->set_data($this->params['customer']);
145 if($customer->save()){
146 // translators: %s is the html of a customer edit link
147 $response_html = sprintf(__('Customer Created ID: %s', 'latepoint'), '<span class="os-notification-link" '.OsCustomerHelper::quick_customer_btn_html($customer->id).'>'.$customer->id.'</span>');
148 $status = LATEPOINT_STATUS_SUCCESS;
149 do_action('latepoint_customer_created', $customer);
150 }else{
151 $response_html = $customer->get_error_messages();
152 $status = LATEPOINT_STATUS_ERROR;
153 }
154 if($this->get_return_format() == 'json'){
155 $this->send_json(array('status' => $status, 'message' => $response_html));
156 }
157 }
158
159
160 /*
161 Update customer
162 */
163
164 public function update(){
165 if(isset($this->params['customer']['id']) && filter_var($this->params['customer']['id'], FILTER_VALIDATE_INT)){
166 $this->check_nonce('edit_customer_'.$this->params['customer']['id']);
167 $customer = new OsCustomerModel($this->params['customer']['id']);
168 if(!$customer || !OsRolesHelper::can_user_make_action_on_model_record($customer, 'edit')){
169 $response_html = __('Access Restricted', 'latepoint');
170 $status = LATEPOINT_STATUS_ERROR;
171 }else{
172 $old_customer_data = $customer->get_data_vars();
173 $customer->set_data($this->params['customer']);
174 if($customer->save()){
175 // translators: %s is the html of a customer edit link
176 $response_html = sprintf(__('Customer Updated ID: %s', 'latepoint'), '<span class="os-notification-link" '.OsCustomerHelper::quick_customer_btn_html($customer->id).'>'.$customer->id.'</span>');
177 $status = LATEPOINT_STATUS_SUCCESS;
178 do_action('latepoint_customer_updated', $customer, $old_customer_data);
179 }else{
180 $response_html = $customer->get_error_messages();
181 $status = LATEPOINT_STATUS_ERROR;
182 }
183 }
184 }else{
185 $response_html = __('Invalid customer ID', 'latepoint');
186 $status = LATEPOINT_STATUS_ERROR;
187 }
188 if($this->get_return_format() == 'json'){
189 $this->send_json(array('status' => $status, 'message' => $response_html));
190 }
191 }
192
193 public function mini_profile(){
194 if(filter_var($this->params['customer_id'], FILTER_VALIDATE_INT)){
195 $customer = new OsCustomerModel($this->params['customer_id']);
196 $this->vars['upcoming_booking'] = $customer->get_future_bookings(1, true);
197 $this->vars['customer'] = $customer;
198
199
200 $pie_labels = [];
201 $pie_colors = [];
202 $pie_values = [];
203 $pie_chart_data = OsBookingHelper::get_stat('bookings', ['group_by' => 'status', 'customer_id' => $customer->id]);
204 $colors = ['#2752E4', '#C066F1', '#26B7DD', '#E8C634', '#19CED6', '#2FEAA3', '#252a3e', '#8d87a5', '#b9b784'];
205 $status_colors = [
206 LATEPOINT_BOOKING_STATUS_APPROVED => '#35d893',
207 LATEPOINT_BOOKING_STATUS_PENDING => '#e6b935',
208 LATEPOINT_BOOKING_STATUS_PAYMENT_PENDING => '#4ca4ef',
209 LATEPOINT_BOOKING_STATUS_CANCELLED => '#f1585d'
210 ];
211 $i = 0;
212 foreach($pie_chart_data as $pie_data){
213 $pie_labels[] = $pie_data['status'];
214 $pie_colors[] = isset($status_colors[$pie_data['status']]) ? $status_colors[$pie_data['status']] : $colors[$i];
215 $pie_values[] = $pie_data['stat'];
216 $i++;
217 }
218
219 $this->vars['pie_chart_data'] = ['labels' => $pie_labels, 'colors' => $pie_colors, 'values' => $pie_values];
220
221
222
223 $this->set_layout('none');
224 $response_html = $this->format_render_return(__FUNCTION__);
225 }else{
226 $status = LATEPOINT_STATUS_ERROR;
227 $response_html = __('Error Accessing Customer', 'latepoint');
228 }
229
230 if($this->get_return_format() == 'json'){
231 $this->send_json(array('status' => $status, 'message' => $response_html));
232 }
233 }
234
235 public function connect_all_to_wp_users(){
236 $customers = new OsCustomerModel();
237 $customers = $customers->where(['wordpress_user_id' => ['OR' => [0, 'IS NULL']]])->get_results_as_models();
238 if($customers){
239 foreach($customers as $customer){
240 $wp_user_id = OsCustomerHelper::create_wp_user_for_customer($customer);
241 if($wp_user_id) {
242 //check if wp user already connected to another customer
243 $connected_customer = new OsCustomerModel();
244 $connected_customer = $connected_customer->where( [ 'wordpress_user_id' => $wp_user_id ] )->set_limit( 1 )->get_results_as_models();
245 if ( !$connected_customer ) {
246 $customer->update_attributes( [ 'wordpress_user_id' => $wp_user_id ] );
247 }
248 }
249 }
250 }
251
252 if($this->get_return_format() == 'json'){
253 $this->send_json(array('status' => LATEPOINT_STATUS_SUCCESS, 'message' => __('Customers Connected', 'latepoint')));
254 }
255 }
256
257 public function disconnect_from_wp_user(){
258 $customer_id = $this->params['customer_id'];
259 $customer = new OsCustomerModel();
260 $customer = $customer->where(['id' => $customer_id])->set_limit(1)->get_results_as_models();
261 if($customer){
262 $customer->update_attributes(['wordpress_user_id' => NULL]);
263 }
264 if($this->get_return_format() == 'json'){
265 $this->send_json(array('status' => LATEPOINT_STATUS_SUCCESS, 'message' => __('Customer Disconnected', 'latepoint')));
266 }
267 }
268
269 public function connect_to_wp_user(){
270 $customer_id = $this->params['customer_id'];
271 $customer = new OsCustomerModel();
272 $customer = $customer->where(['id' => $customer_id])->set_limit(1)->get_results_as_models();
273 if($customer && !$customer->wordpress_user_id){
274 $wp_user = OsCustomerHelper::create_wp_user_for_customer($customer);
275 }
276 if($this->get_return_format() == 'json'){
277 $this->send_json(array('status' => LATEPOINT_STATUS_SUCCESS, 'message' => __('Customer Connected', 'latepoint')));
278 }
279 }
280
281
282 public function index(){
283
284 $this->vars['page_header'] = false;
285 $page_number = isset($this->params['page_number']) ? $this->params['page_number'] : 1;
286 $per_page = OsSettingsHelper::get_number_of_records_per_page();
287 $offset = ($page_number > 1) ? (($page_number - 1) * $per_page) : 0;
288
289
290 $customers = new OsCustomerModel();
291 $query_args = [];
292
293 $filter = isset($this->params['filter']) ? $this->params['filter'] : false;
294
295 // TABLE SEARCH FILTERS
296 if($filter){
297 if($filter['id']) $query_args['id'] = $filter['id'];
298 if($filter['registration_date_from'] && $filter['registration_date_to']){
299 $query_args[LATEPOINT_TABLE_CUSTOMERS.'.created_at >='] = $filter['registration_date_from'];
300 $query_args[LATEPOINT_TABLE_CUSTOMERS.'.created_at <='] = $filter['registration_date_to'];
301 }
302 if($filter['customer']){
303 $query_args['concat_ws(" ", '.LATEPOINT_TABLE_CUSTOMERS.'.first_name,'.LATEPOINT_TABLE_CUSTOMERS.'.last_name) LIKE'] = '%'.$filter['customer'].'%';
304 $this->vars['customer_name_query'] = $filter['customer'];
305 }
306 if($filter['phone']){
307 $query_args['phone LIKE'] = '%'.$filter['phone'].'%';
308 $this->vars['phone_query'] = $filter['phone'];
309 }
310 if($filter['email']){
311 $query_args['email LIKE'] = '%'.$filter['email'].'%';
312 $this->vars['email_query'] = $filter['email'];
313 }
314 }
315
316
317 // OUTPUT CSV IF REQUESTED
318 if(isset($this->params['download']) && $this->params['download'] == 'csv'){
319 $csv_filename = 'customers_'.OsUtilHelper::random_text();
320
321 header("Content-Type: text/csv");
322 header("Content-Disposition: attachment; filename={$csv_filename}.csv");
323
324 $labels_row = [ __('ID', 'latepoint'),
325 __('Name', 'latepoint'),
326 __('Phone', 'latepoint'),
327 __('Email', 'latepoint'),
328 __('Total Appointments', 'latepoint'),
329 __('Next Appointment', 'latepoint'),
330 __('Registered On', 'latepoint') ];
331
332
333 $customers_data = [];
334 $customers_data[] = $labels_row;
335
336
337 $customers_arr = $customers->where($query_args)->filter_allowed_records()->order_by('id desc')->get_results_as_models();
338 if($customers_arr){
339 foreach($customers_arr as $customer){
340 $next_booking = $customer->get_future_bookings(1, true);
341 $values_row = [ $customer->id,
342 $customer->full_name,
343 $customer->phone,
344 $customer->email,
345 $customer->total_bookings_count,
346 $next_booking ? $next_booking->nice_start_datetime : 'n/a',
347 $customer->formatted_created_date()];
348 $values_row = apply_filters('latepoint_customer_row_for_csv_export', $values_row, $customer, $this->params);
349 $customers_data[] = $values_row;
350 }
351 }
352 $customers_data = apply_filters('latepoint_customers_data_for_csv_export', $customers_data, $this->params);
353 OsCSVHelper::array_to_csv($customers_data);
354 return;
355 }
356
357 $customers->where($query_args)->filter_allowed_records();
358 $count_total_customers = clone $customers;
359
360 $total_customers = $count_total_customers->count();
361 $total_pages = ceil($total_customers / $per_page);
362
363 $this->vars['customers'] = $customers->set_limit($per_page)->set_offset($offset)->order_by('id desc')->get_results_as_models();
364 $this->vars['total_customers'] = $total_customers;
365
366 $this->vars['total_pages'] = ceil($total_customers / $per_page);
367 $this->vars['per_page'] = $per_page;
368 $this->vars['current_page_number'] = $page_number;
369
370 $this->vars['showing_from'] = (($page_number - 1) * $per_page) ? (($page_number - 1) * $per_page) : 1;
371 $this->vars['showing_to'] = min($page_number * $per_page, $this->vars['total_customers']);
372
373 $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_customers]);
374 }
375
376
377 public function import_csv_modal( ) {
378 $current_step = $this->params['step'] ?? 'upload_csv';
379 $steps = [
380 'upload_csv' => ['next_step' => 'mapping'],
381 'mapping' => ['next_step' => 'importing'],
382 ];
383 $this->vars['current_step'] = $current_step;
384 $this->vars['next_step'] = array_key_exists($current_step, $steps) ? $steps[$current_step]['next_step'] : 'upload_csv';
385 $this->format_render( __FUNCTION__ );
386 }
387
388 public function import_load_step() {
389 $this->check_nonce( 'import_customers_csv' );
390 $current_step = $this->params['step'] ?? 'upload_csv';
391 $status = LATEPOINT_STATUS_SUCCESS;
392
393 try {
394 switch ($current_step) {
395 case 'upload_csv':
396 $response_html = $this->_handle_upload_step();
397 break;
398
399 case 'mapping':
400 $response_html = $this->_handle_mapping_step();
401 break;
402
403 case 'confirmation':
404 $response_html = $this->_handleConfirmationStep();
405 break;
406
407 default:
408 throw new Exception('Invalid step provided');
409 }
410 } catch (Exception $e) {
411 $response_html = $e->getMessage();
412 $status = LATEPOINT_STATUS_ERROR;
413 }
414
415 $this->send_json(array('status' => $status, 'message' => $response_html));
416 }
417
418 private function _handle_upload_step(): string {
419 $file_path = OsCSVHelper::upload_csv_file( $this->files, 'latepoint_customers_csv' );
420 $csv_data = OsCSVHelper::get_csv_data( $file_path, 1 );
421
422 return $this->render( $this->views_folder . 'import_steps/step_mapping.php', 'none', [
423 'csv_data' => $csv_data,
424 ] );
425 }
426
427 private function _handle_mapping_step(): string {
428 $columnMapping = $this->params['latepoint_column_mapping'] ?? [];
429
430 if (!OsCustomerImportHelper::validate_import_mapping($columnMapping)) {
431 throw new Exception(esc_html__('Email field is required', 'latepoint'));
432 }
433
434 $csv_data = OsCSVHelper::get_csv_data(OsCustomerImportHelper::get_import_tmp_filepath());
435 $validation_result = OsCustomerImportHelper::validate_csv_data($csv_data, $columnMapping);
436
437 return $this->render($this->views_folder . 'import_steps/step_confirmation.php', 'none', [
438 'conflicts' => $validation_result['conflicts'],
439 'can_be_imported' => $validation_result['importable_count'],
440 'latepoint_column_mapping' => $columnMapping
441 ]);
442 }
443
444
445 private function _handleConfirmationStep(): string {
446 $update_existing_customers = !empty($this->params['latepoint_update_customers_acknowledgement']) && OsUtilHelper::is_on($this->params['latepoint_update_customers_acknowledgement']);
447 $column_mapping = !empty($this->params['latepoint_column_mapping']) ? json_decode($this->params['latepoint_column_mapping'], true) : [];
448
449 $file_path = OsCustomerImportHelper::get_import_tmp_filepath();
450 $csv_data = OsCSVHelper::get_csv_data($file_path);
451
452 $importResult = OsCustomerImportHelper::import_customers( $csv_data, $column_mapping, $update_existing_customers );
453
454 // Cleanup after successful import
455 OsCustomerImportHelper::cleanup_stored_file();
456
457 return $this->render($this->views_folder . 'import_steps/step_done.php', 'none', [
458 'skipped_records' => $importResult['skipped_count'],
459 'updated_records' => $importResult['updated_count'],
460 ]);
461 }
462
463
464 }
465
466
467 endif;