skip to Main Content

I am working on a WooCommerce extension that manipulates pricing.
It needs to filter pricing dynamically throughout the store based on user and product.
It also needs to be able to force certain cart items to be free.

Both of these functions work on their own but when I have both together, the free cart item is having its price (zero) overridden by the function that dynamically sets product prices.

I currently have it applying a customer’s unique pricing like so (I’ve stripped out a lot of code to keep this relevant to the issue):

add_filter('woocommerce_product_get_price', 'wcds_custom_price', 10, 2);
function wcds_custom_price($price, $product)
{
    if (did_action('woocommerce_product_get_price') >= 2) {
        return;
    }
    // get price specific to user and product
    $user_id = get_current_user_id();
    $price = wcds_get_price($product, $user_id); // This works
    return $price;
}

This works fine.

I then need to override these prices to zero for certain items that are programmatically added to the cart. The free items are up to a maximum qty – any further quantity of the giveaway product is added as a seperate order item without overriding the price (again I’ve kept this to the relevant code):


add_filter('woocommerce_before_calculate_totals', 'wcds_apply_giveaway_pricing', 999, 1);
function wcds_apply_giveaway_pricing($cart)
{

    // Do not modify prices in admin
    if (is_admin()) {
        return;
    }

    if (did_action('woocommerce_before_calculate_totals') >= 2) {
        return;
    }

    // Example of $giveaway_products (actually set dynamically)
    $giveaway_products = array(
        '107964' => array
        (
            'qty' => '2',
            'giveaway_post_id' => '127753'
        )
    );


    // Example of $giveaway_products (actually set dynamically from cart contents)
    $total_quantities = array(
        '107964' => '5'
    );


    $to_add = array();

    // Loop through cart items and split giveaways
    foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
        $product_id = $cart_item['product_id'];

        if (array_key_exists($product_id, $giveaway_products)) {
            $total_quantity = $total_quantities[$product_id];
            $free_quantity = $giveaway_products[$product_id]['qty'];
            $free_quantity = min($total_quantity, $free_quantity);
            $paid_quantity = max($total_quantity - $free_quantity, 0);

            $to_add[$product_id] = [
                'paid' => $paid_quantity,
                'free' => $free_quantity,
                'giveaway_post_id' => $giveaway_products[$product_id]['giveaway_post_id'],
            ];

            $cart->remove_cart_item($cart_item_key); // Remove original rows for giveaway products
        } else {
            unset($cart_item['giveaway']); // Remove the giveaway flag if not in the giveaway list
        }
    }

    // Add the giveaway products to the cart with the adjusted quantities
    foreach ($to_add as $product_id => $data) {
        // Add paid products to cart
        if ($data['paid'] > 0) {
            $cart->add_to_cart($product_id, $data['paid']);
        }

        // Add free giveaway products to cart
        if ($data['free'] > 0) {
            $giveaway_id = $data['giveaway_post_id'];
            $cart_item_key = $cart->add_to_cart($product_id, $data['free'], '', '', [
                'giveaway' => true,
                'giveaway_id' => $giveaway_id,
            ]);
            // Adjust the price for giveaway items
            $cart_item = $cart->get_cart_item($cart_item_key);
            $cart_item['data']->set_price(0);
            $cart_item['data']->set_name('FREE: ' . $cart_item['data']->get_name()); // Optionally change name for giveaways
        }
    }

}

This second part is successfully setting the name of the relevant cart item but the price is not zero, it is still being set by wcds_custom_price.

If I remove add_filter('woocommerce_product_get_price', 'wcds_custom_price', 10, 2); , then the price of the added cart item is successfully set to zero.

It seems that woocommerce_product_get_price is firing several times AFTER woocommerce_before_calculate_totals (I’ve also tried woocommerce_get_cart_item_from_session) which is setting the price again after I set it to zero.

How can I get both functionalities working?

2

Answers


  1. Chosen as BEST ANSWER

    Looking again at my function that sets the custom prices site-wide, I realised that it is taking the regular_price and sale_price specifically (as well as the customer's custom discounted price) in its logic, returning the lowest of the 3 prices:

    add_filter('woocommerce_product_get_price', 'wcds_custom_price', 10, 2);
    function wcds_custom_price($product, $user_id = null)
    {
        if (did_action('woocommerce_product_get_price') >= 2) {
            return;
        }
    
        // This method does not provision for Variable Products!
        $price_data = array();
    
        // Regular Trade Price
        $regular_price = $product->get_regular_price();
        $price_data['Trade'] = $regular_price;
    
        // Sale / Promotional Price
        $sale_price = $product->get_sale_price();
        if (!empty($sale_price) || $sale_price == 0) {
            $price_data['Promotion'] = $sale_price;
        }
    
        // Customer-discounted Price
        $discount = wcds_get_customer_discount($product, $user_id);
        if ($discount > 0) {
            $discounted_trade_price = floatval($regular_price) * (100 - floatval($discount)) / 100;
            $price_data['Your Price'] = $discounted_trade_price;
        }
    
        return min($price_data);
    
    }
        
    

    So, in my function hooked to woocommerce_before_calculate_totals, I just changed $new_cart_item['data']->set_price(0); to $new_cart_item['data']->set_sale_price(0);.

    This now works perfectly!

    add_filter('woocommerce_before_calculate_totals', 'wcds_apply_giveaway_pricing', 999, 1);
    function wcds_apply_giveaway_pricing($cart)
    {
    
        // Do not modify prices in admin
        if (is_admin()) {
            return;
        }
    
        if (did_action('woocommerce_before_calculate_totals') >= 2) {
            return;
        }
    
        // Example of $giveaway_products (actually set dynamically)
        $giveaway_products = array(
            '107964' => array
            (
                'qty' => '2',
                'giveaway_post_id' => '127753'
            )
        );
    
    
        // Example of $giveaway_products (actually set dynamically from cart contents)
        $total_quantities = array(
            '107964' => '5'
        );
    
    
        $to_add = array();
    
        // Loop through cart items and split giveaways
        foreach ($cart->get_cart() as $cart_item_key => $cart_item) {
            $product_id = $cart_item['product_id'];
    
            if (array_key_exists($product_id, $giveaway_products)) {
                $total_quantity = $total_quantities[$product_id];
                $free_quantity = $giveaway_products[$product_id]['qty'];
                $free_quantity = min($total_quantity, $free_quantity);
                $paid_quantity = max($total_quantity - $free_quantity, 0);
    
                $to_add[$product_id] = [
                    'paid' => $paid_quantity,
                    'free' => $free_quantity,
                    'giveaway_post_id' => $giveaway_products[$product_id]['giveaway_post_id'],
                ];
    
                $cart->remove_cart_item($cart_item_key); // Remove original rows for giveaway products
            } else {
                unset($cart_item['giveaway']); // Remove the giveaway flag if not in the giveaway list
            }
        }
    
        // Add the giveaway products to the cart with the adjusted quantities
        foreach ($to_add as $product_id => $data) {
            // Add paid products to cart
            if ($data['paid'] > 0) {
                $cart->add_to_cart($product_id, $data['paid']);
            }
    
            // Add free giveaway products to cart
            if ($data['free'] > 0) {
                $giveaway_id = $data['giveaway_post_id'];
                $cart_item_key = $cart->add_to_cart($product_id, $data['free'], '', '', [
                    'giveaway' => true,
                    'giveaway_id' => $giveaway_id,
                ]);
                // Adjust the price for giveaway items
                $cart_item = $cart->get_cart_item($cart_item_key);
                $cart_item['data']->set_sale_price(0);
                $cart_item['data']->set_name('FREE: ' . $cart_item['data']->get_name()); // Optionally change name for giveaways
            }
        }
    
    }
    

  2. You can try to use a conditional function that check in your 1st function if the current product is a giveaway in cart, avoiding changing its price, if it’s the case:

    // Conditional function
    function is_this_a_giveaway_in_cart( $product_id ) {
        if( is_admin() || ! WC()->cart ) {
            return;
        }
        $cart_items  = WC()->cart->get_cart();
    
        return $cart_items && in_array( $product_id, array_column( $cart_items, 'giveaway_id' ) );
    }
    
    // Changing product price based on user
    add_filter('woocommerce_product_get_price', 'wcds_custom_price', 10, 2);
    function wcds_custom_price($price, $product)
    {
        if ( did_action('woocommerce_product_get_price') >= 2 ) {
            return;
        }
    
        if ( is_this_a_giveaway_in_cart( $product->get_id() ) ) {
            return;
        }
    
        // Get price specific to user and product
        return wcds_get_price( $product, get_current_user_id() );
    }
    

    It should work.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search