PluginProbe
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More / 2.2.0
StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More v2.2.0
2.3.0 2.2.0 2.1.1 2.1.0 2.0.0 1.10.0 1.9.1 1.9.0 1.2.1 1.2.2 1.3.0 1.3.1 1.3.2 1.3.3 1.4.0 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.5.8 1.6.0 All 59 releases
storeengine / includes / classes / customer.php

customer.php in StoreEngine — Complete eCommerce Solution with Memberships, Licensing, Affiliates & More 2.2.0, at includes/classes/customer.php

1,069 lines 30.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace StoreEngine\Classes;
4
5 use StoreEngine\Utils\Formatting;
6 use StoreEngine\Utils\Geolocation;
7 use StoreEngine\Utils\Helper;
8 use StoreEngine\Utils\ShippingUtils;
9 use StoreEngine\Utils\TaxUtil;
10 use WP_User;
11
12 if ( ! defined( 'ABSPATH' ) ) {
13 exit;
14 }
15
16
17 /**
18 * Class Customer - Represents a customer.
19 */
20 class Customer {
21
22 protected ?int $id = null;
23 protected ?WP_User $user = null;
24
25 protected array $data = [
26 'user_login' => '',
27 'user_email' => '',
28 'user_pass' => null,
29 'user_url' => '',
30 'user_nicename' => '',
31 'display_name' => '',
32 'user_registered' => null,
33 ];
34
35 protected array $new_data = [];
36
37 protected array $core_meta_data = [
38 'nickname' => '',
39 'first_name' => '',
40 'last_name' => '',
41 'description' => '',
42 ];
43 protected array $new_core_meta_data = [];
44
45 protected array $internal_meta_data = [
46 'total_orders' => 0,
47 'total_spent' => 0,
48 'subscribe_to_email' => false,
49 ];
50
51 protected array $new_internal_meta_data = [];
52
53 protected array $billing_address = [
54 'first_name' => '',
55 'last_name' => '',
56 'address_1' => '',
57 'address_2' => '',
58 'state' => '',
59 'city' => '',
60 'postcode' => '',
61 'country' => '',
62 'email' => '',
63 'phone' => '',
64 'company' => '',
65 ];
66
67 protected array $shipping_address = [
68 'first_name' => '',
69 'last_name' => '',
70 'address_1' => '',
71 'address_2' => '',
72 'state' => '',
73 'city' => '',
74 'postcode' => '',
75 'country' => '',
76 'email' => '',
77 'phone' => '',
78 'company' => '',
79 ];
80
81 protected array $draft_address = [];
82
83 /**
84 * Stores if user is VAT exempt for this session.
85 *
86 * @var string
87 */
88 protected $is_vat_exempt = false;
89
90 protected bool $calculated_shipping = false;
91
92 protected bool $in_session = false;
93
94 /**
95 * Initialize the customer object.
96 *
97 * @param int $user_id
98 * @param bool $in_session
99 */
100 public function __construct( int $user_id = 0, bool $in_session = false ) {
101 $this->id = $user_id;
102 $this->in_session = $in_session;
103 $this->get();
104 }
105
106 /**
107 * Get & Set up the customer data.
108 *
109 * @return Customer|false
110 */
111 public function get() {
112 if ( ! $this->get_id() && ! $this->in_session ) {
113 return false;
114 }
115
116 $user = get_userdata( $this->get_id() );
117
118 if ( ! $user && ! $this->in_session ) {
119 return false;
120 }
121
122 if ( $user ) {
123 $this->set_data( $user );
124 }
125
126 if ( $this->in_session && Helper::get_recent_draft_order( 0, null, false ) ) {
127 $order = Helper::get_recent_draft_order( 0, null, false );
128 $keys = [
129 'first_name',
130 'last_name',
131 'address_1',
132 'address_2',
133 'state',
134 'city',
135 'postcode',
136 'country',
137 'email',
138 'phone',
139 'company',
140 ];
141
142 foreach ( $keys as $key ) {
143 if ( method_exists( $order, 'get_billing_' . $key ) && $order->{'get_billing_' . $key}( 'edit' ) ) {
144 $this->draft_address['billing'][ $key ] = $order->{'get_billing_' . $key}( 'edit' );
145 }
146 if ( method_exists( $order, 'get_shipping_' . $key ) && $order->{'get_shipping_' . $key}( 'edit' ) ) {
147 $this->draft_address['shipping'][ $key ] = $order->{'get_shipping_' . $key}( 'edit' );
148 }
149 }
150 }
151
152 if ( $this->in_session ) {
153 $this->set_session_defaults( $this );
154 }
155
156 return $this;
157 }
158
159 public function get_id(): ?int {
160 return $this->id;
161 }
162
163 public function get_wp_user(): ?WP_User {
164 return $this->user;
165 }
166
167 public function get_email() {
168 $value = $this->get_data( 'user_email' );
169 if ( $this->in_session && ! $value ) {
170 $order = Helper::get_recent_draft_order( 0, null, false );
171 $value = $order->get_order_email( 'edit' );
172 }
173
174 return $value;
175 }
176
177 public function get_username() {
178 return $this->get_data( 'user_login' );
179 }
180
181 public function get_user_registered(): ?StoreengineDatetime {
182 return ! empty( $this->get_data( 'user_registered' ) ) ? new StoreengineDatetime( $this->get_data( 'user_registered' ) ) : null;
183 }
184
185 public function get_url() {
186 return $this->get_data( 'user_url' );
187 }
188
189 public function get_nicename() {
190 return $this->get_data( 'user_nicename' );
191 }
192
193 public function get_display_name() {
194 return $this->get_data( 'display_name' );
195 }
196
197 public function get_first_name() {
198 return $this->get_core_meta_data( 'first_name' );
199 }
200
201 public function get_last_name() {
202 return $this->get_core_meta_data( 'last_name' );
203 }
204
205 public function get_full_name() {
206 $full_name = [ $this->get_first_name(), $this->get_last_name() ];
207 $full_name = trim( implode( ' ', array_map( 'trim', $full_name ) ) );
208
209 if ( ! $full_name ) {
210 $full_name = $this->get_display_name();
211 }
212
213 return $full_name;
214 }
215
216 public function get_total_orders() {
217 return $this->get_internal_meta_data( 'total_orders' );
218 }
219
220 public function get_total_spent() {
221 return $this->get_internal_meta_data( 'total_spent' );
222 }
223
224 public function has_subscribe_to_email() {
225 return $this->get_internal_meta_data( 'subscribe_to_email' );
226 }
227
228 public function get_billing_address_1(): ?string {
229 return $this->get_billing_address( 'address_1' );
230 }
231
232 public function get_billing_address_2(): ?string {
233 return $this->get_billing_address( 'address_2' );
234 }
235
236 public function get_billing_city(): ?string {
237 return $this->get_billing_address( 'city' );
238 }
239
240 public function get_billing_state(): ?string {
241 return $this->get_billing_address( 'state' );
242 }
243
244 public function get_billing_postcode(): ?string {
245 return $this->get_billing_address( 'postcode' );
246 }
247
248 /**
249 * @see CheckoutService::update_checkout()
250 */
251 public function get_billing_postal_code(): ?string {
252 return $this->get_billing_address( 'postcode' );
253 }
254
255 public function get_billing_country() {
256 return $this->get_billing_address( 'country' );
257 }
258
259 public function get_billing_email(): ?string {
260 return $this->get_billing_address( 'email' );
261 }
262
263 public function get_billing_phone(): ?string {
264 return $this->get_billing_address( 'phone' );
265 }
266
267 public function get_billing_first_name(): ?string {
268 return $this->get_billing_address( 'first_name' ) ?: $this->get_first_name();
269 }
270
271 public function get_billing_last_name(): ?string {
272 return $this->get_billing_address( 'last_name' ) ?: $this->get_last_name();
273 }
274
275 public function get_billing_full_name(): ?string {
276 $full_name = [ $this->get_billing_first_name(), $this->get_billing_last_name() ];
277 $full_name = trim( implode( ' ', array_map( 'trim', $full_name ) ) );
278
279 if ( ! $full_name ) {
280 $full_name = $this->get_full_name();
281 }
282
283 return $full_name;
284 }
285
286 public function get_billing_company(): ?string {
287 return $this->get_billing_address( 'company' );
288 }
289
290 public function get_shipping_address_1(): ?string {
291 return $this->get_shipping_address( 'address_1' );
292 }
293
294 public function get_shipping_address_2(): ?string {
295 return $this->get_shipping_address( 'address_2' );
296 }
297
298 public function get_shipping_city(): ?string {
299 return $this->get_shipping_address( 'city' );
300 }
301
302 public function get_shipping_state(): ?string {
303 return $this->get_shipping_address( 'state' );
304 }
305
306 public function get_shipping_postal_code(): ?string {
307 return $this->get_shipping_address( 'postcode' );
308 }
309
310 public function get_shipping_postcode(): ?string {
311 return $this->get_shipping_address( 'postcode' );
312 }
313
314 public function get_shipping_country(): ?string {
315 return $this->get_shipping_address( 'country' );
316 }
317
318 public function get_shipping_email(): ?string {
319 return $this->get_shipping_address( 'email' );
320 }
321
322 public function get_shipping_phone(): ?string {
323 return $this->get_shipping_address( 'phone' );
324 }
325
326 public function get_shipping_first_name(): ?string {
327 return $this->get_shipping_address( 'first_name' );
328 }
329
330 public function get_shipping_last_name(): ?string {
331 return $this->get_shipping_address( 'last_name' );
332 }
333
334 public function get_shipping_company(): ?string {
335 return $this->get_shipping_address( 'company' );
336 }
337
338 public function get_subscribe_to_email(): bool {
339 return Formatting::string_to_bool( $this->get_internal_meta_data( 'subscribe_to_email' ) );
340 }
341
342 public function set_data( WP_User $user ) {
343 $prefix = Helper::DB_PREFIX;
344 $this->user = $user;
345 $this->id = $user->ID;
346 $this->data['user_login'] = $user->user_login;
347 $this->data['user_email'] = $user->user_email;
348 $this->data['user_url'] = $user->user_url;
349 $this->data['user_nicename'] = $user->user_nicename;
350 $this->data['display_name'] = $user->display_name;
351 $this->data['user_registered'] = $user->user_registered;
352 $this->core_meta_data['nickname'] = $user->nickname;
353 $this->core_meta_data['first_name'] = $user->first_name;
354 $this->core_meta_data['last_name'] = $user->last_name;
355 $this->core_meta_data['description'] = $user->description;
356 $this->internal_meta_data['total_orders'] = (int) $user->{$prefix . 'total_orders'};
357 $this->internal_meta_data['total_spent'] = (float) $user->{$prefix . 'total_spent'};
358 $this->internal_meta_data['subscribe_to_email'] = Formatting::string_to_bool( $user->{$prefix . 'subscribe_to_email'} );
359 $billing_address = $user->{$prefix . 'billing_address'};
360 $shipping_address = $user->{$prefix . 'shipping_address'};
361
362 if ( $billing_address ) {
363 if ( ! is_array( $billing_address ) ) {
364 $billing_address = maybe_unserialize( $billing_address );
365 }
366
367 if ( ! is_array( $billing_address ) ) {
368 $billing_address = json_decode( $billing_address, true );
369 }
370
371 $this->billing_address = $billing_address ? $billing_address : [];
372 }
373
374 if ( $shipping_address ) {
375 if ( ! is_array( $shipping_address ) ) {
376 $shipping_address = maybe_unserialize( $shipping_address );
377 }
378
379 if ( ! is_array( $shipping_address ) ) {
380 $shipping_address = json_decode( $shipping_address, true );
381 }
382
383 $this->shipping_address = $shipping_address ? $shipping_address : [];
384 }
385 }
386
387 public function set_username( string $username ) {
388 $this->new_data['user_login'] = $username;
389 }
390
391 public function set_email( string $email ) {
392 $this->new_data['user_email'] = $email;
393 }
394
395 public function set_password( string $value ) {
396 $this->new_data['user_pass'] = $value;
397 }
398
399 public function set_url( string $url ) {
400 $this->new_data['user_url'] = $url;
401 }
402
403 public function set_nicename( string $value ) {
404 $this->new_data['user_nicename'] = $value;
405 }
406
407 public function set_user_registered_date( string $value ) {
408 $this->new_data['user_registered'] = $value;
409 }
410
411 public function get_name() {
412 $customer_name = null;
413 if ( ! empty( $this->get_first_name() ) ) {
414 $customer_name = $this->get_first_name() . ' ' . $this->get_last_name();
415 } elseif ( ! empty( $this->get_display_name() ) ) {
416 $customer_name = $this->get_display_name();
417 } else {
418 $customer_name = $this->get_username();
419 }
420
421 return $customer_name;
422 }
423
424 public function set_display_name( string $display_name ) {
425 $this->new_data['display_name'] = $display_name;
426 }
427
428 public function set_total_orders( int $value ) {
429 $this->new_internal_meta_data['total_orders'] = $value;
430 }
431
432 public function set_total_spent( float $value ) {
433 $this->new_internal_meta_data['total_spent'] = $value;
434 }
435
436 public function set_subscribe_to_email( $value ) {
437 $this->new_internal_meta_data['subscribe_to_email'] = Formatting::string_to_bool( $value );
438 }
439
440 public function set_first_name( string $first_name ) {
441 $this->new_core_meta_data['first_name'] = $first_name;
442 }
443
444 public function set_last_name( string $last_name ) {
445 $this->new_core_meta_data['last_name'] = $last_name;
446 }
447
448 public function set_description( string $value ) {
449 $this->new_core_meta_data['description'] = $value;
450 }
451
452 /**
453 * Set customer address to match shop base address.
454 */
455 public function set_billing_address_to_base() {
456 $base = Geolocation::get_customer_default_location();
457 $this->set_billing_location( $base['country'], $base['state'], '', '' );
458 }
459
460 /**
461 * Set customer shipping address to base address.
462 */
463 public function set_shipping_address_to_base() {
464 $base = Geolocation::get_customer_default_location();
465 $this->set_shipping_location( $base['country'], $base['state'], '', '' );
466 }
467
468 /**
469 * Sets all address info at once.
470 *
471 * @param string $country Country.
472 * @param string $state State.
473 * @param string $postcode Postcode.
474 * @param string $city City.
475 */
476 public function set_billing_location( string $country, string $state = '', string $postcode = '', string $city = '' ) {
477 $address_data = $this->draft_address['billing'] ?? $this->billing_address;
478
479 $address_data['address_1'] = '';
480 $address_data['address_2'] = '';
481 $address_data['city'] = $city;
482 $address_data['state'] = $state;
483 $address_data['postcode'] = $postcode;
484 $address_data['country'] = $country;
485
486 $this->billing_address = $address_data;
487 $this->draft_address['billing'] = $address_data;
488 }
489
490 /**
491 * Sets all shipping info at once.
492 *
493 * @param string $country Country.
494 * @param string $state State.
495 * @param string $postcode Postcode.
496 * @param string $city City.
497 */
498 public function set_shipping_location( string $country, string $state = '', string $postcode = '', string $city = '' ) {
499 $address_data = $this->draft_address['shipping'] ?? $this->billing_address;
500
501 $address_data['address_1'] = '';
502 $address_data['address_2'] = '';
503 $address_data['city'] = $city;
504 $address_data['state'] = $state;
505 $address_data['postcode'] = $postcode;
506 $address_data['country'] = $country;
507
508 $this->shipping_address = $address_data;
509 $this->draft_address['shipping'] = $address_data;
510 }
511
512 public function set_billing_address_1( ?string $address_1 ) {
513 $this->billing_address['address_1'] = $address_1;
514 }
515
516 public function set_billing_address_2( ?string $address_2 ) {
517 $this->billing_address['address_2'] = $address_2;
518 }
519
520 public function set_billing_city( ?string $city ) {
521 $this->billing_address['city'] = $city;
522 }
523
524 public function set_billing_state( ?string $state ) {
525 $this->billing_address['state'] = $state;
526 }
527
528 public function set_billing_postcode( ?string $postcode ) {
529 $this->billing_address['postcode'] = $postcode;
530 }
531
532 public function set_billing_country( ?string $country ) {
533 $this->billing_address['country'] = $country;
534 }
535
536 public function set_billing_email( ?string $email ) {
537 $this->billing_address['email'] = $email;
538 }
539
540 public function set_billing_phone( ?string $phone ) {
541 $this->billing_address['phone'] = $phone;
542 }
543
544 public function set_billing_first_name( ?string $first_name ) {
545 $this->billing_address['first_name'] = $first_name;
546 }
547
548 public function set_billing_last_name( ?string $last_name ) {
549 $this->billing_address['last_name'] = $last_name;
550 }
551
552 public function set_billing_company( ?string $company ) {
553 $this->billing_address['company'] = $company;
554 }
555
556 public function set_shipping_address_1( ?string $address_1 ) {
557 $this->shipping_address['address_1'] = $address_1;
558 }
559
560 public function set_shipping_address_2( ?string $address_2 ) {
561 $this->shipping_address['address_2'] = $address_2;
562 }
563
564 public function set_shipping_city( ?string $city ) {
565 $this->shipping_address['city'] = $city;
566 }
567
568 public function set_shipping_state( ?string $state ) {
569 $this->shipping_address['state'] = $state;
570 }
571
572 public function set_shipping_postcode( ?string $postcode ) {
573 $this->shipping_address['postcode'] = $postcode;
574 }
575
576 public function set_shipping_country( ?string $country ) {
577 $this->shipping_address['country'] = $country;
578 }
579
580 public function set_shipping_email( ?string $email ) {
581 $this->shipping_address['email'] = $email;
582 }
583
584 public function set_shipping_phone( ?string $phone ) {
585 $this->shipping_address['phone'] = $phone;
586 }
587
588 public function set_shipping_first_name( ?string $first_name ) {
589 $this->shipping_address['first_name'] = $first_name;
590 }
591
592 public function set_shipping_last_name( ?string $last_name ) {
593 $this->shipping_address['last_name'] = $last_name;
594 }
595
596 public function set_shipping_company( ?string $company ) {
597 $this->shipping_address['company'] = $company;
598 }
599
600 public function save() {
601 if ( 0 === $this->id ) {
602 return $this->create();
603 }
604
605 $this->save_data();
606 $this->save_core_meta_data();
607 $this->save_internal_meta_data();
608 $this->save_address();
609 $this->save_address( 'shipping' );
610
611 return true;
612 }
613
614 protected function create() {
615 $data = $this->new_data;
616 $data = array_filter( $data, fn( $v, $k ) => ( $v !== $this->data[ $k ] ), ARRAY_FILTER_USE_BOTH );
617 $data['role'] = 'storeengine_customer';
618
619 if ( empty( $data['user_login'] ) && ! empty( $data['user_email'] ) ) {
620 $data['user_login'] = $this->generate_username();
621 }
622
623 $user_id = wp_insert_user( $data );
624
625 if ( is_wp_error( $user_id ) ) {
626 return $user_id;
627 }
628
629 $this->id = $user_id;
630 $this->get();
631 $this->save();
632
633 update_user_meta( $user_id, 'storeengine_total_orders', 0 );
634 update_user_meta( $user_id, 'storeengine_total_spent', 0 );
635
636 return $this;
637 }
638
639 protected function generate_username() {
640 $base_username = strstr( $this->get_email(), '@', true );
641 $username = $base_username;
642 $counter = 1;
643
644 while ( username_exists( $username ) ) {
645 $username = $base_username . $counter;
646 $counter ++;
647 }
648
649 return $username;
650 }
651
652 protected function save_data() {
653 $data = $this->new_data;
654 $data = array_filter( $data, fn( $v, $k ) => ( $v !== $this->data[ $k ] ), ARRAY_FILTER_USE_BOTH );
655
656 if ( 0 === count( array_keys( $data ) ) ) {
657 return;
658 }
659
660 wp_update_user( array_merge( $data, [ 'ID' => $this->id ] ) );
661 }
662
663 protected function save_core_meta_data() {
664 $data = $this->new_core_meta_data;
665 $data = array_filter( $data, fn( $v, $k ) => ( $v !== $this->core_meta_data[ $k ] ), ARRAY_FILTER_USE_BOTH );
666
667 if ( 0 === count( array_keys( $data ) ) ) {
668 return;
669 }
670
671 foreach ( $data as $key => $value ) {
672 update_user_meta( $this->id, $key, $value );
673 }
674 }
675
676 protected function save_internal_meta_data() {
677 $data = $this->new_internal_meta_data;
678 $data = array_filter( $data, fn( $v, $k ) => ( $v !== $this->internal_meta_data[ $k ] ), ARRAY_FILTER_USE_BOTH );
679
680 if ( 0 === count( array_keys( $data ) ) ) {
681 return;
682 }
683
684 foreach ( $data as $key => $value ) {
685 update_user_meta( $this->id, Helper::DB_PREFIX . $key, $value );
686 }
687 }
688
689 protected function save_address( string $type = 'billing' ) {
690 $data = $this->billing_address;
691
692 if ( 'shipping' === $type ) {
693 $data = $this->shipping_address;
694 }
695
696 if ( 0 === count( array_keys( $data ) ) ) {
697 return;
698 }
699
700
701 update_user_meta( $this->id, Helper::DB_PREFIX . $type . '_address', $data );
702 }
703
704 protected function get_data( string $prop, string $context = 'view' ) {
705 $value = null;
706
707 if ( array_key_exists( $prop, $this->data ) ) {
708 $value = array_key_exists( $prop, $this->new_data ) ? $this->new_data[ $prop ] : $this->data[ $prop ];
709
710 if ( 'view' === $context ) {
711 /**
712 * @ignore Ignore from Hook parser.
713 */
714 $value = apply_filters( 'storeengine/customer/get/' . $prop, $value, $this );
715 }
716 }
717
718 return $value;
719 }
720
721 protected function get_core_meta_data( string $key ) {
722 if ( array_key_exists( $key, $this->new_core_meta_data ) ) {
723 return $this->new_core_meta_data[ $key ];
724 }
725
726 return $this->core_meta_data[ $key ];
727 }
728
729 protected function get_internal_meta_data( string $key ) {
730 if ( array_key_exists( $key, $this->new_internal_meta_data ) ) {
731 return $this->new_internal_meta_data[ $key ];
732 }
733
734 return $this->internal_meta_data[ $key ];
735 }
736
737 /**
738 * @param string $prop
739 * @param string $context
740 *
741 * @return string|null
742 */
743 protected function get_billing_address( string $prop, string $context = 'view' ): ?string {
744 $value = null;
745
746 if ( array_key_exists( $prop, $this->billing_address ) ) {
747 $value = $this->draft_address['billing'][ $prop ] ?? $this->billing_address[ $prop ];
748
749 if ( 'view' === $context ) {
750 /**
751 * Filter: 'storeengine/order_get_[billing|shipping]_[prop]'
752 *
753 * Allow developers to change the returned value for any order address property.
754 *
755 * @param string $value The address property value.
756 * @param Order $order The order object being read.
757 *
758 * @ignore Ignore from HookParser.
759 */
760 $value = apply_filters( 'storeengine/customer/billing_' . $prop, $value, $this );
761 }
762 }
763
764 return $value;
765 }
766
767 /**
768 * @param string $key
769 * @param string|null $default
770 *
771 * @return string|null
772 */
773 protected function get_shipping_address( string $prop, string $context = 'view' ): ?string {
774 $value = null;
775
776 if ( array_key_exists( $prop, $this->shipping_address ) ) {
777 $value = $this->draft_address['shipping'][ $prop ] ?? $this->shipping_address[ $prop ];
778
779 if ( 'view' === $context ) {
780 /**
781 * Filter: 'storeengine/order_get_[billing|shipping]_[prop]'
782 *
783 * Allow developers to change the returned value for any order address property.
784 *
785 * @param string $value The address property value.
786 * @param Order $order The order object being read.
787 *
788 * @ignore Ignore from HookParser.
789 */
790 $value = apply_filters( 'storeengine/customer/billing_' . $prop, $value, $this );
791 }
792 }
793
794 return $value;
795 }
796
797 /**
798 * Set if customer has tax exemption.
799 *
800 * @param bool|string $is_vat_exempt If is vat exempt.
801 */
802 public function set_is_vat_exempt( $is_vat_exempt ) {
803 $this->is_vat_exempt = Formatting::string_to_bool( $is_vat_exempt );
804 }
805
806 /**
807 * Get if customer is VAT exempt?
808 *
809 * @return bool
810 */
811 public function get_is_vat_exempt() {
812 return $this->is_vat_exempt;
813 }
814
815 /**
816 * Is customer VAT exempt?
817 *
818 * @return bool
819 */
820 public function is_vat_exempt() {
821 return $this->get_is_vat_exempt();
822 }
823
824 /**
825 * Calculated shipping?
826 *
827 * @param bool|string $calculated If shipping is calculated.
828 */
829 public function set_calculated_shipping( $calculated = true ) {
830 $this->calculated_shipping = Formatting::string_to_bool( $calculated );
831 }
832
833 /**
834 * Has customer calculated shipping?
835 *
836 * @return bool
837 */
838 public function get_calculated_shipping(): bool {
839 return $this->calculated_shipping;
840 }
841
842 /**
843 * Has calculated shipping?
844 *
845 * @return bool
846 */
847 public function has_calculated_shipping(): bool {
848 return $this->get_calculated_shipping();
849 }
850
851 public function get_shipping(): array {
852 if ( ! empty( $this->draft_address['shipping'] ) ) {
853 return $this->draft_address['shipping'];
854 } else {
855 return $this->shipping_address;
856 }
857 }
858
859 public function get_billing(): array {
860 if ( ! empty( $this->draft_address['billing'] ) ) {
861 return $this->draft_address['billing'];
862 } else {
863 return $this->billing_address;
864 }
865 }
866
867 /**
868 * Indicates if the customer has a non-empty shipping address.
869 *
870 * Note that this does not indicate if the customer's shipping address
871 * is complete, only that one or more fields are populated.
872 *
873 * @return bool
874 */
875 public function has_shipping_address(): bool {
876 return $this->get_shipping_address_1() || $this->get_shipping_address_2();
877 }
878
879 /**
880 * Checks whether the address is "full" in the sense that it contains all required fields to calculate shipping rates.
881 *
882 * @return bool Whether the customer has a full shipping address (address_1, city, state, postcode, country).
883 * Only required fields are checked.
884 */
885 public function has_full_shipping_address(): bool {
886 // Respect the admin's CheckoutFields configuration: if state / postcode /
887 // city has been disabled at checkout, the shopper has no way to fill it,
888 // so requiring it here would block shipping calc forever. We treat a
889 // disabled field as "not required for shipping."
890 $cf_fields = \StoreEngine\Utils\CheckoutFields::all();
891 $cf_enabled = static fn( string $id ): bool => ! empty( $cf_fields[ $id ]['enabled'] );
892
893 // These are the important fields required to get the shipping rates. Note that while we're respecting the filters
894 // for the shipping calculator below (city, postcode, state), we're not respecting the filter for the country field.
895 // The country field is always required as a bare minimum for shipping.
896 $shipping_address = [
897 'country' => $this->get_shipping_country(),
898 ];
899
900 /**
901 * Filter to not require shipping city for shipping calculation, even if it is required at checkout.
902 * This can be used to allow shipping calculations to be done without a city.
903 *
904 * @param bool $show_city Whether to use the city field. Default true.
905 */
906 if ( $cf_enabled( 'shipping_city' ) && apply_filters( 'storeengine/shipping/calculator_enable_city', true ) ) {
907 $shipping_address['city'] = $this->get_shipping_city();
908 }
909
910 /**
911 * Filter to not require shipping state for shipping calculation, even if it is required at checkout.
912 * This can be used to allow shipping calculations to be done without a state.
913 *
914 * @param bool $show_state Whether to use the state field. Default true.
915 */
916 if ( $cf_enabled( 'shipping_state' ) && apply_filters( 'storeengine/shipping/calculator_enable_state', true ) ) {
917 $shipping_address['state'] = $this->get_shipping_state();
918 }
919
920 /**
921 * Filter to not require shipping postcode for shipping calculation, even if it is required at checkout.
922 * This can be used to allow shipping calculations to be done without a postcode.
923 *
924 * @param bool $show_postcode Whether to use the postcode field. Default true.
925 *
926 * @since 8.4.0
927 */
928 if ( $cf_enabled( 'shipping_post_code' ) && apply_filters( 'storeengine/shipping/calculator_enable_postcode', true ) ) {
929 $shipping_address['postcode'] = $this->get_shipping_postcode();
930 }
931
932 $address_fields = Countries::init()->get_country_locale();
933 $locale_key = ! empty( $shipping_address['country'] ) && array_key_exists( $shipping_address['country'], $address_fields ) ? $shipping_address['country'] : 'default';
934 $default_locale = $address_fields['default'];
935 $country_locale = $address_fields[ $locale_key ] ?? [];
936
937 /**
938 * Checks all shipping address fields against the country's locale settings.
939 *
940 * If there's a `required` setting for the field in the country-specific locale, that setting is used, otherwise
941 * the default locale's setting is used. If the default locale doesn't have a setting either, the field is
942 * considered optional and therefore valid, even if empty.
943 */
944 foreach ( $shipping_address as $key => $value ) {
945 // Skip further checks if the field has a value. From this point on $value is empty.
946 if ( ! empty( $value ) ) {
947 continue;
948 }
949
950 $locale_to_check = isset( $country_locale[ $key ]['required'] ) ? $country_locale : $default_locale;
951
952 // If the locale requires the field return false.
953 if ( isset( $locale_to_check[ $key ]['required'] ) && true === Formatting::string_to_bool( $locale_to_check[ $key ]['required'] ) ) {
954 return false;
955 }
956 }
957
958 return true;
959 }
960
961 /**
962 * Indicates if the customer has a non-empty shipping address.
963 *
964 * Note that this does not indicate if the customer's shipping address
965 * is complete, only that one or more fields are populated.
966 *
967 * @return bool
968 */
969 public function has_billing_address(): bool {
970 return $this->get_billing_address_1() || $this->get_billing_address_2();
971 }
972
973 /**
974 * Get taxable address.
975 *
976 * @return array
977 */
978 public function get_taxable_address(): array {
979 $tax_based_on = TaxUtil::tax_based_on();
980 // Check shipping method at this point to see if we need special handling.
981 // @TODO can use this after implementing separate session data.
982 /*if ( true === apply_filters( 'storeengine/apply_base_tax_for_local_pickup', true ) && count( array_intersect( ShippingUtils::get_chosen_shipping_method_ids(), apply_filters( 'storeengine/local_pickup_methods', [ 'local_pickup' ] ) ) ) > 0 ) {
983 $tax_based_on = 'base';
984 }*/
985
986 if ( 'base' === $tax_based_on ) {
987 $country = Helper::get_settings( 'store_country' );
988 $state = Helper::get_settings( 'store_state' );
989 $postcode = Helper::get_settings( 'store_postcode' );
990 $city = Helper::get_settings( 'store_city' );
991 } elseif ( 'billing' === $tax_based_on ) {
992 $country = $this->get_billing_country();
993 $state = $this->get_billing_state();
994 $postcode = $this->get_billing_postcode();
995 $city = $this->get_billing_city();
996 } else {
997 $country = $this->get_shipping_country();
998 $state = $this->get_shipping_state();
999 $postcode = $this->get_shipping_postcode();
1000 $city = $this->get_shipping_city();
1001 }
1002
1003 /**
1004 * Filters the taxable address for a given customer.
1005 *
1006 * @param array $taxable_address An array of country, state, postcode, and city for the customer's taxable address.
1007 * @param object $customer The customer object for which the taxable address is being requested.
1008 *
1009 * @return array The filtered taxable address for the customer.
1010 */
1011 return apply_filters( 'storeengine/customer_taxable_address', [ $country, $state, $postcode, $city ], $this );
1012 }
1013
1014 /**
1015 * Is customer outside base country (for tax purposes)?
1016 *
1017 * @return bool
1018 */
1019 public function is_customer_outside_base(): bool {
1020 list( $country, $state ) = $this->get_taxable_address();
1021 if ( $country ) {
1022 if ( Helper::get_settings( 'store_country' ) !== $country ) {
1023 return true;
1024 }
1025 if ( Helper::get_settings( 'store_state' ) && Helper::get_settings( 'store_state' ) !== $state ) {
1026 return true;
1027 }
1028 }
1029
1030 return false;
1031 }
1032
1033 /**
1034 * Set default values for the customer object if props are unset.
1035 *
1036 * @param Customer $customer Customer object.
1037 */
1038 protected function set_session_defaults( Customer &$customer ) {
1039 $default = Geolocation::get_customer_default_location();
1040 $has_shipping_address = $customer->has_shipping_address();
1041
1042 if ( ! $customer->get_billing_country() ) {
1043 $customer->set_billing_country( $default['country'] );
1044 $this->draft_address['billing']['country'] = $default['country'];
1045 }
1046
1047 if ( ! $customer->get_shipping_country() && ! $has_shipping_address ) {
1048 $customer->set_shipping_country( $customer->get_billing_country() );
1049 $this->draft_address['shipping']['country'] = $customer->get_billing_country();
1050 }
1051
1052 if ( ! $customer->get_billing_state() ) {
1053 $customer->set_billing_state( $default['state'] );
1054 $this->draft_address['billing']['state'] = $default['state'];
1055 }
1056
1057 if ( ! $customer->get_shipping_state() && ! $has_shipping_address ) {
1058 $customer->set_shipping_state( $customer->get_billing_state() );
1059 $this->draft_address['shipping']['state'] = $customer->get_billing_state();
1060 }
1061
1062 if ( ! $customer->get_billing_email() && is_user_logged_in() ) {
1063 $current_user = wp_get_current_user();
1064 $customer->set_billing_email( $current_user->user_email );
1065 $this->draft_address['billing']['email'] = $current_user->user_email;
1066 }
1067 }
1068 }
1069