Booster for WooCommerce - Version 4.6.0

Version Description

  • 29/10/2019 =
  • Fix indentation by replacing spaces by tabs on SQL, comments, or when there is pure HTML,
Download this release

Release Info

Developer algoritmika
Plugin Icon 128x128 Booster for WooCommerce
Version 4.6.0
Comparing to
See all releases

Code changes from version 4.5.1 to 4.6.0

Files changed (28) hide show
  1. includes/class-wcj-checkout-customization.php +45 -2
  2. includes/class-wcj-eu-vat-number.php +112 -20
  3. includes/class-wcj-multicurrency.php +91 -54
  4. includes/class-wcj-payment-gateways-per-category.php +114 -92
  5. includes/class-wcj-price-by-user-role.php +83 -2
  6. includes/class-wcj-product-add-to-cart.php +3 -3
  7. includes/class-wcj-product-addons.php +91 -13
  8. includes/class-wcj-product-bookings.php +34 -33
  9. includes/class-wcj-product-input-fields.php +17 -15
  10. includes/class-wcj-product-open-pricing.php +19 -11
  11. includes/class-wcj-sorting.php +10 -10
  12. includes/class-wcj-tax-display.php +9 -8
  13. includes/emails/class-wc-email-wcj-custom.php +25 -20
  14. includes/functions/wcj-functions-eu-vat.php +6 -3
  15. includes/functions/wcj-functions-general.php +53 -1
  16. includes/settings/meta-box/wcj-settings-meta-box-product-addons.php +2 -2
  17. includes/settings/wcj-settings-checkout-customization.php +40 -1
  18. includes/settings/wcj-settings-eu-vat-number.php +24 -0
  19. includes/settings/wcj-settings-multicurrency.php +17 -1
  20. includes/settings/wcj-settings-price-by-user-role.php +52 -4
  21. includes/settings/wcj-settings-product-addons.php +10 -2
  22. includes/settings/wcj-settings-product-open-pricing.php +28 -3
  23. includes/shipping/class-wc-shipping-wcj-custom-with-shipping-zones.php +48 -6
  24. includes/shortcodes/class-wcj-shortcodes-orders.php +11 -8
  25. includes/shortcodes/class-wcj-shortcodes-products.php +8 -5
  26. includes/templates/wcj-radio-for-variations.php +15 -15
  27. readme.txt +32 -1
  28. woocommerce-jetpack.php +2 -2
includes/class-wcj-checkout-customization.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Module - Checkout Customization
4
  *
5
- * @version 4.1.0
6
  * @since 2.7.0
7
  * @author Algoritmika Ltd.
8
  */
@@ -134,16 +134,59 @@ class WCJ_Checkout_Customization extends WCJ_Module {
134
  <?php
135
  }
136
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
137
  /**
138
  * restrict_countries_by_customer_ip.
139
  *
140
- * @version 3.4.0
141
  * @since 3.4.0
142
  * @todo (maybe) handle case when `wcj_get_country_by_ip()` returns empty string
143
  * @todo (maybe) for shipping countries - filter `woocommerce_ship_to_countries` option
144
  */
145
  function restrict_countries_by_customer_ip( $countries ) {
 
 
 
 
 
 
146
  $user_country = wcj_get_country_by_ip();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
147
  return array( $user_country => wcj_get_country_name_by_code( $user_country ) );
148
  }
149
 
2
  /**
3
  * Booster for WooCommerce - Module - Checkout Customization
4
  *
5
+ * @version 4.6.0
6
  * @since 2.7.0
7
  * @author Algoritmika Ltd.
8
  */
134
  <?php
135
  }
136
 
137
+ /**
138
+ * Checks if conditions are valid in order for the country restriction to work.
139
+ *
140
+ * @version 4.6.0
141
+ * @since 4.6.0
142
+ *
143
+ * @return bool
144
+ */
145
+ function are_conditions_valid() {
146
+ $valid = false;
147
+ $conditions = $this->get_option( 'wcj_checkout_restrict_countries_by_customer_ip_conditions', array() );
148
+ foreach ( $conditions as $key => $condition ) {
149
+ $function = $condition;
150
+ $valid = $function();
151
+ if ( $valid ) {
152
+ break;
153
+ }
154
+ }
155
+ return $valid;
156
+ }
157
+
158
  /**
159
  * restrict_countries_by_customer_ip.
160
  *
161
+ * @version 4.6.0
162
  * @since 3.4.0
163
  * @todo (maybe) handle case when `wcj_get_country_by_ip()` returns empty string
164
  * @todo (maybe) for shipping countries - filter `woocommerce_ship_to_countries` option
165
  */
166
  function restrict_countries_by_customer_ip( $countries ) {
167
+ if (
168
+ ( 'yes' === $this->get_option( 'wcj_checkout_restrict_countries_by_customer_ip_ignore_admin', 'no' ) && is_admin() ) ||
169
+ ( ! empty( $this->get_option( 'wcj_checkout_restrict_countries_by_customer_ip_conditions', array() ) ) && ! $this->are_conditions_valid() )
170
+ ) {
171
+ return $countries;
172
+ }
173
  $user_country = wcj_get_country_by_ip();
174
+
175
+ // Get country from 'billing_country' user meta
176
+ if (
177
+ 'yes' === get_option( 'wcj_checkout_restrict_countries_by_user_billing_country', 'no' ) &&
178
+ 0 != ( $user_id = get_current_user_id() )
179
+ ) {
180
+ $user_country = ! empty( $user_billing_country = get_user_meta( $user_id, 'billing_country', true ) ) ? $user_billing_country : wcj_get_country_by_ip();
181
+ }
182
+
183
+ // Get country from a manual order ID created by YITH Request a Quote plugin
184
+ if ( 'yes' === get_option( 'wcj_checkout_restrict_countries_based_on_yith_raq', 'no' ) && class_exists( 'YITH_Request_Quote' ) ) {
185
+ $yith_order_id = WC()->session->get( 'order_awaiting_payment' );
186
+ if ( ! empty( $yith_order_id ) ) {
187
+ $user_country = ! empty( $order_billing_country = get_post_meta( $yith_order_id, '_billing_country', true ) ) ? $order_billing_country : wcj_get_country_by_ip();
188
+ }
189
+ }
190
  return array( $user_country => wcj_get_country_name_by_code( $user_country ) );
191
  }
192
 
includes/class-wcj-eu-vat-number.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Module - EU VAT Number
4
  *
5
- * @version 4.3.0
6
  * @since 2.3.9
7
  * @author Algoritmika Ltd.
8
  */
@@ -16,7 +16,7 @@ class WCJ_EU_VAT_Number extends WCJ_Module {
16
  /**
17
  * Constructor.
18
  *
19
- * @version 3.3.0
20
  * @todo [feature] add option to add "Verify" button to frontend
21
  */
22
  function __construct() {
@@ -42,6 +42,8 @@ class WCJ_EU_VAT_Number extends WCJ_Module {
42
  add_action( 'wp_ajax_wcj_validate_eu_vat_number', array( $this, 'wcj_validate_eu_vat_number' ) );
43
  add_action( 'wp_ajax_nopriv_wcj_validate_eu_vat_number', array( $this, 'wcj_validate_eu_vat_number' ) );
44
  add_filter( 'init', array( $this, 'maybe_exclude_vat' ), PHP_INT_MAX );
 
 
45
  add_action( 'woocommerce_after_checkout_validation', array( $this, 'checkout_validate_vat' ), PHP_INT_MAX );
46
  add_filter( 'woocommerce_customer_meta_fields', array( $this, 'add_eu_vat_number_customer_meta_field' ) );
47
  add_filter( 'default_checkout_billing_eu_vat_number', array( $this, 'add_default_checkout_billing_eu_vat_number' ), PHP_INT_MAX, 2 );
@@ -70,6 +72,43 @@ class WCJ_EU_VAT_Number extends WCJ_Module {
70
  }
71
  }
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  /**
74
  * admin_validate_vat_and_maybe_remove_taxes.
75
  *
@@ -344,12 +383,18 @@ class WCJ_EU_VAT_Number extends WCJ_Module {
344
  /**
345
  * wcj_validate_eu_vat_number.
346
  *
347
- * @version 4.0.0
348
  */
349
  function wcj_validate_eu_vat_number( $param ) {
350
- if ( isset( $_POST['wcj_eu_vat_number_to_check'] ) && '' != $_POST['wcj_eu_vat_number_to_check'] ) {
351
- $eu_vat_number_to_check = substr( $_POST['wcj_eu_vat_number_to_check'], 2 );
352
- $eu_vat_number_country_to_check = substr( $_POST['wcj_eu_vat_number_to_check'], 0, 2 );
 
 
 
 
 
 
353
  if ( 'yes' === apply_filters( 'booster_option', 'no', get_option( 'wcj_eu_vat_number_check_ip_location_country', 'no' ) ) ) {
354
  $location = WC_Geolocation::geolocate_ip();
355
  if ( empty( $location['country'] ) ) {
@@ -365,31 +410,41 @@ class WCJ_EU_VAT_Number extends WCJ_Module {
365
  $is_valid = null;
366
  }
367
  wcj_session_set( 'wcj_is_eu_vat_number_valid', $is_valid );
368
- wcj_session_set( 'wcj_eu_vat_number_to_check', ( isset( $_POST['wcj_eu_vat_number_to_check'] ) ? $_POST['wcj_eu_vat_number_to_check'] : '' ) );
 
369
  if ( false === $is_valid ) {
370
- echo '0';
371
  } elseif ( true === $is_valid ) {
372
- echo '1';
373
  } elseif ( null === $is_valid ) {
374
- echo '2';
 
 
 
 
375
  } else {
376
- echo '3'; // unexpected
377
  }
378
- die();
379
  }
380
 
381
  /**
382
- * maybe_exclude_vat.
383
  *
384
- * @version 3.4.0
 
 
 
385
  */
386
- function maybe_exclude_vat() {
387
  if (
388
  ( is_checkout() || is_cart() || defined( 'WOOCOMMERCE_CHECKOUT' ) || defined( 'WOOCOMMERCE_CART' ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) &&
389
  ! empty( WC()->customer ) &&
390
  'yes' === get_option( 'wcj_eu_vat_number_validate', 'yes' ) &&
391
  'yes' === get_option( 'wcj_eu_vat_number_disable_for_valid', 'yes' ) &&
392
- true === wcj_session_get( 'wcj_is_eu_vat_number_valid' ) && null !== wcj_session_get( 'wcj_eu_vat_number_to_check' )
 
 
 
393
  ) {
394
  $preserve_base_country_check_passed = true;
395
  if ( 'yes' === apply_filters( 'booster_option', 'no', get_option( 'wcj_eu_vat_number_preserve_in_base_country', 'no' ) ) ) {
@@ -397,17 +452,33 @@ class WCJ_EU_VAT_Number extends WCJ_Module {
397
  if ( empty( $location['country'] ) ) {
398
  $location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', get_option( 'woocommerce_default_country' ) ) );
399
  }
400
- $selected_country = substr( wcj_session_get( 'wcj_eu_vat_number_to_check' ), 0, 2 );
401
  if ( 'EL' === $selected_country ) {
402
  $selected_country = 'GR';
403
  }
404
  $preserve_base_country_check_passed = ( strtoupper( $location['country'] ) !== strtoupper( $selected_country ) );
405
  }
406
  if ( $preserve_base_country_check_passed ) {
407
- WC()->customer->set_is_vat_exempt( true );
408
  } else {
409
- WC()->customer->set_is_vat_exempt( false );
410
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
411
  } else {
412
  if ( ! empty( WC()->customer ) ) {
413
  WC()->customer->set_is_vat_exempt( false );
@@ -442,13 +513,34 @@ class WCJ_EU_VAT_Number extends WCJ_Module {
442
 
443
  /**
444
  * add_billing_eu_vat_number_field_to_admin_order_display.
 
 
445
  */
446
  function add_billing_eu_vat_number_field_to_admin_order_display( $fields ) {
 
 
447
  $fields[ $this->id ] = array(
448
  'type' => 'text',
449
  'label' => get_option( 'wcj_eu_vat_number_field_label' ),
450
- 'show' => true,
451
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
452
  return $fields;
453
  }
454
 
2
  /**
3
  * Booster for WooCommerce - Module - EU VAT Number
4
  *
5
+ * @version 4.6.0
6
  * @since 2.3.9
7
  * @author Algoritmika Ltd.
8
  */
16
  /**
17
  * Constructor.
18
  *
19
+ * @version 4.6.0
20
  * @todo [feature] add option to add "Verify" button to frontend
21
  */
22
  function __construct() {
42
  add_action( 'wp_ajax_wcj_validate_eu_vat_number', array( $this, 'wcj_validate_eu_vat_number' ) );
43
  add_action( 'wp_ajax_nopriv_wcj_validate_eu_vat_number', array( $this, 'wcj_validate_eu_vat_number' ) );
44
  add_filter( 'init', array( $this, 'maybe_exclude_vat' ), PHP_INT_MAX );
45
+ add_filter( 'woocommerce_cart_tax_totals', array( $this, 'maybe_remove_tax_totals' ), 1 );
46
+ add_filter( 'woocommerce_calculated_total', array( $this, 'maybe_recalculate_tax_totals' ), 1, 2 );
47
  add_action( 'woocommerce_after_checkout_validation', array( $this, 'checkout_validate_vat' ), PHP_INT_MAX );
48
  add_filter( 'woocommerce_customer_meta_fields', array( $this, 'add_eu_vat_number_customer_meta_field' ) );
49
  add_filter( 'default_checkout_billing_eu_vat_number', array( $this, 'add_default_checkout_billing_eu_vat_number' ), PHP_INT_MAX, 2 );
72
  }
73
  }
74
 
75
+ /**
76
+ * maybe_recalculate_tax_totals.
77
+ *
78
+ * @version 4.6.0
79
+ * @since 4.6.0
80
+ *
81
+ * @see https://gist.github.com/TimBHowe/fe9418b9224d8b8cb339
82
+ * @param $total
83
+ * @param $cart
84
+ *
85
+ * @return mixed
86
+ */
87
+ function maybe_recalculate_tax_totals( $total, $cart ) {
88
+ if ( 'yes' === get_option( 'wcj_eu_vat_number_disable_for_valid_on_cart', 'no' ) && $this->need_to_exclude_vat() ) {
89
+ return $total - $cart->get_taxes_total();
90
+ }
91
+ return $total;
92
+ }
93
+
94
+ /**
95
+ * maybe_remove_tax_totals.
96
+ *
97
+ * @version 4.6.0
98
+ * @since 4.6.0
99
+ *
100
+ * @see https://gist.github.com/TimBHowe/fe9418b9224d8b8cb339
101
+ * @param $tax_totals
102
+ *
103
+ * @return array
104
+ */
105
+ function maybe_remove_tax_totals( $tax_totals ) {
106
+ if ( 'yes' === get_option( 'wcj_eu_vat_number_disable_for_valid_on_cart', 'no' ) && $this->need_to_exclude_vat() ) {
107
+ $tax_totals = array();
108
+ }
109
+ return $tax_totals;
110
+ }
111
+
112
  /**
113
  * admin_validate_vat_and_maybe_remove_taxes.
114
  *
383
  /**
384
  * wcj_validate_eu_vat_number.
385
  *
386
+ * @version 4.6.0
387
  */
388
  function wcj_validate_eu_vat_number( $param ) {
389
+ $param = wp_parse_args( $param, array(
390
+ 'wcj_eu_vat_number_to_check' => '',
391
+ 'echo' => true
392
+ ) );
393
+ $eu_vat_number = isset( $param['wcj_eu_vat_number_to_check'] ) && '' != $param['wcj_eu_vat_number_to_check'] ? $param['wcj_eu_vat_number_to_check'] : '';
394
+ $eu_vat_number = empty( $eu_vat_number ) && isset( $_POST['wcj_eu_vat_number_to_check'] ) && '' != $_POST['wcj_eu_vat_number_to_check'] ? $_POST['wcj_eu_vat_number_to_check'] : $eu_vat_number;
395
+ if ( ! empty( $eu_vat_number ) ) {
396
+ $eu_vat_number_to_check = substr( $eu_vat_number, 2 );
397
+ $eu_vat_number_country_to_check = substr( $eu_vat_number, 0, 2 );
398
  if ( 'yes' === apply_filters( 'booster_option', 'no', get_option( 'wcj_eu_vat_number_check_ip_location_country', 'no' ) ) ) {
399
  $location = WC_Geolocation::geolocate_ip();
400
  if ( empty( $location['country'] ) ) {
410
  $is_valid = null;
411
  }
412
  wcj_session_set( 'wcj_is_eu_vat_number_valid', $is_valid );
413
+ wcj_session_set( 'wcj_eu_vat_number_to_check', $eu_vat_number );
414
+ $response = '3';
415
  if ( false === $is_valid ) {
416
+ $response = '0';
417
  } elseif ( true === $is_valid ) {
418
+ $response = '1';
419
  } elseif ( null === $is_valid ) {
420
+ $response = '2';
421
+ }
422
+ if ( $param['echo'] ) {
423
+ echo $response;
424
+ die();
425
  } else {
426
+ return $response;
427
  }
 
428
  }
429
 
430
  /**
431
+ * need_to_exclude_vat.
432
  *
433
+ * @version 4.6.0
434
+ * @since 4.6.0
435
+ *
436
+ * @return bool
437
  */
438
+ function need_to_exclude_vat() {
439
  if (
440
  ( is_checkout() || is_cart() || defined( 'WOOCOMMERCE_CHECKOUT' ) || defined( 'WOOCOMMERCE_CART' ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) &&
441
  ! empty( WC()->customer ) &&
442
  'yes' === get_option( 'wcj_eu_vat_number_validate', 'yes' ) &&
443
  'yes' === get_option( 'wcj_eu_vat_number_disable_for_valid', 'yes' ) &&
444
+ (
445
+ ( true === wcj_session_get( 'wcj_is_eu_vat_number_valid' ) && null !== ( $eu_vat_number = wcj_session_get( 'wcj_eu_vat_number_to_check' ) ) ) ||
446
+ ( 'yes' === get_option( 'wcj_eu_vat_number_disable_for_valid_by_user_vat', 'no' ) && is_user_logged_in() && ! empty( $eu_vat_number = get_user_meta( get_current_user_id(), 'billing_eu_vat_number', true ) ) && '1' === $this->wcj_validate_eu_vat_number( array( 'wcj_eu_vat_number_to_check' => $eu_vat_number, 'echo' => false ) ) )
447
+ )
448
  ) {
449
  $preserve_base_country_check_passed = true;
450
  if ( 'yes' === apply_filters( 'booster_option', 'no', get_option( 'wcj_eu_vat_number_preserve_in_base_country', 'no' ) ) ) {
452
  if ( empty( $location['country'] ) ) {
453
  $location = wc_format_country_state_string( apply_filters( 'woocommerce_customer_default_location', get_option( 'woocommerce_default_country' ) ) );
454
  }
455
+ $selected_country = substr( $eu_vat_number, 0, 2 );
456
  if ( 'EL' === $selected_country ) {
457
  $selected_country = 'GR';
458
  }
459
  $preserve_base_country_check_passed = ( strtoupper( $location['country'] ) !== strtoupper( $selected_country ) );
460
  }
461
  if ( $preserve_base_country_check_passed ) {
462
+ return true;
463
  } else {
464
+ return false;
465
  }
466
+ } else {
467
+ if ( ! empty( WC()->customer ) ) {
468
+ return false;
469
+ }
470
+ }
471
+ return false;
472
+ }
473
+
474
+ /**
475
+ * maybe_exclude_vat.
476
+ *
477
+ * @version 4.6.0
478
+ */
479
+ function maybe_exclude_vat() {
480
+ if ( $this->need_to_exclude_vat() ) {
481
+ WC()->customer->set_is_vat_exempt( true );
482
  } else {
483
  if ( ! empty( WC()->customer ) ) {
484
  WC()->customer->set_is_vat_exempt( false );
513
 
514
  /**
515
  * add_billing_eu_vat_number_field_to_admin_order_display.
516
+ *
517
+ * @version 4.6.0
518
  */
519
  function add_billing_eu_vat_number_field_to_admin_order_display( $fields ) {
520
+ $vat_number = '';
521
+
522
  $fields[ $this->id ] = array(
523
  'type' => 'text',
524
  'label' => get_option( 'wcj_eu_vat_number_field_label' ),
525
+ 'show' => true
526
  );
527
+
528
+ // Try to read meta from 'vat_number' if '_billing_eu_vat_number' is empty
529
+ if ( 'yes' === get_option( 'wcj_eu_vat_number_read_vat_number_meta', 'no' ) ) {
530
+ global $post;
531
+ $order = wc_get_order( $post->ID );
532
+ if ( is_a( $order, 'WC_Order' ) ) {
533
+ $metas = array( '_billing_eu_vat_number', '_vat_number', '_billing_vat_number' );
534
+ foreach ( $metas as $meta ) {
535
+ $vat_number = get_post_meta( $order->get_id(), $meta, true );
536
+ if ( ! empty( $vat_number ) ) {
537
+ break;
538
+ }
539
+ }
540
+ $fields[ $this->id ]['value'] = $vat_number;
541
+ }
542
+ }
543
+
544
  return $fields;
545
  }
546
 
includes/class-wcj-multicurrency.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Module - Multicurrency (Currency Switcher)
4
  *
5
- * @version 4.5.0
6
  * @since 2.4.3
7
  * @author Algoritmika Ltd.
8
  */
@@ -91,7 +91,7 @@ class WCJ_Multicurrency extends WCJ_Module {
91
  /**
92
  * Handles third party compatibility
93
  *
94
- * @version 4.5.0
95
  * @since 4.3.0
96
  */
97
  function handle_third_party_compatibility(){
@@ -124,6 +124,13 @@ class WCJ_Multicurrency extends WCJ_Module {
124
  if ( 'yes' === get_option( 'wcj_multicurrency_compatibility_wc_import' , 'no' ) ) {
125
  add_filter( 'woocommerce_product_importer_parsed_data', array( $this, 'fix_wc_product_import' ), 10, 2 );
126
  }
 
 
 
 
 
 
 
127
  }
128
 
129
  /**
@@ -155,7 +162,7 @@ class WCJ_Multicurrency extends WCJ_Module {
155
  /**
156
  * Fixes sort by price when `wcj_multicurrency_per_product_enabled` is enabled.
157
  *
158
- * @version 4.5.0
159
  * @since 4.5.0
160
  *
161
  * @param $args
@@ -168,7 +175,7 @@ class WCJ_Multicurrency extends WCJ_Module {
168
  if (
169
  $orderby != 'price' ||
170
  is_admin() ||
171
- ! is_main_query() ||
172
  'no' === get_option( 'wcj_multicurrency_per_product_enabled', 'no' ) ||
173
  get_option( 'woocommerce_currency' ) === $this->get_current_currency_code() ||
174
  1 === $this->get_currency_exchange_rate( $this->get_current_currency_code()
@@ -193,9 +200,9 @@ class WCJ_Multicurrency extends WCJ_Module {
193
  $clauses['join'] .= " {$min_max_join} ";
194
  }
195
  if ( 'DESC' === $order ) {
196
- $clauses['fields'] .= ", CAST(IFNULL(MAX(cast(pm.meta_value as signed))/{$exchange_rate},max_price) AS signed) AS wcj_price ";
197
  } else {
198
- $clauses['fields'] .= ", CAST(IFNULL(MIN(cast(pm.meta_value as signed))/{$exchange_rate},min_price) AS signed) AS wcj_price ";
199
  }
200
  $clauses['orderby'] = " wcj_price {$order}, wc_product_meta_lookup.product_id {$order} ";
201
 
@@ -210,7 +217,7 @@ class WCJ_Multicurrency extends WCJ_Module {
210
  * First it removes products witch `_wcj_multicurrency_per_product_regular_price_{$current_currency_code}` meta don't match min and max.
211
  * Then it adds products witch `_wcj_multicurrency_per_product_regular_price_{$current_currency_code}` meta match min and max.
212
  *
213
- * @version 4.5.0
214
  * @since 4.5.0
215
  *
216
  * @see WC_Query::price_filter_post_clauses()
@@ -245,8 +252,8 @@ class WCJ_Multicurrency extends WCJ_Module {
245
  if ( false === strpos( $args['join'], $min_max_join ) ) {
246
  $args['join'] .= " {$min_max_join} ";
247
  }
248
- $args['fields'] .= ", CAST(IFNULL(MIN(cast(pm.meta_value as signed))/{$exchange_rate},min_price) AS signed) AS wcj_min_price ";
249
- $args['fields'] .= ", CAST(IFNULL(MAX(cast(pm.meta_value as signed))/{$exchange_rate},max_price) AS signed) AS wcj_max_price ";
250
  $args['where'] = preg_replace( '/and wc_product_meta_lookup.min_price >= \d.* and wc_product_meta_lookup.max_price <= \d.*\s/i', '', $args['where'] );
251
  $args['groupby'] .= " having wcj_min_price >= $min_price AND wcj_min_price <= $max_price ";
252
  return $args;
@@ -257,7 +264,7 @@ class WCJ_Multicurrency extends WCJ_Module {
257
  *
258
  * It works comparing min and max values from "_wcj_multicurrency_per_product_regular_price_{currency_code}" meta as well as min and max price from wc_product_meta_lookup
259
  *
260
- * @version 4.5.0
261
  * @since 4.5.0
262
  *
263
  * @see WC_Widget_Price_Filter::get_filtered_price()
@@ -279,9 +286,9 @@ class WCJ_Multicurrency extends WCJ_Module {
279
  global $wpdb;
280
  $current_currency_code = $this->get_current_currency_code();
281
  $exchange_rate = $this->get_currency_exchange_rate( $current_currency_code );
282
- $args = WC()->query->get_main_query()->query_vars;
283
- $tax_query = isset( $args['tax_query'] ) ? $args['tax_query'] : array();
284
- $meta_query = isset( $args['meta_query'] ) ? $args['meta_query'] : array();
285
 
286
  if ( ! is_post_type_archive( 'product' ) && ! empty( $args['taxonomy'] ) && ! empty( $args['term'] ) ) {
287
  $tax_query[] = array(
@@ -301,21 +308,21 @@ class WCJ_Multicurrency extends WCJ_Module {
301
  $search_query_sql = $search ? ' AND ' . $search : '';
302
 
303
  $sql = "
304
- SELECT min(wcj_min_price) as min_price, max(wcj_max_price) as max_price
305
- FROM(
306
- SELECT cast(IFNULL(MIN(cast(pm.meta_value as signed))/{$exchange_rate},min_price) as signed) AS wcj_min_price, cast(IFNULL(MAX(cast(pm.meta_value as signed))/{$exchange_rate},max_price) as signed) AS wcj_max_price
307
- FROM {$wpdb->wc_product_meta_lookup}
308
- LEFT JOIN {$wpdb->postmeta} AS pm on pm.post_id = product_id AND (pm.meta_key IN ('_wcj_multicurrency_per_product_min_price_{$current_currency_code}','_wcj_multicurrency_per_product_max_price_{$current_currency_code}') and pm.meta_value!='')
309
- WHERE product_id IN (
310
- SELECT ID FROM {$wpdb->posts}
311
- " . $tax_query_sql['join'] . $meta_query_sql['join'] . "
312
- WHERE {$wpdb->posts}.post_type IN ('" . implode( "','", array_map( 'esc_sql', apply_filters( 'woocommerce_price_filter_post_type', array( 'product' ) ) ) ) . "')
313
- AND {$wpdb->posts}.post_status = 'publish'
314
- " . $tax_query_sql['where'] . $meta_query_sql['where'] . $search_query_sql . '
315
- )
316
- group by product_id
317
- ) as wcj_min_max_price
318
- ';
319
  return $sql;
320
  }
321
 
@@ -358,10 +365,10 @@ class WCJ_Multicurrency extends WCJ_Module {
358
 
359
  /**
360
  * Adds compatibility with WooCommerce Price Filter widget.
361
- *
362
- * @see price-slider.js, init_price_filter()
363
- *
364
- * @version 4.5.0
365
  * @since 4.3.0
366
  */
367
  function add_compatibility_with_price_filter_widget() {
@@ -376,7 +383,7 @@ class WCJ_Multicurrency extends WCJ_Module {
376
  }
377
  ?>
378
  <input type="hidden" id="wcj_mc_exchange_rate" value="<?php echo esc_html( $exchange_rate ) ?>"/>
379
- <script>
380
  var wcj_mc_pf_slider = {
381
  slider: null,
382
  convert_rate: 1,
@@ -459,7 +466,7 @@ class WCJ_Multicurrency extends WCJ_Module {
459
  wcj_mc_pf.init(price_filters);
460
  }
461
  });
462
- </script>
463
  <?php
464
  }
465
 
@@ -575,7 +582,7 @@ class WCJ_Multicurrency extends WCJ_Module {
575
  /**
576
  * Gets all products, or products with variations containing meta '_wcj_multicurrency_per_product_regular_price_{currency}' or '_wcj_multicurrency_per_product_sale_price_{currency}'.
577
  *
578
- * @version 4.5.0
579
  * @since 4.5.0
580
  *
581
  * @param $currency
@@ -589,14 +596,14 @@ class WCJ_Multicurrency extends WCJ_Module {
589
 
590
  global $wpdb;
591
  $product_ids = $wpdb->get_col( "
592
- SELECT p.ID
593
- FROM {$wpdb->posts} AS p
594
- LEFT JOIN {$wpdb->posts} AS p2 ON p.ID = p2.ID OR (p2.post_parent = p.ID AND p2.post_type='product_variation')
595
- LEFT JOIN {$wpdb->postmeta} AS pm ON (p2.ID = pm.post_id) AND (pm.meta_key IN ('_wcj_multicurrency_per_product_regular_price_{$currency}','_wcj_multicurrency_per_product_sale_price_{$currency}') AND pm.meta_value!='')
596
- WHERE p.post_status = 'publish' AND p.post_type IN ('product')
597
- AND pm.meta_value != 'null'
598
- GROUP BY p.ID
599
- ");
600
 
601
  return $product_ids;
602
  }
@@ -861,10 +868,23 @@ class WCJ_Multicurrency extends WCJ_Module {
861
  }
862
  }
863
 
 
 
 
 
 
 
 
 
 
 
 
 
 
864
  /**
865
  * change_price.
866
  *
867
- * @version 4.5.0
868
  */
869
  function change_price( $price, $_product ) {
870
  if ( '' === $price ) {
@@ -875,29 +895,44 @@ class WCJ_Multicurrency extends WCJ_Module {
875
  return $price;
876
  }
877
 
 
 
 
 
 
 
 
 
 
 
878
  // Per product
879
  if ( 'yes' === get_option( 'wcj_multicurrency_per_product_enabled' , 'yes' ) && null != $_product ) {
880
- $_product_id = wcj_get_product_id( $_product );
881
  if (
882
  'yes' === get_option( 'wcj_multicurrency_per_product_make_empty', 'no' ) &&
883
  'yes' === get_post_meta( $_product_id, '_' . 'wcj_multicurrency_per_product_make_empty_' . $this->get_current_currency_code(), true )
884
  ) {
885
- return '';
 
 
886
  } elseif ( '' != ( $regular_price_per_product = get_post_meta( $_product_id, '_' . 'wcj_multicurrency_per_product_regular_price_' . $this->get_current_currency_code(), true ) ) ) {
887
- $_current_filter = current_filter();
888
  if ( 'woocommerce_get_price_including_tax' == $_current_filter || 'woocommerce_get_price_excluding_tax' == $_current_filter ) {
889
- return wcj_get_product_display_price( $_product );
890
-
 
891
  } elseif ( WCJ_PRODUCT_GET_PRICE_FILTER == $_current_filter || 'woocommerce_variation_prices_price' == $_current_filter || 'woocommerce_product_variation_get_price' == $_current_filter || in_array( $_current_filter, $this->additional_price_filters ) ) {
892
  $sale_price_per_product = get_post_meta( $_product_id, '_' . 'wcj_multicurrency_per_product_sale_price_' . $this->get_current_currency_code(), true );
893
- return ( '' != $sale_price_per_product && $sale_price_per_product < $regular_price_per_product ) ? $sale_price_per_product : $regular_price_per_product;
894
-
 
895
  } elseif ( WCJ_PRODUCT_GET_REGULAR_PRICE_FILTER == $_current_filter || 'woocommerce_variation_prices_regular_price' == $_current_filter || 'woocommerce_product_variation_get_regular_price' == $_current_filter ) {
896
- return $regular_price_per_product;
897
-
 
898
  } elseif ( WCJ_PRODUCT_GET_SALE_PRICE_FILTER == $_current_filter || 'woocommerce_variation_prices_sale_price' == $_current_filter || 'woocommerce_product_variation_get_sale_price' == $_current_filter ) {
899
  $sale_price_per_product = get_post_meta( $_product_id, '_' . 'wcj_multicurrency_per_product_sale_price_' . $this->get_current_currency_code(), true );
900
- return ( '' != $sale_price_per_product ) ? $sale_price_per_product : $price;
 
 
901
  }
902
  }
903
  }
@@ -916,10 +951,12 @@ class WCJ_Multicurrency extends WCJ_Module {
916
  $price = floor( $price );
917
  break;
918
  }
 
919
  return $price;
920
  }
921
 
922
  // No changes
 
923
  return $price;
924
  }
925
 
2
  /**
3
  * Booster for WooCommerce - Module - Multicurrency (Currency Switcher)
4
  *
5
+ * @version 4.6.0
6
  * @since 2.4.3
7
  * @author Algoritmika Ltd.
8
  */
91
  /**
92
  * Handles third party compatibility
93
  *
94
+ * @version 4.6.0
95
  * @since 4.3.0
96
  */
97
  function handle_third_party_compatibility(){
124
  if ( 'yes' === get_option( 'wcj_multicurrency_compatibility_wc_import' , 'no' ) ) {
125
  add_filter( 'woocommerce_product_importer_parsed_data', array( $this, 'fix_wc_product_import' ), 10, 2 );
126
  }
127
+
128
+ // WPC Product Bundles plugin
129
+ add_action( 'woocommerce_init', function () {
130
+ if ( 'yes' === get_option( 'wcj_multicurrency_compatibility_wpc_product_bundle', 'no' ) ) {
131
+ wcj_remove_class_filter( 'woocommerce_add_to_cart', 'WPcleverWoosb', 'woosb_add_to_cart' );
132
+ }
133
+ }, 99 );
134
  }
135
 
136
  /**
162
  /**
163
  * Fixes sort by price when `wcj_multicurrency_per_product_enabled` is enabled.
164
  *
165
+ * @version 4.6.0
166
  * @since 4.5.0
167
  *
168
  * @param $args
175
  if (
176
  $orderby != 'price' ||
177
  is_admin() ||
178
+ ! is_main_query() ||
179
  'no' === get_option( 'wcj_multicurrency_per_product_enabled', 'no' ) ||
180
  get_option( 'woocommerce_currency' ) === $this->get_current_currency_code() ||
181
  1 === $this->get_currency_exchange_rate( $this->get_current_currency_code()
200
  $clauses['join'] .= " {$min_max_join} ";
201
  }
202
  if ( 'DESC' === $order ) {
203
+ $clauses['fields'] .= ", (IFNULL(MAX((pm.meta_value +0))/{$exchange_rate},max_price) +0) AS wcj_price ";
204
  } else {
205
+ $clauses['fields'] .= ", (IFNULL(MIN((pm.meta_value +0))/{$exchange_rate},min_price) +0) AS wcj_price ";
206
  }
207
  $clauses['orderby'] = " wcj_price {$order}, wc_product_meta_lookup.product_id {$order} ";
208
 
217
  * First it removes products witch `_wcj_multicurrency_per_product_regular_price_{$current_currency_code}` meta don't match min and max.
218
  * Then it adds products witch `_wcj_multicurrency_per_product_regular_price_{$current_currency_code}` meta match min and max.
219
  *
220
+ * @version 4.6.0
221
  * @since 4.5.0
222
  *
223
  * @see WC_Query::price_filter_post_clauses()
252
  if ( false === strpos( $args['join'], $min_max_join ) ) {
253
  $args['join'] .= " {$min_max_join} ";
254
  }
255
+ $args['fields'] .= ", (IFNULL(MIN((pm.meta_value +0))/{$exchange_rate},min_price) +0) AS wcj_min_price ";
256
+ $args['fields'] .= ", (IFNULL(MAX((pm.meta_value +0))/{$exchange_rate},max_price) +0) AS wcj_max_price ";
257
  $args['where'] = preg_replace( '/and wc_product_meta_lookup.min_price >= \d.* and wc_product_meta_lookup.max_price <= \d.*\s/i', '', $args['where'] );
258
  $args['groupby'] .= " having wcj_min_price >= $min_price AND wcj_min_price <= $max_price ";
259
  return $args;
264
  *
265
  * It works comparing min and max values from "_wcj_multicurrency_per_product_regular_price_{currency_code}" meta as well as min and max price from wc_product_meta_lookup
266
  *
267
+ * @version 4.6.0
268
  * @since 4.5.0
269
  *
270
  * @see WC_Widget_Price_Filter::get_filtered_price()
286
  global $wpdb;
287
  $current_currency_code = $this->get_current_currency_code();
288
  $exchange_rate = $this->get_currency_exchange_rate( $current_currency_code );
289
+ $args = WC()->query->get_main_query()->query_vars;
290
+ $tax_query = isset( $args['tax_query'] ) ? $args['tax_query'] : array();
291
+ $meta_query = isset( $args['meta_query'] ) ? $args['meta_query'] : array();
292
 
293
  if ( ! is_post_type_archive( 'product' ) && ! empty( $args['taxonomy'] ) && ! empty( $args['term'] ) ) {
294
  $tax_query[] = array(
308
  $search_query_sql = $search ? ' AND ' . $search : '';
309
 
310
  $sql = "
311
+ SELECT FLOOR(min(wcj_min_price)) as min_price, CEILING(max(wcj_max_price)) as max_price
312
+ FROM(
313
+ SELECT (IFNULL(MIN((pm.meta_value +0))/{$exchange_rate},min_price) +0) AS wcj_min_price, (IFNULL(MAX((pm.meta_value +0))/{$exchange_rate},max_price) + 0) AS wcj_max_price
314
+ FROM {$wpdb->wc_product_meta_lookup}
315
+ LEFT JOIN {$wpdb->postmeta} AS pm on pm.post_id = product_id AND (pm.meta_key IN ('_wcj_multicurrency_per_product_min_price_{$current_currency_code}','_wcj_multicurrency_per_product_max_price_{$current_currency_code}') and pm.meta_value!='')
316
+ WHERE product_id IN (
317
+ SELECT ID FROM {$wpdb->posts}
318
+ " . $tax_query_sql['join'] . $meta_query_sql['join'] . "
319
+ WHERE {$wpdb->posts}.post_type IN ('" . implode( "','", array_map( 'esc_sql', apply_filters( 'woocommerce_price_filter_post_type', array( 'product' ) ) ) ) . "')
320
+ AND {$wpdb->posts}.post_status = 'publish'
321
+ " . $tax_query_sql['where'] . $meta_query_sql['where'] . $search_query_sql . '
322
+ )
323
+ group by product_id
324
+ ) as wcj_min_max_price
325
+ ';
326
  return $sql;
327
  }
328
 
365
 
366
  /**
367
  * Adds compatibility with WooCommerce Price Filter widget.
368
+ *
369
+ * @see price-slider.js, init_price_filter()
370
+ *
371
+ * @version 4.6.0
372
  * @since 4.3.0
373
  */
374
  function add_compatibility_with_price_filter_widget() {
383
  }
384
  ?>
385
  <input type="hidden" id="wcj_mc_exchange_rate" value="<?php echo esc_html( $exchange_rate ) ?>"/>
386
+ <script>
387
  var wcj_mc_pf_slider = {
388
  slider: null,
389
  convert_rate: 1,
466
  wcj_mc_pf.init(price_filters);
467
  }
468
  });
469
+ </script>
470
  <?php
471
  }
472
 
582
  /**
583
  * Gets all products, or products with variations containing meta '_wcj_multicurrency_per_product_regular_price_{currency}' or '_wcj_multicurrency_per_product_sale_price_{currency}'.
584
  *
585
+ * @version 4.6.0
586
  * @since 4.5.0
587
  *
588
  * @param $currency
596
 
597
  global $wpdb;
598
  $product_ids = $wpdb->get_col( "
599
+ SELECT p.ID
600
+ FROM {$wpdb->posts} AS p
601
+ LEFT JOIN {$wpdb->posts} AS p2 ON p.ID = p2.ID OR (p2.post_parent = p.ID AND p2.post_type='product_variation')
602
+ LEFT JOIN {$wpdb->postmeta} AS pm ON (p2.ID = pm.post_id) AND (pm.meta_key IN ('_wcj_multicurrency_per_product_regular_price_{$currency}','_wcj_multicurrency_per_product_sale_price_{$currency}') AND pm.meta_value!='')
603
+ WHERE p.post_status = 'publish' AND p.post_type IN ('product')
604
+ AND pm.meta_value != 'null'
605
+ GROUP BY p.ID
606
+ " );
607
 
608
  return $product_ids;
609
  }
868
  }
869
  }
870
 
871
+ /**
872
+ * Saves price so it won't be necessary to calculate it multiple times.
873
+ * @version 4.6.0
874
+ * @since 4.6.0
875
+ *
876
+ * @param $price
877
+ * @param $product_id
878
+ * @param $filter
879
+ */
880
+ function save_price( $price, $product_id, $filter ) {
881
+ WCJ()->modules['multicurrency']->calculated_products_prices[ $product_id ][ $filter ] = $price;
882
+ }
883
+
884
  /**
885
  * change_price.
886
  *
887
+ * @version 4.6.0
888
  */
889
  function change_price( $price, $_product ) {
890
  if ( '' === $price ) {
895
  return $price;
896
  }
897
 
898
+ $_product_id = wcj_get_product_id( $_product );
899
+ $do_save = ( 'yes' === get_option( 'wcj_multicurrency_multicurrency_save_prices', 'no' ) );
900
+ $_current_filter = current_filter();
901
+ if ( '' == $_current_filter ) {
902
+ $_current_filter = 'wcj_filter__none';
903
+ }
904
+ if ( $do_save && isset( WCJ()->modules['multicurrency']->calculated_products_prices[ $_product_id ][ $_current_filter ] ) ) {
905
+ return WCJ()->modules['multicurrency']->calculated_products_prices[ $_product_id ][ $_current_filter ];
906
+ }
907
+
908
  // Per product
909
  if ( 'yes' === get_option( 'wcj_multicurrency_per_product_enabled' , 'yes' ) && null != $_product ) {
 
910
  if (
911
  'yes' === get_option( 'wcj_multicurrency_per_product_make_empty', 'no' ) &&
912
  'yes' === get_post_meta( $_product_id, '_' . 'wcj_multicurrency_per_product_make_empty_' . $this->get_current_currency_code(), true )
913
  ) {
914
+ $price = '';
915
+ $this->save_price( $price, $_product_id, $_current_filter );
916
+ return $price;
917
  } elseif ( '' != ( $regular_price_per_product = get_post_meta( $_product_id, '_' . 'wcj_multicurrency_per_product_regular_price_' . $this->get_current_currency_code(), true ) ) ) {
 
918
  if ( 'woocommerce_get_price_including_tax' == $_current_filter || 'woocommerce_get_price_excluding_tax' == $_current_filter ) {
919
+ $price = wcj_get_product_display_price( $_product );
920
+ $this->save_price( $price, $_product_id, $_current_filter );
921
+ return $price;
922
  } elseif ( WCJ_PRODUCT_GET_PRICE_FILTER == $_current_filter || 'woocommerce_variation_prices_price' == $_current_filter || 'woocommerce_product_variation_get_price' == $_current_filter || in_array( $_current_filter, $this->additional_price_filters ) ) {
923
  $sale_price_per_product = get_post_meta( $_product_id, '_' . 'wcj_multicurrency_per_product_sale_price_' . $this->get_current_currency_code(), true );
924
+ $price = ( '' != $sale_price_per_product && $sale_price_per_product < $regular_price_per_product ) ? $sale_price_per_product : $regular_price_per_product;
925
+ $this->save_price( $price, $_product_id, $_current_filter );
926
+ return $price;
927
  } elseif ( WCJ_PRODUCT_GET_REGULAR_PRICE_FILTER == $_current_filter || 'woocommerce_variation_prices_regular_price' == $_current_filter || 'woocommerce_product_variation_get_regular_price' == $_current_filter ) {
928
+ $price = $regular_price_per_product;
929
+ $this->save_price( $price, $_product_id, $_current_filter );
930
+ return $price;
931
  } elseif ( WCJ_PRODUCT_GET_SALE_PRICE_FILTER == $_current_filter || 'woocommerce_variation_prices_sale_price' == $_current_filter || 'woocommerce_product_variation_get_sale_price' == $_current_filter ) {
932
  $sale_price_per_product = get_post_meta( $_product_id, '_' . 'wcj_multicurrency_per_product_sale_price_' . $this->get_current_currency_code(), true );
933
+ $price = ( '' != $sale_price_per_product ) ? $sale_price_per_product : $price;
934
+ $this->save_price( $price, $_product_id, $_current_filter );
935
+ return $price;
936
  }
937
  }
938
  }
951
  $price = floor( $price );
952
  break;
953
  }
954
+ $this->save_price( $price, $_product_id, $_current_filter );
955
  return $price;
956
  }
957
 
958
  // No changes
959
+ $this->save_price( $price, $_product_id, $_current_filter );
960
  return $price;
961
  }
962
 
includes/class-wcj-payment-gateways-per-category.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Module - Gateways per Product or Category
4
  *
5
- * @version 4.1.0
6
  * @since 2.2.0
7
  * @author Algoritmika Ltd.
8
  */
@@ -32,121 +32,143 @@ class WCJ_Payment_Gateways_Per_Category extends WCJ_Module {
32
  add_filter( 'woocommerce_available_payment_gateways', array( $this, 'filter_available_payment_gateways_per_category' ), 100 );
33
  $this->do_use_variations = ( 'yes' === get_option( 'wcj_gateways_per_category_use_variations', 'no' ) );
34
  }
 
35
  }
36
 
37
  /**
38
- * filter_available_payment_gateways_per_category.
39
  *
40
- * @version 4.1.0
41
- * @todo [dev] (maybe) `if ( ! is_checkout() ) { return $available_gateways; }`
 
 
 
 
 
42
  */
43
- function filter_available_payment_gateways_per_category( $available_gateways ) {
44
-
45
- if ( ! isset( WC()->cart ) ) {
46
- return $available_gateways;
47
- }
48
-
49
- foreach ( $available_gateways as $gateway_id => $gateway ) {
50
-
51
- // Including by categories
52
- $categories_in = get_option( 'wcj_gateways_per_category_' . $gateway_id );
53
- if ( ! empty( $categories_in ) ) {
54
- $do_skip = true;
55
- foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
56
- $product_categories = get_the_terms( $values['product_id'], 'product_cat' );
57
- if ( empty( $product_categories ) ) {
58
- continue; // ... to next product in the cart
59
- }
60
- foreach( $product_categories as $product_category ) {
61
- if ( in_array( $product_category->term_id, $categories_in ) ) {
62
- // Current gateway is OK, breaking to check next gateway (no need to check other categories of the product)
63
- $do_skip = false;
64
- break;
65
- }
66
- }
67
- if ( ! $do_skip ) {
68
- // Current gateway is OK, breaking to check next gateway (no need to check other products in the cart)
69
- break;
70
- }
71
  }
72
- if ( $do_skip ) {
73
- // Skip (i.e. hide/unset) current gateway - no products of needed categories found in the cart
74
- if ( isset( $available_gateways[ $gateway_id ] ) ) {
75
- unset( $available_gateways[ $gateway_id ] );
76
  }
77
- continue; // ... to next gateway
78
  }
79
  }
 
 
 
 
80
 
81
- // Excluding by categories
82
- $categories_excl = get_option( 'wcj_gateways_per_category_excl_' . $gateway_id );
83
- if ( ! empty( $categories_excl ) ) {
84
- $do_skip = false;
85
- foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
86
- $product_categories = get_the_terms( $values['product_id'], 'product_cat' );
87
- if ( empty( $product_categories ) ) {
88
- continue; // ... to next product in the cart
89
- }
90
- foreach( $product_categories as $product_category ) {
91
- if ( in_array( $product_category->term_id, $categories_excl ) ) {
92
- // Skip (i.e. hide/unset) current gateway
93
- if ( isset( $available_gateways[ $gateway_id ] ) ) {
94
- unset( $available_gateways[ $gateway_id ] );
95
- }
96
- $do_skip = true;
97
- break;
98
- }
99
- }
100
- if ( $do_skip ) {
101
  break;
102
  }
103
  }
104
- if ( $do_skip ) {
105
- continue; // ... to next gateway
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  }
107
  }
 
 
 
 
108
 
109
- // Including by products
110
- $products_in = wcj_maybe_convert_string_to_array( apply_filters( 'booster_option', array(), get_option( 'wcj_gateways_per_products_' . $gateway_id ) ) );
111
- if ( ! empty( $products_in ) ) {
112
- $do_skip = true;
113
- foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
114
- $product_id = ( $this->do_use_variations ? ( ! empty( $values['variation_id'] ) ? $values['variation_id'] : $values['product_id'] ) : $values['product_id'] );
115
- if ( in_array( $product_id, $products_in ) ) {
116
- // Current gateway is OK
117
- $do_skip = false;
118
- break;
119
- }
120
  }
121
- if ( $do_skip ) {
122
- // Skip (i.e. hide/unset) current gateway
123
- if ( isset( $available_gateways[ $gateway_id ] ) ) {
124
- unset( $available_gateways[ $gateway_id ] );
125
- }
126
- continue; // ... to next gateway
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
127
  }
128
  }
 
129
 
130
- // Excluding by products
131
- $products_excl = wcj_maybe_convert_string_to_array( apply_filters( 'booster_option', array(), get_option( 'wcj_gateways_per_products_excl_' . $gateway_id ) ) );
132
- if ( ! empty( $products_excl ) ) {
133
- $do_skip = false;
134
- foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
135
- $product_id = ( $this->do_use_variations ? ( ! empty( $values['variation_id'] ) ? $values['variation_id'] : $values['product_id'] ) : $values['product_id'] );
136
- if ( in_array( $product_id, $products_excl ) ) {
137
- // Skip (i.e. hide/unset) current gateway
138
- if ( isset( $available_gateways[ $gateway_id ] ) ) {
139
- unset( $available_gateways[ $gateway_id ] );
140
- }
141
- $do_skip = true;
142
- break;
143
- }
144
  }
145
- if ( $do_skip ) {
146
- continue; // ... to next gateway
 
147
  }
148
  }
 
149
 
 
 
 
150
  }
151
 
152
  return $available_gateways;
2
  /**
3
  * Booster for WooCommerce - Module - Gateways per Product or Category
4
  *
5
+ * @version 4.6.0
6
  * @since 2.2.0
7
  * @author Algoritmika Ltd.
8
  */
32
  add_filter( 'woocommerce_available_payment_gateways', array( $this, 'filter_available_payment_gateways_per_category' ), 100 );
33
  $this->do_use_variations = ( 'yes' === get_option( 'wcj_gateways_per_category_use_variations', 'no' ) );
34
  }
35
+
36
  }
37
 
38
  /**
39
+ * is_gateway_allowed.
40
  *
41
+ * @version 4.6.0
42
+ * @since 4.6.0
43
+ *
44
+ * @param $gateway_id
45
+ * @param array $products
46
+ *
47
+ * @return bool
48
  */
49
+ function is_gateway_allowed( $gateway_id, $products = array() ) {
50
+ // Including by categories
51
+ $categories_in = $this->get_option( 'wcj_gateways_per_category_' . $gateway_id );
52
+ if ( ! empty( $categories_in ) ) {
53
+ $current_check = false;
54
+ foreach ( $products as $product_id ) {
55
+ $product_categories = get_the_terms( $product_id, 'product_cat' );
56
+ if ( empty( $product_categories ) ) {
57
+ continue; // ... to next product
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  }
59
+ foreach ( $product_categories as $product_category ) {
60
+ if ( in_array( $product_category->term_id, $categories_in ) ) {
61
+ $current_check = true;
62
+ break;
63
  }
 
64
  }
65
  }
66
+ if ( ! $current_check ) {
67
+ return false;
68
+ }
69
+ }
70
 
71
+ // Excluding by categories
72
+ $categories_excl = $this->get_option( 'wcj_gateways_per_category_excl_' . $gateway_id );
73
+ if ( ! empty( $categories_excl ) ) {
74
+ $current_check = true;
75
+ foreach ( $products as $product_id ) {
76
+ $product_categories = get_the_terms( $product_id, 'product_cat' );
77
+ if ( empty( $product_categories ) ) {
78
+ continue; // ... to next product
79
+ }
80
+ foreach ( $product_categories as $product_category ) {
81
+ if ( in_array( $product_category->term_id, $categories_excl ) ) {
82
+ $current_check = false;
 
 
 
 
 
 
 
 
83
  break;
84
  }
85
  }
86
+ }
87
+ if ( ! $current_check ) {
88
+ return false;
89
+ }
90
+ }
91
+
92
+ // Including by products
93
+ $products_in = wcj_maybe_convert_string_to_array( apply_filters( 'booster_option', array(), $this->get_option( 'wcj_gateways_per_products_' . $gateway_id ) ) );
94
+ if ( ! empty( $products_in ) ) {
95
+ $current_check = false;
96
+ foreach ( $products as $product_id ) {
97
+ if ( in_array( $product_id, $products_in ) ) {
98
+ // Current gateway is OK
99
+ $current_check = true;
100
+ break;
101
  }
102
  }
103
+ if ( ! $current_check ) {
104
+ return false;
105
+ }
106
+ }
107
 
108
+ // Excluding by products
109
+ $products_excl = wcj_maybe_convert_string_to_array( apply_filters( 'booster_option', array(), $this->get_option( 'wcj_gateways_per_products_excl_' . $gateway_id ) ) );
110
+ if ( ! empty( $products_excl ) ) {
111
+ $current_check = true;
112
+ foreach ( $products as $product_id ) {
113
+ if ( in_array( $product_id, $products_excl ) ) {
114
+ $current_check = false;
115
+ break;
 
 
 
116
  }
117
+ }
118
+ if ( ! $current_check ) {
119
+ return false;
120
+ }
121
+ }
122
+
123
+ return true;
124
+ }
125
+
126
+ /**
127
+ * filter_available_payment_gateways_per_category.
128
+ *
129
+ * @version 4.6.0
130
+ * @todo [dev] (maybe) `if ( ! is_checkout() ) { return $available_gateways; }`
131
+ */
132
+ function filter_available_payment_gateways_per_category( $available_gateways ) {
133
+ $cart_products = array();
134
+ $order_products = array();
135
+
136
+ // Check if it is on Checkout Page
137
+ if ( is_checkout() && ! is_wc_endpoint_url( 'order-pay' ) ) {
138
+ foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
139
+ $cart_products[] = $product_id = ( $this->do_use_variations ? ( ! empty( $values['variation_id'] ) ? $values['variation_id'] : $values['product_id'] ) : $values['product_id'] );
140
+ }
141
+
142
+ // Check if it is on checkout/order-pay/xxx page
143
+ } elseif ( is_wc_endpoint_url( 'order-pay' ) ) {
144
+ $url_arr = preg_split( '/[\/\?]/', $_SERVER['REQUEST_URI'] );
145
+ if ( in_array( 'order-pay', $url_arr ) ) {
146
+ $order_pay_index = array_search( 'order-pay', $url_arr );
147
+ $order_id = intval( $url_arr[ $order_pay_index + 1 ] );
148
+ $order = wc_get_order( $order_id );
149
+ foreach ( $order->get_items() as $item_id => $values ) {
150
+ $order_products[] = $product_id = ( $this->do_use_variations ? ( ! empty( $values['variation_id'] ) ? $values['variation_id'] : $values['product_id'] ) : $values['product_id'] );
151
  }
152
  }
153
+ }
154
 
155
+ // Check gateways
156
+ $unavailable_gateways = array();
157
+ foreach ( $available_gateways as $gateway_id => $gateway ) {
158
+ if ( ! empty( $cart_products ) ) {
159
+ if ( ! $this->is_gateway_allowed( $gateway_id, $cart_products ) ) {
160
+ $unavailable_gateways[] = $gateway_id;
 
 
 
 
 
 
 
 
161
  }
162
+ } elseif ( ! empty( $order_products ) ) {
163
+ if ( ! $this->is_gateway_allowed( $gateway_id, $order_products ) ) {
164
+ $unavailable_gateways[] = $gateway_id;
165
  }
166
  }
167
+ }
168
 
169
+ // Remove invalid gateways
170
+ if ( count( $unavailable_gateways ) > 0 ) {
171
+ $available_gateways = array_diff_key( $available_gateways, array_flip( $unavailable_gateways ) );
172
  }
173
 
174
  return $available_gateways;
includes/class-wcj-price-by-user-role.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Module - Price based on User Role
4
  *
5
- * @version 3.6.0
6
  * @since 2.5.0
7
  * @author Algoritmika Ltd.
8
  * @todo Fix "Make Empty Price" option for variable products
@@ -17,7 +17,7 @@ class WCJ_Price_By_User_Role extends WCJ_Module {
17
  /**
18
  * Constructor.
19
  *
20
- * @version 3.6.0
21
  * @since 2.5.0
22
  */
23
  function __construct() {
@@ -46,7 +46,88 @@ class WCJ_Price_By_User_Role extends WCJ_Module {
46
  add_action( 'admin_notices', array( $this, 'admin_notices' ) );
47
  // Admin settings - "copy price" buttons
48
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_script' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  }
 
 
50
  }
51
 
52
  /**
2
  /**
3
  * Booster for WooCommerce - Module - Price based on User Role
4
  *
5
+ * @version 4.6.0
6
  * @since 2.5.0
7
  * @author Algoritmika Ltd.
8
  * @todo Fix "Make Empty Price" option for variable products
17
  /**
18
  * Constructor.
19
  *
20
+ * @version 4.6.0
21
  * @since 2.5.0
22
  */
23
  function __construct() {
46
  add_action( 'admin_notices', array( $this, 'admin_notices' ) );
47
  // Admin settings - "copy price" buttons
48
  add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_script' ) );
49
+ add_filter( 'woocommerce_hide_invisible_variations', array( $this, 'show_empty_price_variations' ) );
50
+ add_action('woocommerce_before_single_variation',array($this,'remove_single_variation_hooks'));
51
+ add_action('wcj_before_get_terms',array($this,'remove_wpml_functions_before_get_terms'));
52
+ add_action('wcj_after_get_terms',array($this,'remove_wpml_functions_after_get_terms'));
53
+ }
54
+ }
55
+
56
+ /**
57
+ * remove_wpml_functions_before_get_terms.
58
+ *
59
+ * @see https://wpml.org/forums/topic/get-all-terms-of-all-languages-outside-loop/
60
+ *
61
+ * @version 4.6.0
62
+ * @since 4.6.0
63
+ */
64
+ function remove_wpml_functions_before_get_terms() {
65
+ if ( 'no' === get_option( 'wcj_price_by_user_role_wpml_get_terms_all_lang', 'no' ) ) {
66
+ return;
67
+ }
68
+ // remove WPML term filters
69
+ global $sitepress;
70
+ remove_filter( 'get_terms_args', array( $sitepress, 'get_terms_args_filter' ) );
71
+ remove_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ) );
72
+ remove_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ) );
73
+ }
74
+
75
+ /**
76
+ * remove_wpml_functions_after_get_terms
77
+ *
78
+ * @see http://support.themeblvd.com/forums/topic/wpml-sitepress-php-error-on-backend-due-to-layout-builder/
79
+ *
80
+ * @version 4.6.0
81
+ * @since 4.6.0
82
+ */
83
+ function remove_wpml_functions_after_get_terms() {
84
+ if ( 'no' === get_option( 'wcj_price_by_user_role_wpml_get_terms_all_lang', 'no' ) ) {
85
+ return;
86
+ }
87
+ // restore WPML term filters
88
+ global $sitepress;
89
+ add_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ), 10, 3 );
90
+ add_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ) );
91
+ add_filter( 'get_terms_args', array( $sitepress, 'get_terms_args_filter' ), 10, 2 );
92
+ }
93
+
94
+ /**
95
+ * remove_single_variation_hooks.
96
+ *
97
+ * @version 4.6.0
98
+ * @since 4.6.0
99
+ */
100
+ function remove_single_variation_hooks() {
101
+ global $product;
102
+ if ( ! empty( $product->get_price() ) ) {
103
+ return;
104
+ }
105
+ if ( 'yes' === get_option( 'wcj_price_by_user_role_remove_single_variation', 'no' ) ) {
106
+ remove_action( 'woocommerce_single_variation', 'woocommerce_single_variation', apply_filters( 'wcj_remove_single_variation_priority', 10 ) );
107
+ }
108
+ if ( 'yes' === get_option( 'wcj_price_by_user_role_remove_add_to_cart_btn', 'no' ) ) {
109
+ remove_action( 'woocommerce_single_variation', 'woocommerce_single_variation_add_to_cart_button', apply_filters( 'wcj_remove_single_variation_add_to_cart_button_priority', 20 ) );
110
+ }
111
+ }
112
+
113
+ /**
114
+ * show_empty_price_variations.
115
+ *
116
+ * @version 4.6.0
117
+ * @since 4.6.0
118
+ *
119
+ * @param $hide
120
+ *
121
+ * @return bool
122
+ */
123
+ function show_empty_price_variations( $hide ) {
124
+ if (
125
+ 'no' === get_option( 'wcj_price_by_user_role_show_empty_price_variations', 'no' )
126
+ ) {
127
+ return $hide;
128
  }
129
+ $hide = false;
130
+ return $hide;
131
  }
132
 
133
  /**
includes/class-wcj-product-add-to-cart.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Module - Product Add To Cart
4
  *
5
- * @version 4.3.0
6
  * @since 2.2.0
7
  * @author Algoritmika Ltd.
8
  */
@@ -318,8 +318,8 @@ class WCJ_Product_Add_To_Cart extends WCJ_Module {
318
  }
319
 
320
  /**
321
- * Adds radio buttons template for variations
322
- * @version 4.3.0
323
  * @since 4.3.0
324
  * @see woocommerce_variable_add_to_cart()
325
  */
2
  /**
3
  * Booster for WooCommerce - Module - Product Add To Cart
4
  *
5
+ * @version 4.6.0
6
  * @since 2.2.0
7
  * @author Algoritmika Ltd.
8
  */
318
  }
319
 
320
  /**
321
+ * Adds radio buttons template for variations
322
+ * @version 4.6.0
323
  * @since 4.3.0
324
  * @see woocommerce_variable_add_to_cart()
325
  */
includes/class-wcj-product-addons.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Module - Product Addons
4
  *
5
- * @version 4.5.0
6
  * @since 2.5.3
7
  * @author Algoritmika Ltd.
8
  * @todo admin order view (names)
@@ -14,6 +14,7 @@ if ( ! class_exists( 'WCJ_Product_Addons' ) ) :
14
 
15
  class WCJ_Product_Addons extends WCJ_Module {
16
 
 
17
  /**
18
  * Constructor.
19
  *
@@ -29,6 +30,7 @@ class WCJ_Product_Addons extends WCJ_Module {
29
  $this->short_desc = __( 'Product Addons', 'woocommerce-jetpack' );
30
  $this->desc = __( 'Add (paid/free/discount) addons to products.', 'woocommerce-jetpack' );
31
  $this->link_slug = 'woocommerce-product-addons';
 
32
  parent::__construct();
33
 
34
  if ( $this->is_enabled() ) {
@@ -264,7 +266,7 @@ class WCJ_Product_Addons extends WCJ_Module {
264
  /**
265
  * price_change_ajax.
266
  *
267
- * @version 4.5.0
268
  * @since 2.5.3
269
  */
270
  function price_change_ajax( $param ) {
@@ -276,12 +278,13 @@ class WCJ_Product_Addons extends WCJ_Module {
276
  $addons = $this->get_product_addons( $parent_product_id );
277
  $the_addons_price = 0;
278
  foreach ( $addons as $addon ) {
 
279
  if ( isset( $_POST[ $addon['checkbox_key'] ] ) ) {
280
  if ( ( 'checkbox' === $addon['type'] || '' == $addon['type'] ) || ( 'text' == $addon['type'] && '' != $_POST[ $addon['checkbox_key'] ] ) ) {
281
- $the_addons_price += (float) $addon['price_value'];
282
  } elseif ( 'radio' === $addon['type'] || 'select' === $addon['type'] ) {
283
  $labels = $this->clean_and_explode( PHP_EOL, $addon['label_value'] );
284
- $prices = $this->clean_and_explode( PHP_EOL, $addon['price_value'] );
285
  if ( count( $labels ) === count( $prices ) ) {
286
  foreach ( $labels as $i => $label ) {
287
  if ( $_POST[ $addon['checkbox_key'] ] == sanitize_title( $label ) ) {
@@ -415,6 +418,34 @@ class WCJ_Product_Addons extends WCJ_Module {
415
  return $addons;
416
  }
417
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
418
  /**
419
  * add_info_to_order_item_meta_wc3.
420
  *
@@ -545,21 +576,22 @@ class WCJ_Product_Addons extends WCJ_Module {
545
  /**
546
  * add_addons_price_to_cart_item_data.
547
  *
548
- * @version 3.7.0
549
  * @since 2.5.3
550
  */
551
  function add_addons_price_to_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
552
  $addons = $this->get_product_addons( $product_id );
553
  foreach ( $addons as $addon ) {
 
554
  if ( isset( $_POST[ $addon['checkbox_key'] ] ) ) {
555
  if ( ( 'checkbox' === $addon['type'] || '' == $addon['type'] ) || ( 'text' == $addon['type'] && '' != $_POST[ $addon['checkbox_key'] ] ) ) {
556
- $cart_item_data[ $addon['price_key'] ] = $addon['price_value'];
557
  $cart_item_data[ $addon['label_key'] ] = $addon['label_value'];
558
  if ( 'text' == $addon['type'] ) {
559
  $cart_item_data[ $addon['label_key'] ] .= ' (' . $_POST[ $addon['checkbox_key'] ] . ')';
560
  }
561
  } elseif ( 'radio' === $addon['type'] || 'select' === $addon['type'] ) {
562
- $prices = $this->clean_and_explode( PHP_EOL, $addon['price_value'] );
563
  $labels = $this->clean_and_explode( PHP_EOL, $addon['label_value'] );
564
  if ( count( $labels ) === count( $prices ) ) {
565
  foreach ( $labels as $i => $label ) {
@@ -579,7 +611,7 @@ class WCJ_Product_Addons extends WCJ_Module {
579
  /**
580
  * add_addons_to_frontend.
581
  *
582
- * @version 4.0.0
583
  * @since 2.5.3
584
  */
585
  function add_addons_to_frontend() {
@@ -611,7 +643,7 @@ class WCJ_Product_Addons extends WCJ_Module {
611
  '%addon_input%' => '<input type="checkbox" id="' . $addon['checkbox_key'] . '" class="' . $addon['class'] . '" name="' . $addon['checkbox_key'] . '"' . $is_checked . $is_required . '>',
612
  '%addon_id%' => $addon['checkbox_key'],
613
  '%addon_label%' => $addon['label_value'],
614
- '%addon_price%' => wc_price( wcj_get_product_display_price( $_product, $this->maybe_convert_currency( $addon['price_value'], $_product ) ) ),
615
  '%addon_tooltip%' => $maybe_tooltip,
616
  ), get_option( 'wcj_product_addons_template_type_checkbox',
617
  '<p>%addon_input% <label for="%addon_id%">%addon_label% (%addon_price%)</label>%addon_tooltip%</p>' ) );
@@ -624,7 +656,7 @@ class WCJ_Product_Addons extends WCJ_Module {
624
  '%addon_input%' => '<input type="text" id="' . $addon['checkbox_key'] . '" class="' . $addon['class'] . '" name="' . $addon['checkbox_key'] . '" placeholder="' . $addon['placeholder'] . '" value="' . $default_value . '"' . $is_required . '>',
625
  '%addon_id%' => $addon['checkbox_key'],
626
  '%addon_label%' => $addon['label_value'],
627
- '%addon_price%' => wc_price( wcj_get_product_display_price( $_product, $this->maybe_convert_currency( $addon['price_value'], $_product ) ) ),
628
  '%addon_tooltip%' => $maybe_tooltip,
629
  ), get_option( 'wcj_product_addons_template_type_text',
630
  '<p><label for="%addon_id%">%addon_label% (%addon_price%)</label> %addon_input%%addon_tooltip%</p>' ) );
@@ -655,14 +687,14 @@ class WCJ_Product_Addons extends WCJ_Module {
655
  '%addon_input%' => '<input type="radio" id="' . $addon['checkbox_key'] . '-' . $label . '" class="' . $addon['class'] . '" name="' . $addon['checkbox_key'] . '" value="' . $label . '"' . $is_checked . $is_required . '>',
656
  '%addon_id%' => $addon['checkbox_key'] . '-' . $label,
657
  '%addon_label%' => $labels[ $i ],
658
- '%addon_price%' => wc_price( wcj_get_product_display_price( $_product, $this->maybe_convert_currency( $prices[ $i ], $_product ) ) ),
659
  '%addon_tooltip%' => $maybe_tooltip,
660
  ), get_option( 'wcj_product_addons_template_type_radio',
661
  '<p>%addon_input% <label for="%addon_id%">%addon_label% (%addon_price%)</label>%addon_tooltip%</p>' ) );
662
  } else {
663
  $select_option = wcj_handle_replacements( array(
664
  '%addon_label%' => $labels[ $i ],
665
- '%addon_price%' => wc_price( wcj_get_product_display_price( $_product, $this->maybe_convert_currency( $prices[ $i ], $_product ) ) ),
666
  ), get_option( 'wcj_product_addons_template_type_select_option', '%addon_label% (%addon_price%)' ) );
667
  $select_options .= '<option value="' . $label . '"' . $is_checked . '>' . $select_option . '</option>';
668
  }
@@ -684,13 +716,59 @@ class WCJ_Product_Addons extends WCJ_Module {
684
  }
685
  // Output
686
  if ( ! empty( $html ) ) {
687
- echo wcj_handle_replacements( array(
688
  '%addons_html%' => $html,
689
  ), get_option( 'wcj_product_addons_template_final', '<div id="wcj_product_addons">%addons_html%</div>' ) );
 
690
  $this->are_addons_displayed = true;
691
  }
692
  }
693
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
694
  }
695
 
696
  endif;
2
  /**
3
  * Booster for WooCommerce - Module - Product Addons
4
  *
5
+ * @version 4.6.0
6
  * @since 2.5.3
7
  * @author Algoritmika Ltd.
8
  * @todo admin order view (names)
14
 
15
  class WCJ_Product_Addons extends WCJ_Module {
16
 
17
+
18
  /**
19
  * Constructor.
20
  *
30
  $this->short_desc = __( 'Product Addons', 'woocommerce-jetpack' );
31
  $this->desc = __( 'Add (paid/free/discount) addons to products.', 'woocommerce-jetpack' );
32
  $this->link_slug = 'woocommerce-product-addons';
33
+
34
  parent::__construct();
35
 
36
  if ( $this->is_enabled() ) {
266
  /**
267
  * price_change_ajax.
268
  *
269
+ * @version 4.6.0
270
  * @since 2.5.3
271
  */
272
  function price_change_ajax( $param ) {
278
  $addons = $this->get_product_addons( $parent_product_id );
279
  $the_addons_price = 0;
280
  foreach ( $addons as $addon ) {
281
+ $price_value = $this->replace_price_template_vars( $addon['price_value'], $_POST['product_id'] );
282
  if ( isset( $_POST[ $addon['checkbox_key'] ] ) ) {
283
  if ( ( 'checkbox' === $addon['type'] || '' == $addon['type'] ) || ( 'text' == $addon['type'] && '' != $_POST[ $addon['checkbox_key'] ] ) ) {
284
+ $the_addons_price += (float) $price_value;
285
  } elseif ( 'radio' === $addon['type'] || 'select' === $addon['type'] ) {
286
  $labels = $this->clean_and_explode( PHP_EOL, $addon['label_value'] );
287
+ $prices = $this->clean_and_explode( PHP_EOL, $price_value );
288
  if ( count( $labels ) === count( $prices ) ) {
289
  foreach ( $labels as $i => $label ) {
290
  if ( $_POST[ $addon['checkbox_key'] ] == sanitize_title( $label ) ) {
418
  return $addons;
419
  }
420
 
421
+ /**
422
+ * Replaces template vars on price, like % or any other possibility.
423
+ *
424
+ * If it doesn't have any template var, just return the price.
425
+ *
426
+ * @version 4.6.0
427
+ * @since 4.6.0
428
+ *
429
+ * @param $price_value
430
+ * @param $product_id
431
+ *
432
+ * @return string
433
+ */
434
+ function replace_price_template_vars( $price_value, $product_id ) {
435
+ if ( preg_match_all( '/\d+%/m', $price_value, $matches ) > 0 ) {
436
+ $product_price = get_post_meta( $product_id, '_price', true );
437
+ $new_prices = array();
438
+ $preg_replace_find = array();
439
+ foreach ( $matches[0] as $match ) {
440
+ $preg_replace_find[] = '/' . $match . '/';
441
+ $percent_value = preg_replace( '/\%/', '', $match );
442
+ $new_prices[] = $product_price * ( $percent_value / 100 );
443
+ }
444
+ $price_value = preg_replace( $preg_replace_find, $new_prices, $price_value );
445
+ }
446
+ return $price_value;
447
+ }
448
+
449
  /**
450
  * add_info_to_order_item_meta_wc3.
451
  *
576
  /**
577
  * add_addons_price_to_cart_item_data.
578
  *
579
+ * @version 4.6.0
580
  * @since 2.5.3
581
  */
582
  function add_addons_price_to_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
583
  $addons = $this->get_product_addons( $product_id );
584
  foreach ( $addons as $addon ) {
585
+ $price_value = $this->replace_price_template_vars( $addon['price_value'], $variation_id ? $variation_id : $product_id );
586
  if ( isset( $_POST[ $addon['checkbox_key'] ] ) ) {
587
  if ( ( 'checkbox' === $addon['type'] || '' == $addon['type'] ) || ( 'text' == $addon['type'] && '' != $_POST[ $addon['checkbox_key'] ] ) ) {
588
+ $cart_item_data[ $addon['price_key'] ] = $price_value;
589
  $cart_item_data[ $addon['label_key'] ] = $addon['label_value'];
590
  if ( 'text' == $addon['type'] ) {
591
  $cart_item_data[ $addon['label_key'] ] .= ' (' . $_POST[ $addon['checkbox_key'] ] . ')';
592
  }
593
  } elseif ( 'radio' === $addon['type'] || 'select' === $addon['type'] ) {
594
+ $prices = $this->clean_and_explode( PHP_EOL, $price_value );
595
  $labels = $this->clean_and_explode( PHP_EOL, $addon['label_value'] );
596
  if ( count( $labels ) === count( $prices ) ) {
597
  foreach ( $labels as $i => $label ) {
611
  /**
612
  * add_addons_to_frontend.
613
  *
614
+ * @version 4.6.0
615
  * @since 2.5.3
616
  */
617
  function add_addons_to_frontend() {
643
  '%addon_input%' => '<input type="checkbox" id="' . $addon['checkbox_key'] . '" class="' . $addon['class'] . '" name="' . $addon['checkbox_key'] . '"' . $is_checked . $is_required . '>',
644
  '%addon_id%' => $addon['checkbox_key'],
645
  '%addon_label%' => $addon['label_value'],
646
+ '%addon_price%' => $this->format_addon_price( $addon['price_value'], $_product ),
647
  '%addon_tooltip%' => $maybe_tooltip,
648
  ), get_option( 'wcj_product_addons_template_type_checkbox',
649
  '<p>%addon_input% <label for="%addon_id%">%addon_label% (%addon_price%)</label>%addon_tooltip%</p>' ) );
656
  '%addon_input%' => '<input type="text" id="' . $addon['checkbox_key'] . '" class="' . $addon['class'] . '" name="' . $addon['checkbox_key'] . '" placeholder="' . $addon['placeholder'] . '" value="' . $default_value . '"' . $is_required . '>',
657
  '%addon_id%' => $addon['checkbox_key'],
658
  '%addon_label%' => $addon['label_value'],
659
+ '%addon_price%' => $this->format_addon_price( $addon['price_value'], $_product ),
660
  '%addon_tooltip%' => $maybe_tooltip,
661
  ), get_option( 'wcj_product_addons_template_type_text',
662
  '<p><label for="%addon_id%">%addon_label% (%addon_price%)</label> %addon_input%%addon_tooltip%</p>' ) );
687
  '%addon_input%' => '<input type="radio" id="' . $addon['checkbox_key'] . '-' . $label . '" class="' . $addon['class'] . '" name="' . $addon['checkbox_key'] . '" value="' . $label . '"' . $is_checked . $is_required . '>',
688
  '%addon_id%' => $addon['checkbox_key'] . '-' . $label,
689
  '%addon_label%' => $labels[ $i ],
690
+ '%addon_price%' => $this->format_addon_price( $prices[ $i ], $_product ),
691
  '%addon_tooltip%' => $maybe_tooltip,
692
  ), get_option( 'wcj_product_addons_template_type_radio',
693
  '<p>%addon_input% <label for="%addon_id%">%addon_label% (%addon_price%)</label>%addon_tooltip%</p>' ) );
694
  } else {
695
  $select_option = wcj_handle_replacements( array(
696
  '%addon_label%' => $labels[ $i ],
697
+ '%addon_price%' => $this->format_addon_price( $prices[ $i ], $_product ),
698
  ), get_option( 'wcj_product_addons_template_type_select_option', '%addon_label% (%addon_price%)' ) );
699
  $select_options .= '<option value="' . $label . '"' . $is_checked . '>' . $select_option . '</option>';
700
  }
716
  }
717
  // Output
718
  if ( ! empty( $html ) ) {
719
+ $html = wcj_handle_replacements( array(
720
  '%addons_html%' => $html,
721
  ), get_option( 'wcj_product_addons_template_final', '<div id="wcj_product_addons">%addons_html%</div>' ) );
722
+ echo $this->remove_empty_parenthesis($html);
723
  $this->are_addons_displayed = true;
724
  }
725
  }
726
 
727
+ /**
728
+ * remove_empty_parenthesis.
729
+ *
730
+ * @version 4.6.0
731
+ * @since 4.6.0
732
+ *
733
+ * @param $html
734
+ *
735
+ * @return string
736
+ */
737
+ function remove_empty_parenthesis( $html ) {
738
+ return preg_replace( '/\(\)/', '', $html );
739
+ }
740
+
741
+ /**
742
+ * Formats the addon price.
743
+ *
744
+ * @version 4.6.0
745
+ * @since 4.6.0
746
+ *
747
+ * @param $_product
748
+ * @param $price
749
+ *
750
+ * @return string
751
+ */
752
+ function format_addon_price( $price, $_product ) {
753
+ $show_raw_price = false;
754
+ if ( preg_match_all( '/\d+%/m', $price ) > 0 ) {
755
+ if ( is_a( $_product, 'WC_Product_Simple' ) ) {
756
+ $price = $this->replace_price_template_vars( $price, $_product->get_id() );
757
+ } else {
758
+ $show_raw_price = true;
759
+ }
760
+ }
761
+ if ( $show_raw_price ) {
762
+ if ( 'yes' === get_option( 'wcj_product_addons_template_hide_percentage_price', 'yes' ) ) {
763
+ return '';
764
+ } else {
765
+ return $price;
766
+ }
767
+ } else {
768
+ return wc_price( wcj_get_product_display_price( $_product, $this->maybe_convert_currency( $price, $_product ) ) );
769
+ }
770
+ }
771
+
772
  }
773
 
774
  endif;
includes/class-wcj-product-bookings.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Module - Bookings
4
  *
5
- * @version 4.3.0
6
  * @since 2.5.0
7
  * @author Algoritmika Ltd.
8
  */
@@ -306,40 +306,41 @@ class WCJ_Product_Bookings extends WCJ_Module {
306
  }
307
 
308
  /**
309
- * Create custom style for bookings product page
310
- *
311
- * @version 4.3.0
312
  * @since 2.5.0
313
  */
314
- function create_custom_style(){
315
- ?>
316
- <style>
317
- .wcj-loader {
318
- display:none;
319
- border: 4px solid #f3f3f3;
320
- border-radius: 50%;
321
- border-top: 4px solid #999999;
322
- width: 25px;height: 25px;
323
- -webkit-animation: spin 1s linear infinite; animation: spin 1s linear infinite;
324
- }
325
- /* Safari */
326
- @-webkit-keyframes spin {
327
- 0% { -webkit-transform: rotate(0deg); }
328
- 100% { -webkit-transform: rotate(360deg); }
329
- }
330
- @keyframes spin {
331
- 0% { transform: rotate(0deg); }
332
- 100% { transform: rotate(360deg); }
333
- }
334
- .wcj-bookings-price-wrapper{
335
- margin-bottom:25px;
336
- }
337
- .wcj-bookings-price-wrapper.loading .wcj-loader{
338
- display:block;
339
- }
340
- </style>
341
- <?php
342
- }
 
343
 
344
  /**
345
  * add_input_fields_to_frontend.
2
  /**
3
  * Booster for WooCommerce - Module - Bookings
4
  *
5
+ * @version 4.6.0
6
  * @since 2.5.0
7
  * @author Algoritmika Ltd.
8
  */
306
  }
307
 
308
  /**
309
+ * Create custom style for bookings product page
310
+ *
311
+ * @version 4.6.0
312
  * @since 2.5.0
313
  */
314
+ function create_custom_style() {
315
+ ?>
316
+ <style>
317
+ .wcj-loader {
318
+ display: none;
319
+ border: 4px solid #f3f3f3;
320
+ border-radius: 50%;
321
+ border-top: 4px solid #999999;
322
+ width: 25px;
323
+ height: 25px;
324
+ -webkit-animation: spin 1s linear infinite;
325
+ animation: spin 1s linear infinite;
326
+ }
327
+
328
+ /* Safari */
329
+ @-webkit-keyframes spin {
330
+ 0% { -webkit-transform: rotate(0deg); }
331
+ 100% { -webkit-transform: rotate(360deg); }
332
+ }
333
+
334
+ @keyframes spin {
335
+ 0% { transform: rotate(0deg); }
336
+ 100% { transform: rotate(360deg); }
337
+ }
338
+
339
+ .wcj-bookings-price-wrapper { margin-bottom: 25px; }
340
+ .wcj-bookings-price-wrapper.loading .wcj-loader { display: block; }
341
+ </style>
342
+ <?php
343
+ }
344
 
345
  /**
346
  * add_input_fields_to_frontend.
includes/class-wcj-product-input-fields.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Module - Product Input Fields
4
  *
5
- * @version 4.5.1
6
  * @author Algoritmika Ltd.
7
  */
8
 
@@ -50,7 +50,7 @@ class WCJ_Product_Input_Fields extends WCJ_Module {
50
  /**
51
  * preserve_linebreaks_admin.
52
  *
53
- * @version 4.5.0
54
  * @since 4.5.0
55
  */
56
  function preserve_linebreaks_admin() {
@@ -59,8 +59,9 @@ class WCJ_Product_Input_Fields extends WCJ_Module {
59
  }
60
  ?>
61
  <style>
62
- #woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr td, #woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr td
63
- {white-space: pre-wrap;}
 
64
  </style>
65
  <?php
66
  }
@@ -68,7 +69,7 @@ class WCJ_Product_Input_Fields extends WCJ_Module {
68
  /**
69
  * preserve_linebreaks_frontend.
70
  *
71
- * @version 4.5.1
72
  * @since 4.5.0
73
  */
74
  function preserve_linebreaks_frontend() {
@@ -76,16 +77,17 @@ class WCJ_Product_Input_Fields extends WCJ_Module {
76
  return;
77
  }
78
  ?>
79
- <style>
80
- .woocommerce-cart-form__cart-item.cart_item .product-name dl dd,
81
- .woocommerce-checkout-review-order-table .product-name dl dd {
82
- white-space: pre-wrap !important;
83
- }
84
- .woocommerce-cart-form__cart-item.cart_item .product-name dt,
85
- .woocommerce-checkout-review-order-table .product-name dt {
86
- display: block;
87
- }
88
- </style>
 
89
  <?php
90
  }
91
 
2
  /**
3
  * Booster for WooCommerce - Module - Product Input Fields
4
  *
5
+ * @version 4.6.0
6
  * @author Algoritmika Ltd.
7
  */
8
 
50
  /**
51
  * preserve_linebreaks_admin.
52
  *
53
+ * @version 4.6.0
54
  * @since 4.5.0
55
  */
56
  function preserve_linebreaks_admin() {
59
  }
60
  ?>
61
  <style>
62
+ #woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.display_meta tr td, #woocommerce-order-items .woocommerce_order_items_wrapper table.woocommerce_order_items table.meta tr td {
63
+ white-space: pre-wrap;
64
+ }
65
  </style>
66
  <?php
67
  }
69
  /**
70
  * preserve_linebreaks_frontend.
71
  *
72
+ * @version 4.6.0
73
  * @since 4.5.0
74
  */
75
  function preserve_linebreaks_frontend() {
77
  return;
78
  }
79
  ?>
80
+ <style>
81
+ .woocommerce-cart-form__cart-item.cart_item .product-name dl dd,
82
+ .woocommerce-checkout-review-order-table .product-name dl dd {
83
+ white-space: pre-wrap !important;
84
+ }
85
+
86
+ .woocommerce-cart-form__cart-item.cart_item .product-name dt,
87
+ .woocommerce-checkout-review-order-table .product-name dt {
88
+ display: block;
89
+ }
90
+ </style>
91
  <?php
92
  }
93
 
includes/class-wcj-product-open-pricing.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Module - Product Open Pricing
4
  *
5
- * @version 4.4.0
6
  * @since 2.4.8
7
  * @author Algoritmika Ltd.
8
  */
@@ -16,7 +16,7 @@ class WCJ_Product_Open_Pricing extends WCJ_Module {
16
  /**
17
  * Constructor.
18
  *
19
- * @version 4.1.0
20
  * @since 2.4.8
21
  */
22
  function __construct() {
@@ -58,6 +58,13 @@ class WCJ_Product_Open_Pricing extends WCJ_Module {
58
  add_action( 'manage_product_posts_custom_column', array( $this, 'render_product_column_open_pricing' ), PHP_INT_MAX );
59
  }
60
  $this->shop_currency = get_option( 'woocommerce_currency' );
 
 
 
 
 
 
 
61
  }
62
  }
63
 
@@ -90,14 +97,14 @@ class WCJ_Product_Open_Pricing extends WCJ_Module {
90
  /**
91
  * maybe_convert_price_currency.
92
  *
93
- * @version 4.2.0
94
  * @since 4.2.0
95
  */
96
- function maybe_convert_price_currency( $price ) {
97
  if ( 'switched_currency' === get_option( 'wcj_product_open_price_currency_switcher', 'shop_currency' ) ) {
98
  // Multicurrency (Currency Switcher) module
99
  if ( WCJ()->modules['multicurrency']->is_enabled() ) {
100
- $price = WCJ()->modules['multicurrency']->change_price( $price, null );
101
  }
102
  }
103
  return $price;
@@ -117,16 +124,17 @@ class WCJ_Product_Open_Pricing extends WCJ_Module {
117
  /**
118
  * add_price_info_to_loop.
119
  *
120
- * @version 4.2.0
121
  * @since 2.8.0
122
  */
123
  function add_price_info_to_loop() {
124
  $_product_id = get_the_ID();
 
125
  if ( $this->is_open_price_product( $_product_id ) ) {
126
  $replaceable_values = array(
127
- '%default_price%' => $this->wc_price_shop_currency( $this->maybe_convert_price_currency( get_post_meta( $_product_id, '_' . 'wcj_product_open_price_default_price', true ) ) ),
128
- '%min_price%' => $this->wc_price_shop_currency( $this->maybe_convert_price_currency( get_post_meta( $_product_id, '_' . 'wcj_product_open_price_min_price', true ) ) ),
129
- '%max_price%' => $this->wc_price_shop_currency( $this->maybe_convert_price_currency( get_post_meta( $_product_id, '_' . 'wcj_product_open_price_max_price', true ) ) ),
130
  );
131
  echo wcj_handle_replacements( $replaceable_values, $this->loop_price_info_template );
132
  }
@@ -286,12 +294,12 @@ class WCJ_Product_Open_Pricing extends WCJ_Module {
286
  /**
287
  * get_open_price.
288
  *
289
- * @version 4.4.0
290
  * @since 2.4.8
291
  */
292
  function get_open_price( $price, $_product ) {
293
  if ( $this->is_open_price_product( $_product ) && isset( $_product->wcj_open_price ) ) {
294
- if ( 'WC_Product_Woosb' === get_class( $_product ) ) {
295
  // "WPC Product Bundles for WooCommerce" plugin
296
  return $price;
297
  }
2
  /**
3
  * Booster for WooCommerce - Module - Product Open Pricing
4
  *
5
+ * @version 4.6.0
6
  * @since 2.4.8
7
  * @author Algoritmika Ltd.
8
  */
16
  /**
17
  * Constructor.
18
  *
19
+ * @version 4.6.0
20
  * @since 2.4.8
21
  */
22
  function __construct() {
58
  add_action( 'manage_product_posts_custom_column', array( $this, 'render_product_column_open_pricing' ), PHP_INT_MAX );
59
  }
60
  $this->shop_currency = get_option( 'woocommerce_currency' );
61
+
62
+ // WPC Product Bundles plugin
63
+ add_action( 'woocommerce_init', function () {
64
+ if ( 'yes' === get_option( 'wcj_product_open_price_woosb_product_bundles_remove_atc', 'no' ) ) {
65
+ wcj_remove_class_filter( 'woocommerce_add_to_cart', 'WPcleverWoosb', 'woosb_add_to_cart' );
66
+ }
67
+ }, 99 );
68
  }
69
  }
70
 
97
  /**
98
  * maybe_convert_price_currency.
99
  *
100
+ * @version 4.6.0
101
  * @since 4.2.0
102
  */
103
+ function maybe_convert_price_currency( $price, $product = null ) {
104
  if ( 'switched_currency' === get_option( 'wcj_product_open_price_currency_switcher', 'shop_currency' ) ) {
105
  // Multicurrency (Currency Switcher) module
106
  if ( WCJ()->modules['multicurrency']->is_enabled() ) {
107
+ $price = WCJ()->modules['multicurrency']->change_price( $price, $product );
108
  }
109
  }
110
  return $price;
124
  /**
125
  * add_price_info_to_loop.
126
  *
127
+ * @version 4.6.0
128
  * @since 2.8.0
129
  */
130
  function add_price_info_to_loop() {
131
  $_product_id = get_the_ID();
132
+ $product = wc_get_product( $_product_id );
133
  if ( $this->is_open_price_product( $_product_id ) ) {
134
  $replaceable_values = array(
135
+ '%default_price%' => $this->wc_price_shop_currency( $this->maybe_convert_price_currency( get_post_meta( $_product_id, '_' . 'wcj_product_open_price_default_price', true ), $product ) ),
136
+ '%min_price%' => $this->wc_price_shop_currency( $this->maybe_convert_price_currency( get_post_meta( $_product_id, '_' . 'wcj_product_open_price_min_price', true ), $product ) ),
137
+ '%max_price%' => $this->wc_price_shop_currency( $this->maybe_convert_price_currency( get_post_meta( $_product_id, '_' . 'wcj_product_open_price_max_price', true ), $product ) ),
138
  );
139
  echo wcj_handle_replacements( $replaceable_values, $this->loop_price_info_template );
140
  }
294
  /**
295
  * get_open_price.
296
  *
297
+ * @version 4.6.0
298
  * @since 2.4.8
299
  */
300
  function get_open_price( $price, $_product ) {
301
  if ( $this->is_open_price_product( $_product ) && isset( $_product->wcj_open_price ) ) {
302
+ if ( 'no' === get_option( 'wcj_product_open_price_woosb_product_bundles_replace_prices', 'no' ) && 'WC_Product_Woosb' === get_class( $_product ) ) {
303
  // "WPC Product Bundles for WooCommerce" plugin
304
  return $price;
305
  }
includes/class-wcj-sorting.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Module - Sorting
4
  *
5
- * @version 4.3.0
6
  * @author Algoritmika Ltd.
7
  */
8
 
@@ -66,18 +66,18 @@ class WCJ_Sorting extends WCJ_Module {
66
 
67
  /**
68
  * Restores default WooCommerce sorting on Avada Theme.
69
- *
70
- * @version 4.3.0
71
  * @since 4.3.0
72
  */
73
  function restore_default_sorting_on_avada() {
74
  add_action( 'wp_head', function () {
75
  ?>
76
- <style>
77
- form.woocommerce-ordering {
78
- display: inline-block;
79
- }
80
- </style>
81
  <?php
82
  } );
83
  add_action( 'woocommerce_before_shop_loop', function () {
@@ -93,8 +93,8 @@ class WCJ_Sorting extends WCJ_Module {
93
  }
94
 
95
  /**
96
- * Restores default WooCommerce sorting
97
- *
98
  * @version 4.3.0
99
  * @since 4.3.0
100
  */
2
  /**
3
  * Booster for WooCommerce - Module - Sorting
4
  *
5
+ * @version 4.6.0
6
  * @author Algoritmika Ltd.
7
  */
8
 
66
 
67
  /**
68
  * Restores default WooCommerce sorting on Avada Theme.
69
+ *
70
+ * @version 4.6.0
71
  * @since 4.3.0
72
  */
73
  function restore_default_sorting_on_avada() {
74
  add_action( 'wp_head', function () {
75
  ?>
76
+ <style>
77
+ form.woocommerce-ordering {
78
+ display: inline-block;
79
+ }
80
+ </style>
81
  <?php
82
  } );
83
  add_action( 'woocommerce_before_shop_loop', function () {
93
  }
94
 
95
  /**
96
+ * Restores default WooCommerce sorting
97
+ *
98
  * @version 4.3.0
99
  * @since 4.3.0
100
  */
includes/class-wcj-tax-display.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Module - Tax Display
4
  *
5
- * @version 4.1.0
6
  * @since 3.2.4
7
  * @author Algoritmika Ltd.
8
  */
@@ -45,7 +45,6 @@ class WCJ_Tax_Display extends WCJ_Module {
45
  add_action( 'init', array( $this, 'tax_display_toggle_param' ), PHP_INT_MAX );
46
  add_filter( 'option_woocommerce_tax_display_shop', array( $this, 'tax_display_toggle' ), PHP_INT_MAX );
47
  }
48
-
49
  }
50
  }
51
 
@@ -85,7 +84,7 @@ class WCJ_Tax_Display extends WCJ_Module {
85
  /**
86
  * tax_display_by_user_role.
87
  *
88
- * @version 4.1.0
89
  * @since 3.2.0
90
  */
91
  function tax_display_by_user_role( $value ) {
@@ -93,11 +92,13 @@ class WCJ_Tax_Display extends WCJ_Module {
93
  return $value;
94
  }
95
  if ( '' != ( $display_taxes_by_user_role_roles = get_option( 'wcj_product_listings_display_taxes_by_user_role_roles', '' ) ) ) {
96
- $current_user_first_role = wcj_get_current_user_first_role();
97
- if ( in_array( $current_user_first_role, $display_taxes_by_user_role_roles ) ) {
98
- $option_name = 'option_woocommerce_tax_display_shop' === current_filter() ? 'wcj_product_listings_display_taxes_by_user_role_' . $current_user_first_role : 'wcj_product_listings_display_taxes_on_cart_by_user_role_' . $current_user_first_role;
99
- if ( 'no_changes' != ( $tax_display = get_option( $option_name, 'no_changes' ) ) ) {
100
- return $tax_display;
 
 
101
  }
102
  }
103
  }
2
  /**
3
  * Booster for WooCommerce - Module - Tax Display
4
  *
5
+ * @version 4.6.0
6
  * @since 3.2.4
7
  * @author Algoritmika Ltd.
8
  */
45
  add_action( 'init', array( $this, 'tax_display_toggle_param' ), PHP_INT_MAX );
46
  add_filter( 'option_woocommerce_tax_display_shop', array( $this, 'tax_display_toggle' ), PHP_INT_MAX );
47
  }
 
48
  }
49
  }
50
 
84
  /**
85
  * tax_display_by_user_role.
86
  *
87
+ * @version 4.6.0
88
  * @since 3.2.0
89
  */
90
  function tax_display_by_user_role( $value ) {
92
  return $value;
93
  }
94
  if ( '' != ( $display_taxes_by_user_role_roles = get_option( 'wcj_product_listings_display_taxes_by_user_role_roles', '' ) ) ) {
95
+ $current_user_roles = wcj_get_current_user_all_roles();
96
+ foreach ( $current_user_roles as $current_user_first_role ) {
97
+ if ( in_array( $current_user_first_role, $display_taxes_by_user_role_roles ) ) {
98
+ $option_name = 'option_woocommerce_tax_display_shop' === current_filter() ? 'wcj_product_listings_display_taxes_by_user_role_' . $current_user_first_role : 'wcj_product_listings_display_taxes_on_cart_by_user_role_' . $current_user_first_role;
99
+ if ( 'no_changes' != ( $tax_display = get_option( $option_name, 'no_changes' ) ) ) {
100
+ return $tax_display;
101
+ }
102
  }
103
  }
104
  }
includes/emails/class-wc-email-wcj-custom.php CHANGED
@@ -4,7 +4,7 @@
4
  *
5
  * An email sent to recipient list when selected triggers are called.
6
  *
7
- * @version 4.1.0
8
  * @since 2.3.9
9
  * @author Algoritmika Ltd.
10
  * @extends WC_Email
@@ -105,17 +105,17 @@ class WC_Email_WCJ_Custom extends WC_Email {
105
  /**
106
  * trigger.
107
  *
108
- * @version 4.1.0
109
  */
110
- function trigger( $order_id ) {
111
 
112
  if ( ! $this->is_enabled() ) {
113
  return;
114
  }
115
 
116
- if ( $order_id ) {
117
 
118
- $this->object = wc_get_order( $order_id ); // must be `object` as it's named so in parent class (`WC_Email`). E.g. for attachments.
119
 
120
  if ( 'woocommerce_checkout_order_processed_notification' === current_filter() ) {
121
  // Check status
@@ -135,28 +135,33 @@ class WC_Email_WCJ_Custom extends WC_Email {
135
  }
136
  }
137
 
138
- if ( $this->customer_email ) {
139
- $this->recipient = wcj_get_order_billing_email( $this->object );
140
- } elseif ( false !== strpos( $this->recipient, '%customer%' ) ) {
141
- $this->recipient = str_replace( '%customer%', wcj_get_order_billing_email( $this->object ), $this->recipient );
142
- }
 
 
 
 
143
 
144
- $this->find['order-date'] = '{order_date}';
145
- $this->find['order-number'] = '{order_number}';
146
 
147
- $this->replace['order-date'] = date_i18n( wc_date_format(), strtotime( wcj_get_order_date( $this->object ) ) );
148
- $this->replace['order-number'] = $this->object->get_order_number();
149
 
150
- global $post;
151
- $post = ( WCJ_IS_WC_VERSION_BELOW_3 ? $this->object->post : get_post( $order_id ) );
152
- setup_postdata( $post );
153
- $_GET['order_id'] = $order_id;
 
154
  }
155
 
156
  $this->recipient = do_shortcode( $this->recipient );
157
 
158
  if ( ! $this->get_recipient() ) {
159
- if ( $order_id ) {
160
  wp_reset_postdata();
161
  }
162
  return;
@@ -164,7 +169,7 @@ class WC_Email_WCJ_Custom extends WC_Email {
164
 
165
  $this->send( $this->get_recipient(), do_shortcode( $this->get_subject() ), do_shortcode( $this->get_content() ), $this->get_headers(), $this->get_attachments() );
166
 
167
- if ( $order_id ) {
168
  wp_reset_postdata();
169
  }
170
  }
4
  *
5
  * An email sent to recipient list when selected triggers are called.
6
  *
7
+ * @version 4.6.0
8
  * @since 2.3.9
9
  * @author Algoritmika Ltd.
10
  * @extends WC_Email
105
  /**
106
  * trigger.
107
  *
108
+ * @version 4.6.0
109
  */
110
+ function trigger( $object_id ) {
111
 
112
  if ( ! $this->is_enabled() ) {
113
  return;
114
  }
115
 
116
+ if ( $object_id ) {
117
 
118
+ $this->object = wc_get_order( $object_id ); // must be `object` as it's named so in parent class (`WC_Email`). E.g. for attachments.
119
 
120
  if ( 'woocommerce_checkout_order_processed_notification' === current_filter() ) {
121
  // Check status
135
  }
136
  }
137
 
138
+ if ( 'woocommerce_created_customer_notification' === current_filter() ) {
139
+ $user = get_user_by( 'ID', $object_id );
140
+ $this->recipient = $user->user_email;
141
+ } else {
142
+ if ( $this->customer_email ) {
143
+ $this->recipient = wcj_get_order_billing_email( $this->object );
144
+ } elseif ( false !== strpos( $this->recipient, '%customer%' ) ) {
145
+ $this->recipient = str_replace( '%customer%', wcj_get_order_billing_email( $this->object ), $this->recipient );
146
+ }
147
 
148
+ $this->find['order-date'] = '{order_date}';
149
+ $this->find['order-number'] = '{order_number}';
150
 
151
+ $this->replace['order-date'] = date_i18n( wc_date_format(), strtotime( wcj_get_order_date( $this->object ) ) );
152
+ $this->replace['order-number'] = $this->object->get_order_number();
153
 
154
+ global $post;
155
+ $post = ( WCJ_IS_WC_VERSION_BELOW_3 ? $this->object->post : get_post( $object_id ) );
156
+ setup_postdata( $post );
157
+ $_GET['order_id'] = $object_id;
158
+ }
159
  }
160
 
161
  $this->recipient = do_shortcode( $this->recipient );
162
 
163
  if ( ! $this->get_recipient() ) {
164
+ if ( $object_id ) {
165
  wp_reset_postdata();
166
  }
167
  return;
169
 
170
  $this->send( $this->get_recipient(), do_shortcode( $this->get_subject() ), do_shortcode( $this->get_content() ), $this->get_headers(), $this->get_attachments() );
171
 
172
+ if ( $object_id ) {
173
  wp_reset_postdata();
174
  }
175
  }
includes/functions/wcj-functions-eu-vat.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Functions - EU VAT
4
  *
5
- * @version 3.2.2
6
  * @since 2.9.0
7
  * @author Algoritmika Ltd.
8
  */
@@ -13,13 +13,16 @@ if ( ! function_exists( 'wcj_validate_vat_no_soap' ) ) {
13
  /**
14
  * wcj_validate_vat_no_soap.
15
  *
16
- * @version 2.9.0
17
  * @since 2.5.7
18
  * @return mixed: bool on successful checking (can be true or false), null otherwise
19
  */
20
  function wcj_validate_vat_no_soap( $country_code, $vat_number, $method ) {
21
  $country_code = strtoupper( $country_code );
22
- $api_url = "http://ec.europa.eu/taxation_customs/vies/viesquer.do?ms=" . $country_code . "&vat=" . $vat_number;
 
 
 
23
  switch ( $method ) {
24
  case 'file_get_contents':
25
  if ( ini_get( 'allow_url_fopen' ) ) {
2
  /**
3
  * Booster for WooCommerce - Functions - EU VAT
4
  *
5
+ * @version 4.6.0
6
  * @since 2.9.0
7
  * @author Algoritmika Ltd.
8
  */
13
  /**
14
  * wcj_validate_vat_no_soap.
15
  *
16
+ * @version 4.6.0
17
  * @since 2.5.7
18
  * @return mixed: bool on successful checking (can be true or false), null otherwise
19
  */
20
  function wcj_validate_vat_no_soap( $country_code, $vat_number, $method ) {
21
  $country_code = strtoupper( $country_code );
22
+ $api_url = add_query_arg( array(
23
+ 'ms' => $country_code,
24
+ 'vat' => $vat_number,
25
+ ), 'http://ec.europa.eu/taxation_customs/vies/viesquer.do' );
26
  switch ( $method ) {
27
  case 'file_get_contents':
28
  if ( ini_get( 'allow_url_fopen' ) ) {
includes/functions/wcj-functions-general.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Functions - General
4
  *
5
- * @version 4.5.0
6
  * @author Algoritmika Ltd.
7
  * @todo add `wcj_add_actions()` and `wcj_add_filters()`
8
  */
@@ -849,4 +849,56 @@ if ( ! function_exists( 'wcj_get_cart_item_quantities' ) ) {
849
 
850
  return $quantities;
851
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
852
  }
2
  /**
3
  * Booster for WooCommerce - Functions - General
4
  *
5
+ * @version 4.6.0
6
  * @author Algoritmika Ltd.
7
  * @todo add `wcj_add_actions()` and `wcj_add_filters()`
8
  */
849
 
850
  return $quantities;
851
  }
852
+ }
853
+
854
+ if ( ! function_exists( 'wcj_remove_class_filter' ) ) {
855
+ /**
856
+ * Remove filter added with a callback to a class without access.
857
+ *
858
+ * @see https://gist.github.com/tripflex/c6518efc1753cf2392559866b4bd1a53
859
+ *
860
+ * @version 4.6.0
861
+ * @since 4.6.0
862
+ *
863
+ * @param $tag
864
+ * @param string $class_name
865
+ * @param string $method_name
866
+ * @param int $priority
867
+ *
868
+ * @return bool
869
+ */
870
+ function wcj_remove_class_filter( $tag, $class_name = '', $method_name = '', $priority = 10 ) {
871
+ global $wp_filter;
872
+ $is_hook_removed = false;
873
+ if ( ! empty( $wp_filter[ $tag ]->callbacks[ $priority ] ) ) {
874
+ $methods = wp_list_pluck( $wp_filter[ $tag ]->callbacks[ $priority ], 'function' );
875
+ $found_hooks = ! empty( $methods ) ? wp_list_filter( $methods, array( 1 => $method_name ) ) : array();
876
+ foreach ( $found_hooks as $hook_key => $hook ) {
877
+ if ( ! empty( $hook[0] ) && is_object( $hook[0] ) && get_class( $hook[0] ) === $class_name ) {
878
+ $wp_filter[ $tag ]->remove_filter( $tag, $hook, $priority );
879
+ $is_hook_removed = true;
880
+ }
881
+ }
882
+ }
883
+ return $is_hook_removed;
884
+ }
885
+ }
886
+
887
+ if ( ! function_exists( 'wcj_remove_class_filter' ) ) {
888
+ /**
889
+ * Remove action added with a callback to a class without access.
890
+ *
891
+ * @see https://gist.github.com/tripflex/c6518efc1753cf2392559866b4bd1a53
892
+ *
893
+ * @version 4.6.0
894
+ * @since 4.6.0
895
+ *
896
+ * @param $tag
897
+ * @param string $class_name
898
+ * @param string $method_name
899
+ * @param int $priority
900
+ */
901
+ function wcj_remove_class_action( $tag, $class_name = '', $method_name = '', $priority = 10 ) {
902
+ wcj_remove_class_filter( $tag, $class_name, $method_name, $priority );
903
+ }
904
  }
includes/settings/meta-box/wcj-settings-meta-box-product-addons.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Settings Meta Box - Product Addons
4
  *
5
- * @version 3.7.0
6
  * @since 2.8.0
7
  * @author Algoritmika Ltd.
8
  */
@@ -70,7 +70,7 @@ for ( $i = 1; $i <= $total_number; $i++ ) {
70
  ),
71
  array(
72
  'title' => __( 'Price(s)', 'woocommerce-jetpack' ),
73
- 'tooltip' => __( 'For radio and select enter one value per line.', 'woocommerce-jetpack' ),
74
  'name' => 'wcj_product_addons_per_product_price_' . $i,
75
  'default' => 0,
76
  'type' => 'textarea',
2
  /**
3
  * Booster for WooCommerce - Settings Meta Box - Product Addons
4
  *
5
+ * @version 4.6.0
6
  * @since 2.8.0
7
  * @author Algoritmika Ltd.
8
  */
70
  ),
71
  array(
72
  'title' => __( 'Price(s)', 'woocommerce-jetpack' ),
73
+ 'tooltip' => __( 'For radio and select enter one value per line.', 'woocommerce-jetpack' ) . '<br /><br />' . __( "You can use the % symbol to set a percentage of product's price, like 10%", 'woocommerce-jetpack' ),
74
  'name' => 'wcj_product_addons_per_product_price_' . $i,
75
  'default' => 0,
76
  'type' => 'textarea',
includes/settings/wcj-settings-checkout-customization.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Settings - Checkout Customization
4
  *
5
- * @version 4.1.0
6
  * @since 2.8.0
7
  * @author Algoritmika Ltd.
8
  */
@@ -33,6 +33,45 @@ return array(
33
  'type' => 'checkbox',
34
  'custom_attributes' => apply_filters( 'booster_message', '', 'disabled' ),
35
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
36
  array(
37
  'type' => 'sectionend',
38
  'id' => 'wcj_checkout_restrict_countries_options',
2
  /**
3
  * Booster for WooCommerce - Settings - Checkout Customization
4
  *
5
+ * @version 4.6.0
6
  * @since 2.8.0
7
  * @author Algoritmika Ltd.
8
  */
33
  'type' => 'checkbox',
34
  'custom_attributes' => apply_filters( 'booster_message', '', 'disabled' ),
35
  ),
36
+ array(
37
+ 'title' => __( 'Ignore on Admin', 'woocommerce-jetpack' ),
38
+ 'desc' => __( 'Enable', 'woocommerce-jetpack' ),
39
+ 'desc_tip' => __( 'Ignores restriction on admin', 'woocommerce-jetpack' ),
40
+ 'id' => 'wcj_checkout_restrict_countries_by_customer_ip_ignore_admin',
41
+ 'default' => 'no',
42
+ 'type' => 'checkbox',
43
+ 'custom_attributes' => apply_filters( 'booster_message', '', 'disabled' ),
44
+ ),
45
+ array(
46
+ 'title' => __( 'Restrict By Customer\'s Billing Country', 'woocommerce-jetpack' ),
47
+ 'desc' => __( 'Enable', 'woocommerce-jetpack' ),
48
+ 'desc_tip' => __( 'Restricts based on Customer\'s Billing Country, ignoring other restrictions', 'woocommerce-jetpack' ),
49
+ 'id' => 'wcj_checkout_restrict_countries_by_user_billing_country',
50
+ 'default' => 'no',
51
+ 'type' => 'checkbox',
52
+ 'custom_attributes' => apply_filters( 'booster_message', '', 'disabled' ),
53
+ ),
54
+ array(
55
+ 'title' => __( 'Restrict based on a YITH manual order', 'woocommerce-jetpack' ),
56
+ 'desc' => __( 'Enable', 'woocommerce-jetpack' ),
57
+ 'desc_tip' => __( 'Enable if you are creating a manual order using "YITH WooCommerce Request a Quote" plugin and selecting the billing country manually', 'woocommerce-jetpack' ),
58
+ 'id' => 'wcj_checkout_restrict_countries_based_on_yith_raq',
59
+ 'default' => 'no',
60
+ 'type' => 'checkbox',
61
+ 'custom_attributes' => apply_filters( 'booster_message', '', 'disabled' ),
62
+ ),
63
+ array(
64
+ 'title' => __( 'Conditions', 'woocommerce-jetpack' ),
65
+ 'desc_tip' => __( 'The restriction will work only if some condition is true.', 'woocommerce-jetpack' ).'<br /> '.__( 'Leave it empty if you want to restrict countries everywhere.', 'woocommerce-jetpack' ),
66
+ 'id' => 'wcj_checkout_restrict_countries_by_customer_ip_conditions',
67
+ 'default' => 'no',
68
+ 'type' => 'multiselect',
69
+ 'class' => 'chosen_select',
70
+ 'options' => array(
71
+ 'is_cart' => __( 'Is Cart', 'popup-notices-for-woocommerce' ),
72
+ 'is_checkout' => __( 'Is Checkout', 'popup-notices-for-woocommerce' ),
73
+ )
74
+ ),
75
  array(
76
  'type' => 'sectionend',
77
  'id' => 'wcj_checkout_restrict_countries_options',
includes/settings/wcj-settings-eu-vat-number.php CHANGED
@@ -103,6 +103,22 @@ $settings = array(
103
  'default' => 'yes',
104
  'type' => 'checkbox',
105
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
  array(
107
  'title' => __( 'Preserve VAT in Base Country', 'woocommerce-jetpack' ),
108
  'desc_tip' => sprintf( __( 'This will validate the VAT, but won\'t exempt VAT for base country VAT numbers. Base (i.e. store) country is set in %s.', 'woocommerce-jetpack' ),
@@ -212,6 +228,14 @@ $settings = array(
212
  'default' => '',
213
  'type' => 'text',
214
  ),
 
 
 
 
 
 
 
 
215
  array(
216
  'type' => 'sectionend',
217
  'id' => 'wcj_eu_vat_number_advanced_options',
103
  'default' => 'yes',
104
  'type' => 'checkbox',
105
  ),
106
+ array(
107
+ 'title' => __( 'Exempt VAT on Cart', 'woocommerce-jetpack' ),
108
+ 'desc' => __( 'Yes', 'woocommerce-jetpack' ),
109
+ 'desc_tip' => __( 'Exempts VAT even on Cart page.', 'woocommerce-jetpack' ),
110
+ 'id' => 'wcj_eu_vat_number_disable_for_valid_on_cart',
111
+ 'default' => 'no',
112
+ 'type' => 'checkbox',
113
+ ),
114
+ array(
115
+ 'title' => __( 'Exempt VAT by Customer\'s EU VAT', 'woocommerce-jetpack' ),
116
+ 'desc' => __( 'Yes', 'woocommerce-jetpack' ),
117
+ 'desc_tip' => __( 'Exempts VAT by checking previously registered EU VAT numbers from customers.', 'woocommerce-jetpack' ),
118
+ 'id' => 'wcj_eu_vat_number_disable_for_valid_by_user_vat',
119
+ 'default' => 'no',
120
+ 'type' => 'checkbox',
121
+ ),
122
  array(
123
  'title' => __( 'Preserve VAT in Base Country', 'woocommerce-jetpack' ),
124
  'desc_tip' => sprintf( __( 'This will validate the VAT, but won\'t exempt VAT for base country VAT numbers. Base (i.e. store) country is set in %s.', 'woocommerce-jetpack' ),
228
  'default' => '',
229
  'type' => 'text',
230
  ),
231
+ array(
232
+ 'title' => __( "Read '_vat_number' meta", 'woocommerce-jetpack' ),
233
+ 'desc_tip' => sprintf(__( "Try to add compatibility with <a href='%s' target='_blank'>EU VAT Number</a> plugin, reading meta from '_vat_number' meta", 'woocommerce-jetpack' ),'https://woocommerce.com/products/eu-vat-number/'),
234
+ 'desc' => __( 'Enable', 'woocommerce-jetpack' ),
235
+ 'id' => 'wcj_eu_vat_number_read_vat_number_meta',
236
+ 'default' => 'no',
237
+ 'type' => 'checkbox',
238
+ ),
239
  array(
240
  'type' => 'sectionend',
241
  'id' => 'wcj_eu_vat_number_advanced_options',
includes/settings/wcj-settings-multicurrency.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Settings - Multicurrency (Currency Switcher)
4
  *
5
- * @version 4.5.0
6
  * @since 2.8.0
7
  * @author Algoritmika Ltd.
8
  * @todo "pretty prices"
@@ -146,6 +146,14 @@ $settings = array(
146
  'default' => 'no',
147
  'type' => 'checkbox',
148
  ),
 
 
 
 
 
 
 
 
149
  array(
150
  'type' => 'sectionend',
151
  'id' => 'wcj_multicurrency_compatibility',
@@ -179,6 +187,14 @@ $settings = array(
179
  'default' => 'no',
180
  'type' => 'checkbox',
181
  ),
 
 
 
 
 
 
 
 
182
  array(
183
  'type' => 'sectionend',
184
  'id' => 'wcj_multicurrency_options_adv',
2
  /**
3
  * Booster for WooCommerce - Settings - Multicurrency (Currency Switcher)
4
  *
5
+ * @version 4.6.0
6
  * @since 2.8.0
7
  * @author Algoritmika Ltd.
8
  * @todo "pretty prices"
146
  'default' => 'no',
147
  'type' => 'checkbox',
148
  ),
149
+ array(
150
+ 'title' => __( 'WPC Product Bundles', 'woocommerce-jetpack' ),
151
+ 'desc' => __( 'Enable', 'woocommerce-jetpack' ),
152
+ 'desc_tip' => sprintf( __( 'Adds compatibility with <a href="%s" target="_blank">WPC Product Bundles</a> plugin', 'woocommerce-jetpack' ), 'https://wordpress.org/plugins/woo-product-bundle/' ),
153
+ 'id' => 'wcj_multicurrency_compatibility_wpc_product_bundle',
154
+ 'default' => 'no',
155
+ 'type' => 'checkbox',
156
+ ),
157
  array(
158
  'type' => 'sectionend',
159
  'id' => 'wcj_multicurrency_compatibility',
187
  'default' => 'no',
188
  'type' => 'checkbox',
189
  ),
190
+ array(
191
+ 'title' => __( 'Save Calculated Products Prices', 'woocommerce-jetpack' ),
192
+ 'desc_tip' => __( 'This may help if you are experiencing compatibility issues with other plugins.', 'woocommerce-jetpack' ),
193
+ 'desc' => __( 'Enable', 'woocommerce-jetpack' ),
194
+ 'id' => 'wcj_multicurrency_multicurrency_save_prices',
195
+ 'default' => 'no',
196
+ 'type' => 'checkbox',
197
+ ),
198
  array(
199
  'type' => 'sectionend',
200
  'id' => 'wcj_multicurrency_options_adv',
includes/settings/wcj-settings-price-by-user-role.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Settings - Price based on User Role
4
  *
5
- * @version 3.8.0
6
  * @since 2.8.0
7
  * @author Algoritmika Ltd.
8
  */
@@ -66,24 +66,68 @@ $settings = array(
66
  'type' => 'checkbox',
67
  ),
68
  array(
69
- 'title' => __( 'Advanced: Price Filters Priority', 'woocommerce-jetpack' ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  'desc_tip' => __( 'Priority for all module\'s price filters. Set to zero to use default priority.' ),
71
  'id' => 'wcj_price_by_user_role_advanced_price_hooks_priority',
72
  'default' => 0,
73
  'type' => 'number',
74
  ),
75
  array(
76
- 'title' => __( 'Advanced: Price Changes', 'woocommerce-jetpack' ),
77
  'desc' => __( 'Disable price based on user role for products with "Price Changes"', 'woocommerce-jetpack' ),
78
  'desc_tip' => __( 'Try enabling this checkbox, if you are having compatibility issues with other plugins.', 'woocommerce-jetpack' ),
79
  'id' => 'wcj_price_by_user_role_check_for_product_changes_price',
80
  'default' => 'no',
81
  'type' => 'checkbox',
82
  ),
 
 
 
 
 
 
 
 
83
  array(
84
  'type' => 'sectionend',
85
- 'id' => 'wcj_price_by_user_role_options',
86
  ),
 
 
87
  array(
88
  'title' => __( 'Roles & Multipliers', 'woocommerce-jetpack' ),
89
  'type' => 'title',
@@ -136,6 +180,8 @@ $taxonomies = array(
136
  'option_id' => 'tag',
137
  ),
138
  );
 
 
139
  foreach ( $taxonomies as $taxonomy ) {
140
  $product_taxonomies_options = array();
141
  $product_taxonomies = get_terms( $taxonomy['id'], 'orderby=name&hide_empty=0' );
@@ -192,4 +238,6 @@ foreach ( $taxonomies as $taxonomy ) {
192
  ),
193
  ) );
194
  }
 
 
195
  return $settings;
2
  /**
3
  * Booster for WooCommerce - Settings - Price based on User Role
4
  *
5
+ * @version 4.6.0
6
  * @since 2.8.0
7
  * @author Algoritmika Ltd.
8
  */
66
  'type' => 'checkbox',
67
  ),
68
  array(
69
+ 'title' => __( 'Show Empty Price Variations', 'woocommerce-jetpack' ),
70
+ 'desc' => __( 'Show', 'woocommerce-jetpack' ),
71
+ 'desc_tip' => __( 'Show "empty price" variations. This will also hide out of stock messages.', 'woocommerce-jetpack' ),
72
+ 'id' => 'wcj_price_by_user_role_show_empty_price_variations',
73
+ 'default' => 'no',
74
+ 'type' => 'checkbox',
75
+ ),
76
+ array(
77
+ 'title' => __( 'Remove Empty Price Variation Callback', 'woocommerce-jetpack' ),
78
+ 'desc' => __( 'Remove', 'woocommerce-jetpack' ),
79
+ 'desc_tip' => __( 'Remove "woocommerce_single_variation" callback from "woocommerce_single_variation" hook on "empty price" variations.', 'woocommerce-jetpack' ),
80
+ 'id' => 'wcj_price_by_user_role_remove_single_variation',
81
+ 'default' => 'no',
82
+ 'type' => 'checkbox',
83
+ ),
84
+ array(
85
+ 'title' => __( 'Remove Empty Price Add to Cart Button Callback', 'woocommerce-jetpack' ),
86
+ 'desc' => __( 'Remove', 'woocommerce-jetpack' ),
87
+ 'desc_tip' => __( 'Remove "woocommerce_single_variation_add_to_cart_button" callback from "woocommerce_single_variation" hook on "empty price" variations.', 'woocommerce-jetpack' ),
88
+ 'id' => 'wcj_price_by_user_role_remove_add_to_cart_btn',
89
+ 'default' => 'no',
90
+ 'type' => 'checkbox',
91
+ ),
92
+ array(
93
+ 'type' => 'sectionend',
94
+ 'id' => 'wcj_price_by_user_role_options',
95
+ ),
96
+
97
+ array(
98
+ 'title' => __( 'Advanced', 'woocommerce-jetpack' ),
99
+ 'type' => 'title',
100
+ 'id' => 'wcj_price_by_user_role_options_adv',
101
+ ),
102
+ array(
103
+ 'title' => __( 'Price Filters Priority', 'woocommerce-jetpack' ),
104
  'desc_tip' => __( 'Priority for all module\'s price filters. Set to zero to use default priority.' ),
105
  'id' => 'wcj_price_by_user_role_advanced_price_hooks_priority',
106
  'default' => 0,
107
  'type' => 'number',
108
  ),
109
  array(
110
+ 'title' => __( 'Price Changes', 'woocommerce-jetpack' ),
111
  'desc' => __( 'Disable price based on user role for products with "Price Changes"', 'woocommerce-jetpack' ),
112
  'desc_tip' => __( 'Try enabling this checkbox, if you are having compatibility issues with other plugins.', 'woocommerce-jetpack' ),
113
  'id' => 'wcj_price_by_user_role_check_for_product_changes_price',
114
  'default' => 'no',
115
  'type' => 'checkbox',
116
  ),
117
+ array(
118
+ 'title' => __( 'WPML: Get Terms in All Languages', 'woocommerce-jetpack' ),
119
+ 'desc' => __( 'Enable', 'woocommerce-jetpack' ),
120
+ 'desc_tip' => __( 'Get tags and taxonomies in all languages', 'woocommerce-jetpack' ),
121
+ 'id' => 'wcj_price_by_user_role_wpml_get_terms_all_lang',
122
+ 'default' => 'no',
123
+ 'type' => 'checkbox',
124
+ ),
125
  array(
126
  'type' => 'sectionend',
127
+ 'id' => 'wcj_price_by_user_role_options_adv',
128
  ),
129
+
130
+
131
  array(
132
  'title' => __( 'Roles & Multipliers', 'woocommerce-jetpack' ),
133
  'type' => 'title',
180
  'option_id' => 'tag',
181
  ),
182
  );
183
+
184
+ do_action('wcj_before_get_terms');
185
  foreach ( $taxonomies as $taxonomy ) {
186
  $product_taxonomies_options = array();
187
  $product_taxonomies = get_terms( $taxonomy['id'], 'orderby=name&hide_empty=0' );
238
  ),
239
  ) );
240
  }
241
+ do_action('wcj_after_get_terms');
242
+
243
  return $settings;
includes/settings/wcj-settings-product-addons.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Settings - Product Addons
4
  *
5
- * @version 4.5.0
6
  * @since 2.8.0
7
  * @author Algoritmika Ltd.
8
  * @todo (maybe) add `woocommerce_payment_complete` to `$qty_triggers` (also maybe add this trigger to "PDF Invoicing" module)
@@ -104,7 +104,7 @@ for ( $i = 1; $i <= $total_number; $i++ ) {
104
  ),
105
  array(
106
  'desc' => __( 'Price(s)', 'woocommerce-jetpack' ),
107
- 'desc_tip' => __( 'For radio and select enter one value per line.', 'woocommerce-jetpack' ),
108
  'id' => 'wcj_product_addons_all_products_price_' . $i,
109
  'default' => 0,
110
  'type' => 'textarea',
@@ -269,6 +269,14 @@ $settings = array_merge( $settings, array(
269
  'type' => 'title',
270
  'id' => 'wcj_product_addons_template_options',
271
  ),
 
 
 
 
 
 
 
 
272
  array(
273
  'title' => __( 'Each Addon - Title', 'woocommerce-jetpack' ),
274
  'desc' => wcj_message_replaced_values( array( '%addon_id%', '%addon_title%' ) ),
2
  /**
3
  * Booster for WooCommerce - Settings - Product Addons
4
  *
5
+ * @version 4.6.0
6
  * @since 2.8.0
7
  * @author Algoritmika Ltd.
8
  * @todo (maybe) add `woocommerce_payment_complete` to `$qty_triggers` (also maybe add this trigger to "PDF Invoicing" module)
104
  ),
105
  array(
106
  'desc' => __( 'Price(s)', 'woocommerce-jetpack' ),
107
+ 'desc_tip' => __( 'For radio and select enter one value per line.', 'woocommerce-jetpack' ) . '<br /><br />' . __( "You can use the % symbol to set a percentage of product's price, like 10%", 'woocommerce-jetpack' ),
108
  'id' => 'wcj_product_addons_all_products_price_' . $i,
109
  'default' => 0,
110
  'type' => 'textarea',
269
  'type' => 'title',
270
  'id' => 'wcj_product_addons_template_options',
271
  ),
272
+ array(
273
+ 'title' => __( 'Hide Percentage Price', 'woocommerce-jetpack' ),
274
+ 'desc' => __( 'Hide', 'woocommerce-jetpack' ),
275
+ 'desc_tip' => __( 'Hide percentage price when % is set on prices', 'woocommerce-jetpack' ),
276
+ 'id' => 'wcj_product_addons_template_hide_percentage_price',
277
+ 'default' => 'yes',
278
+ 'type' => 'checkbox',
279
+ ),
280
  array(
281
  'title' => __( 'Each Addon - Title', 'woocommerce-jetpack' ),
282
  'desc' => wcj_message_replaced_values( array( '%addon_id%', '%addon_title%' ) ),
includes/settings/wcj-settings-product-open-pricing.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Settings - Product Open Pricing
4
  *
5
- * @version 4.4.0
6
  * @since 2.8.0
7
  * @author Algoritmika Ltd.
8
  * @todo set default for "wcj_product_open_price_enable_js_validation" to "yes"
@@ -151,9 +151,34 @@ return array(
151
  'default' => 'no',
152
  'type' => 'checkbox',
153
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  array(
155
  'title' => __( 'Product Bundles', 'woocommerce-jetpack' ),
156
- 'desc_tip' => __( 'For "WPC Product Bundles for WooCommerce" plugin.', 'woocommerce-jetpack' ),
157
  'id' => 'wcj_product_open_price_woosb_product_bundles_divide',
158
  'default' => 'no',
159
  'type' => 'select',
@@ -165,6 +190,6 @@ return array(
165
  ),
166
  array(
167
  'type' => 'sectionend',
168
- 'id' => 'wcj_product_open_price_options',
169
  ),
170
  );
2
  /**
3
  * Booster for WooCommerce - Settings - Product Open Pricing
4
  *
5
+ * @version 4.6.0
6
  * @since 2.8.0
7
  * @author Algoritmika Ltd.
8
  * @todo set default for "wcj_product_open_price_enable_js_validation" to "yes"
151
  'default' => 'no',
152
  'type' => 'checkbox',
153
  ),
154
+ array(
155
+ 'type' => 'sectionend',
156
+ 'id' => 'wcj_product_open_price_options',
157
+ ),
158
+ array(
159
+ 'title' => __( 'Product Bundles', 'woocommerce-jetpack' ),
160
+ 'desc' => sprintf( __( 'For <a href="%s">"WPC Product Bundles for WooCommerce"</a> plugin.', 'woocommerce-jetpack' ), 'https://wordpress.org/plugins/woo-product-bundle/' ),
161
+ 'type' => 'title',
162
+ 'id' => 'wcj_product_open_price_woosb_product_bundles',
163
+ ),
164
+ array(
165
+ 'title' => __( 'Remove "add to cart" hook', 'woocommerce-jetpack' ),
166
+ 'desc' => __( 'Remove', 'woocommerce-jetpack' ),
167
+ 'desc_tip' => __( 'Try to remove "add to cart" hook from Product Bundles', 'woocommerce-jetpack' ),
168
+ 'id' => 'wcj_product_open_price_woosb_product_bundles_remove_atc',
169
+ 'default' => 'no',
170
+ 'type' => 'checkbox',
171
+ ),
172
+ array(
173
+ 'title' => __( 'Replace Prices', 'woocommerce-jetpack' ),
174
+ 'desc' => __( 'Replace', 'woocommerce-jetpack' ),
175
+ 'desc_tip' => __( 'Try to replace Product Bundles prices', 'woocommerce-jetpack' ),
176
+ 'id' => 'wcj_product_open_price_woosb_product_bundles_replace_prices',
177
+ 'default' => 'no',
178
+ 'type' => 'checkbox',
179
+ ),
180
  array(
181
  'title' => __( 'Product Bundles', 'woocommerce-jetpack' ),
 
182
  'id' => 'wcj_product_open_price_woosb_product_bundles_divide',
183
  'default' => 'no',
184
  'type' => 'select',
190
  ),
191
  array(
192
  'type' => 'sectionend',
193
+ 'id' => 'wcj_product_open_price_woosb_product_bundles',
194
  ),
195
  );
includes/shipping/class-wc-shipping-wcj-custom-with-shipping-zones.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Shipping - Custom Shipping with Shipping Zones
4
  *
5
- * @version 4.5.0
6
  * @since 2.5.6
7
  * @author Algoritmika Ltd.
8
  */
@@ -31,7 +31,7 @@ if ( ! class_exists( 'WC_Shipping_WCJ_Custom_W_Zones' ) ) :
31
  /**
32
  * Init settings
33
  *
34
- * @version 4.5.0
35
  * @since 2.5.6
36
  * @access public
37
  * @return void
@@ -58,6 +58,7 @@ if ( ! class_exists( 'WC_Shipping_WCJ_Custom_W_Zones' ) ) :
58
  $this->max_weight = $this->get_option( 'max_weight' );
59
  $this->type = $this->get_option( 'type' );
60
  $this->apply_formula = apply_filters( 'booster_option', 'no', $this->get_option( 'apply_formula' ) );
 
61
  $this->weight_table_total_rows = $this->get_option( 'weight_table_total_rows' );
62
 
63
  // Save settings in admin
@@ -80,7 +81,7 @@ if ( ! class_exists( 'WC_Shipping_WCJ_Custom_W_Zones' ) ) :
80
  /**
81
  * sanitize_settings.
82
  *
83
- * @version 4.5.0
84
  * @since 4.5.0
85
  *
86
  * @param $settings
@@ -89,7 +90,21 @@ if ( ! class_exists( 'WC_Shipping_WCJ_Custom_W_Zones' ) ) :
89
  * @return
90
  */
91
  function sanitize_settings( $settings, $shipping_method ) {
92
- $settings = array_filter( $settings );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
93
  return $settings;
94
  }
95
 
@@ -158,7 +173,7 @@ if ( ! class_exists( 'WC_Shipping_WCJ_Custom_W_Zones' ) ) :
158
  /**
159
  * Initialise Settings Form Fields
160
  *
161
- * @version 3.9.0
162
  * @since 2.5.6
163
  */
164
  function init_instance_form_fields() {
@@ -198,6 +213,19 @@ if ( ! class_exists( 'WC_Shipping_WCJ_Custom_W_Zones' ) ) :
198
  'desc_tip' => true,
199
  'css' => 'width:100%',
200
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
201
  'min_weight' => array(
202
  'title' => __( 'Min Weight', 'woocommerce' ),
203
  'type' => 'number',
@@ -305,7 +333,7 @@ if ( ! class_exists( 'WC_Shipping_WCJ_Custom_W_Zones' ) ) :
305
  /**
306
  * calculate_shipping function.
307
  *
308
- * @version 3.4.0
309
  * @since 2.5.6
310
  * @access public
311
  * @param mixed $package
@@ -335,6 +363,20 @@ if ( ! class_exists( 'WC_Shipping_WCJ_Custom_W_Zones' ) ) :
335
  'cost' => $this->maybe_apply_formula( $cost ),
336
  'calc_tax' => 'per_order',
337
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
  // Register the rate
339
  $this->add_rate( $rate );
340
  }
2
  /**
3
  * Booster for WooCommerce - Shipping - Custom Shipping with Shipping Zones
4
  *
5
+ * @version 4.6.0
6
  * @since 2.5.6
7
  * @author Algoritmika Ltd.
8
  */
31
  /**
32
  * Init settings
33
  *
34
+ * @version 4.6.0
35
  * @since 2.5.6
36
  * @access public
37
  * @return void
58
  $this->max_weight = $this->get_option( 'max_weight' );
59
  $this->type = $this->get_option( 'type' );
60
  $this->apply_formula = apply_filters( 'booster_option', 'no', $this->get_option( 'apply_formula' ) );
61
+ $this->cost_rounding = apply_filters( 'booster_option', 'no_round', $this->get_option( 'cost_rounding', 'no_round' ) );
62
  $this->weight_table_total_rows = $this->get_option( 'weight_table_total_rows' );
63
 
64
  // Save settings in admin
81
  /**
82
  * sanitize_settings.
83
  *
84
+ * @version 4.6.0
85
  * @since 4.5.0
86
  *
87
  * @param $settings
90
  * @return
91
  */
92
  function sanitize_settings( $settings, $shipping_method ) {
93
+ $settings = array_filter( $settings );
94
+ $total_rows = $settings['weight_table_total_rows'];
95
+ $keys_to_remove = array();
96
+ foreach ( $settings as $key => $value ) {
97
+ if (
98
+ preg_match( '/(?<=row_)\d+/m', $key, $results ) &&
99
+ $results[0] > $total_rows
100
+ ) {
101
+ $keys_to_remove[] = $key;
102
+ }
103
+ }
104
+ // Remove keys greater then 'total rows' amount
105
+ if ( count( $keys_to_remove ) > 0 ) {
106
+ $settings = array_diff_key( $settings, array_flip( $keys_to_remove ) );
107
+ }
108
  return $settings;
109
  }
110
 
173
  /**
174
  * Initialise Settings Form Fields
175
  *
176
+ * @version 4.6.0
177
  * @since 2.5.6
178
  */
179
  function init_instance_form_fields() {
213
  'desc_tip' => true,
214
  'css' => 'width:100%',
215
  ),
216
+ 'cost_rounding' => array(
217
+ 'title' => __( 'Cost Rounding', 'woocommerce-jetpack' ),
218
+ 'desc_tip' => __( 'How the final cost will be rounded.', 'woocommerce-jetpack' ),
219
+ 'css' => 'width:100%',
220
+ 'default' => 'no_round',
221
+ 'type' => 'select',
222
+ 'options' => array(
223
+ 'no_round' => __( 'No rounding', 'woocommerce-jetpack' ),
224
+ 'round' => __( 'Round', 'woocommerce-jetpack' ),
225
+ 'round_up' => __( 'Round up', 'woocommerce-jetpack' ),
226
+ 'round_down' => __( 'Round down', 'woocommerce-jetpack' ),
227
+ ),
228
+ ),
229
  'min_weight' => array(
230
  'title' => __( 'Min Weight', 'woocommerce' ),
231
  'type' => 'number',
333
  /**
334
  * calculate_shipping function.
335
  *
336
+ * @version 4.6.0
337
  * @since 2.5.6
338
  * @access public
339
  * @param mixed $package
363
  'cost' => $this->maybe_apply_formula( $cost ),
364
  'calc_tax' => 'per_order',
365
  );
366
+
367
+ // Rounding
368
+ switch ( $this->cost_rounding ) {
369
+ case 'round':
370
+ $rate['cost'] = round( $rate['cost'], wc_get_rounding_precision() );
371
+ break;
372
+ case 'round_up':
373
+ $rate['cost'] = ceil( $rate['cost'] );
374
+ break;
375
+ case 'round_down':
376
+ $rate['cost'] = floor( $rate['cost'] );
377
+ break;
378
+ }
379
+
380
  // Register the rate
381
  $this->add_rate( $rate );
382
  }
includes/shortcodes/class-wcj-shortcodes-orders.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Shortcodes - Orders
4
  *
5
- * @version 4.5.0
6
  * @author Algoritmika Ltd.
7
  */
8
 
@@ -98,7 +98,7 @@ class WCJ_Orders_Shortcodes extends WCJ_Shortcodes {
98
  /**
99
  * add_extra_atts.
100
  *
101
- * @version 4.1.0
102
  */
103
  function add_extra_atts( $atts ) {
104
  $modified_atts = array_merge( array(
@@ -142,6 +142,7 @@ class WCJ_Orders_Shortcodes extends WCJ_Shortcodes {
142
  'color' => 'black',
143
  'currency' => '',
144
  'doc_type' => 'invoice',
 
145
  ), $atts );
146
 
147
  return $modified_atts;
@@ -1163,17 +1164,19 @@ class WCJ_Orders_Shortcodes extends WCJ_Shortcodes {
1163
  /**
1164
  * wcj_order_taxes_html.
1165
  *
1166
- * @version 2.5.3
1167
  * @since 2.5.3
1168
  */
1169
  function wcj_order_taxes_html( $atts ) {
1170
  $order_taxes = $this->the_order->get_taxes();
1171
- $taxes_html = '';
1172
  foreach ( $order_taxes as $order_tax ) {
1173
- $taxes_html .= ( isset( $order_tax['label'] ) ) ? $order_tax['label'] . ': ' : '';
1174
- $amount = 0;
1175
- $amount += ( isset( $order_tax['tax_amount'] ) ) ? $order_tax['tax_amount'] : 0;
1176
- $amount += ( isset( $order_tax['shipping_tax_amount'] ) ) ? $order_tax['shipping_tax_amount'] : 0;
 
 
1177
  $taxes_html .= $this->wcj_price_shortcode( $amount, $atts ) . '<br>';
1178
  }
1179
  return $taxes_html;
2
  /**
3
  * Booster for WooCommerce - Shortcodes - Orders
4
  *
5
+ * @version 4.6.0
6
  * @author Algoritmika Ltd.
7
  */
8
 
98
  /**
99
  * add_extra_atts.
100
  *
101
+ * @version 4.6.0
102
  */
103
  function add_extra_atts( $atts ) {
104
  $modified_atts = array_merge( array(
142
  'color' => 'black',
143
  'currency' => '',
144
  'doc_type' => 'invoice',
145
+ 'show_label' => true
146
  ), $atts );
147
 
148
  return $modified_atts;
1164
  /**
1165
  * wcj_order_taxes_html.
1166
  *
1167
+ * @version 4.6.0
1168
  * @since 2.5.3
1169
  */
1170
  function wcj_order_taxes_html( $atts ) {
1171
  $order_taxes = $this->the_order->get_taxes();
1172
+ $taxes_html = '';
1173
  foreach ( $order_taxes as $order_tax ) {
1174
+ if ( true === filter_var( $atts['show_label'], FILTER_VALIDATE_BOOLEAN ) ) {
1175
+ $taxes_html .= ( isset( $order_tax['label'] ) ) ? $order_tax['label'] . ': ' : '';
1176
+ }
1177
+ $amount = 0;
1178
+ $amount += ( isset( $order_tax['tax_amount'] ) ) ? $order_tax['tax_amount'] : 0;
1179
+ $amount += ( isset( $order_tax['shipping_tax_amount'] ) ) ? $order_tax['shipping_tax_amount'] : 0;
1180
  $taxes_html .= $this->wcj_price_shortcode( $amount, $atts ) . '<br>';
1181
  }
1182
  return $taxes_html;
includes/shortcodes/class-wcj-shortcodes-products.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Booster for WooCommerce - Shortcodes - Products
4
  *
5
- * @version 4.2.0
6
  * @author Algoritmika Ltd.
7
  */
8
 
@@ -857,7 +857,7 @@ class WCJ_Products_Shortcodes extends WCJ_Shortcodes {
857
  /**
858
  * wcj_product_wholesale_price_table.
859
  *
860
- * @version 4.0.0
861
  * @todo (maybe) `if ( 'yes' === $atts['add_percent_row'] )` for 'fixed' or 'price_directly'; `if ( 'yes' === $atts['add_discount_row'] )` for 'percent' or 'price_directly'
862
  */
863
  function wcj_product_wholesale_price_table( $atts ) {
@@ -887,16 +887,18 @@ class WCJ_Products_Shortcodes extends WCJ_Shortcodes {
887
 
888
  $wholesale_price_levels = array();
889
  if ( wcj_is_product_wholesale_enabled_per_product( $product_id ) ) {
890
- for ( $i = 1; $i <= apply_filters( 'booster_option', 1, get_post_meta( $product_id, '_' . 'wcj_wholesale_price_levels_number' . $role_option_name_addon, true ) ); $i++ ) {
891
  $level_qty = get_post_meta( $product_id, '_' . 'wcj_wholesale_price_level_min_qty' . $role_option_name_addon . '_' . $i, true );
892
  $discount = get_post_meta( $product_id, '_' . 'wcj_wholesale_price_level_discount' . $role_option_name_addon . '_' . $i, true );
893
- $wholesale_price_levels[] = array( 'quantity' => $level_qty, 'discount' => $discount, );
 
894
  }
895
  } else {
896
  for ( $i = 1; $i <= apply_filters( 'booster_option', 1, get_option( 'wcj_wholesale_price_levels_number' . $role_option_name_addon, 1 ) ); $i++ ) {
897
  $level_qty = get_option( 'wcj_wholesale_price_level_min_qty' . $role_option_name_addon . '_' . $i, PHP_INT_MAX );
898
  $discount = get_option( 'wcj_wholesale_price_level_discount_percent' . $role_option_name_addon . '_' . $i, 0 );
899
- $wholesale_price_levels[] = array( 'quantity' => $level_qty, 'discount' => $discount, );
 
900
  }
901
  }
902
 
@@ -949,6 +951,7 @@ class WCJ_Products_Shortcodes extends WCJ_Shortcodes {
949
  // Simple etc.
950
  $the_price = wcj_get_product_display_price( $this->the_product );
951
  $the_price = apply_filters( 'wcj_product_wholesale_price_table_price_before', $the_price, $this->the_product );
 
952
  $the_price_original = $the_price;
953
  if ( 'price_directly' === $discount_type ) {
954
  $the_price = $wholesale_price_level['discount'];
2
  /**
3
  * Booster for WooCommerce - Shortcodes - Products
4
  *
5
+ * @version 4.6.0
6
  * @author Algoritmika Ltd.
7
  */
8
 
857
  /**
858
  * wcj_product_wholesale_price_table.
859
  *
860
+ * @version 4.6.0
861
  * @todo (maybe) `if ( 'yes' === $atts['add_percent_row'] )` for 'fixed' or 'price_directly'; `if ( 'yes' === $atts['add_discount_row'] )` for 'percent' or 'price_directly'
862
  */
863
  function wcj_product_wholesale_price_table( $atts ) {
887
 
888
  $wholesale_price_levels = array();
889
  if ( wcj_is_product_wholesale_enabled_per_product( $product_id ) ) {
890
+ for ( $i = 1; $i <= apply_filters( 'booster_option', 1, get_post_meta( $product_id, '_' . 'wcj_wholesale_price_levels_number' . $role_option_name_addon, true ) ); $i ++ ) {
891
  $level_qty = get_post_meta( $product_id, '_' . 'wcj_wholesale_price_level_min_qty' . $role_option_name_addon . '_' . $i, true );
892
  $discount = get_post_meta( $product_id, '_' . 'wcj_wholesale_price_level_discount' . $role_option_name_addon . '_' . $i, true );
893
+ $discount = ! empty( $discount ) ? $discount : 0;
894
+ $wholesale_price_levels[] = array( 'quantity' => $level_qty, 'discount' => (float) $discount, );
895
  }
896
  } else {
897
  for ( $i = 1; $i <= apply_filters( 'booster_option', 1, get_option( 'wcj_wholesale_price_levels_number' . $role_option_name_addon, 1 ) ); $i++ ) {
898
  $level_qty = get_option( 'wcj_wholesale_price_level_min_qty' . $role_option_name_addon . '_' . $i, PHP_INT_MAX );
899
  $discount = get_option( 'wcj_wholesale_price_level_discount_percent' . $role_option_name_addon . '_' . $i, 0 );
900
+ $discount = ! empty( $discount ) ? $discount : 0;
901
+ $wholesale_price_levels[] = array( 'quantity' => $level_qty, 'discount' => (float) $discount, );
902
  }
903
  }
904
 
951
  // Simple etc.
952
  $the_price = wcj_get_product_display_price( $this->the_product );
953
  $the_price = apply_filters( 'wcj_product_wholesale_price_table_price_before', $the_price, $this->the_product );
954
+ $the_price = empty( $the_price ) ? 0 : $the_price;
955
  $the_price_original = $the_price;
956
  if ( 'price_directly' === $discount_type ) {
957
  $the_price = $wholesale_price_level['discount'];
includes/templates/wcj-radio-for-variations.php CHANGED
@@ -2,7 +2,7 @@
2
  /**
3
  * Variable product add to cart - radio inputs
4
  *
5
- * @version 4.3.0
6
  * @since 2.4.8
7
  * @author Algoritmika Ltd.
8
  */
@@ -13,24 +13,24 @@ $attribute_keys = array_keys( $attributes );
13
  ?>
14
 
15
  <?php if ( empty( $available_variations ) && false !== $available_variations ) : ?>
16
- <p class="stock out-of-stock"><?php esc_html_e( 'This product is currently out of stock and unavailable.', 'woocommerce' ); ?></p>
17
  <?php else : ?>
18
- <table class="" cellspacing="0">
19
- <tbody>
20
- <tr>
21
- <th colspan="2">
22
- <?php $attribute_labels = array_map( 'wc_attribute_label', array_keys( $attributes ) ); ?>
23
- <?php echo implode( ' X ', $attribute_labels ); ?>
24
- <?php echo wp_kses_post( apply_filters( 'woocommerce_reset_variations_link', '<a style="float:right" class="reset_variations" href="#">' . esc_html__( 'Clear', 'woocommerce' ) . '</a>' ) ); ?>
25
- </th>
26
- </tr>
27
  <?php foreach ( $available_variations as $variation ) : ?>
28
- <tr>
29
  <?php wcj_variation_radio_button( $product, $variation ); ?>
30
- </tr>
31
  <?php endforeach; ?>
32
- </tbody>
33
- </table>
34
  <?php
35
  foreach ( $product->get_attributes() as $attribute_name => $options ) {
36
  echo '<input type="hidden" name="attribute_' . $attribute_name . '" value="">';
2
  /**
3
  * Variable product add to cart - radio inputs
4
  *
5
+ * @version 4.6.0
6
  * @since 2.4.8
7
  * @author Algoritmika Ltd.
8
  */
13
  ?>
14
 
15
  <?php if ( empty( $available_variations ) && false !== $available_variations ) : ?>
16
+ <p class="stock out-of-stock"><?php esc_html_e( 'This product is currently out of stock and unavailable.', 'woocommerce' ); ?></p>
17
  <?php else : ?>
18
+ <table class="" cellspacing="0">
19
+ <tbody>
20
+ <tr>
21
+ <th colspan="2">
22
+ <?php $attribute_labels = array_map( 'wc_attribute_label', array_keys( $attributes ) ); ?>
23
+ <?php echo implode( ' X ', $attribute_labels ); ?>
24
+ <?php echo wp_kses_post( apply_filters( 'woocommerce_reset_variations_link', '<a style="float:right" class="reset_variations" href="#">' . esc_html__( 'Clear', 'woocommerce' ) . '</a>' ) ); ?>
25
+ </th>
26
+ </tr>
27
  <?php foreach ( $available_variations as $variation ) : ?>
28
+ <tr>
29
  <?php wcj_variation_radio_button( $product, $variation ); ?>
30
+ </tr>
31
  <?php endforeach; ?>
32
+ </tbody>
33
+ </table>
34
  <?php
35
  foreach ( $product->get_attributes() as $attribute_name => $options ) {
36
  echo '<input type="hidden" name="attribute_' . $attribute_name . '" value="">';
readme.txt CHANGED
@@ -3,7 +3,7 @@ Contributors: algoritmika, anbinder, debugeris, karzin
3
  Tags: woocommerce, booster for woocommerce, woocommerce jetpack
4
  Requires at least: 4.4
5
  Tested up to: 5.2
6
- Stable tag: 4.5.1
7
  License: GNU General Public License v3.0
8
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
9
 
@@ -193,6 +193,37 @@ You can see the differences between versions in this [table](https://booster.io/
193
 
194
  == Changelog ==
195
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  = 4.5.1 - 18/09/2019 =
197
  * Fix - PRODUCTS - Product Input Fields - Improve 'Line Break' style on frontend.
198
  * Fix - CART & CHECKOUT - Checkout Custom Fields - Fix 'Call to a member function get_cart() on null' on order status update.
3
  Tags: woocommerce, booster for woocommerce, woocommerce jetpack
4
  Requires at least: 4.4
5
  Tested up to: 5.2
6
+ Stable tag: 4.6.0
7
  License: GNU General Public License v3.0
8
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
9
 
193
 
194
  == Changelog ==
195
 
196
+ = 4.6.0 - 29/10/2019 =
197
+ * Fix indentation by replacing spaces by tabs on SQL, comments, or when there is pure HTML, <script> or <style> tags.
198
+ * Fix - CART & CHECKOUT - EU VAT Number - Fix `wcj_validate_vat_no_soap()` function with correct api_url.
199
+ * Fix - EMAILS & MISC. - Custom Emails - Fix fatal error when trying to send custom emails using 'Created Customer Notification' trigger.
200
+ * Fix - PRICES & CURRENCIES - Multicurrency (Currency Switcher) - Replace cast by +0 technique on some queries to get the whole price.
201
+ * Fix - PRICES & CURRENCIES - Product Open Pricing (Name Your Price) - Fix wrong price on loop.
202
+ * Fix - Shortcodes - Products - `[wcj_product_wholesale_price_table]` Fix "A non-numeric value encountered" PHP warning.
203
+ * Dev - CART & CHECKOUT - Checkout Customization - Add option to ignore restriction on admin.
204
+ * Dev - CART & CHECKOUT - Checkout Customization - Add option to restrict countries based on a manual order from the "YITH WooCommerce Request a Quote" plugin having the billing country selected manually.
205
+ * Dev - CART & CHECKOUT - Checkout Customization - Add option to restrict countries based on Customer's Billing Country, ignoring other restrictions.
206
+ * Dev - CART & CHECKOUT - Checkout Customization - Add option to restrict countries based on conditions, like `is_cart()` and `is_checkout()`.
207
+ * Dev - CART & CHECKOUT - EU VAT Number - Add option to read meta from '_vat_number' meta trying to add compatibility with the plugin EU VAT Number.
208
+ * Dev - CART & CHECKOUT - EU VAT Number - Add option to exempt VAT on cart page.
209
+ * Dev - CART & CHECKOUT - EU VAT Number - Add option to exempt VAT by checking previously registered EU VAT numbers from customers.
210
+ * Dev - CART & CHECKOUT - EU VAT Number - Improve `wcj_validate_eu_vat_number()` function by adding `wcj_eu_vat_number_to_check` and `echo` parameters.
211
+ * Dev - Functions - General - Add functions to remove filters and actions added with callbacks to classes without access.
212
+ * Dev - PAYMENT GATEWAYS - Gateways per Product or Category - Allow looking for products in the order too besides cart, making it work even on My Account > Checkout > order-pay pages.
213
+ * Dev - PRICES & CURRENCIES - Multicurrency (Currency Switcher) - "Advanced: Saved Calculated Products Prices" option added.
214
+ * Dev - PRICES & CURRENCIES - Multicurrency (Currency Switcher) - Add compatibility with "WPC Product Bundles for WooCommerce" plugin.
215
+ * Dev - PRICES & CURRENCIES - Price by User Role - Add option to show empty price variations.
216
+ * Dev - PRICES & CURRENCIES - Price by User Role - Add option to remove "woocommerce_single_variation_add_to_cart_button" callback on empty price variations.
217
+ * Dev - PRICES & CURRENCIES - Price by User Role - Add option to remove "woocommerce_single_variation" callback on empty price variations.
218
+ * Dev - PRICES & CURRENCIES - Price by User Role - Add option to get product tags and categories in all languages when using WPML.
219
+ * Dev - PRICES & CURRENCIES - Product Open Pricing (Name Your Price) - Add two new compatibility options with 'WPC Product Bundles for WooCommerce' plugin.
220
+ * Dev - PRODUCTS - Product Addons - Add option to use the % symbol to set a percentage of product's price, like 10%.
221
+ * Dev - PRODUCTS - Product Addons - Add option to hide the percentage price on frontend. Parenthesis will be removed in that case preventing from being displayed empty.
222
+ * Dev - PRODUCTS - Tax Display - Allow `tax_display_by_user_role()` to search on multiple roles.
223
+ * Dev - SHIPPING & ORDERS - Custom Shipping - Remove rules greater than the Total Rows amount.
224
+ * Dev - SHIPPING & ORDERS - Custom Shipping - Add 'Cost Rounding' option.
225
+ * Dev - Shortcodes - Orders - `[wcj_order_taxes_html]` - Add parameter `show_label`.
226
+
227
  = 4.5.1 - 18/09/2019 =
228
  * Fix - PRODUCTS - Product Input Fields - Improve 'Line Break' style on frontend.
229
  * Fix - CART & CHECKOUT - Checkout Custom Fields - Fix 'Call to a member function get_cart() on null' on order status update.
woocommerce-jetpack.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Booster for WooCommerce
4
  Plugin URI: https://booster.io
5
  Description: Supercharge your WooCommerce site with these awesome powerful features. More than 100 modules. All in one WooCommerce plugin.
6
- Version: 4.5.1
7
  Author: Algoritmika Ltd
8
  Author URI: https://booster.io
9
  Text Domain: woocommerce-jetpack
@@ -57,7 +57,7 @@ final class WC_Jetpack {
57
  * @var string
58
  * @since 2.4.7
59
  */
60
- public $version = '4.5.1';
61
 
62
  /**
63
  * @var WC_Jetpack The single instance of the class
3
  Plugin Name: Booster for WooCommerce
4
  Plugin URI: https://booster.io
5
  Description: Supercharge your WooCommerce site with these awesome powerful features. More than 100 modules. All in one WooCommerce plugin.
6
+ Version: 4.6.0
7
  Author: Algoritmika Ltd
8
  Author URI: https://booster.io
9
  Text Domain: woocommerce-jetpack
57
  * @var string
58
  * @since 2.4.7
59
  */
60
+ public $version = '4.6.0';
61
 
62
  /**
63
  * @var WC_Jetpack The single instance of the class