PluginProbe ʕ •ᴥ•ʔ
LatePoint – Calendar Booking Plugin for Appointments and Events / trunk
LatePoint – Calendar Booking Plugin for Appointments and Events vtrunk
5.6.5 5.6.4 5.6.3 5.6.2 5.6.1 5.6.0 5.5.2 5.5.1 5.5.0 5.4.2 trunk 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.1.91 5.1.92 5.1.93 5.1.94 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1
latepoint / lib / models / customer_model.php
latepoint / lib / models Last commit date
activity_model.php 3 months ago agent_meta_model.php 3 months ago agent_model.php 3 months ago booking_meta_model.php 3 months ago booking_model.php 17 hours ago bundle_meta_model.php 3 months ago bundle_model.php 1 week ago cart_item_model.php 3 months ago cart_meta_model.php 3 months ago cart_model.php 2 weeks ago connector_model.php 3 months ago customer_meta_model.php 3 months ago customer_model.php 1 month ago invoice_model.php 2 weeks ago join_bundles_services_model.php 3 months ago location_category_model.php 3 months ago location_model.php 3 months ago meta_model.php 3 months ago model.php 2 days ago off_period_model.php 3 months ago order_intent_meta_model.php 3 months ago order_intent_model.php 1 week ago order_item_model.php 3 months ago order_meta_model.php 3 months ago order_model.php 1 month ago otp_model.php 3 months ago payment_request_model.php 3 months ago process_job_model.php 3 months ago process_model.php 1 month ago recurrence_model.php 3 months ago service_category_model.php 3 months ago service_meta_model.php 3 months ago service_model.php 3 months ago session_model.php 3 months ago settings_model.php 3 months ago step_settings_model.php 3 months ago transaction_intent_model.php 3 months ago transaction_model.php 3 months ago transaction_refund_model.php 3 months ago work_period_model.php 3 months ago
customer_model.php
537 lines
1 <?php
2
3 /**
4 * @property string $full_name
5 */
6 class OsCustomerModel extends OsModel {
7 var $id,
8 $uuid,
9 $first_name,
10 $last_name,
11 $password,
12 $email,
13 $phone,
14 $account_nonse,
15 $status,
16 $activation_key,
17 $google_user_id,
18 $facebook_user_id,
19 $avatar_image_id,
20 $is_guest,
21 $notes,
22 $admin_notes,
23 $wordpress_user_id,
24 $meta_class = 'OsCustomerMetaModel',
25 $updated_at,
26 $created_at;
27
28 function __construct( $id = false ) {
29 $this->table_name = LATEPOINT_TABLE_CUSTOMERS;
30 $this->nice_names = array(
31 'first_name' => __( 'Customer First Name', 'latepoint' ),
32 'email' => __( 'Email Address', 'latepoint' ),
33 'phone' => __( 'Phone Number', 'latepoint' ),
34 'last_name' => __( 'Customer Last Name', 'latepoint' ),
35 );
36
37 parent::__construct( $id );
38 }
39
40 public function generate_data_vars(): array {
41 return [
42 'id' => $this->id,
43 'first_name' => $this->first_name,
44 'last_name' => $this->last_name,
45 'full_name' => $this->full_name,
46 'email' => $this->email,
47 'phone' => $this->phone,
48 ];
49 }
50
51 public function get_meta_by_key( $meta_key, $default = false ) {
52 if ( $this->is_new_record() ) {
53 return $default;
54 }
55
56 $meta = new OsCustomerMetaModel();
57
58 return $meta->get_by_key( $meta_key, $this->id, $default );
59 }
60
61 /**
62 * @param int $limit
63 * @param bool $filter_allowed_records
64 *
65 * @return OsOrderModel[]
66 */
67 public function get_orders( int $limit = 0, bool $filter_allowed_records = false ): array {
68 $orders = new OsOrderModel();
69 if ( $limit ) {
70 $orders = $orders->set_limit( $limit );
71 }
72 if ( $filter_allowed_records ) {
73 $orders->filter_allowed_records();
74 }
75
76 return $orders->where( [ 'customer_id' => $this->id ] )->order_by( 'created_at desc' )->get_results_as_models();
77 }
78
79 public function get_initials() {
80 return mb_substr( $this->first_name, 0, 1 ) . mb_substr( $this->last_name, 0, 1 );
81 }
82
83 public function delete_meta_by_key( $meta_key ) {
84 if ( $this->is_new_record() ) {
85 return true;
86 }
87
88 $meta = new OsCustomerMetaModel();
89
90 return $meta->delete_by_key( $meta_key, $this->id );
91 }
92
93 public function save_meta_by_key( $meta_key, $meta_value ) {
94 if ( $this->is_new_record() ) {
95 return false;
96 }
97
98 $meta = new OsCustomerMetaModel();
99
100 return $meta->save_by_key( $meta_key, $meta_value, $this->id );
101 }
102
103 public function set_timezone_name( $timezone_name = false ) {
104 if ( ! $timezone_name ) {
105 $timezone_name = OsTimeHelper::get_timezone_name_from_session();
106 }
107 $this->save_meta_by_key( 'timezone_name', $timezone_name );
108 }
109
110 public function get_selected_timezone_name() {
111 if ( OsSettingsHelper::is_on( 'steps_show_timezone_selector' ) ) {
112 return $this->get_meta_by_key( 'timezone_name', OsTimeHelper::get_timezone_name_from_session() );
113 } else {
114 return OsTimeHelper::get_wp_timezone_name();
115 }
116 }
117
118 public function get_selected_timezone_obj() {
119 $timezone_obj = new DateTimeZone( $this->get_selected_timezone_name() );
120
121 return $timezone_obj;
122 }
123
124
125 public function delete( $id = false ) {
126 if ( ! $id && isset( $this->id ) ) {
127 $id = $this->id;
128 }
129 $bookings = new OsBookingModel();
130 $bookings_to_delete = $bookings->where( [ 'customer_id' => $id ] )->get_results_as_models();
131 if ( $bookings_to_delete ) {
132 foreach ( $bookings_to_delete as $booking ) {
133 $booking->delete();
134 }
135 }
136 $orders = new OsOrderModel();
137 $orders_to_delete = $orders->where( [ 'customer_id' => $id ] )->get_results_as_models();
138 if ( $orders_to_delete ) {
139 foreach ( $orders_to_delete as $order ) {
140 $order->delete();
141 }
142 }
143 $transactions = new OsTransactionModel();
144 $transactions_to_delete = $transactions->where( [ 'customer_id' => $id ] )->get_results_as_models();
145 if ( $transactions_to_delete ) {
146 foreach ( $transactions_to_delete as $transaction ) {
147 $transaction->delete();
148 }
149 }
150 $customer_metas = new OsCustomerMetaModel();
151 $customer_metas_to_delete = $customer_metas->where( [ 'object_id' => $id ] )->get_results_as_models();
152 if ( $customer_metas_to_delete ) {
153 foreach ( $customer_metas_to_delete as $customer_meta ) {
154 $customer_meta->delete();
155 }
156 }
157
158 return parent::delete( $id );
159 }
160
161
162 public function get_bookings( $limit = false, $filter_allowed_records = false ) {
163 $bookings = new OsBookingModel();
164 if ( $limit ) {
165 $bookings = $bookings->set_limit( $limit );
166 }
167 if ( $filter_allowed_records ) {
168 $bookings->filter_allowed_records();
169 }
170
171 return $bookings->where( [ 'customer_id' => $this->id ] )->get_results_as_models();
172 }
173
174 public function get_past_bookings( $limit = false, $filter_allowed_records = false ) {
175 $bookings = new OsBookingModel();
176 if ( $limit ) {
177 $bookings = $bookings->set_limit( $limit );
178 }
179 if ( $filter_allowed_records ) {
180 $bookings->filter_allowed_records();
181 }
182
183 return $bookings->should_not_be_cancelled()->where(
184 array(
185 'customer_id' => $this->id,
186 'OR' => array(
187 'start_date <' => OsTimeHelper::today_date( 'Y-m-d' ),
188 'AND' => array(
189 'start_date' => OsTimeHelper::today_date( 'Y-m-d' ),
190 'start_time <' => OsTimeHelper::get_current_minutes(),
191 ),
192 ),
193 )
194 )->get_results_as_models();
195 }
196
197 /**
198 * @return OsBundleModel[]
199 */
200 public function get_not_scheduled_bundles(): array {
201 $non_scheduled_bundles = [];
202 $orders = new OsOrderModel();
203 $orders = $orders->where( [ 'customer_id' => $this->id ] )->get_results_as_models();
204 if ( $orders ) {
205 foreach ( $orders as $order ) {
206 $bundles = $order->get_bundles_from_order_items();
207 if ( $bundles ) {
208 foreach ( $bundles as $order_item_id => $bundle ) {
209 $bundle_services = $bundle->get_services();
210 foreach ( $bundle_services as $bundle_service ) {
211 $bookings = new OsBookingModel();
212 $total_scheduled_bookings = $bookings->where(
213 [
214 'order_item_id' => $order_item_id,
215 'service_id' => $bundle_service->id,
216 ]
217 )->should_not_be_cancelled()->count();
218 if ( $total_scheduled_bookings < $bundle_service->join_attributes['quantity'] ) {
219 $non_scheduled_bundles[ $order_item_id ] = $bundle;
220 break;
221 }
222 }
223 }
224 }
225 }
226 }
227
228 return $non_scheduled_bundles;
229 }
230
231 public function get_cancelled_bookings( $limit = false, $filter_allowed_records = false ) {
232 $bookings = new OsBookingModel();
233 if ( $limit ) {
234 $bookings = $bookings->set_limit( $limit );
235 }
236 if ( $filter_allowed_records ) {
237 $bookings->filter_allowed_records();
238 }
239
240 return $bookings->should_be_cancelled()->order_by( 'start_date, start_time asc' )->where( [ 'customer_id' => $this->id ] )->get_results_as_models();
241 }
242
243 public function get_future_bookings( $limit = false, $filter_allowed_records = false ) {
244 $bookings = new OsBookingModel();
245 if ( $limit ) {
246 $bookings = $bookings->set_limit( $limit );
247 }
248 if ( $filter_allowed_records ) {
249 $bookings->filter_allowed_records();
250 }
251
252 return $bookings->should_not_be_cancelled()->order_by( 'start_date, start_time asc' )->where( [ 'customer_id' => $this->id ] )->should_be_in_future()->get_results_as_models();
253 }
254
255
256 public function get_future_bookings_count( $filter_allowed_records = false ) {
257 $bookings = new OsBookingModel();
258 if ( $filter_allowed_records ) {
259 $bookings->filter_allowed_records();
260 }
261
262 return $bookings->should_not_be_cancelled()->where(
263 array(
264 'customer_id' => $this->id,
265 'OR' => array(
266 'start_date >' => OsTimeHelper::today_date( 'Y-m-d' ),
267 'AND' => array(
268 'start_date' => OsTimeHelper::today_date( 'Y-m-d' ),
269 'start_time >' => OsTimeHelper::get_current_minutes(),
270 ),
271 ),
272 )
273 )->count();
274 }
275
276 public function get_total_bookings_count( $filter_allowed_records = false ) {
277 $bookings = new OsBookingModel();
278 if ( $filter_allowed_records ) {
279 $bookings->filter_allowed_records();
280 }
281
282 return $bookings->select( 'count(id) as total_bookings' )->where( array( 'customer_id' => $this->id ) )->count();
283 }
284
285
286 public function filter_allowed_records(): OsModel {
287 if ( ! OsRolesHelper::are_all_records_allowed() ) {
288 $this->select( LATEPOINT_TABLE_CUSTOMERS . '.*' )->join( LATEPOINT_TABLE_BOOKINGS, [ 'customer_id' => LATEPOINT_TABLE_CUSTOMERS . '.id' ] )->group_by( LATEPOINT_TABLE_CUSTOMERS . '.id' );
289 if ( ! OsRolesHelper::are_all_records_allowed( 'agent' ) ) {
290 $this->filter_where_conditions( [ LATEPOINT_TABLE_BOOKINGS . '.agent_id' => OsRolesHelper::get_allowed_records( 'agent' ) ] );
291 }
292 if ( ! OsRolesHelper::are_all_records_allowed( 'location' ) ) {
293 $this->filter_where_conditions( [ LATEPOINT_TABLE_BOOKINGS . '.location_id' => OsRolesHelper::get_allowed_records( 'location' ) ] );
294 }
295 if ( ! OsRolesHelper::are_all_records_allowed( 'service' ) ) {
296 $this->filter_where_conditions( [ LATEPOINT_TABLE_BOOKINGS . '.service_id' => OsRolesHelper::get_allowed_records( 'service' ) ] );
297 }
298 }
299
300 return $this;
301 }
302
303 public function primary_contact_type() {
304 $contact_type = OsAuthHelper::get_selected_customer_authentication_field_type();
305 switch ( $contact_type ) {
306 case 'phone':
307 return $this->phone;
308 break;
309 default:
310 return $this->email;
311 break;
312 }
313 }
314
315 public function update_password( $password ) {
316 // update connected wp user password
317 if ( OsAuthHelper::can_wp_users_login_as_customers() && $this->wordpress_user_id ) {
318 // Only reset the WP password for non-privileged accounts to prevent takeover of admin/editor users
319 if ( ! OsCustomerHelper::is_wp_user_safe_for_customer_link( (int) $this->wordpress_user_id ) ) {
320 return $this->update_attributes(
321 [
322 'password' => wp_hash_password( $password ),
323 'is_guest' => false,
324 ]
325 );
326 }
327
328 $is_logged_in = OsWpUserHelper::get_current_user_id() == $this->wordpress_user_id;
329 $logged_in_wp_user = $is_logged_in ? OsWpUserHelper::get_current_user() : false;
330
331 // user is getting logged out after changing a password - we need to check if the user that we are changing password for is currently logged in, if it is - make sure to log them back in after password change
332 wp_set_password( $password, $this->wordpress_user_id );
333 if ( $is_logged_in && $logged_in_wp_user ) {
334 OsAuthHelper::login_wp_user( $logged_in_wp_user );
335 }
336 }
337
338 return $this->update_attributes(
339 [
340 'password' => wp_hash_password( $password ),
341 'is_guest' => false,
342 ]
343 );
344 }
345
346 protected function get_full_name() {
347 return trim( join( ' ', array( $this->first_name, $this->last_name ) ) );
348 }
349
350 protected function get_default_status() {
351 return 'pending_verification';
352 }
353
354 protected function before_create() {
355 if ( ! isset( $this->is_guest ) ) {
356 $this->is_guest = true;
357 }
358 if ( empty( $this->status ) ) {
359 $this->status = $this->get_default_status();
360 }
361 if ( empty( $this->password ) ) {
362 $this->password = wp_hash_password( bin2hex( openssl_random_pseudo_bytes( 8 ) ) );
363 }
364 if ( empty( $this->activation_key ) ) {
365 $this->activation_key = sha1( wp_rand( 10000, 99999 ) . time() . $this->email );
366 }
367 if ( empty( $this->account_nonse ) ) {
368 $this->account_nonse = sha1( wp_rand( 10000, 99999 ) . time() . $this->activation_key );
369 }
370 }
371
372
373 public function get_avatar_url() {
374 return OsCustomerHelper::get_avatar_url( $this );
375 }
376
377 public function get_avatar_image() {
378 return OsCustomerHelper::get_avatar_image( $this );
379 }
380
381 // if this was a guest account without a set password and social login was not used, you can login just by email
382 public function can_login_without_password() {
383 return ( $this->is_guest && empty( $this->google_user_id ) && empty( $this->facebook_user_id ) );
384 }
385
386 public function prepare_data_before_it_is_set( $data ) {
387 if ( isset( $data['first_name'] ) ) {
388 $data['first_name'] = sanitize_text_field( $data['first_name'] );
389 }
390 if ( isset( $data['last_name'] ) ) {
391 $data['last_name'] = sanitize_text_field( $data['last_name'] );
392 }
393 if ( isset( $data['phone'] ) ) {
394 $data['phone'] = OsUtilHelper::sanitize_phone_number( $data['phone'] );
395 }
396 if ( isset( $data['notes'] ) ) {
397 $data['notes'] = sanitize_textarea_field( $data['notes'] );
398 }
399
400 return $data;
401 }
402
403
404 protected function before_save() {
405 if ( empty( $this->uuid ) ) {
406 $this->uuid = OsUtilHelper::generate_uuid();
407 }
408 }
409
410 public function get_uuid(): string {
411 if ( ! $this->is_new_record() && empty( $this->uuid ) ) {
412 $this->update_attributes( [ 'uuid' => OsUtilHelper::generate_uuid() ] );
413 }
414 return $this->uuid ?? '';
415 }
416
417 protected function allowed_params( $role = 'admin' ) {
418 switch ( $role ) {
419 case 'admin':
420 $allowed_params = [
421 'id',
422 'uuid',
423 'first_name',
424 'last_name',
425 'email',
426 'phone',
427 'avatar_image_id',
428 'is_guest',
429 'notes',
430 'admin_notes',
431 'wordpress_user_id',
432 'password',
433 ];
434 break;
435 case 'customer':
436 case 'public':
437 $allowed_params = [
438 'first_name',
439 'last_name',
440 'email',
441 'phone',
442 'avatar_image_id',
443 'notes',
444 'password',
445 ];
446 break;
447 }
448
449 return $allowed_params;
450 }
451
452 protected function params_to_save( $role = 'admin' ) {
453 $params_to_save = array(
454 'id',
455 'uuid',
456 'first_name',
457 'last_name',
458 'email',
459 'phone',
460 'password',
461 'activation_key',
462 'account_nonse',
463 'avatar_image_id',
464 'status',
465 'is_guest',
466 'notes',
467 'admin_notes',
468 'wordpress_user_id',
469 'google_user_id',
470 'facebook_user_id',
471 );
472
473 return $params_to_save;
474 }
475
476 /**
477 * Validates and constructs a set of validation rules for properties.
478 *
479 * The method determines the validation rules to be applied to customer properties
480 * based on the context such as whether alternative validation is enabled, or based on
481 * default fields and specific settings. It also provides flexibility via filters to
482 * modify the resulting validation rules.
483 *
484 * @param bool $alternative_validation Indicates whether to use an alternative set of validation rules. Defaults to false.
485 *
486 * @return array An associative array where keys are property names and values are arrays of validation rules.
487 */
488 protected function properties_to_validate( $alternative_validation = false ) {
489 // if alternative validation is enabled - use a different scope of rules (useful when you don't need to run all validations for example on social login)
490 if ( $alternative_validation ) {
491 $validations = array(
492 'email' => array( 'presence', 'email', 'uniqueness' ),
493 );
494 } else {
495 $validations = array(
496 'first_name' => array( 'presence' ),
497 'last_name' => array( 'presence' ),
498 'email' => array( 'presence', 'email' ),
499 );
500
501 $default_fields = OsSettingsHelper::get_default_fields_for_customer();
502 foreach ( $default_fields as $name => $field ) {
503 if ( $field['required'] && $field['active'] ) {
504 $validations[ $name ][] = 'presence';
505 $validations[ $name ] = array_unique( $validations[ $name ] );
506 } else {
507 if ( isset( $validations[ $name ] ) ) {
508 $validations[ $name ] = array_diff( $validations[ $name ], [ 'presence' ] );
509 }
510 }
511 }
512 if ( OsAuthHelper::is_customer_auth_enabled() ) {
513 // auth enabled
514 $auth_field = OsAuthHelper::get_selected_customer_authentication_field_type();
515 if ( $auth_field == 'email' ) {
516 $validations['email'][] = 'uniqueness';
517 }
518 if ( $auth_field == 'phone' ) {
519 $validations['phone'][] = 'uniqueness';
520 }
521 } else {
522 // auth disabled
523 $merge_data = OsSettingsHelper::get_settings_value( 'default_contact_merge_behavior', 'email' );
524 if ( $merge_data == 'email' ) {
525 $validations['email'][] = 'uniqueness';
526 }
527 if ( $merge_data == 'phone' ) {
528 $validations['phone'][] = 'uniqueness';
529 }
530 }
531 $validations = apply_filters( 'latepoint_customer_model_validations', $validations );
532 }
533
534 return $validations;
535 }
536 }
537