Writing an addition to the site, want to modify the price in the cart. Have the following code:
function apd_product_custom_price($cart_item_data, $product_id)
{
if (isset($_POST['use_rewards']) && !empty($_POST['use_rewards']))
{
$cart_item_data['use_rewards'] = $_POST['use_rewards'];
}
return $cart_item_data;
}
add_filter('woocommerce_add_cart_item_data', 'apd_product_custom_price', 99, 2);
function apd_apply_custom_price_to_cart_item($cart_object)
{
if( !WC()->session->__isset( 'reload_checkout' )) {
foreach ($cart_object->cart_contents as $value) {
if(isset($value['use_rewards'])) {
$price = $value['data']->get_price() -
$value['use_rewards'];
$value['data']->set_price($price);
}
}
}
}
add_action('woocommerce_before_calculate_totals', 'apd_apply_custom_price_to_cart_item',10);
By some reason the hook woocommerce_before_calculate_totals fires twice. if I replace the code in the function apd_apply_custom_price_to_cart_item($cart_object) with just echo 1; it displays 11 in the cart page. Can some please help?
I forgot the link I should refer to for a solution. You can use this at the top of your function:
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
Most likely some other plugins hooks to 'woocommerce_before_calculate_totals' action.
Try to disable plugins one by one and see witch one is causing this behaviour.
In my case I found that WooCommerce-multilingual plugin hooks to woocommerce_before_calculate_totals action.
So I did this:
add_action( 'woocommerce_before_calculate_totals', '_wm_ponc_upte_prc', 99 );
function _wm_ponc_upte_prc( $cart_object ) {
if ( !WC()->session->__isset( "reload_checkout" ) ) {
foreach ( WC()->cart->get_cart() as $key => $value ) {
// checking if user checked checkbox (optional)
if ( isset( $value['wm_ponc_fee'] ) && ( $value[ 'wm_ponc_fee' ] === 'yes' ) ) {
// your calculations ....
// and Remove your action after you are done.
remove_action( 'woocommerce_before_calculate_totals', '_wm_ponc_upte_prc', 99 );
}
}
}
}
I have removed action after custom calculations. And that's it.
I see exactly the same issue - my hooked function is firing twice, leading to the option price being added at 2x the cost. My solution was to load the product, get the price from that and then add my incremental cost to that.
function tbk_woo_update_option_price( $cart_object ) {
$option_price = 3.50;
foreach ( $cart_object->get_cart() as $cart_item_key => $cart_item ) {
$item_id = $cart_item['data']->id;
//Hook seems to be firing twice for some reason... Get the price from the original product data, not from the cart...
$product = wc_get_product( $item_id );
$original_price = $product->get_price();
$new_price = $original_price + $option_price;
$cart_item['data']->set_price( $new_price );
}
}
add_action( 'woocommerce_before_calculate_totals', 'tbk_woo_update_option_price', 1000, 1 );
Related
I need to deactivate a checkout date picker generated by a plugin when a product on cart is virtual.
Here's the hook they gave for that:
apply_filters('woocommerce_delivery_disabled_dates', $disableDates);
Based on that information, this is my code attempt:
add_filter( 'woocommerce_checkout_fields' , 'disable_dates' );
function disable_dates( $fields ) {
$only_virtual = true;
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// Check if there are non-virtual products
if ( ! $cart_item['data']->is_virtual() ) $only_virtual = false;
}
if( $only_virtual ) {
apply_filters(‘woocommerce_delivery_disabled_dates’, $disableDates);
}
return $fields;
}
However this does not give the desired result, any advice how to hide the checkout date picker when the cart contains a virtual product?
The main issues is that $disabledDates is undefined - I would however change $fields to $disableDates as it makes a bit more sense. See below:
apply_filters('woocommerce_delivery_disabled_dates', $disableDates);
add_filter( 'woocommerce_checkout_fields' , 'disable_dates' );
function disable_dates( $disableDates ) {
$only_virtual = true;
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
// Check if there are non-virtual products
if ( ! $cart_item['data']->is_virtual() ) $only_virtual = false;
}
if( $only_virtual ) {
apply_filters('woocommerce_delivery_disabled_dates', $disableDates);
}
return $disableDates;
}
The $disableDates variable is the input argument for your callback of the hook which you named $fields ( I think )
Ps. this is just a guess based on the code you posted. There is quite a bit that is not clear to me, but $disableDates in your original code clearly should have a value.
No need to use the woocommerce_checkout_fields hook,
the logic can be applied in the appropriate hook.
So you get:
function filter_woocommerce_delivery_disabled_dates( $disableDates ) {
// WC Cart NOT null
if ( ! is_null( WC()->cart ) ) {
// Loop through cart items
foreach ( WC()->cart->get_cart_contents() as $cart_item_key => $cart_item ) {
// Check if there are virtual products
if ( $cart_item['data']->is_virtual() ) {
$disableDates = true;
break;
}
}
}
return $disableDates;
}
add_filter( 'woocommerce_delivery_disabled_dates', 'filter_woocommerce_delivery_disabled_dates', 10, 1 );
I am working on a WordPress site that uses WooCommerce. I found this piece of code that allows me to set a freight shipping option based on weight. It worked great until the client also wanted the same freight shipping option if a freight shipping class is selected on a product.
add_filter( 'woocommerce_package_rates', 'bbloomer_woocommerce_tiered_shipping', 10, 2 );
function bbloomer_woocommerce_tiered_shipping( $rates, $package ) {
if ( WC()->cart->cart_contents_weight < 300 ) {
if ( isset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'], $rates['fedex:FEDEX_GROUND'] ) );
} else {
if ( isset( $rates['flat_rate:10'] ) ) unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'],$rates['fedex:FEDEX_GROUND'] );
}
return $rates;
}
Then I found another piece of code that could set a shipping class to a shipping option. The code I found was mean to unset shipping options that are available but I want to isset the freight shipping since we unset it via the cart weight. However, I am running into an issue where the freight shipping class can no longer be set to isset without causing major issues. Below is what I tried to do for the freight shipping.
add_filter( 'woocommerce_package_rates', 'freight_shipping_class_only', 10, 2 );
function freight_shipping_class_only( $rates, $package ) {
$shipping_class_target = 193;
$in_cart = false;
foreach( WC()->cart->cart_contents as $key => $values ) {
if( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
$in_cart = true;
break;
}
}
if( $in_cart ) {
unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'],$rates['fedex:FEDEX_GROUND'] ); // shipping method with ID (to find it, see screenshot below)
isset( $rates['flat_rate:10'] );
}
return $rates;
}
Is there a way to set both the cart weight and the freight shipping class in one function? It would be ideal since I want them both to do the exact same thing by unset all FedEx shipping option and isset the freight shipping option.
Let me know if you need me to be clearer or if you have any tips.
Thanks!
As you are using 2 times the same hook, you can simply merge your functions in one.
Now with php [isset()][1] you need to use it in as a condition in an IF statement to check if a variable is set (exist), but it has no effect alone outside your IF statement in your function.
So in your first function the following doesn't has any effect:
if ( isset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'], $rates['fedex:FEDEX_GROUND'] ) );
So you can try this compact and efficient code instead:
add_filter( 'woocommerce_package_rates', 'freight_shipping_class_only', 20, 2 );
function freight_shipping_class_only( $rates, $package ) {
// HERE the targeted shipping class ID
$targeted_shipping_class = 193;
// Loop through cart items and checking
$found = false;
foreach( $package['contents'] as $item ) {
if( $item['data']->get_shipping_class_id() == $targeted_shipping_class ){
$found = true;
break;
}
}
// The condition
if ( isset( $rates['flat_rate:10'] ) && ( WC()->cart->get_cart_contents_weight() >= 300 || $found ) ){
unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'], $rates['fedex:FEDEX_GROUND'] );
}
return $rates;
}
Code goes in function.php file of the active child theme (or active theme). It should work.
I found a solution, it isn't exactly what I wanted but it works. What I did was change the cart weight to anything over 300 pounds unset the FedEx shipping options. Then unset FedEx shipping options for the freight shipping class. Which still left the freight shipping option visible on non-freight items, which I hid using CSS.
I am still open for a better way to do this. So if you have suggestions please send them my way.
Below is what I ended up doing.
add_filter( 'woocommerce_package_rates', 'bbloomer_woocommerce_tiered_shipping', 10, 2 );
function bbloomer_woocommerce_tiered_shipping( $rates, $package ) {
if ( WC()->cart->cart_contents_weight < 300 ) {
if ( isset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'], $rates['fedex:FEDEX_GROUND'] ) );
} else {
if ( isset( $rates['flat_rate:10'] ) ) unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'],$rates['fedex:FEDEX_GROUND'] );
}
return $rates;
}
/** Freight Shipping Class Only **/
add_filter( 'woocommerce_package_rates', 'freight_shipping_class_only', 10, 2 );
function freight_shipping_class_only( $rates, $package ) {
$shipping_class_target = 193;
$in_cart = false;
foreach( WC()->cart->cart_contents as $key => $values ) {
if( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
$in_cart = true;
break;
}
}
if( $in_cart ) {
unset( $rates['fedex:PRIORITY_OVERNIGHT'], $rates['fedex:FEDEX_2_DAY'],$rates['fedex:FEDEX_GROUND'] );
}
return $rates;
}
Currently i have some custom calculation of product price based on different situation. When customer added a product in to cart then the custom price is set in session data , cart_item_data['my-price'] and i implemented using add_filter( 'woocommerce_add_cart_item') function and everything seems to working now .
Now the price in view cart page, checkout page is correct with my cart_item_data['my-price'].
But the only problem i am facing is the price is not updated in woocommerce mini cart that is appeared in the menu ,How can i change this ?
When i google i see a filter
add_filter('woocommerce_cart_item_price');
but i can't understand how to use this i do the following
add_filter('woocommerce_cart_item_price','modify_cart_product_price',10,3);
function modify_cart_product_price( $price, $cart_item, $cart_item_key){
if($cart_item['my-price']!==0){
$price =$cart_item['my-price'];
}
return $price;
//exit;
}
Here individual price is getting correct , but total price is wrong
Updated (october 2021)
For testing this successfully (and as I don't know how you make calculations), I have added a custom hidden field in product add to cart form with the following:
// The hidden product custom field
add_action( 'woocommerce_before_add_to_cart_button', 'add_gift_wrap_field' );
function add_gift_wrap_field() {
global $product;
// The fake calculated price
?>
<input type="hidden" id="my-price" name="my-price" value="115">
<?php
}
When product is added to cart, this my-price custom field is also submitted (posted). To set this value in cart object I use the following function:
add_filter( 'woocommerce_add_cart_item', 'custom_cart_item_prices', 20, 2 );
function custom_cart_item_prices( $cart_item_data, $cart_item_key ) {
// Get and set your price calculation
if( isset( $_POST['my-price'] ) ){
$cart_item_data['my-price'] = $_POST['my-price'];
// Every add to cart action is set as a unique line item
$cart_item_data['unique_key'] = md5( microtime().rand() );
}
return $cart_item_data;
}
Now to apply (set) the new calculated price my-price to the cart item, I use this last function:
// For mini cart *(cart item displayed price)*
add_action( 'woocommerce_cart_item_price', 'filter_cart_item_price', 10, 2 );
function filter_cart_item_price( $price, $cart_item ) {
if ( ! is_checkout() && isset($cart_item['my-price']) ) {
$args = array( 'price' => floatval( $cart_item['my-price'] ) );
if ( WC()->cart->display_prices_including_tax() ) {
$product_price = wc_get_price_including_tax( $cart_item['data'], $args );
} else {
$product_price = wc_get_price_excluding_tax( $cart_item['data'], $args );
}
return wc_price( $product_price );
}
return $price;
}
add_action( 'woocommerce_before_calculate_totals', 'set_calculated_cart_item_price', 20, 1 );
function set_calculated_cart_item_price( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Loop through cart items
foreach ( $cart->get_cart() as $cart_item ){
if( isset( $cart_item['my-price'] ) && ! empty( $cart_item['my-price'] ) || $cart_item['my-price'] != 0 ){
// Set the calculated item price (if there is one)
$cart_item['data']->set_price( $cart_item['my-price'] );
}
}
}
All code goes in function.php file of your active child theme (or active theme).
Tested and works
I am getting the dynamic custom price in a variable that I want to pass to the hooked function in woocommerce_before_calculate_totals hook in cart. But it isn't working.
This is my code:
$add=200; //I want to pass this variable in add_action hook
add_action( 'woocommerce_before_calculate_totals', 'add_custom_total_price');
function add_custom_total_price($cart_object) {
global $woocommerce;
$custom_price =$add; // This is my custom price
foreach ( $cart_object->cart_contents as $key => $value ) {
$value['data']->price = $custom_price;
}
}
How can I achieve this?
Thanks
To make it work you just need to define $custom_price variables as global in your function, this way:
$custom_price = 200;
add_action( 'woocommerce_before_calculate_totals', 'add_custom_item_price', 10, 1 );
function add_custom_item_price( $cart_object ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
global $custom_price;
foreach ( $cart_object->get_cart() as $item_values ) {
$item_values['data']->price = $custom_price;
}
}
This code is tested and working (for woocommerce versions 2.5+ and 2.6+).
Naturally this code goes on function.php file of your active child theme or theme.
To add Price in your woocommerce cart before calculate total -
add_action( 'woocommerce_before_calculate_totals', 'add_custom_total_price');
function add_custom_total_price($cart_object) {
global $woocommerce,$add,$custom_price;
$add=200;//Dynamic Price variable
$custom_price =$add;//Dynamic Price variable pass to custom price
foreach ( $cart_object->cart_contents as $key => $value ) {
$value['data']->price = $custom_price;
}
}
$replace_order = new WC_Cart();
$replace_order->empty_cart( true );
$replace_order->add_to_cart( "256", "1");
The above code add product 256 to the Cart 1 time. But the issue I'm having is that I want to be able to completely override the product price... as far as I can tell, the only thing I can do it apply a coupon to the Cart.
Is there a way to completely override the price to something totally custom?
Here is the code for overriding price of product in cart
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price' );
function add_custom_price( $cart_object ) {
$custom_price = 10; // This will be your custome price
foreach ( $cart_object->cart_contents as $key => $value ) {
$value['data']->price = $custom_price;
// for WooCommerce version 3+ use:
// $value['data']->set_price($custom_price);
}
}
Hope it will be useful...
You need to introduce an if statement for checking product id, in above code:
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price' );
function add_custom_price( $cart_object ) {
$custom_price = 10; // This will be your custome price
$target_product_id = 598;
foreach ( $cart_object->cart_contents as $value ) {
if ( $value['product_id'] == $target_product_id ) {
$value['data']->price = $custom_price;
}
/*
// If your target product is a variation
if ( $value['variation_id'] == $target_product_id ) {
$value['data']->price = $custom_price;
}
*/
}
}
Add this code anywhere and make sure that this code is always executable.
After adding this code, when you'll call:
global $woocommerce;
$woocommerce->cart->add_to_cart(598);
Only this product will be added with overridden price, other products added to cart will be ignored for overriding prices.
Hope this will be helpful.
I have tried all above code samples and latest woocommerce 3.0 is not support any of the above example. Use below code and working perfectly for me.
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price' );
function add_custom_price( $cart_object ) {
$custom_price = 10; // This will be your custom price
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
$cart_item['data']->set_price($custom_price);
}
}
After release of woocommerce version 3.0.0 product price is update on add to cart using set_price($price) function. The example is given as below :
add_action( 'woocommerce_before_calculate_totals', 'mj_custom_price' );
function mj_custom_price( $cart_object ) {
$woo_ver = WC()->version;
$custom_price = 10;
foreach ( $cart_object->cart_contents as $key => $value )
{
if($woo_ver < "3.0.0" && $woo_ver < "2.7.0")
{
$value['data']->price = $custom_price;
}
else
{
$value['data']->set_price($custom_price);
}
}
}
Many Thanks
For the Wordpress and Woocommerce latest version,Please use like this
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price' );
function add_custom_price( $cart_object ) {
foreach ( $cart_object->cart_contents as $key => $value ) {
$custom_price = 5;
$value['data']->set_price($custom_price);
}
}
For eveeryone that got here from Google. The above is now deprecated as i found out updating to WooCommerce 3.0.1.
Instead of the above you now need to use set_price instead of price
Here is an example:
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price' );
function add_custom_price( $cart_object ) {
$custom_price = 10; // This will be your custome price
foreach ( $cart_object->cart_contents as $key => $value ) {
$value['data']->set_price = $custom_price;
}
}
I hope this helps people in the future :)
This is how i did it, first i add my custom price to cart_item_data witch can save custom data to cart items, then i use woocommerce_before_calculate_totals, loop the cart and add the previously added price.
function add_donation_to_cart() {
$cart_item_data = array('price' => $_REQUEST['donate_amount']);
$woocommerce->cart->add_to_cart( 5395, 1, '', array(), $cart_item_data);
}
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price' );
function add_custom_price( $cart ) {
foreach ( $cart->cart_contents as $key => $value ) {
$value['data']->price = $value['price'];
}
}
With WooCommerce 2.5 I found this to be a 2-part process. The first step is to change the run-time display of pricing when added to the cart via the woocommerce_add_cart_item filter. The second part is to set the persistent session data which is read during checkout via the woocommerce_get_cart_item_from_session filter. This seems to be faster than hooking the calculate totals filters (such as woocommerce_before_calculate_totals) as they are run very frequently in WooCommerce.
More details here:
woocommerce change price while add to cart
To make it dynamic ( override price for each item in cart separately ), you need to save the override product price in session with cart item key as session key using woocommerce_add_to_cart hook.
by using these session values you can calculate correct Cart Total and make the altered price appear in the Order Item as well
You can use the following
add_filter( 'woocommerce_cart_item_price', 'kd_custom_price_message' );
function kd_custom_price_message( $price ) {
$textafter = ' USD';
return $price . $textafter;
}