PluginProbe
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell / 3.13.3
WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell v3.13.3
3.13.3 3.13.2 3.13.1 3.13.0 3.12.13 3.12.12 3.12.11 3.12.10 3.12.9 3.12.8 3.12.7 3.12.6 3.12.5 3.12.4 3.12.3 3.12.1 3.12.2 3.12.0 3.11.1 3.11.0 3.10.9 3.10.8 3.10.7 3.10.6 2.8.16 All 261 releases
wpfunnels / public / modules / type / class-wpfnl-public-wc.php

class-wpfnl-public-wc.php in WPFunnels – Funnel Builder for WooCommerce with Checkout & One Click Upsell 3.13.3, at public/modules/type/class-wpfnl-public-wc.php

691 lines 28.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Woocommerce dependent public class
4 *
5 * @package WPFunnels\FunnelType
6 */
7 namespace WPFunnels\FunnelType;
8
9 use WPFunnels\Wpfnl_functions;
10 use WPFunnels\FunnelType\Wpfnl_Public_Funnel_Type;
11 use WPFunnels\lms\helper\Wpfnl_lms_learndash_functions;
12 use WPFunnels\Meta\Wpfnl_Default_Meta;
13 use WPFunnels\Discount\WpfnlDiscount;
14
15 class Wpfnl_Public_Wc extends Wpfnl_Public_Funnel_Type
16 {
17
18 /**
19 * When order bump accept/reject for woocommerce
20 *
21 * @return Array
22 */
23 public function wpfnl_order_bump_trigger( $step_id, $product_id, $quantity, $key, $user_id, $funnel_id , $checker ){
24
25 //=== Custom price configured when you add sale price or apply coupon or add discount value ===//
26 $ob_settings = get_post_meta($step_id, 'order-bump-settings', true);
27 $ob_settings = apply_filters( 'wpfunnels/order_bump_settings_on_frontend', $ob_settings, $funnel_id, $step_id );
28 $replaceable_ob = $this->get_replaceable_ob_products( $ob_settings );
29 $order_bump_settings = $ob_settings[$key];
30 $_product = wc_get_product($product_id);
31
32 $data = array();
33 if( $_product ){
34 $discount_type = $order_bump_settings['discountOption'];
35 $discount_apply_to = $_product->is_on_sale() ? 'sale' : 'regular' ;
36 $product_price = $this->get_product_price( $_product, $discount_apply_to );
37 $original_price = $product_price; // Store original price before discount
38
39 if( 'discount-percentage' === $discount_type || 'discount-price' === $discount_type ) {
40 $discount_apply_to = isset($order_bump_settings['discountapply']) ? $order_bump_settings['discountapply'] : 'regular';
41 $discount_value = isset($order_bump_settings['discountValue']) ? $order_bump_settings['discountValue'] : 0;
42 $product_price = $this->get_product_price( $_product, $discount_apply_to );
43 $original_price = $product_price; // Store price before applying order bump discount
44 if( 'discount-price' === $discount_type && 1 < $quantity ){
45 $discount_value = $this->get_percentage( $product_price, $discount_value, $quantity );
46 $discount_type = 'discount-percentage';
47 }
48
49 $product_price = $this->calculate_custom_price( $discount_type, $discount_value, $product_price );
50 // Now $original_price has the price before order bump discount, and $product_price has the discounted price
51 }
52
53 // Get custom product title from order bump settings
54 // For auto-match mode, use actual product name instead of placeholder
55 $is_auto_match = isset($order_bump_settings['offerSelectionMode']) && $order_bump_settings['offerSelectionMode'] === 'auto-match';
56 if( $is_auto_match ) {
57 $custom_product_title = $_product->get_name();
58 } else {
59 $custom_product_title = isset($order_bump_settings['productName']) ? $order_bump_settings['productName'] : '';
60 }
61
62 // Clean and store prices for cart and display
63 $discounted_price = preg_replace('/[^\d.]/', '', $product_price );
64 $original_price_clean = preg_replace('/[^\d.]/', '', $original_price );
65
66 $ob_cart_item_data = [
67 'custom_price' => $discounted_price,
68 'wpfnl_order_bump' => true,
69 'wpfnl_ob_title' => $custom_product_title,
70 'wpfnl_ob_quantity' => $quantity,
71 'wpfnl_ob_original_price' => $original_price_clean, // Store original price for display
72 ];
73
74
75 /**
76 * Apply filters to modify the orderbump product price data.
77 *
78 * @param float $custom_price The custom price of the product.
79 * @return float The modified custom price.
80 *
81 * @since 3.1.2
82 */
83 $ob_cart_item_data['custom_price'] = apply_filters( 'wpfunnels/modify_orderbump_product_price_data', $ob_cart_item_data['custom_price'] );
84
85 $should_replace_first_product = isset($order_bump_settings['isReplace']) ? $order_bump_settings['isReplace'] : 'no';
86 $replace_settings = isset($order_bump_settings['replaceSettings']) ? $order_bump_settings['replaceSettings'] : [
87 'isAllReplace' => 'yes',
88 'selectedProducts' => [],
89 ];
90 add_filter( 'woocommerce_checkout_cart_item_quantity', array($this, 'wpfnl_checkout_cart_item_quantity'), 10, 3 );
91 if ($checker == "true") {
92 $data = $this->wpfnl_order_bump_accept( $step_id, $product_id, $quantity, $key, $user_id, $funnel_id, $replaceable_ob, $ob_cart_item_data, $should_replace_first_product, $replace_settings, $order_bump_settings );
93 }else{
94 $data = $this->wpfnl_order_bump_reject( $step_id, $product_id, $quantity, $key, $user_id, $funnel_id, $replaceable_ob, $ob_cart_item_data, $should_replace_first_product, $replace_settings );
95 }
96 }
97
98 return $data;
99 }
100
101
102 /**
103 * Get percentage of product price that should be replaced by product price when discount type is discount-price option.
104 *
105 * @param float $product_price The price of the product.
106 * @param float $discount_value The discount value.
107 * @param int $quantity The quantity of the product.
108 *
109 * @return float The percentage of product price that should be replaced.
110 *
111 * @since 3.4.15
112 */
113 public function get_percentage( $product_price, $discount_value, $quantity ) {
114 $percentage = 0;
115 $product_price = floatval( $product_price );
116 $quantity = intval( $quantity );
117 $discount_value = floatval( $discount_value );
118
119 if( $discount_value > 0 && ( $product_price * $quantity ) > 0 ){
120 $percentage = ( $discount_value * 100 ) / ( $product_price * $quantity );
121 }
122 return $percentage;
123 }
124
125
126 /**
127 * When order bump accept for woocommerce
128 *
129 * @param $step_id, $product_id, $quantity, $key, $user_id, $funnel_id, $replaceable_ob, $ob_cart_item_data, $should_replace_first_product, $replace_settings, $order_bump_settings
130 *
131 * @return Array
132 */
133 private function wpfnl_order_bump_accept( $step_id, $product_id, $quantity, $key, $user_id, $funnel_id, $replaceable_ob, $ob_cart_item_data, $should_replace_first_product, $replace_settings, $order_bump_settings = array() ){
134
135 if( Wpfnl_functions::is_wc_active() ){
136 $cookie_name = 'wpfnl_order_bump';
137 $cookie = isset( $_COOKIE[$cookie_name] ) ? json_decode( wp_unslash( $_COOKIE[$cookie_name] ), true ) : array();
138
139 $cookie['order_bump_accepted'] = 'yes';
140
141 // Check if 'order_bump_product' exists and is an array, if not initialize it as an array
142 if (!isset($cookie['order_bump_product']) || !is_array($cookie['order_bump_product'])) {
143 $cookie['order_bump_product'] = array();
144 }
145
146 // Append the product_id to the 'order_bump_product' array if it doesn't already exist
147 if (!in_array($product_id, $cookie['order_bump_product'])) {
148 $cookie['order_bump_product'][] = $product_id;
149 }
150
151 ob_start();
152 setcookie( $cookie_name, wp_json_encode( $cookie ), time() + 3600 * 6, '/', COOKIE_DOMAIN );
153 ob_end_flush();
154
155 WC()->session->set('order_bump_accepted', 'yes');
156 // if order bump is checked we will empty the cart in the first place. The we have to check if isReplace is yes or no.
157 // if replace first product is enabled, then we will only add the order bump product
158 // else we will add all the checkout product along with the order bump product
159 if ( $should_replace_first_product == 'yes' ) {
160
161 $checkout_products = get_post_meta( $step_id, '_wpfnl_checkout_products', true);
162 $is_gbf = get_post_meta( $funnel_id, 'is_global_funnel', true);
163
164 if(( isset($replace_settings['isAllReplace']) && 'yes' === $replace_settings['isAllReplace'] ) || 'yes' === $is_gbf ){
165 if( 'yes' === $is_gbf ){
166 if(isset( $_COOKIE['wpfunnels_global_funnel_specific_product'] ) ){
167 $checkout_products = json_decode( wp_unslash( $_COOKIE['wpfunnels_global_funnel_specific_product'] ), true );
168 }
169 }
170
171 // When isAllReplace is 'yes', replace ALL products (main products + other order bumps)
172 // Empty the cart completely
173 \WC()->cart->empty_cart();
174
175 // Add only the new order bump product
176 // Check if product_id is actually a variation ID
177 $_temp_product = wc_get_product($product_id);
178 if( $_temp_product && $_temp_product->is_type('variation') ) {
179 // It's a variation - get parent ID
180 $parent_id = $_temp_product->get_parent_id();
181 $variation_id = $product_id;
182 $variation_data = $_temp_product->get_variation_attributes();
183 \WC()->cart->add_to_cart($parent_id, $quantity, $variation_id, $variation_data, $ob_cart_item_data);
184 } else {
185 // Simple product
186 \WC()->cart->add_to_cart($product_id, $quantity, 0, [], $ob_cart_item_data);
187 }
188 }else{
189 if( isset($replace_settings['selectedProducts']) ){
190 foreach ( \WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
191 // Skip if this cart item is an order bump - we only want to replace main products
192 if ( isset($cart_item['wpfnl_order_bump']) && $cart_item['wpfnl_order_bump'] ) {
193 continue;
194 }
195
196 $car_product_id = isset($cart_item['variation_id']) && $cart_item['variation_id'] ? $cart_item['variation_id'] : $cart_item['product_id'];
197 $key = array_search( $car_product_id, array_column($replace_settings['selectedProducts'], 'id'));
198 if( $key !== false ){
199 \WC()->cart->remove_cart_item($cart_item_key);
200
201 // Check if product_id is actually a variation ID
202 $_temp_product = wc_get_product($product_id);
203 if( $_temp_product && $_temp_product->is_type('variation') ) {
204 // It's a variation - get parent ID
205 $parent_id = $_temp_product->get_parent_id();
206 $variation_id = $product_id;
207 $variation_data = $_temp_product->get_variation_attributes();
208 \WC()->cart->add_to_cart($parent_id, $quantity, $variation_id, $variation_data, $ob_cart_item_data);
209 } else {
210 // Simple product
211 \WC()->cart->add_to_cart( $product_id, $quantity, 0, [], $ob_cart_item_data);
212 }
213 break;
214 }
215 }
216 }
217 }
218 } else {
219 // Check if product_id is actually a variation ID
220 $_temp_product = wc_get_product($product_id);
221 if( $_temp_product && $_temp_product->is_type('variation') ) {
222 // It's a variation - get parent ID
223 $parent_id = $_temp_product->get_parent_id();
224 $variation_id = $product_id;
225 $variation_data = $_temp_product->get_variation_attributes();
226 \WC()->cart->add_to_cart($parent_id, $quantity, $variation_id, $variation_data, $ob_cart_item_data);
227 } else {
228 // Simple product
229 \WC()->cart->add_to_cart( $product_id, $quantity, 0, [], $ob_cart_item_data);
230 }
231 }
232
233 // Generate Product Object and Name.
234 $product = wc_get_product( $product_id );
235 // Use custom title if available, otherwise use product name
236 $product_name = !empty($ob_cart_item_data['wpfnl_ob_title']) ? $ob_cart_item_data['wpfnl_ob_title'] : ($product ? $product->get_name() : '');
237
238 // Calculate display price considering quantity and discount
239 $unit_price = floatval($ob_cart_item_data['custom_price']);
240 $original_unit_price = floatval($ob_cart_item_data['wpfnl_ob_original_price']);
241 $quantity_int = intval($quantity);
242
243 $total_price = $unit_price * $quantity_int;
244 $original_total_price = $original_unit_price * $quantity_int;
245
246 // Format price display (show as sale price if there's a discount)
247 // Round to 2 decimal places for accurate comparison
248 $total_price = round($total_price, 2);
249 $original_total_price = round($original_total_price, 2);
250
251 if ($original_total_price > $total_price) {
252 $formatted_price = wc_format_sale_price($original_total_price, $total_price);
253 } else {
254 $formatted_price = wc_price($total_price);
255 }
256
257 // Get custom description from order bump settings, fallback to WooCommerce product description
258 // For auto-match mode, always use actual product description
259 $product_description = '';
260 $is_auto_match = isset($order_bump_settings['offerSelectionMode']) && $order_bump_settings['offerSelectionMode'] === 'auto-match';
261
262 if( $is_auto_match && $product ) {
263 // For auto-match, use actual product description
264 $product_description = $product->get_description() ?: $product->get_short_description();
265 } else {
266 // For manual mode, use custom description from settings (keep empty if not set)
267 $product_description = isset($order_bump_settings['productDescriptionText']) ? $order_bump_settings['productDescriptionText'] : '';
268 }
269
270 $data = [
271 'status' => 'success',
272 'message' => __('Successfully added', 'wpfnl'),
273 'order_bump' => true,
274 'product_name' => $product_name,
275 'product_description' => $product_description,
276 'product_price' => $formatted_price,
277 'product_image' => $product ? wp_get_attachment_url( $product->get_image_id() ) : '',
278 'isAllReplace' => ($should_replace_first_product == 'yes' && isset($replace_settings['isAllReplace']) && 'yes' === $replace_settings['isAllReplace']) ? true : false,
279 ];
280 do_action( 'wpfunnels/order_bump_accepted', $step_id, $product_id );
281
282 return $data;
283 }
284 return [];
285 }
286
287
288 /**
289 * When order bump reject for woocommerce
290 *
291 * @param $step_id, $product_id, $quantity, $key, $user_id, $funnel_id, $replaceable_ob, $ob_cart_item_data, $should_replace_first_product
292 *
293 * @return Array
294 */
295 private function wpfnl_order_bump_reject( $step_id, $product_id, $quantity, $key, $user_id, $funnel_id, $replaceable_ob, $ob_cart_item_data, $should_replace_first_product, $replace_settings ){
296
297 if( Wpfnl_functions::is_wc_active() ){
298
299 // fetch main products
300 $checkout_meta = new Wpfnl_Default_Meta();
301 $main_products = $checkout_meta->get_main_products( $funnel_id, $step_id );
302
303 $cookie_name = 'wpfnl_order_bump';
304 $cookie = isset( $_COOKIE[$cookie_name] ) ? json_decode( wp_unslash( $_COOKIE[$cookie_name] ), true ) : array();
305
306 $cookie['order_bump_accepted'] = 'no';
307 if( !isset($cookie['order_bump_accepted']) ){
308 $cookie['order_bump_accepted'] = 'no';
309 }
310
311 // Check if 'order_bump_product' exists and is an array
312 if (isset($cookie['order_bump_product']) && is_array($cookie['order_bump_product'])) {
313 // Find the index of the product_id in the array
314 $index = array_search($product_id, $cookie['order_bump_product']);
315 if ($index !== false) {
316 // Remove the product_id from the array
317 unset($cookie['order_bump_product'][$index]);
318 // Re-index the array to remove gaps
319 $cookie['order_bump_product'] = array_values($cookie['order_bump_product']);
320 }
321 }
322
323 ob_start();
324 setcookie( $cookie_name, wp_json_encode( $cookie ), time() + 3600 * 6, '/', COOKIE_DOMAIN );
325 ob_end_flush();
326
327 WC()->session->set('order_bump_accepted', 'no');
328
329 foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
330 if ( isset($cart_item['wpfnl_order_bump']) && $cart_item['wpfnl_order_bump'] && $cart_item['product_id'] == $product_id) {
331 //remove single product
332 // if( $cart_item['quantity'] == $quantity ){
333 WC()->cart->remove_cart_item($cart_item_key);
334 // }
335 }
336 else if ( isset($cart_item['wpfnl_order_bump']) && $cart_item['wpfnl_order_bump'] && $cart_item['variation_id'] == $product_id ) {
337 // if( $cart_item['quantity'] == $quantity ){
338
339 WC()->cart->remove_cart_item($cart_item_key);
340
341 // }
342 }
343 }
344 if ( $should_replace_first_product == 'yes' ) {
345 // Store existing order bumps before clearing (except the one being removed)
346 $existing_order_bumps = array();
347 foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
348 if ( isset($cart_item['wpfnl_order_bump']) && $cart_item['wpfnl_order_bump'] ) {
349 // Check if this is the order bump being removed
350 $cart_product_id = isset($cart_item['variation_id']) && $cart_item['variation_id'] ? $cart_item['variation_id'] : $cart_item['product_id'];
351 if( $cart_product_id != $product_id ) {
352 // This is a different order bump - keep it
353 $existing_order_bumps[$cart_item_key] = $cart_item;
354 }
355 }
356 }
357
358 \WC()->cart->empty_cart();
359 $is_gbf = get_post_meta( $funnel_id, 'is_global_funnel', true);
360 if( 'yes' === $is_gbf ){
361 if(isset( $_COOKIE['wpfunnels_global_funnel_product'] ) ){
362 $main_products = json_decode( wp_unslash( $_COOKIE['wpfunnels_global_funnel_product'] ), true );
363 $main_products = json_decode( $main_products );
364 }
365 }
366 $discount_instance = new WpfnlDiscount();
367 $discount = $discount_instance->get_discount_settings( $step_id );
368
369 foreach ( $main_products as $main_product ) {
370 $custom_price = [];
371 $product_id = isset($main_product['variation_id']) ? $main_product['variation_id'] : $main_product['id'];
372 $product = wc_get_product($product_id);
373 if( isset($discount['discountOptions'], $discount['discountapplyto']) ){
374 if( !$discount_instance->maybe_time_bound_discount( $step_id ) || ($discount_instance->maybe_time_bound_discount( $step_id ) && $discount_instance->maybe_validate_discount_time( $step_id )) ){
375 if( 'original' !== $discount['discountOptions'] ){
376 $regular_price = $product->get_regular_price();
377 $sale_price = $product->get_sale_price();
378 if( 'sale' === $discount['discountapplyto'] ){
379 $custom_price = [
380 'custom_price' => $sale_price ? $sale_price : $regular_price,
381 ];
382 }else{
383 $custom_price = [
384 'custom_price' => $regular_price ? $regular_price : $product->get_the_price(),
385 ];
386 }
387 }
388 }
389 }
390
391 if( 'variable' === $product->get_type() ){
392 $variations = $product->get_available_variations();
393 $quantity = $main_product['quantity'];
394 $this->add_default_variation($funnel_id, $step_id, $product_id, $product, $variations, $quantity);
395 }else{
396 if( 'variation' === $product->get_type() ){
397 WC()->cart->add_to_cart( $main_product['id'], $main_product['quantity'], isset($main_product['variation_id']) ? $main_product['variation_id'] : '' , isset($main_product['variations']) ?$main_product['variations'] : [], $custom_price);
398 }else{
399 WC()->cart->add_to_cart( $main_product['id'], $main_product['quantity'],0, [], $custom_price);
400 }
401 }
402 }
403
404 // Re-add existing order bumps that were preserved
405 foreach ( $existing_order_bumps as $cart_item ) {
406 $variation_id = isset($cart_item['variation_id']) ? $cart_item['variation_id'] : 0;
407 $variation = isset($cart_item['variation']) ? $cart_item['variation'] : array();
408 \WC()->cart->add_to_cart( $cart_item['product_id'], $cart_item['quantity'], $variation_id, $variation, $cart_item );
409 }
410 }
411 $data = [
412 'status' => 'success',
413 'message' => __('Successfully removed', 'wpfnl'),
414 ];
415 do_action( 'wpfunnels/order_bump_rejected', $step_id, $product_id );
416
417 return $data;
418 }
419 return [];
420 }
421
422
423 /**
424 * Add default variation to cart
425 *
426 * @param $product
427 * @param $variations
428 * @param $quantity
429 *
430 * @throws \Exception
431 */
432 private function add_default_variation($funnel_id, $checkout_id,$product_id,$product,$variations,$quantity){
433 $i = 0;
434 $formatted_variation = [];
435 $is_default_variation = false;
436 foreach ($variations as $variation) {
437
438 if($variation['is_in_stock'] ){
439
440 if($product->get_default_attributes()){
441 $attributes = $product->get_attributes();
442 $def_attributes = $product->get_default_attributes();
443 foreach($attributes as $attribute_key=>$attribute_value){
444
445 $def_attr = $product->get_default_attributes();
446 if(isset($def_attr[$attribute_key])){
447 $attribute_name = str_replace( 'attribute_', '', $attribute_key );
448 $default_value = $product->get_variation_default_attribute($attribute_name);
449 $formatted_variation['attribute_'.$attribute_name] = $default_value;
450 $is_default_variation = true;
451 // $default_attr[] = $default_value;
452
453 }
454 else{
455 $is_default_variation = true;
456 $attribute_name = str_replace( 'attribute_', '', $attribute_key );
457 $attr_value = $product->get_attribute( $attribute_name );
458 $attr_value = strtolower($attr_value);
459 if (strpos($attr_value, '|')) {
460 $attr_array = explode("|",$attr_value);
461 }else{
462 $attr_array = explode(",",$attr_value);
463 }
464 $formatted_variation['attribute_'.$attribute_name] = $attr_array[0];
465 // $default_attr[] = $attr_array[0];
466 }
467
468 }
469 }else{
470 if($i==0){
471 $attributes = $product->get_attributes();
472 foreach ( $attributes as $key => $value) {
473 $attr_value = $product->get_attribute( $key );
474 $attr_value = strtolower($attr_value);
475 if (strpos($attr_value, '|')) {
476 $attr_array = explode("|",$attr_value);
477 }else{
478 $attr_array = explode(",",$attr_value);
479 }
480 $is_custom_default_variation = true;
481 $formatted_variation['attribute_'.$key] = $attr_array[0];
482 }
483 }
484
485
486 // WC()->cart->add_to_cart($variation['variation_id'], $quantity);
487 }
488
489 if( $is_default_variation || $is_custom_default_variation){
490 $variation_id = (new \WC_Product_Data_Store_CPT())->find_matching_product_variation(
491 new \WC_Product($product_id),
492 $formatted_variation
493 );
494 if( !$variation_id ){
495 $_product = wc_get_product($product_id);
496 if( $_product ){
497 $variations = $_product->get_available_variations();
498 $variations_id = wp_list_pluck( $variations, 'variation_id' );
499 $variation_id = $variations_id[0];
500 $formatted_variation = wc_get_product_variation_attributes($variation_id);
501 }
502 }
503
504 $price_type = $this->get_product_price_type( $funnel_id, $checkout_id );
505
506 if( $price_type == 'original' || $price_type == 'sale' ){
507 $variation_object = wc_get_product( $variation_id );
508 $cart_item_data = [
509 'custom_price' => $variation_object->is_on_sale() ? $variation_object->get_sale_price() : $variation_object->get_regular_price()
510 ];
511 }else{
512 $cart_item_data = [
513 'custom_price' => get_post_meta($variation_id, '_regular_price', true) ? get_post_meta($variation_id, '_regular_price', true) : get_post_meta($variation_id, '_price', true)
514 ];
515 }
516
517 $added = WC()->cart->add_to_cart( $product_id, $quantity, $variation_id, $formatted_variation, $cart_item_data);
518
519 break; // Stop the main loop
520 }
521 $i++;
522 }
523 }
524 }
525
526
527 /**
528 * Get price type
529 */
530 public function get_product_price_type( $funnel_id, $step_id ){
531
532 $is_gbf = get_post_meta( $funnel_id, 'is_global_funnel', true );
533 if( $is_gbf == 'no' || !$is_gbf ){
534
535 $discount_instance = new WpfnlDiscount();
536 $discount = $discount_instance->get_discount_settings( $step_id );
537
538 if( is_array($discount) ){
539 if( $discount['discountOptions'] !== 'original' ){
540 if( $discount['discountapplyto'] == '' || $discount['discountapplyto'] == 'regular' ){
541 return 'regular';
542 }else{
543 return 'sale';
544 }
545 }
546 }
547 }
548 return 'original';
549 }
550
551
552 /**
553 * Get replaceable orderbump products
554 */
555 private function get_replaceable_ob_products( $ob_settings ){
556
557
558 $replaceable_ob = [];
559 foreach( $ob_settings as $key=>$settings ){
560 if( $settings['isReplace'] === 'yes' ){
561 $replaceable_ob[] = [
562 'id' => $settings['product'],
563 'quantity' => $settings['quantity'],
564 ];
565 }
566 }
567
568 return $replaceable_ob;
569
570 }
571
572
573 /**
574 * Get calculable price
575 *
576 * @param \WC_Product $product
577 * @param $discount_apply
578 *
579 * @return string
580 *
581 * @since 2.0.5
582 */
583 private function get_product_price( \WC_Product $product, $discount_apply = 'regular' ) {
584 $price = $product->get_regular_price();
585 if ($product->get_type() == 'variable' || $product->get_type() == 'variable-subscription') {
586 $price = $product->get_variation_regular_price('min') ? $product->get_variation_regular_price('min') : $product->get_price();
587 } else {
588 $price = $product->get_regular_price() ? $product->get_regular_price() : $product->get_price();
589 }
590
591 $final_price = $discount_apply === 'sale' && $product->get_sale_price() ? $product->get_sale_price() : $price;
592
593 return apply_filters('wpfunnels/modify_order_bump_price_on_main_order', $final_price);
594 }
595
596
597 /**
598 * Calculate Discount.
599 *
600 * @since 1.1.5
601 */
602 private function calculate_custom_price($discount_type, $discount_value, $product_price)
603 {
604 $custom_price = $product_price;
605
606 if (!empty($discount_type)) {
607 if ('discount-percentage' === $discount_type) {
608 if ( $discount_value > 0 && $discount_value <= 100) {
609 $custom_price = $product_price - (($product_price * $discount_value) / 100);
610 }else{
611 $custom_price = $product_price;
612 }
613 } elseif ('discount-price' === $discount_type) {
614 if ($discount_value > 0 && $product_price >= $discount_value ) {
615 $custom_price = $product_price - $discount_value;
616 }else{
617 $custom_price = $product_price;
618 }
619 }
620 }
621
622 return number_format($custom_price, 2);
623 }
624
625
626
627 /**
628 * Update quantity from checkout page
629 *
630 * @param $quantity, $cart_item, $cart_item_key
631 */
632 public function wpfnl_checkout_cart_item_quantity( $quantity, $cart_item, $cart_item_key ) {
633
634 $step_id = 0;
635 $isQuantity = 'no';
636
637 if( wp_doing_ajax() ) {
638 $step_id = isset($_POST['step_id']) ? $_POST['step_id'] : 0;
639 } else {
640 $step_id = get_the_ID();
641 }
642
643 // Check if this is an order bump product - if so, don't modify the quantity display
644 if( isset($cart_item['wpfnl_order_bump']) && $cart_item['wpfnl_order_bump'] ){
645 return $quantity;
646 }
647
648 // When Pro is active, its own `woocommerce_checkout_cart_item_quantity` filter
649 // (registered unconditionally) already builds the current quantity selector markup.
650 // Rebuilding it here with this legacy markup would overwrite/corrupt that output
651 // whenever this filter gets dynamically added during the order bump accept flow.
652 if ( Wpfnl_functions::is_wpfnl_pro_activated() ) {
653 return $quantity;
654 }
655
656 $isQuantity = get_post_meta($step_id, '_wpfnl_quantity_support',true);
657 $order_bump_product = get_post_meta($step_id,'order-bump-settings',true);
658 $quantityLimit = get_post_meta($step_id, '_wpfnl_quantity_limit', true);
659 if($isQuantity === 'yes'){
660 // Check if this is a legacy single order bump structure
661 if(isset($order_bump_product['product']) && isset($order_bump_product['isEnabled'])){
662
663 if( ($order_bump_product['product'] == $cart_item["product_id"]) && $order_bump_product['isEnabled'] == 'yes' ){
664 return $quantity;
665 }
666 }
667
668 $variations = json_encode($cart_item['variation']);
669 $product_id = $cart_item["product_id"];
670 $quantity = $cart_item["quantity"];
671 $variation_id = $cart_item["variation_id"];
672 $isQuantityLimit = false;
673 $set_quantity = 0;
674
675 if( isset($quantityLimit['isEnabled']) && $quantityLimit['isEnabled'] === 'yes' ){
676 $set_quantity = $quantityLimit['quantity'];
677 $isQuantityLimit = true;
678 }
679
680 if( $isQuantityLimit ){
681 $quantity = "× <input type='number' min='1' step='1' max='".$set_quantity."' value='".$quantity."' class='wpfnl-quantity-setect' data-product-id='".$product_id."' data-variation='".$variations."' data-variation-id='".$variation_id."' data-quantity-limit='".$set_quantity."' data-set-quantity='yes'/>";
682 }else{
683 $quantity = "× <input type='number' min='1' step='1' value='".$quantity."' class='wpfnl-quantity-setect' data-product-id='".$product_id."' data-variation='".$variations."' data-variation-id='".$variation_id."' data-set-quantity='no' />";
684 }
685 }
686 return $quantity;
687
688 }
689
690 }
691