I use this to unset shipping for product with specific tag:
function specific_products_shipping_methods( $rates, $package ){
// etiquette cocolis
$terms = array( 'cocolis' );
$taxonomy = 'product_tag';
foreach( $package['contents'] as $cart_item ) {
if ( has_term( $terms, $taxonomy, $cart_item['product_id'] ) )
{
unset( $rates['oik_weight_zone_shipping_49'] );
unset( $rates['chrono10'] );
unset( $rates['chrono13'] );
add_filter('woocommerce_shipping_chosen_method', 'reset_default_shipping_method', 10, 2);
}
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'specific_products_shipping_methods', 10, 2 );
function reset_default_shipping_method( $method, $available_methods ) {
$default_method = 'per_product';
if( array_key_exists($method, $available_methods ) )
return $default_method;
else
return $method;
}
This works fine until update woo 4.8.0
Any idea what’s wrong ?
Thx,
There are some mistakes in your code, try the following revisited and optimized code instead:
add_filter( 'woocommerce_package_rates', 'specific_products_shipping_methods', 10, 2 );
function specific_products_shipping_methods( $rates, $package ){
$terms = array('cocolis'); // Array of term slugs, names or ids
$taxonomy = 'product_tag'; // WooCommerce product tag taxonomy
$rate_keys = array('oik_weight_zone_shipping_49', 'chrono10', 'chrono13'); // Shipping rates to be hidden
$has_unset = false; // Initializing
// Loop through cart items for the current shipping package
foreach( $package['contents'] as $cart_item ) {
// Targeting specific product tags
if ( has_term( $terms, $taxonomy, $cart_item['product_id'] ) ) {
// Loop through shipping rates to be removed (hidden)
foreach( $rate_keys as $rate_key ) {
if (isset($rates[$rate_key]) ) {
unset($rates[$rate_key]);
$has_unset = true; // Flag as unset to enable the filter below
}
}
}
}
if ( $has_unset ) {
add_filter('woocommerce_shipping_chosen_method', 'reset_default_shipping_method', 10, 3 );
}
return $rates;
}
function reset_default_shipping_method( $default, $rates, $chosen_method ) {
$targeted_method = 'per_product'; // The shipping method rate to set as chosen one
if( isset($rates[$targeted_method]) ) {
$default = $targeted_method;
}
return $default;
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works in WooCommerce 4.8+.
Don't forget to empty your cart to refresh WooCommerce shipping caches
Note: For WooCommerce categories, define the $taxonomy variable to product_cat instead.
Related
So I have some custom fields in my billing section on the checkout page, that I only want to display if a product with ID (603) is in the cart.
Currently, my code below works to hide the fields if there is a product in the cart that is not id 603, but one issue is when I have 603 and another product in the cart it unsets the fields,
what is the best way to either hide the fields if 603 is not in the cart or to just show them if it is?
this is the current code I was using
function conditional_checkout_fields_products( $fields ) {
$cart = WC()->cart->get_cart();
foreach ( $cart as $item_key => $values ) {
$product = $values['data'];
if ( $product->id != 603 ) {
unset( $fields['billing']['billing_prev_injuries'] );
unset( $fields['billing']['billing_dogs_events'] );
unset( $fields['billing']['billing_dogs_age'] );
unset( $fields['billing']['billing_dogs_breed'] );
unset( $fields['billing']['billing_dogs_name'] );
}
}
return $fields;
}
add_filter( 'woocommerce_checkout_fields', 'conditional_checkout_fields_products' );
The following will do the job:
add_filter( 'woocommerce_checkout_fields', 'conditional_checkout_fields_products' );
function conditional_checkout_fields_products( $fields ) {
$is_in_cart = false;
foreach ( WC()->cart->get_cart() as $cart_item ) {
if ( $cart_item['data']->get_id() == 603 ) {
$is_in_cart = true;
break;
}
}
if ( ! $is_in_cart ) {
unset( $fields['billing']['billing_prev_injuries'] );
unset( $fields['billing']['billing_dogs_events'] );
unset( $fields['billing']['billing_dogs_age'] );
unset( $fields['billing']['billing_dogs_breed'] );
unset( $fields['billing']['billing_dogs_name'] );
}
return $fields;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
With Woocommerce, I would like to hide all shipping methods except "Local pickup" when a defined products category is in cart…
The code below does that for other product types, except variable products:
add_filter( 'woocommerce_package_rates', 'custom_shipping_methods', 100, 2 );
function custom_shipping_methods( $rates, $package ){
// Define/replace here your correct category slug (!)
$cat_slug = 'my_category_slug';
$prod_cat = false;
// Going through each item in cart to see if there is anyone of your category
foreach ( WC()->cart->get_cart() as $values ) {
$product = $values['data'];
// compatibility with WC +3
$product_id = method_exists( $product, 'get_id' ) ? $product->get_id() : $product->id;
if ( has_term( $cat_slug, 'product_cat', $product_id ) )
$prod_cat = true;
}
$rates_arr = array();
if ( $prod_cat ) {
foreach($rates as $rate_id => $rate) { // <== There was a mistake here
if ('local_pickup' === $rate->method_id) {
$rates_arr[ $rate_id ] = $rate;
}
}
}
return !empty( $rates_arr ) ? $rates_arr : $rates;
}
What can I do to make it work for variable products too? Any help is appreciated.
I have revisited your code and here is correct way to make it work for variable products too:
add_filter( 'woocommerce_package_rates', 'product_category_hide_shipping_methods', 90, 2 );
function product_category_hide_shipping_methods( $rates, $package ){
// HERE set your product categories in the array (IDs, slugs or names)
$categories = array( 'clothing');
$found = false;
// Loop through each cart item Checking for the defined product categories
foreach( $package['contents'] as $cart_item ) {
if ( has_term( $categories, 'product_cat', $cart_item['product_id'] ) ){
$found = true;
break;
}
}
$rates_arr = array();
if ( $found ) {
foreach($rates as $rate_id => $rate) {
if ('local_pickup' === $rate->method_id) {
$rates_arr[ $rate_id ] = $rate;
}
}
}
return !empty( $rates_arr ) ? $rates_arr : $rates;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
You should need to refresh the shipping caches:
1) First this code is already saved on your function.php file.
2) In Shipping settings, enter in a Shipping Zone and disable a Shipping Method and "save". Then re-enable that Shipping Method and "save". You are done.
I have set up a hidden input item using woocommerce_before_add_to_cart_button hook
function add_gift_wrap_field() {
?>`<input type="hidden" id="price_val" name="added_price" value="100.34">`<?php
}
add_action( 'woocommerce_before_add_to_cart_button', 'add_gift_wrap_field' );
Saving fields against the product:
function save_gift_wrap_fee( $cart_item_data, $product_id ) {
if( isset( $_POST['added_price'] ) ) {
$cart_item_data = array();
$cart_item_data[ "gift_wrap_fee" ] = "YES";
$cart_item_data[ "gift_wrap_price" ] = 100;
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'save_gift_wrap_fee', 99, 2 );
Now I can echo out the $_POST['added_price'] inside this woocommerce_before_calculate_totals hook.
The code I written is shown below:
function calculate_gift_wrap_fee( $cart_object ) {
if( !WC()->session->__isset( "reload_checkout" )) {
/* Gift wrap price */
$additionalPrice = number_format($_POST['added_price'], 2);
$additionalPrice = floatval($additionalPrice);
//echo $additionalPrice; exit(); shows the value
foreach ( $cart_object->cart_contents as $key => $value ) {
if( isset( $value["gift_wrap_fee"] ) ) {
/* Woocommerce 3.0 + */
$orgPrice = floatval( $value['data']->get_price() );
$value['data']->set_price( $orgPrice + $additionalPrice ); //not adding the $additionalPrice here.
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'calculate_gift_wrap_fee', 99 );
What am I doing wrong here?
Here is the complete clean, tested and working solution based on your code question.
Here I have included your custom field key/value in the cart object for the related cart item, instead of getting the Post value in your calculate_gift_wrap_fee() function.
This way it's not possible to loose the custom field value and the new price calculation is reliable.
I have commented 'gift_wrap_fee' and 'gift_wrap_price' as they are not really needed now (but you can uncomment them if you like).
Here is this code:
// The hidden product custom field
add_action( 'woocommerce_before_add_to_cart_button', 'add_gift_wrap_field' );
function add_gift_wrap_field() {
?>
<input type="hidden" id="price_val" name="added_price" value="100.34">
<?php
}
// Adding the custom field to as custom data for this cart item in the cart object
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_fields_data_to_cart', 10, 2 );
function save_custom_fields_data_to_cart( $cart_item_data, $product_id ) {
$bool = false;
$data = array();
if( ! empty( $_REQUEST['added_price'] ) ) {
$cart_item_data['custom_data']['added_price'] = $_REQUEST['added_price'];
// Below this 2 values are not really needed (I think)
// (You can uncomment them if needed)
## $cart_item_data['custom_data']['gift_wrap_fee'] = 'YES';
## $cart_item_data['custom_data']['gift_wrap_price'] = 100;
// below statement make sure every add to cart action as unique line item
$cart_item_data['custom_data']['unique_key'] = md5( microtime().rand() );
WC()->session->set( 'custom_data', $data );
}
return $cart_item_data;
}
// Changing the cart item price based on custom field calculation
add_action( 'woocommerce_before_calculate_totals', 'calculate_gift_wrap_fee', 10, 1 );
function calculate_gift_wrap_fee( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Iterating though cart items
foreach ( $cart->get_cart() as $cart_item ) {
// Continue if we get the custom data for the current cart item
if( ! empty( $cart_item['custom_data'] ) ){
// Get the custom field "added price" value
$added_price = number_format( $cart_item['custom_data']['added_price'], 2 );
// The WC_Product object
$product = $cart_item['data'];
// Get the price (WooCommerce versions 2.5.x to 3+)
$product_price = method_exists( $product, 'get_price' ) ? floatval(product->get_price()) : floatval($product->price);
// New price calculation
$new_price = $product_price + $added_price;
// Set the calculeted price (WooCommerce versions 2.5.x to 3+)
method_exists( $product, 'set_price' ) ? $product->set_price( $new_price ) : $product->price = $new_price;
}
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works on WooCommerce versions from 2.5.x to 3+.
I have set up a hidden input item using woocommerce_before_add_to_cart_button hook
function add_gift_wrap_field() {
?>`<input type="hidden" id="price_val" name="added_price" value="100.34">`<?php
}
add_action( 'woocommerce_before_add_to_cart_button', 'add_gift_wrap_field' );
Saving fields against the product:
function save_gift_wrap_fee( $cart_item_data, $product_id ) {
if( isset( $_POST['added_price'] ) ) {
$cart_item_data = array();
$cart_item_data[ "gift_wrap_fee" ] = "YES";
$cart_item_data[ "gift_wrap_price" ] = 100;
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'save_gift_wrap_fee', 99, 2 );
Now I can echo out the $_POST['added_price'] inside this woocommerce_before_calculate_totals hook.
The code I written is shown below:
function calculate_gift_wrap_fee( $cart_object ) {
if( !WC()->session->__isset( "reload_checkout" )) {
/* Gift wrap price */
$additionalPrice = number_format($_POST['added_price'], 2);
$additionalPrice = floatval($additionalPrice);
//echo $additionalPrice; exit(); shows the value
foreach ( $cart_object->cart_contents as $key => $value ) {
if( isset( $value["gift_wrap_fee"] ) ) {
/* Woocommerce 3.0 + */
$orgPrice = floatval( $value['data']->get_price() );
$value['data']->set_price( $orgPrice + $additionalPrice ); //not adding the $additionalPrice here.
}
}
}
}
add_action( 'woocommerce_before_calculate_totals', 'calculate_gift_wrap_fee', 99 );
What am I doing wrong here?
Here is the complete clean, tested and working solution based on your code question.
Here I have included your custom field key/value in the cart object for the related cart item, instead of getting the Post value in your calculate_gift_wrap_fee() function.
This way it's not possible to loose the custom field value and the new price calculation is reliable.
I have commented 'gift_wrap_fee' and 'gift_wrap_price' as they are not really needed now (but you can uncomment them if you like).
Here is this code:
// The hidden product custom field
add_action( 'woocommerce_before_add_to_cart_button', 'add_gift_wrap_field' );
function add_gift_wrap_field() {
?>
<input type="hidden" id="price_val" name="added_price" value="100.34">
<?php
}
// Adding the custom field to as custom data for this cart item in the cart object
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_fields_data_to_cart', 10, 2 );
function save_custom_fields_data_to_cart( $cart_item_data, $product_id ) {
$bool = false;
$data = array();
if( ! empty( $_REQUEST['added_price'] ) ) {
$cart_item_data['custom_data']['added_price'] = $_REQUEST['added_price'];
// Below this 2 values are not really needed (I think)
// (You can uncomment them if needed)
## $cart_item_data['custom_data']['gift_wrap_fee'] = 'YES';
## $cart_item_data['custom_data']['gift_wrap_price'] = 100;
// below statement make sure every add to cart action as unique line item
$cart_item_data['custom_data']['unique_key'] = md5( microtime().rand() );
WC()->session->set( 'custom_data', $data );
}
return $cart_item_data;
}
// Changing the cart item price based on custom field calculation
add_action( 'woocommerce_before_calculate_totals', 'calculate_gift_wrap_fee', 10, 1 );
function calculate_gift_wrap_fee( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
return;
// Iterating though cart items
foreach ( $cart->get_cart() as $cart_item ) {
// Continue if we get the custom data for the current cart item
if( ! empty( $cart_item['custom_data'] ) ){
// Get the custom field "added price" value
$added_price = number_format( $cart_item['custom_data']['added_price'], 2 );
// The WC_Product object
$product = $cart_item['data'];
// Get the price (WooCommerce versions 2.5.x to 3+)
$product_price = method_exists( $product, 'get_price' ) ? floatval(product->get_price()) : floatval($product->price);
// New price calculation
$new_price = $product_price + $added_price;
// Set the calculeted price (WooCommerce versions 2.5.x to 3+)
method_exists( $product, 'set_price' ) ? $product->set_price( $new_price ) : $product->price = $new_price;
}
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works on WooCommerce versions from 2.5.x to 3+.
I’m trying to unset Flat Rate shipping method only if cart has products both with and without shipping class. If all product in cart have shipping class then it should stay.
Have this shipping method: Flat Rate - (flat_rate1) (instance_id=1)
And these shipping classes: 50, 100 and so on, with same way named Slugs: 50, 100…
Flat Rate shipping method has costs set up for these shipping classes, Main Cost and No shipping class cost for this method are not set, so it only appears for products in cart that have shipping classes set.
Got it working
add_filter( 'woocommerce_package_rates', 'unset_shipping_for_unmatched_items', 100, 2 );
function unset_shipping_for_unmatched_items( $rates, $package ) {
// Initialisation
$shipping_classes = array( 50, 100, 150, 200, 250, 300 );
$cart_items = WC()->cart->get_cart();
$cart_items_count = WC()->cart->get_cart_contents_count();
$items_match = false;
$inArray = 0;
$notInArray = 0;
foreach( $cart_items as $cart_item ){
if( in_array( $cart_item[ 'data' ]->get_shipping_class(), $shipping_classes ) && $cart_items_count > 1 ) {
$inArray++;
} else {
$notInArray++;
}
}
if( ( $cart_items_count == $notInArray ) || ( $cart_items_count == $inArray ) ){
$items_match = false;
} else {
$items_match = true;
}
if( $items_match )
unset( $rates['flat_rate:6'] );
return $rates;
}
In WooCommerce the shipping methods ID slugs are a little different, I mean there is a typo error. You will need to add : between the name and the number in the slug: 'flat_rate6'.
Also once you get a matching cart item shipping class, you can break the loop.
Last thing: This hook has 2 available arguments: $rates and $package.
So your code will be:
add_filter( 'woocommerce_package_rates', 'hide_shipping_when_class_is_in_cart', 100, 2 );
function hide_shipping_when_class_is_in_cart( $rates, $package ) {
// Initialisation
$shipping_classes = array( 50, 100, 150, 200, 250, 300 );
$class_exists = false;
foreach( WC()->cart->get_cart() as $cart_item )
if( in_array( $cart_item[ 'data' ]->get_shipping_class_id(), $shipping_classes ) ) {
$class_exists = true;
break; // Stop the loop
}
if( $class_exists )
unset( $rates['flat_rate:6'] );
return $rates;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This should work now.