PluginProbe
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO / 1.4.17
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO v1.4.17
1.4.17 1.4.16 1.4.15 1.4.14 1.4.13 1.4.12 1.4.11 1.4.10 1.4.9 1.4.8 1.4.7 1.4.6 1.4.5 1.4.4 1.4.3 1.4.2 1.4.1 1.4.0 1.3.54 1.3.53 1.3.52 1.3.51 1.3.50 1.3.49 1.3.48 All 180 releases
← All changes | app/Disco.php +66 -49 1.4.11.4.17 View file →
@@ -10,8 +10,9 @@
10 10 namespace Disco\App;
11 11
12 12 use Disco\App\Calc\CalcFactory;
13 13 use Disco\App\Features\UserLimit;
14 +use Disco\App\Utility\Config;
14 15 use Disco\App\Utility\Settings;
15 16
16 17 /**
17 18 * Class Disco
@@ -71,18 +72,9 @@
71 72 * Get a discount limit form campaign.
72 73 *
73 74 * Compare with total product meta and apply discount
74 75 */
75 - $discount_limit = $intent->campaign->discount_max_user;
76 - $total_applied_campaign = ( new UserLimit )->disco_get_total_applied_campaign( $intent->campaign->id );
77 -
78 - if (
79 - ! empty( $discount_limit )
80 - && (
81 - $discount_limit >= 0
82 - && $total_applied_campaign >= $discount_limit
83 - )
84 - ) {
76 + if ( ( new UserLimit )->disco_is_limit_reached( $intent->campaign ) ) {
85 77 continue;
86 78 }
87 79
88 80 $discount = $intent->get_discounts( (float) $price, $product );
@@ -90,10 +82,30 @@
90 82 if ( empty( $discount ) ) {
91 83 continue;
92 84 }
93 85
94 - // Store discount keyed by campaign id so the winning amount maps back to the right campaign.
95 - $discounts[ $intent->campaign->id ] = $discount;
86 + /**
87 + * Normalize each campaign's amount before the amounts compete.
88 + *
89 + * A percent amount is derived from the product price, which a
90 + * currency switcher has already converted, so it arrives in the
91 + * active currency. A fixed amount is the rule value as typed, so
92 + * it arrives in the store's base currency. Comparing the two
93 + * directly picks a winner by exchange rate rather than by size:
94 + * percent scales with the rate and fixed does not, so `max` would
95 + * select percent on almost every product and `min` would select
96 + * fixed. Converting here, with each campaign's own discount type,
97 + * puts every amount in the same currency before min/max runs.
98 + *
99 + * Filtering per campaign also matches what CartIntent and the
100 + * Calc classes already do, so every path now compares like for
101 + * like.
102 + */
103 + $discounts[ $intent->campaign->id ] = (float) apply_filters(
104 + 'disco_final_discounted_amount',
105 + (float) $discount,
106 + $this->campaign_discount_type( $intent->campaign )
107 + );
96 108 }
97 109
98 110 // If no discount is applied, return the original price.
99 111 if ( empty( $discounts ) ) {
@@ -99,20 +111,12 @@
99 111 if ( empty( $discounts ) ) {
100 112 return $price;
101 113 }
102 114
103 - $discount_type = $intent->campaign->discount_rules[0]['discount_type'];
104 -
105 115 // Pick the winning discount amount, then map it back to the campaign that produced it.
106 - $base_discount = $this->min_max_average( array_values( $discounts ) );
107 - $this->applied_campaign_id = (int) array_search( $base_discount, $discounts, true );
116 + $discounted_amount = $this->min_max_average( array_values( $discounts ) );
117 + $this->applied_campaign_id = (int) array_search( $discounted_amount, $discounts, true );
108 118
109 - /**
110 - * Get the min or max discount amount according to plugin settings.
111 - * Apply the filter to modify final discounted amount based on discount types.
112 - */
113 - $discounted_amount = apply_filters( 'disco_final_discounted_amount', $base_discount, $discount_type );
114 -
115 119 if ( $discounted_amount <= $price ) {
116 120 // Get an applied campaign from DiscountLimit class.
117 121 ( new UserLimit )->disco_start_session_on_checkout( $this->applied_campaign_id );
118 122
@@ -149,18 +153,9 @@
149 153 * Get a discount limit form campaign.
150 154 *
151 155 * Compare with total product meta and apply discount
152 156 */
153 - $discount_limit = $intent->campaign->discount_max_user;
154 - $total_applied_campaign = ( new UserLimit )->disco_get_total_applied_campaign( $intent->campaign->id );
155 -
156 - if (
157 - ! empty( $discount_limit )
158 - && (
159 - $discount_limit >= 0
160 - && $total_applied_campaign >= $discount_limit
161 - )
162 - ) {
157 + if ( ( new UserLimit )->disco_is_limit_reached( $intent->campaign ) ) {
163 158 continue;
164 159 }
165 160
166 161 $items = $this->get_items_for_discount( $cart, $intent->campaign );
@@ -275,21 +270,21 @@
275 270 * @param \WC_Cart $cart WooCommerce cart object to calculate discounts for.
276 271 * @return array|false|\WC_Cart Cart object with applied discounts or false if no discounts are applicable.
277 272 */
278 273 public function get_cart_items_discount_for_bogo( $cart ) {//phpcs:ignore
279 - if ( ! $this->cart_is_valid() ) {
280 - return $cart;
281 - }
274 + if ( ! $this->cart_is_valid() ) {
275 + return $cart;
276 + }
282 277
283 - if ( ! defined( 'DOING_AJAX' ) && is_admin() ) {
284 - return $cart;
285 - }
278 + if ( ! defined( 'DOING_AJAX' ) && is_admin() ) {
279 + return $cart;
280 + }
286 281
287 - // Init Cart - Based Intents except Product & Shipping Intent.
288 - $this->intents = $this->prepare_intents( array( 'BOGO' ) );
282 + // Init Cart - Based Intents except Product & Shipping Intent.
283 + $this->intents = $this->prepare_intents( array( 'BOGO' ) );
289 284
290 285 return $this->prepare_item_discounts_bogo_free( $this->intents, $cart );
291 - }
286 + }
292 287
293 288 /**
294 289 * Apply the discount to shipping.
295 290 *
@@ -329,16 +324,10 @@
329 324 * Get a discount limit from campaign.
330 325 *
331 326 * Compare with total applied count and skip if the user limit is hit.
332 327 */
333 - $discount_limit = $intent->campaign->discount_max_user;
334 -
335 - if ( ! empty( $discount_limit ) ) {
336 - $total_applied_campaign = ( new UserLimit )->disco_get_total_applied_campaign( $intent->campaign->id );
337 -
338 - if ( $total_applied_campaign >= $discount_limit ) {
339 - continue;
340 - }
328 + if ( ( new UserLimit )->disco_is_limit_reached( $intent->campaign ) ) {
329 + continue;
341 330 }
342 331
343 332 $items = $this->get_items_for_discount( $cart, $intent->campaign );
344 333
@@ -465,7 +454,35 @@
465 454 return false;
466 455 }
467 456
468 457 return in_array( $page_name, $pages, true );
458 + }
459 +
460 + /**
461 + * Resolve a campaign's discount type.
462 + *
463 + * Read through get_discount_rules() rather than the raw config property.
464 + * That accessor is what the intents use, so the type reported here is the
465 + * one the discount was actually calculated with: it decodes a rules payload
466 + * stored as JSON and fills in the defaults, neither of which the magic
467 + * property does. The property is also undeclared, so PHPStan cannot see it.
468 + *
469 + * @param \Disco\App\Utility\Config $campaign Campaign config.
470 + * @return string Discount type, or an empty string when it cannot be determined.
471 + */
472 + private function campaign_discount_type( Config $campaign ): string {
473 + $rules = $campaign->get_discount_rules();
474 +
475 + if ( ! is_array( $rules ) || ! isset( $rules[0] ) || ! is_object( $rules[0] ) ) {
476 + return '';
477 + }
478 +
479 + $discount_type = $rules[0]->discount_type ?? '';
480 +
481 + if ( ! is_scalar( $discount_type ) ) {
482 + return '';
483 + }
484 +
485 + return (string) $discount_type;
469 486 }
470 487
471 488 }