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
Looking again at my function that sets the custom prices site-wide, I realised that it is taking the
regular_price
andsale_price
specifically (as well as the customer's custom discounted price) in its logic, returning the lowest of the 3 prices: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!
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:
It should work.