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/Calc/CalcFixed.php +65 -10 1.4.11.4.17 View file →
@@ -77,25 +77,48 @@
77 77 public function calculate_discount(): float { // phpcs:ignore
78 78 $discount_amount = 0;
79 79 $fixed_discount = $this->rule['discount_value'];
80 80 $quantity = (int) $this->item['quantity'];
81 +
82 + /**
83 + * "Count Quantity As" combined / variations: a fixed amount is a flat
84 + * line discount, so eligibility (any eligible unit) gates it once rather
85 + * than scaling with the eligible unit count.
86 + */
87 + if ( isset( $this->item['disco_forced_qty'] ) ) {
88 + $forced = max( 0, (int) $this->item['disco_forced_qty'] );
89 + $this->discounted_quantities = $forced;
90 + $amount = 0.0;
91 +
92 + if ( $forced > 0 ) {
93 + $amount = (float) $fixed_discount * $this->pooled_bundle_count( $forced );
94 + }
95 +
96 + return apply_filters( 'disco_final_discounted_amount', $amount, 'fixed' );
97 + }
98 +
81 99 $min = $this->rule['min'] ? (int) $this->rule['min'] : 0; // phpcs:ignore
82 100 $max = $this->rule['max'] ? (int) $this->rule['max'] : 0; // phpcs:ignore
83 101 $recursive = isset( $this->rule['recursive'] ) && 'yes' === $this->rule['recursive']; // phpcs:ignore
84 - $item_id = $this->item['product_id'];
85 - $rule_ids = array_column( $this->rule['get_ids'], 'id' );
102 + $item_id = $this->item['product_id'];
103 + $rule_ids = array_column( $this->rule['get_ids'], 'id' );
86 104 $discount_qty = $this->rule['get_quantity'] ? (int) $this->rule['get_quantity'] : 0; // phpcs:ignore
87 105
88 106 // Default discount for quantities.
89 107 $discount_for_quantities = $min;
90 108
91 - // Apply for the Bulk Discount when max is set.
92 - if ( $max && $quantity >= $max ) {
109 + /**
110 + * Bulk / range capping — only when NOT recursive. Recursive rules ignore
111 + * max: the bundle size is always min, so the qualifying quantity must not
112 + * be capped to max here, otherwise floor( qty / base ) would divide by
113 + * max instead of min and undercount the bundles.
114 + */
115 + if ( ! $recursive && $max && $quantity >= $max ) {
93 116 $discount_for_quantities = $max;
94 117 }
95 118
96 119 // If quantity between min and max then apply discount.
97 - if ( $quantity >= $min && $quantity <= $max ) {
120 + if ( ! $recursive && $quantity >= $min && $quantity <= $max ) {
98 121 $discount_for_quantities = $quantity;
99 122 }
100 123
101 124 // Apply for the Bundle & BOGO Discount when recursive is set.
@@ -117,13 +140,17 @@
117 140 $discount_for_quantities = $discount_qty;
118 141 $multiplier = $discount_qty;
119 142 }
120 143
121 - if ( $recursive ) {
122 - if ( $quantity % $min === 0 || $quantity > $discount_for_quantities ) {
123 - $multiplier = floor( $quantity / $discount_for_quantities );
124 - $discount_for_quantities *= $multiplier;
125 - }
144 + if ( $recursive && $min > 0 && $quantity >= $min ) {
145 + /**
146 + * Recursive: the number of bundles is floor( qty / min ). Divide by
147 + * min, never by $discount_for_quantities — for BuyXGetY the latter
148 + * holds the reward quantity (get_quantity) and would inflate the
149 + * multiplier (e.g. buy 2 get 1 with 2 in cart → 1 bundle, not 2).
150 + */
151 + $multiplier = (int) floor( $quantity / $min );
152 + $discount_for_quantities *= $multiplier;
126 153 }
127 154
128 155 // Limit discount to Min or Max quantity.
129 156 if ( $quantity >= $discount_for_quantities ) {
@@ -158,7 +185,35 @@
158 185 return $this->item['line_subtotal'] - $discount;
159 186 }
160 187
161 188 return 0.0;
189 + }
190 +
191 + /**
192 + * How many times a flat amount is charged for a pooled group.
193 + *
194 + * The pooled path hands this class the whole qualifying quantity on a single
195 + * line, so the bundle count comes from that quantity rather than from a per
196 + * line multiplier. A non recursive rule is charged once however large the
197 + * pool; a recursive one is charged once per whole bundle in it.
198 + *
199 + * @param int $pooled_quantity Qualifying quantity across the pooled lines.
200 + */
201 + private function pooled_bundle_count( int $pooled_quantity ): int {
202 + if ( ! isset( $this->rule['recursive'] ) || 'yes' !== $this->rule['recursive'] ) {
203 + return 1;
204 + }
205 +
206 + $minimum = 0;
207 +
208 + if ( isset( $this->rule['min'] ) ) {
209 + $minimum = absint( $this->rule['min'] );
210 + }
211 +
212 + if ( $minimum < 1 ) {
213 + return 1;
214 + }
215 +
216 + return max( 1, (int) floor( $pooled_quantity / $minimum ) );
162 217 }
163 218
164 219 }