PluginProbe
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO / 1.4.3
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO v1.4.3
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.4.3, at functions/bogo.php

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