PluginProbe
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO / trunk
Discount Rules for WooCommerce – Disco | Dynamic Pricing, Conditions, Bulk, Bundle, BOGO vtrunk
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 1.3.44 All 176 releases
disco / functions / product.php

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

362 lines 10.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php // phpcs:disable
2 /**
3 * Disco
4 *
5 * @package Disco
6 * @author Ohidul Islam <wahid0003@gmail.com>
7 * @link http://domain.tld
8 * @license GPL 2.0+
9 * @copyright 2022 WebAppick
10 */
11
12 // Ensure the file is not accessed directly.
13 if ( ! defined( 'ABSPATH' ) ) {
14 exit;
15 }
16
17 use Disco\App\Disco;
18 use Disco\App\Utility\Settings;
19
20 if ( !function_exists( 'disco_get_discounted_price' ) ) {
21
22 /**
23 * Get the discounted price of a product.
24 *
25 * @param float $price Product Price.
26 * @param \WC_Product $product Product Object.
27 * @return float
28 */
29 function disco_get_discounted_price( $price, $product ) {
30 if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
31 return $price; // Return original price in admin area.
32 }
33
34 return ( new Disco )->get_product_discounted_price( $price, $product );
35 }
36
37 }
38
39 if ( ! function_exists( 'disco_init_price_cache' ) ) {
40
41 /**
42 * Initialize the global price cache.
43 *
44 * @return void
45 */
46 function disco_init_price_cache() {
47 global $disco_price_cache;
48
49 if ( ! isset( $disco_price_cache ) ) {
50 $disco_price_cache = [];
51 }
52 }
53
54 // Initialize cache on load
55 disco_init_price_cache();
56 }
57
58 if ( ! function_exists( 'disco_should_cache_price' ) ) {
59
60 /**
61 * Check if current page should use price cache.
62 * Don't cache on checkout/cart/thankyou where user limits may change.
63 *
64 * @return bool
65 */
66 function disco_should_cache_price() {
67 // Allow filter to force disable cache (useful for tests)
68 if ( apply_filters( 'disco_disable_price_cache', false ) ) {
69 return false;
70 }
71
72 // Don't cache during AJAX (cart updates, checkout, etc.)
73 if ( wp_doing_ajax() ) {
74 return false;
75 }
76
77 // Don't cache on checkout or cart pages
78 if ( function_exists( 'is_checkout' ) && is_checkout() ) {
79 return false;
80 }
81
82 if ( function_exists( 'is_cart' ) && is_cart() ) {
83 return false;
84 }
85
86 // Cache on shop, category, product pages
87 return true;
88 }
89 }
90
91 if ( ! function_exists( 'disco_clear_price_cache' ) ) {
92
93 /**
94 * Clear the discounted price cache.
95 *
96 * @return void
97 */
98 function disco_clear_price_cache() {
99 global $disco_price_cache;
100 $disco_price_cache = [];
101 }
102 }
103
104 if ( ! function_exists( 'disco_discounted_price' ) ) {
105
106 /**
107 * Applies discounted price logic to product prices (simple + variation).
108 *
109 * @param float $price Product Price.
110 * @param \WC_Product $product Product Object.
111 * @return float
112 */
113 function disco_discounted_price( $price, $product ) {
114 global $disco_price_cache;
115 static $running = false;
116
117 if ( $running || ! is_object( $product ) || ! $price ) {
118 return $price;
119 }
120
121 $product_id = $product->get_id();
122
123 // Allow external code to exclude a product from Disco discounts.
124 if ( apply_filters( 'disco_exclude_product_from_discount', false, $product_id, $product ) ) {
125 return $price;
126 }
127
128 $cache_key = $product_id . '_' . $price;
129
130 // Use cache only on safe pages (shop, category, product)
131 if ( disco_should_cache_price() && isset( $disco_price_cache[ $cache_key ] ) ) {
132 return $disco_price_cache[ $cache_key ];
133 }
134
135 $running = true;
136
137 // Remove other filters that might interfere (safety for sale override).
138 remove_filter( 'woocommerce_product_get_sale_price', '__return_false' );
139 remove_filter( 'woocommerce_product_get_price', '__return_false' );
140
141 $discounted_price = disco_get_discounted_price( $price, $product );
142
143 // Respect WooCommerce native sale price if it's lower
144 if ( $product->get_type() !== 'variable' ) {
145 $sale_price = $product->get_sale_price();
146
147 if ( $sale_price && $sale_price < $discounted_price ) {
148 $discounted_price = $sale_price;
149 }
150 }
151
152 $running = false;
153
154 $result = round( $discounted_price, wc_get_price_decimals() );
155
156 // Cache result on safe pages
157 if ( disco_should_cache_price() ) {
158 $disco_price_cache[ $cache_key ] = $result;
159 }
160
161 return $result;
162 }
163
164 // Apply to simple and variation product prices (priority 999 for better compatibility)
165 add_filter( 'woocommerce_product_get_price', 'disco_discounted_price', 999, 2 );
166 add_filter( 'woocommerce_product_get_sale_price', 'disco_discounted_price', 999, 2 );
167 add_filter( 'woocommerce_product_variation_get_price', 'disco_discounted_price', 999, 2 );
168 add_filter( 'woocommerce_product_variation_get_sale_price', 'disco_discounted_price', 999, 2 );
169 }
170
171 if ( ! function_exists( 'disco_variable_product_discounted_price_html' ) ) {
172
173 /**
174 * Show variable price range with discount — includes tax if tax is enabled.
175 *
176 * @param string $price_html Price HTML.
177 * @param \WC_Product $product Product Object.
178 * @return string
179 */
180 function disco_variable_product_discounted_price_html( $price_html, $product ) {
181 if ( ! $product instanceof WC_Product_Variable ) {
182 return $price_html;
183 }
184
185 $variation_ids = $product->get_children();
186 $regular_prices = array();
187 $discounted_prices = array();
188
189 // Use cached tax settings to avoid repeated get_option() calls
190 $tax = Settings::get_tax_settings();
191
192 foreach ( $variation_ids as $variation_id ) {
193 $variation = wc_get_product( $variation_id );
194
195 if ( ! $variation || ! $variation->is_purchasable() ) {
196 continue;
197 }
198
199 if ( $tax['enabled'] && $tax['include_tax'] ) {
200 $regular_price = wc_get_price_including_tax( $variation, [ 'price' => $variation->get_regular_price() ] );
201 $sale_price = wc_get_price_including_tax( $variation, [ 'price' => $variation->get_price() ] );
202 } else {
203 $regular_price = (float) $variation->get_regular_price();
204 $sale_price = (float) $variation->get_price();
205 }
206
207 if ( $regular_price <= 0 ) {
208 continue;
209 }
210
211 $regular_prices[] = $regular_price;
212 $discounted_prices[] = $sale_price;
213 }
214
215 if ( empty( $regular_prices ) || empty( $discounted_prices ) ) {
216 return $price_html;
217 }
218
219 $regular_min = min( $regular_prices );
220 $regular_max = max( $regular_prices );
221 $discounted_min = min( $discounted_prices );
222 $discounted_max = max( $discounted_prices );
223
224 $is_discount_applied = (
225 round( $regular_min, 2 ) > round( $discounted_min, 2 ) ||
226 round( $regular_max, 2 ) > round( $discounted_max, 2 )
227 );
228
229 if ( ! $is_discount_applied ) {
230 return $price_html;
231 }
232
233 /**
234 * If min and max prices are the same, show single price.
235 * Otherwise, show price range.
236 * Strike-through regular price based on Disco settings.
237 */
238 if ( round( $discounted_min, 2 ) === round( $discounted_max, 2 ) ) {
239 if ( ! Disco::is_strike_through_applicable( Disco::get_page_name() ) ) {
240 return wc_price( $discounted_min );
241 }
242 return '<del>' . wc_price( $regular_min ) . '</del> <ins>' . wc_price( $discounted_min ) . '</ins>';
243 }
244
245 $discount_range = wc_price( $discounted_min ) . '' . wc_price( $discounted_max );
246 $regular_range = wc_price( $regular_min ) . '' . wc_price( $regular_max );
247
248 if( ! Disco::is_strike_through_applicable( Disco::get_page_name() ) ) {
249 return $discount_range;
250 }
251
252 return '<del>' . $regular_range . '</del><br><ins>' . $discount_range . '</ins>';
253 }
254
255 add_filter( 'woocommerce_variable_price_html', 'disco_variable_product_discounted_price_html', 999, 2 );
256 }
257
258 if ( ! function_exists( 'disco_discounted_price_html' ) ) {
259
260 /**
261 * Display Product Sale Price & Regular Price with or without Tax.
262 *
263 * @param string $price_html Product Price HTML.
264 * @param \WC_Product $product Product Object.
265 */
266 function disco_discounted_price_html( string $price_html, WC_Product $product ): string {
267
268 if ( ! $product || ! is_a( $product, 'WC_Product' ) ) {
269 return $price_html;
270 }
271
272 if( $product->get_type() === 'variable' ) {
273 return $price_html;
274 }
275
276 // Use cached tax settings to avoid repeated get_option() calls
277 $tax = Settings::get_tax_settings();
278
279 if ( $tax['enabled'] && $tax['include_tax'] ) {
280 $regular_price = wc_get_price_including_tax( $product, array( 'price' => $product->get_regular_price() ) );
281 $sale_price = wc_get_price_including_tax( $product, array( 'price' => $product->get_price() ) );
282 } else {
283 $regular_price = wc_get_price_excluding_tax( $product, array( 'price' => $product->get_regular_price() ) );
284 $sale_price = wc_get_price_excluding_tax( $product, array( 'price' => $product->get_price() ) );
285 }
286
287 $page_name = Disco::get_page_name();
288
289 if (
290 ! Disco::is_strike_through_applicable( $page_name ) &&
291 in_array( Settings::get( 'on_sale_badge' ), [ 'show_all', 'do_not_show' ], true )
292 ) {
293 return wc_price( $sale_price );
294 }
295
296 if ( $sale_price < $regular_price ) {
297 $price_html = '<del>' . wc_price( $regular_price ) . '</del> <ins>' . wc_price( $sale_price ) . '</ins>';
298 }
299
300 return $price_html;
301 }
302
303 add_filter( 'woocommerce_get_price_html', 'disco_discounted_price_html', 999, 2 );
304 }
305
306 if ( ! function_exists( 'disco_show_product_as_on_sale' ) ) {
307
308 /**
309 * Show variable product as on sale if any variation is on sale.
310 *
311 * @param bool $is_on_sale Whether the product is on sale.
312 * @param \WC_Product $product Product Object.
313 * @return bool
314 */
315 function disco_show_product_as_on_sale( $is_on_sale, $product ) {
316 if ( ! $product || ! is_a( $product, 'WC_Product' ) ) {
317 return $is_on_sale;
318 }
319
320 $on_sale_status = Settings::get( 'on_sale_badge' );
321
322 //Show on sale badge based on WooCommerce and Disco settings.
323 if ( 'do_not_show' === $on_sale_status ) {
324 return false;
325 }
326
327 if( 'show_all' === $on_sale_status ) {
328 if ( $product->is_type( 'variable' ) ) {
329 $children = $product->get_children();
330
331 foreach ( $children as $variation_id ) {
332 $variation = wc_get_product( $variation_id );
333
334 if ( ! $variation || ! $variation->is_purchasable() ) {
335 continue;
336 }
337
338 $regular_price = (float) $variation->get_regular_price();
339 $current_price = (float) $variation->get_price(); // This includes filters (like discounts)
340
341 // If the variation has a price reduction (dynamic or static)
342 if ( $regular_price > 0 && $current_price < $regular_price ) {
343 return true;
344 }
345 }
346 }
347
348 // For simple products, check if the price is discounted.
349 $regular_price = (float) $product->get_regular_price();
350 $current_price = (float) $product->get_price();
351 // If the product has a price reduction (dynamic or static)
352 if ( $regular_price > 0 && $current_price < $regular_price ) {
353 return true;
354 }
355 }
356
357 return $is_on_sale;
358 }
359
360 add_filter( 'woocommerce_product_is_on_sale', 'disco_show_product_as_on_sale', 20, 2 );
361 }
362