PluginProbe
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO / 1.3.10
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO v1.3.10
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 1.3.47 1.3.46 All 178 releases
disco / functions / bogo.php

bogo.php in Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO 1.3.10, at functions/bogo.php

292 lines 8.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // phpcs:disable
3 /**
4 * Disco
5 *
6 * @package Disco
7 * @author Ohidul Islam <wahid0003@gmail.com>
8 * @link http://domain.tld
9 * @license GPL 2.0+
10 * @copyright 2022 WebAppick
11 */
12
13 // Ensure the file is not accessed directly.
14 if (!defined('ABSPATH')) {
15 exit;
16 }
17
18 use Disco\App\Disco;
19 use Disco\App\Utility\Helper;
20 use Disco\App\Utility\Settings;
21
22 if ( ! function_exists( 'disco_cart_apply_free_items' ) ) {
23 /**
24 * Apply free items to the cart based on BOGO rules.
25 *
26 * @param \WC_Cart $cart Cart Object.
27 */
28 function disco_cart_apply_free_items( $cart ) {
29 if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
30 return;
31 }
32
33 if ( Settings::get( 'discount_priority_type' ) === 'woocommerce_coupon' ) {
34 return;
35 }
36
37 // Prevent multiple executions during the same request
38 static $is_processing = false;
39 if ( $is_processing ) {
40 return;
41 }
42
43 if ( $cart->is_empty() ) {
44 return;
45 }
46
47 $is_processing = true;
48
49 try {
50 // Calculate discounts - free products are automatically excluded in IntentHelper
51 $disco = new Disco();
52 $discounts = $disco->get_cart_items_discount_for_bogo( $cart );
53 $discounts = is_array( $discounts ) ? $discounts : array();
54
55 $free_ids = $discounts['get_ids'] ?? array();
56 $free_qty = $discounts['get_qty'] ?? 0;
57 $bogo_type = $discounts['bogo_type'] ?? 'products';
58
59 // Only process if we have valid discount data
60 if ( ! empty( $free_qty ) && ! empty( $free_ids ) ) {
61 // Remove invalid free items from cart
62 disco_remove_invalid_free_items( $cart, $free_ids, $bogo_type );
63
64 // Add or update free items
65 if ( $bogo_type === 'categories' ) {
66 disco_apply_category_based_free_items( $cart, $free_ids, $free_qty );
67 } else {
68 disco_apply_product_based_free_items( $cart, $free_ids, $free_qty );
69 }
70 } else {
71 // No valid BOGO, remove all free items
72 disco_remove_all_free_items( $cart );
73 }
74 } finally {
75 $is_processing = false;
76 }
77 }
78
79 add_action( 'woocommerce_before_calculate_totals', 'disco_cart_apply_free_items', 5 );
80 }
81
82 if ( ! function_exists( 'disco_remove_all_free_items' ) ) {
83 /**
84 * Remove all free items from cart when BOGO criteria are no longer met.
85 *
86 * @param \WC_Cart $cart Cart Object.
87 */
88 function disco_remove_all_free_items( $cart ) {
89 $items_to_remove = array();
90
91 foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
92 if ( ! empty( $cart_item['is_free_product'] ) ) {
93 $items_to_remove[] = $cart_item_key;
94 }
95 }
96
97 foreach ( $items_to_remove as $cart_item_key ) {
98 $cart->remove_cart_item( $cart_item_key );
99 }
100 }
101 }
102
103 if ( ! function_exists( 'disco_remove_invalid_free_items' ) ) {
104 /**
105 * Remove free items that are no longer valid.
106 *
107 * @param \WC_Cart $cart Cart Object.
108 * @param array $free_ids Valid free product/category IDs.
109 * @param string $bogo_type Type of BOGO (products/categories).
110 */
111 function disco_remove_invalid_free_items( $cart, $free_ids, $bogo_type ) {
112 $items_to_remove = array();
113
114 foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
115 if ( empty( $cart_item['is_free_product'] ) ) {
116 continue;
117 }
118
119 $product_id = $cart_item['product_id'];
120 $should_remove = false;
121
122 if ( empty( $free_ids ) ) {
123 // No valid free items, remove all free products
124 $should_remove = true;
125 } elseif ( $bogo_type === 'categories' ) {
126 // Check if product is in valid categories
127 $should_remove = ! Helper::is_in_category( $product_id, $free_ids );
128 } else {
129 // Check if product ID is in valid list
130 $should_remove = ! in_array( $product_id, $free_ids, true );
131 }
132
133 if ( $should_remove ) {
134 $items_to_remove[] = $cart_item_key;
135 }
136 }
137
138 // Remove items after iteration to avoid modifying array during loop
139 foreach ( $items_to_remove as $cart_item_key ) {
140 $cart->remove_cart_item( $cart_item_key );
141 }
142 }
143 }
144
145 if ( ! function_exists( 'disco_apply_category_based_free_items' ) ) {
146 /**
147 * Apply free items based on category rules.
148 *
149 * @param \WC_Cart $cart Cart Object.
150 * @param array $free_ids Category IDs for free products.
151 * @param int $free_qty Quantity of free items allowed.
152 */
153 function disco_apply_category_based_free_items( $cart, $free_ids, $free_qty ) {
154 // First, enforce quantity limits on existing free items and count them
155 $existing_free_count = 0;
156
157 foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
158 if ( ! empty( $cart_item['is_free_product'] ) ) {
159 $product_id = $cart_item['product_id'];
160
161 if ( Helper::is_in_category( $product_id, $free_ids ) ) {
162 $item_qty = $cart_item['quantity'];
163
164 // Check if this item's quantity exceeds remaining allowed free qty
165 $remaining_allowed = $free_qty - $existing_free_count;
166
167 if ( $item_qty > $remaining_allowed && $remaining_allowed > 0 ) {
168 // Reduce quantity to allowed amount
169 $cart->set_quantity( $cart_item_key, $remaining_allowed );
170 $existing_free_count += $remaining_allowed;
171 } elseif ( $remaining_allowed <= 0 ) {
172 // No more free items allowed, remove free flag or remove item
173 $cart->remove_cart_item( $cart_item_key );
174 } else {
175 $existing_free_count += $item_qty;
176 }
177 }
178 }
179 }
180
181 // Calculate how many more items we can mark as free
182 $remaining_free_qty = $free_qty - $existing_free_count;
183
184 if ( $remaining_free_qty <= 0 ) {
185 return; // Already have enough free items
186 }
187
188 $free_count = 0;
189
190 foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
191 if ( $free_count >= $remaining_free_qty ) {
192 break;
193 }
194
195 // Skip items already marked as free
196 if ( ! empty( $cart_item['is_free_product'] ) ) {
197 continue;
198 }
199
200 $product_id = $cart_item['product_id'];
201
202 if ( Helper::is_in_category( $product_id, $free_ids ) ) {
203 $qty_to_mark = $remaining_free_qty - $free_count;
204
205 // Only mark item free if the full quantity fits
206 if ( $cart_item['quantity'] <= $qty_to_mark ) {
207 $cart->cart_contents[ $cart_item_key ]['is_free_product'] = true;
208 $free_count += $cart_item['quantity'];
209 }
210 }
211 }
212 }
213 }
214
215 if ( ! function_exists( 'disco_apply_product_based_free_items' ) ) {
216 /**
217 * Apply free items based on product rules.
218 *
219 * @param \WC_Cart $cart Cart Object.
220 * @param array $free_ids Product IDs for free products.
221 * @param int $free_qty Quantity of free items.
222 */
223 function disco_apply_product_based_free_items( $cart, $free_ids, $free_qty ) {
224 foreach ( $free_ids as $product_id ) {
225 $found = false;
226
227 foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
228 if (
229 intval( $cart_item['product_id'] ) === intval( $product_id )
230 && ! empty( $cart_item['is_free_product'] )
231 ) {
232 $current_qty = $cart_item['quantity'];
233 if ( $current_qty !== $free_qty ) {
234 $cart->set_quantity( $cart_item_key, $free_qty );
235 }
236 $found = true;
237 break;
238 }
239 }
240
241 if ( ! $found ) {
242 $cart->add_to_cart( $product_id, $free_qty, 0, array(), array( 'is_free_product' => true ) );
243 }
244 }
245 }
246 }
247
248 if ( ! function_exists( 'disco_set_free_product_price' ) ) {
249 /**
250 * Set the price of free products to zero.
251 *
252 * @param \WC_Cart $cart Cart Object.
253 */
254 function disco_set_free_product_price( $cart ) {
255 if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
256 return;
257 }
258
259 foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) {
260 if ( empty( $cart_item['is_free_product'] ) ) {
261 continue;
262 }
263
264 $cart_item['data']->set_price( 0 );
265 $cart_item['data']->set_regular_price( 0 );
266 $cart->cart_contents[ $cart_item_key ]['free_product_note'] = __( 'This item is added as free!', 'disco' );
267 }
268 }
269 add_action( 'woocommerce_before_calculate_totals', 'disco_set_free_product_price', 10 );
270 }
271
272 if ( ! function_exists( 'disco_free_product_label' ) ) {
273 /**
274 * Display free product label in cart.
275 *
276 * @param array $item_data Item data.
277 * @param array $cart_item Cart item.
278 * @return array
279 */
280 function disco_free_product_label( $item_data, $cart_item ) {
281 if ( ! empty( $cart_item['free_product_note'] ) ) {
282 $item_data[] = array(
283 'key' => __( 'Note', 'disco' ),
284 'value' => $cart_item['free_product_note'],
285 );
286 }
287 return $item_data;
288 }
289
290 add_filter( 'woocommerce_get_item_data', 'disco_free_product_label', 10, 2 );
291 }
292