PluginProbe
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO / 1.4.14
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO v1.4.14
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 1.3.47 1.3.46 1.3.45 All 177 releases
disco / app / Calc / CalcPercent.php

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

211 lines 6.1 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 CalcPercent 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 mixed $discounted_quantities
27 */
28 private $discounted_quantities;
29
30 /**
31 * @var array $rule Campaign Rule.
32 */
33 private $rule;
34
35 /**
36 * @var array $item Cart Item.
37 */
38 private $item;
39
40 /**
41 * @var string $discount_intent
42 */
43 private $discount_intent;
44
45 /**
46 * CalcPercent constructor.
47 *
48 * @param array $rule Campaign Rule.
49 * @param array $item Cart Item.
50 * @param object $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
58 // @phpstan-ignore-next-line
59 $this->discount_intent = $campaign->get_discount_intent(); // phpcs:ignore
60 }
61
62 /**
63 * Calculate Discount.
64 *
65 * @return array $discount
66 */
67 public function calculate(): array {
68 $discount = $this->calculate_discount();
69
70 return array(
71 'price' => $this->calculate_price( $discount ),
72 'discount' => $discount,
73 'line_subtotal' => $this->calculate_line_subtotal( $discount ),
74 'discounted_quantities' => $this->discounted_quantities,
75 );
76 }
77
78 /**
79 * Calculate Discount.
80 *
81 * @return float Discount Amount.
82 */
83 public function calculate_discount(): float { // phpcs:ignore
84 $discount_amount = 0;
85 $discount_value = $this->rule['discount_value'];
86 $price = CalcFactory::get_price( $this->item );
87 $quantity = (int) $this->item['quantity'];
88
89 /**
90 * "Count Quantity As" combined / variations mode: eligibility and the
91 * eligible unit count were already resolved by QuantityCounter, so
92 * discount only that many units and skip the per-line min/max/recursive
93 * derivation below.
94 */
95 if ( isset( $this->item['disco_forced_qty'] ) ) {
96 $forced = max( 0, (int) $this->item['disco_forced_qty'] );
97 $product_discount = $price ? $price * $discount_value / 100 : 0; // phpcs:ignore
98 $this->discounted_quantities = $forced;
99
100 return apply_filters( 'disco_final_discounted_amount', $forced * $product_discount, 'percent' );
101 }
102
103 $min = $this->rule['min'] ? (int) $this->rule['min'] : 0; //phpcs:ignore
104 $max = ! empty( $this->rule['max'] ) ? (int) $this->rule['max'] : ( $this->discount_intent === 'Bulk' ? PHP_INT_MAX : 0 ); // phpcs:ignore
105 $recursive = ! empty( $this->rule['recursive'] ) && $this->rule['recursive'] === 'yes'; // phpcs:ignore
106 $item_id = $this->item['product_id']; // phpcs:ignore
107 $rule_ids = array_column( $this->rule['get_ids'], 'id' ); // phpcs:ignore
108 $discount_qty = $this->rule['get_quantity'] ? (int) $this->rule['get_quantity'] : 0; // phpcs:ignore
109
110 // Default discount for quantities.
111 $discount_for_quantities = $min;
112
113 /**
114 * Bulk / range capping — only when NOT recursive. Recursive rules ignore
115 * max: the bundle size is always min, so the qualifying quantity must not
116 * be capped to max here, otherwise floor( qty / base ) would divide by
117 * max instead of min and undercount the bundles.
118 */
119 if ( ! $recursive && $max && $quantity >= $max ) {
120 $discount_for_quantities = $max;
121 }
122
123 // If quantity between min and max then apply discount.
124 if ( ! $recursive && $quantity >= $min && $quantity <= $max ) {
125 $discount_for_quantities = $quantity;
126 }
127
128 /**
129 * If discount intent is BuyXGetY then set the discount for quantities to 1.
130 * This is to ensure that the discount for selected item for BOGO discount.
131 * Here no need to check for min and max quantity.
132 */
133
134 if (
135 $this->discount_intent === 'BuyXGetY'
136 && ! empty( $rule_ids )
137 && (
138 in_array( $item_id, $rule_ids, true )
139 || $this->is_in_category( $item_id, $rule_ids )
140 )
141 ) {
142 $discount_for_quantities = $discount_qty;
143 }
144
145 // Apply for the Bundle & BOGO Discount when recursive is set.
146 $multiplier = 1;
147
148 if ( $recursive && $min > 0 && $quantity >= $min ) {
149 /**
150 * Recursive: the number of bundles is floor( qty / min ). Divide by
151 * min, never by $discount_for_quantities — for BuyXGetY the latter
152 * holds the reward quantity (get_quantity) and would inflate the
153 * multiplier (e.g. buy 2 get 1 with 2 in cart → 1 bundle, not 2).
154 */
155 $multiplier = (int) floor( $quantity / $min );
156 $discount_for_quantities *= $multiplier;
157 }
158
159 // Discount per product.
160 $product_discount = 0;
161
162 if ( $price ) {
163 $product_discount = $price * $discount_value / 100;
164 }
165
166 // Limit discount to Min or Max quantity.
167 if ( $quantity >= $discount_for_quantities ) {
168 $discount_for_quantities = min( $quantity, $discount_for_quantities );
169
170 // Set the discounted quantity for BOGO.
171 if ( ! empty( $this->rule['get_quantity'] ) ) {
172 $discount_for_quantities = (int) $this->rule['get_quantity'];
173 $discount_for_quantities *= $multiplier;
174 }
175
176 $discount_amount = $discount_for_quantities * $product_discount;
177 }
178
179 $this->discounted_quantities = $discount_for_quantities;
180
181 return apply_filters( 'disco_final_discounted_amount', $discount_amount, 'percent' );
182 }
183
184 /**
185 * Calculate Price.
186 *
187 * @param float $discount Discount Amount.
188 * @return float Price.
189 */
190 public function calculate_price( $discount ): float {
191 $discounted_subtotal = $this->calculate_line_subtotal( $discount );
192
193 return $discounted_subtotal / $this->item['quantity'];
194 }
195
196 /**
197 * Calculate Line Subtotal.
198 *
199 * @param float $discount Discount Amount.
200 * @return float Line Subtotal.
201 */
202 public function calculate_line_subtotal( $discount ): float {
203 if ( isset( $this->item['line_subtotal'] ) ) {
204 return $this->item['line_subtotal'] - $discount;
205 }
206
207 return 0.0;
208 }
209
210 }
211