diff --git a/modules/upsell-order-bump/assets/src/components/appearance/template/overview-area/OverViewArea.js b/modules/upsell-order-bump/assets/src/components/appearance/template/overview-area/OverViewArea.js index 719d648c..717da48c 100644 --- a/modules/upsell-order-bump/assets/src/components/appearance/template/overview-area/OverViewArea.js +++ b/modules/upsell-order-bump/assets/src/components/appearance/template/overview-area/OverViewArea.js @@ -15,7 +15,9 @@ function OverViewArea(props) { var offerMessage = "20% off only for you"; if ( createBumpData.offer_type === 'discount' ) { - offerAmout = createBumpData.offer_product_regular_price - createBumpData.offer_product_regular_price*createBumpData.offer_amount/100; + // Use the current price (sale price if available, otherwise regular price) for discount calculation + const currentPrice = createBumpData.offer_product_regular_price; + offerAmout = currentPrice - currentPrice*createBumpData.offer_amount/100; offerMessage = createBumpData.offer_amount + "% off only for you"; } else if ( createBumpData.offer_type === 'price' ) { offerAmout = createBumpData.offer_amount; diff --git a/modules/upsell-order-bump/includes/EnqueueScript.php b/modules/upsell-order-bump/includes/EnqueueScript.php index edeecf3d..0fe2830c 100644 --- a/modules/upsell-order-bump/includes/EnqueueScript.php +++ b/modules/upsell-order-bump/includes/EnqueueScript.php @@ -183,11 +183,11 @@ public function prodcut_list() { ); $_product = wc_get_product( $product->ID ); - $sale_price = $_product->get_sale_price(); + $current_price = $_product->get_price(); $regular_price = $_product->get_regular_price(); // Prepare woocommerce price data. - $price = esc_html( $regular_price ); + $price = esc_html( $current_price ); $price = wp_strip_all_tags( html_entity_decode( wc_price( $price ) ) ); // Render woocommerce price with currency symbol. @@ -206,7 +206,7 @@ public function prodcut_list() { // Get categories csv. $category_names = implode( ', ', $category_names ); - if ( $_product->is_type( 'simple' ) && $regular_price ) { + if ( $_product->is_type( 'simple' ) && $current_price ) { $simple_product_for_offer[] = array( 'price' => $price, 'value' => $product->ID, @@ -259,11 +259,20 @@ public function prodcut_list_for_view() { $_product = wc_get_product( $product->ID ); if ( $_product->is_type( 'simple' ) ) { + // Use get_price() method directly since it gives the current price + $current_price = $_product->get_price(); + $regular_price = $_product->get_regular_price(); + + // Ensure we have valid numeric values for number_format + $current_price = is_numeric( $current_price ) ? (float) $current_price : 0.00; + $regular_price = is_numeric( $regular_price ) ? (float) $regular_price : $current_price; + $product_list_for_view[ $product->ID ] = array( 'ID' => $product->ID, 'post_title' => $_product->get_title(), 'image_url' => wp_get_attachment_url( get_post_thumbnail_id( $product->ID ), 'thumbnail' ), - 'regular_price' => number_format( (int) $_product->get_regular_price(), 2 ), + 'regular_price' => number_format( $regular_price, 2 ), + 'current_price' => number_format( $current_price, 2 ), ); } if ( $_product->is_type( 'variable' ) ) { @@ -272,7 +281,21 @@ public function prodcut_list_for_view() { foreach ( $variations as $variation ) { $variation_id = $variation['variation_id']; $variation_attributes = $variation['attributes']; - $regular_price = number_format( $variation['display_regular_price'], 2 ); + $variation_product = wc_get_product( $variation_id ); + + // Ensure we have a valid variation product + if ( ! $variation_product ) { + continue; + } + + $current_variation_price = $variation_product->get_price(); + $variation_regular_price = $variation_product->get_regular_price(); + + // Ensure we have valid numeric values for number_format + $current_variation_price = is_numeric( $current_variation_price ) ? (float) $current_variation_price : 0.00; + $variation_regular_price = is_numeric( $variation_regular_price ) ? (float) $variation_regular_price : $current_variation_price; + + $formatted_price = number_format( $current_variation_price, 2 ); $variation_root_name = $_product->get_title(); $variation_name = $variation_root_name . '(' . implode( ', ', $variation_attributes ) . ')'; $image_url = $variation['image']['url']; @@ -281,7 +304,8 @@ public function prodcut_list_for_view() { 'ID' => $variation_id, 'post_title' => $variation_name, 'image_url' => $image_url, - 'regular_price' => $regular_price, + 'regular_price' => number_format( $variation_regular_price, 2 ), + 'current_price' => $formatted_price, ); } } diff --git a/modules/upsell-order-bump/includes/OrderBump.php b/modules/upsell-order-bump/includes/OrderBump.php index ed1d64dd..149657e7 100644 --- a/modules/upsell-order-bump/includes/OrderBump.php +++ b/modules/upsell-order-bump/includes/OrderBump.php @@ -28,6 +28,8 @@ class OrderBump implements HookRegistry { public function register_hooks(): void { add_action( 'woocommerce_review_order_before_submit', array( $this, 'bump_product_frontend_view' ) ); add_action( 'woocommerce_before_calculate_totals', array( $this, 'woocommerce_custom_price_to_cart_item' ) ); + add_action( 'woocommerce_cart_item_removed', array( $this, 'handle_target_product_removal' ), 10, 2 ); + add_action( 'woocommerce_after_cart_item_quantity_update', array( $this, 'handle_cart_quantity_update' ), 10, 4 ); } /** @@ -46,12 +48,33 @@ public function bump_product_frontend_view() { $bump_list = get_posts( $args_bump ); foreach ( $all_cart_products as $value ) { + // Get categories from the current cart item (variation or simple product) $cat_ids = $value['data']->get_category_ids(); foreach ( $cat_ids as $cat_id ) { - $all_cart_category_ids[] = $cat_id; + $all_cart_category_ids[] = (int) $cat_id; + } + + // For variable products, also get categories from the parent product + if ( isset($value['variation_id']) && $value['variation_id'] > 0 ) { + $parent_product = wc_get_product( $value['product_id'] ); + if ( $parent_product ) { + $parent_cat_ids = $parent_product->get_category_ids(); + foreach ( $parent_cat_ids as $cat_id ) { + $all_cart_category_ids[] = (int) $cat_id; + } + } + } + + // For variable products, include both parent product ID and variation ID + // Convert to integers to ensure consistent data types + $all_cart_product_ids[] = (int) $value['product_id']; + if ( isset($value['variation_id']) && $value['variation_id'] > 0 ) { + $all_cart_product_ids[] = (int) $value['variation_id']; } - $all_cart_product_ids[] = $value['variation_id'] === 0 ? $value['product_id'] : $value['variation_id']; } + + // Remove duplicate category IDs + $all_cart_category_ids = array_unique( $all_cart_category_ids ); foreach ( $bump_list as $bump ) { $bump_info = maybe_unserialize( $bump->post_excerpt ); $bump_info = (object) $bump_info; @@ -64,13 +87,15 @@ public function bump_product_frontend_view() { $checked = 'checked'; } - $_product = wc_get_product( $offer_product_id ); - $regular_price = $_product->get_regular_price(); - if ( 'discount' === $offer_type ) { - $offer_price = ( $regular_price - ( $regular_price * $offer_amount / 100 ) ); - } else { - $offer_price = $offer_amount; - } + $_product = wc_get_product( $offer_product_id ); + $regular_price = $_product->get_regular_price(); + // Use sale price if available, otherwise use regular price for discount calculation + $current_price = $_product->get_sale_price() ? $_product->get_sale_price() : $regular_price; + if ( 'discount' === $offer_type ) { + $offer_price = ( $current_price - ( $current_price * $offer_amount / 100 ) ); + } else { + $offer_price = $offer_amount; + } $cart = WC()->cart; $product_already_added_from_shop = false; @@ -94,12 +119,18 @@ public function bump_product_frontend_view() { $bump_type = ! empty( $bump_info->bump_type ) ? esc_html( $bump_info->bump_type ) : 'products'; if ( $bump_type === 'products' ) { $target_products = ! empty( $bump_info->target_products ) ? wc_clean( $bump_info->target_products ) : array(); - if ( $target_products && ( count( $all_cart_product_ids ) !== count( array_diff( $all_cart_product_ids, $target_products ) ) ) ) { + // Convert target products to integers to ensure consistent data types + $target_products = array_map( 'intval', $target_products ); + + // Check if any target products are in cart (simple intersection check) + if ( $target_products && ! empty( array_intersect( $all_cart_product_ids, $target_products ) ) ) { include __DIR__ . '/../templates/bump-product-front-view.php'; } } else { $target_categories = ! empty( $bump_info->target_categories ) ? wc_clean( $bump_info->target_categories ) : array(); - if ( $target_categories && ( count( $all_cart_category_ids ) !== count( array_diff( $all_cart_category_ids, $target_categories ) ) ) ) { + + // Check if any target categories are in cart (simple intersection check) + if ( $target_categories && ! empty( array_intersect( $all_cart_category_ids, $target_categories ) ) ) { include __DIR__ . '/../templates/bump-product-front-view.php'; } } @@ -121,4 +152,164 @@ public function woocommerce_custom_price_to_cart_item( $cart_object ) { } } } + + /** + * Handle when a target product is removed from cart - clean up any related bump products. + * + * @param string $cart_item_key The cart item key. + * @param object $cart The cart object. + */ + public function handle_target_product_removal( $cart_item_key, $cart ) { + $removed_item = $cart->removed_cart_contents[ $cart_item_key ]; + if ( ! $removed_item ) { + return; + } + + $removed_product_id = $removed_item['product_id']; + $removed_variation_id = isset( $removed_item['variation_id'] ) ? $removed_item['variation_id'] : 0; + + // For variable products, check both parent and variation IDs + $removed_item_ids = array( $removed_product_id ); + if ( $removed_variation_id > 0 ) { + $removed_item_ids[] = $removed_variation_id; + } + + $this->validate_bump_products_after_removal( $removed_item_ids ); + } + + /** + * Handle cart quantity updates - if quantity becomes 0, treat as removal. + * + * @param string $cart_item_key The cart item key. + * @param int $quantity New quantity. + * @param int $old_quantity Old quantity. + * @param object $cart The cart object. + */ + public function handle_cart_quantity_update( $cart_item_key, $quantity, $old_quantity, $cart ) { + // If quantity was reduced to 0, the item is effectively removed + if ( $quantity === 0 && $old_quantity > 0 ) { + $cart_item = $cart->cart_contents[ $cart_item_key ]; + if ( $cart_item ) { + $product_id = $cart_item['product_id']; + $variation_id = isset( $cart_item['variation_id'] ) ? $cart_item['variation_id'] : 0; + + // For variable products, check both parent and variation IDs + $item_ids = array( $product_id ); + if ( $variation_id > 0 ) { + $item_ids[] = $variation_id; + } + + $this->validate_bump_products_after_removal( $item_ids ); + } + } + } + + /** + * Validate and clean up bump products when their target products are removed. + * + * @param array $removed_item_ids Array of removed product/variation IDs. + */ + private function validate_bump_products_after_removal( $removed_item_ids ) { + // Get all bump offers + $args_bump = array( + 'post_type' => 'sgsb_order_bump', + 'posts_per_page' => -1, + ); + $bump_list = get_posts( $args_bump ); + + foreach ( $bump_list as $bump ) { + $bump_info = maybe_unserialize( $bump->post_excerpt ); + $bump_info = (object) $bump_info; + + $bump_type = ! empty( $bump_info->bump_type ) ? esc_html( $bump_info->bump_type ) : 'products'; + $offer_product_id = $bump_info->offer_product; + + // Check if the removed item was a target for this bump offer + $was_target = false; + + if ( $bump_type === 'products' ) { + $target_products = ! empty( $bump_info->target_products ) ? wc_clean( $bump_info->target_products ) : array(); + $was_target = ! empty( array_intersect( $removed_item_ids, $target_products ) ); + } else { + // For category-based bumps, check if removed product was in target categories + $was_target = false; + foreach ( $removed_item_ids as $removed_item_id ) { + $removed_product_categories = wp_get_post_terms( $removed_item_id, 'product_cat', array( 'fields' => 'ids' ) ); + $target_categories = ! empty( $bump_info->target_categories ) ? wc_clean( $bump_info->target_categories ) : array(); + if ( ! empty( array_intersect( $removed_product_categories, $target_categories ) ) ) { + $was_target = true; + break; + } + } + } + + if ( $was_target ) { + // Check if there are still other target products in cart + $remaining_targets = $this->get_remaining_target_products( $bump_info, $bump_type ); + + if ( empty( $remaining_targets ) ) { + // No more target products in cart, remove or reset the bump product + $this->handle_orphaned_bump_product( $offer_product_id ); + } + } + } + } + + /** + * Get remaining target products in cart for a bump offer. + * + * @param object $bump_info The bump offer info. + * @param string $bump_type The bump type (products or categories). + * @return array Array of remaining target product IDs. + */ + private function get_remaining_target_products( $bump_info, $bump_type ) { + $cart = WC()->cart; + $remaining_targets = array(); + + foreach ( $cart->get_cart() as $cart_item ) { + $cart_product_id = $cart_item['product_id']; + $cart_variation_id = isset( $cart_item['variation_id'] ) ? $cart_item['variation_id'] : 0; + + // For variable products, check both parent and variation IDs + $cart_item_ids = array( $cart_product_id ); + if ( $cart_variation_id > 0 ) { + $cart_item_ids[] = $cart_variation_id; + } + + if ( $bump_type === 'products' ) { + $target_products = ! empty( $bump_info->target_products ) ? wc_clean( $bump_info->target_products ) : array(); + if ( ! empty( array_intersect( $cart_item_ids, $target_products ) ) ) { + $remaining_targets = array_merge( $remaining_targets, $cart_item_ids ); + } + } else { + $cart_categories = wp_get_post_terms( $cart_product_id, 'product_cat', array( 'fields' => 'ids' ) ); + $target_categories = ! empty( $bump_info->target_categories ) ? wc_clean( $bump_info->target_categories ) : array(); + if ( ! empty( array_intersect( $cart_categories, $target_categories ) ) ) { + $remaining_targets = array_merge( $remaining_targets, $cart_item_ids ); + } + } + } + + return $remaining_targets; + } + + /** + * Handle orphaned bump product - remove it from cart. + * + * @param int $offer_product_id The bump offer product ID. + */ + private function handle_orphaned_bump_product( $offer_product_id ) { + $cart = WC()->cart; + + foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) { + $cart_product_id = $cart_item['product_id']; + $cart_variation_id = isset( $cart_item['variation_id'] ) ? $cart_item['variation_id'] : 0; + $cart_item_id = $cart_variation_id > 0 ? $cart_variation_id : $cart_product_id; + + // Check if this is the bump product with custom pricing and remove it + if ( $cart_item_id == $offer_product_id && isset( $cart_item['custom_price'] ) ) { + $cart->remove_cart_item( $cart_item_key ); + } + } + } }