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
disco / app / Calc / CalcFixed.php

CalcFixed.php in Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO 1.4.17, at app/Calc/CalcFixed.php

220 lines 6.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Disco\App\Calc;
4
5 /**
6 * Class CalcPercent
7 *
8 * @package Disco
9 * @subpackage Disco\App\Calc
10 * @author Ohidul Islam <wahid0003@gmail.com>
11 * @link https://webappick.com
12 * @license https://opensource.org/licenses/gpl-license.php GNU Public License
13 * @category Plugin
14 */
15 class CalcFixed extends CalcAbstract {
16
17 use \Disco\App\Intents\IntentHelper;
18
19 /**
20 * @var object $cart Cart.
21 * @phpstan-ignore-next-line
22 */
23 private $cart;
24
25 /**
26 * @var float
27 */
28 private $discounted_quantities;
29
30 /**
31 * @var string $discount_intent
32 */
33 private $discount_intent;
34
35 /**
36 * @var array $rule Campaign Rule.
37 */
38 private $rule;
39
40 /**
41 * @var array $item Cart Item.
42 */
43 private $item;
44
45 /**
46 * CalcFixed constructor.
47 *
48 * @param array $rule Campaign Rule.
49 * @param array $item Cart Item.
50 * @param \WC_Cart $cart Cart.
51 * @param object $campaign Campaign.
52 */
53 public function __construct( $rule, $item, $cart, $campaign ) {
54 $this->item = $item;
55 $this->rule = $rule;
56 $this->cart = $cart;
57 // @phpstan-ignore-next-line
58 $this->discount_intent = $campaign->get_discount_intent();
59 }
60
61 public function calculate(): array {
62 $discount = $this->calculate_discount();
63
64 return array(
65 'price' => $this->calculate_price( $discount ),
66 'discount' => $discount,
67 'line_subtotal' => $this->calculate_line_subtotal( $discount ),
68 'discounted_quantities' => $this->discounted_quantities,
69 );
70 }
71
72 /**
73 * Calculate Discount.
74 *
75 * @return float Discount Amount.
76 */
77 public function calculate_discount(): float { // phpcs:ignore
78 $discount_amount = 0;
79 $fixed_discount = $this->rule['discount_value'];
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
99 $min = $this->rule['min'] ? (int) $this->rule['min'] : 0; // phpcs:ignore
100 $max = $this->rule['max'] ? (int) $this->rule['max'] : 0; // phpcs:ignore
101 $recursive = isset( $this->rule['recursive'] ) && 'yes' === $this->rule['recursive']; // phpcs:ignore
102 $item_id = $this->item['product_id'];
103 $rule_ids = array_column( $this->rule['get_ids'], 'id' );
104 $discount_qty = $this->rule['get_quantity'] ? (int) $this->rule['get_quantity'] : 0; // phpcs:ignore
105
106 // Default discount for quantities.
107 $discount_for_quantities = $min;
108
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 ) {
116 $discount_for_quantities = $max;
117 }
118
119 // If quantity between min and max then apply discount.
120 if ( ! $recursive && $quantity >= $min && $quantity <= $max ) {
121 $discount_for_quantities = $quantity;
122 }
123
124 // Apply for the Bundle & BOGO Discount when recursive is set.
125 $multiplier = 1;
126
127 /**
128 * If discount intent is BuyXGetY then set the discount for quantities to 1.
129 * This is to ensure that the discount for selected item for BOGO discount.
130 * Here no need to check for min and max quantity.
131 */
132 if (
133 $this->discount_intent === 'BuyXGetY'
134 && ! empty( $rule_ids )
135 && (
136 in_array( $item_id, $rule_ids, true )
137 || $this->is_in_category( $item_id, $rule_ids )
138 )
139 ) {
140 $discount_for_quantities = $discount_qty;
141 $multiplier = $discount_qty;
142 }
143
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;
153 }
154
155 // Limit discount to Min or Max quantity.
156 if ( $quantity >= $discount_for_quantities ) {
157 $discount_amount = $fixed_discount * $multiplier;
158 }
159
160 $this->discounted_quantities = $discount_for_quantities;
161
162 return apply_filters( 'disco_final_discounted_amount', $discount_amount, 'fixed' );
163 }
164
165 /**
166 * Calculate Price.
167 *
168 * @param float $discount Discount Amount.
169 * @return float Price.
170 */
171 public function calculate_price( $discount ): float {
172 $discounted_subtotal = $this->calculate_line_subtotal( $discount );
173
174 return $discounted_subtotal / $this->item['quantity'];
175 }
176
177 /**
178 * Calculate Line Subtotal.
179 *
180 * @param float $discount Discount Amount.
181 * @return float Line Subtotal.
182 */
183 public function calculate_line_subtotal( $discount ): float {
184 if ( isset( $this->item['line_subtotal'] ) ) {
185 return $this->item['line_subtotal'] - $discount;
186 }
187
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 ) );
217 }
218
219 }
220