Woocommerce Hide Shipping method based on category - woocommerce

I am trying to disabel/hide a shipping method based on a category in Woocommerce (2.1.12).
I successfully achieved hiding a paymeny method using this function:
function filter_gateways($gateways){
$category_ID = array('14'); // <----------- add the ids for which u want to turn off "cash on delivery"
global $woocommerce;
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
$terms = get_the_terms( $values['product_id'], 'product_cat' );
foreach ($terms as $term) {
if( in_array( $term->term_id, $category_ID ) ) {
unset($gateways['cod']);
break;
}
break;
}
}
return $gateways;
}
add_filter('woocommerce_available_payment_gateways','filter_gateways');
However, disabling a shipping method seems not to be working.
I tried three different snippets.
No.1 (not working):
function custom_shipping_methods( $available_methods ) {
$category_ID = array('14'); // <----------- add the ids for which u want to turn off "local pickup"
global $woocommerce;
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
$terms = get_the_terms( $values['product_id'], 'product_cat' );
foreach ($terms as $term) {
if( in_array( $term->term_id, $category_ID ) ) {
unset( $available_methods['local_pickup'] );
break;
}
break;
}
}
return $available_methods;
}
add_filter( 'woocommerce_available_shipping_methods', 'custom_shipping_methods' );
No.2 (not working):
function custom_shipping_methods( $is_available ) {
$category_ID = array('14'); // <----------- add the ids for which u want to turn off "local pickup"
global $woocommerce;
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
$terms = get_the_terms( $values['product_id'], 'product_cat' );
foreach ($terms as $term) {
if( in_array( $term->term_id, $category_ID ) ) {
$is_available = false;
break;
}
break;
}
}
return $is_available;
}
add_filter( 'woocommerce_shipping_local_pickup_is_available', 'custom_shipping_methods' );
No.3 (not working) I tried this because apparently newer versions of woocommerce use the filter below:
function hide_local_pickup( $rates, $package ) {
$category_ID = array('14'); // <----------- add the ids for which u want to turn off "local pickup"
global $woocommerce;
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
$terms = get_the_terms( $values['product_id'], 'product_cat' );
foreach ($terms as $term) {
if( in_array( $term->term_id, $category_ID ) ) {
unset( $rates['local_pickup'] );
break;
}
break;
}
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'hide_local_pickup' , 10, 2 );

I had a similar request and combined your solution with some others that I found and got what you need working.
Apparently woocommerce doesn't include tags or categories in the cart array, so you have to go get it first, then do your function.
Here is a working function below to unset the 'local_pickup' from the available shipping methods:
add_filter( 'woocommerce_package_rates', 'hide_shipping_based_on_tag' , 10, 1 );
function check_cart_for_share() {
// specify the category id's you want to hide local_pickup
$category_ID = array(
'6', // books
'7', // prints
'13', // candles
'8', // note cards
'9', // origonal art
);
global $woocommerce;
$cart = $woocommerce->cart->cart_contents;
$found = false;
// loop through the array looking for the categories. Switch to true if the category is found.
foreach ($woocommerce->cart->cart_contents as $key => $values ) {
$terms = get_the_terms( $values['product_id'], 'product_cat' );
foreach ($terms as $term) {
if( in_array( $term->term_id, $category_ID ) ) {
$found = true;
break;
}
}
}
return $found;
}
function hide_shipping_based_on_tag( $available_methods ) {
// use the function above to check the cart for the categories.
if ( check_cart_for_share() ) {
// remove the method you want
unset( $available_methods['local_pickup'] ); // Replace "local_pickup" with the shipping option that you want to remove.
}
// return the available methods without the one you unset.
return $available_methods;
}

Related

Add message below shipping methods based on taxonomy terms in WooCommerce

I'd like to add a custom message below the available shipping methods in the cart, but only if ALL products in the cart have a tag named 'express'.
Therefore I use this snippet:
add_action( 'woocommerce_after_shipping_rate', 'action_after_shipping_rate', 20, 2 );
function action_after_shipping_rate ( $method, $index ) {
if( 'flat_rate:1' === $method->id ) {
echo __("<p>Arriving on your chosen date between 9am - 1pm Perfect for business addresses & special occasions</p>");
}
if( 'flat_rate:2' === $method->id ) {
echo __("<p>Arriving on your chosen date between 9am - 7pm Perfect for residential addresses</p>");
}
}
add_filter( 'woocommerce_shipping_details', 'hide_shipping_details', 10, 2 );
function hide_shipping_details( $rates, $package ) {
$terms = array( 'express' );
$taxonomy = 'product_tag';
$found = false;
foreach( $package['contents'] as $cart_item ) {
if ( !has_term( $terms, $taxonomy, $cart_item['product_id'] ) ){
$found = true;
break;
}
}
if ( $found === false ) {
remove_action( 'woocommerce_after_shipping_rate', 'action_after_shipping_rate', 20, 2 );
}
}
But right now, the custom message remains if 1 or 2 products have the tag 'express' but not ALL products. Can someone help me solve this problem?
No need to call from one hook to another, while you can just loop through all the products in cart in the woocommerce_after_shipping_rate hook
So you get:
function action_woocommerce_after_shipping_rate( $method, $index ) {
// Settings
$terms = array( 'express', 'tag-1' );
$taxonomy = 'product_tag';
// Initialize
$flag = false;
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
// Checks if the current product has NOT any of given terms
if ( ! has_term( $terms, $taxonomy, $cart_item['product_id'] ) ) {
$flag = true;
break;
}
}
// When false
if ( ! $flag ) {
// Compare
if ( $method->get_id() === 'flat_rate:1' ) {
$text = 'My text 1';
} elseif ( $method->get_id() === 'flat_rate:3' ) {
$text = 'My text 2';
} elseif ( $method->get_id() === 'local_pickup:1' ) {
$text = 'My text 3';
}
// When isset
if ( isset( $text ) ) {
// Output
echo '<p>' . sprintf( __( '%s', 'woocommerce' ), $text ) . '</p>';
}
}
}
add_action( 'woocommerce_after_shipping_rate', 'action_woocommerce_after_shipping_rate', 10, 2 );

Disable payment gateway when specific products in WooCommerce cart

I want to unset the payment method 'bacs' for the product ID 6197.
I have tried the code below, but this does not have the desired result.
add_filter( 'woocommerce_available_payment_gateways', 'wp_unset_gateway_by_id', 10, 2 );`
function wp_unset_gateway_by_id( $available_gateways, $products) {
global $woocommerce;
$unset = false;
$product_ids = array('6197');
if( in_array( $product->get_id(), $product_ids ) || ( $product->is_type('variation') && in_array( $product->get_parent_id(), $product_ids ) ) ){
unset( $available_gateways['bacs'] );
return $available_gateways;
}
}
I believe I am doing something wrong, but I am relatively new to this. Any advice would be appreciated.
$products is not passed as argument to the woocommerce_available_payment_gateways filter hook
Apply it in the following way:
function filter_woocommerce_available_payment_gateways( $payment_gateways ) {
// Not on admin
if ( is_admin() ) return $payment_gateways;
// The targeted product ids (several can be added, separated by a comma)
$targeted_ids = array( 6197 );
// Flag
$found = false;
// WC Cart
if ( WC()->cart ) {
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
if ( array_intersect( $targeted_ids, array( $cart_item['product_id'], $cart_item['variation_id'] ) ) ) {
$found = true;
break;
}
}
}
// True
if ( $found ) {
// Bacs
if ( isset( $payment_gateways['bacs'] ) ) {
unset( $payment_gateways['bacs'] );
}
}
return $payment_gateways;
}
add_filter( 'woocommerce_available_payment_gateways', 'filter_woocommerce_available_payment_gateways', 10, 1 );

Check if at least one of item has specific attribute term in WooCommerce order

I want to check if, at least, one of item has specific attribute term in WooCommerce order to display something in the mail order.
Attribute tax is 'pa_labels' and the term is 'tree' but something is missing... Any Idea ?
add_action( 'woocommerce_thankyou', 'webroom_check_product_attr_in_order', 5 );
function webroom_check_product_attr_in_order( $order_id ) {
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
$attr_in_order = false;
$items = $order->get_items();
foreach ( $items as $item ) {
$product_id = $item['product_id'];
if ( has_term( 'tree', 'pa_labels', $product_id ) ) {
$attr_in_order = true;
break;
}
}
// Echo content only if $attr_in_order == true
if ( $attr_in_order ) {
add_action( 'woocommerce_email_customer_details', 'custom_woocommerce_email_customer_details', 25);
}
}
You can try something like this:
// check if in the order there is at least one product with the attribute "pa_labels" => "tree"
add_action( 'woocommerce_thankyou', 'webroom_check_product_attr_in_order', 5 );
function webroom_check_product_attr_in_order( $order_id ) {
if ( ! $order_id ) {
return;
}
$order = wc_get_order( $order_id );
$attr_in_order = false;
foreach ( $order->get_items() as $item ) {
$product = $item->get_product();
if ( has_term( 'tree', 'pa_labels', $product->get_id() ) ) {
$attr_in_order = true;
break;
}
}
// Echo content only if $attr_in_order == true
if ( $attr_in_order ) {
custom_woocommerce_email_customer_details();
}
}
// add content to the customer details in the email
add_action( 'woocommerce_email_customer_details', 'custom_woocommerce_email_customer_details', 25);
function custom_woocommerce_email_customer_details() {
// do stuff
}
The code must be added to the functions.php of the active theme.

Get product ID inside a custom WooCommerce/Plugin filter

I am trying to change the product edit url which is created from this plugins function:
function dokan_edit_product_url( $product ) {
if ( ! $product instanceof WC_Product ) {
$product = wc_get_product( $product );
}
if ( ! $product ) {
return false;
}
if ( 'publish' === $product->get_status() ) {
$url = trailingslashit( get_permalink( $product->get_id() ) ) . 'edit/';
} else {
$url = add_query_arg(
[
'product_id' => $product->get_id(),
'action' => 'edit',
],
dokan_get_navigation_url( 'products' )
);
}
return apply_filters( 'dokan_get_edit_product_url', $url, $product );
}
You can see it has a apply_filters available. So I am trying to create a filter to modify the URL to be: example.com/edit-product/product-id
add_filter( 'dokan_get_edit_product_url', function() {
// I need to get the PRODUCT ID here somehow.
$url = 'example.com/dashboard/edit-product' . $product_id;
return $url;
} );
How can I get the product ID in my filter? I need to grab the product ID and attach it to: example.com/dashboard/edit-product/ + product_id
Here is one attempt:
add_filter( 'dokan_get_edit_product_url', function( $product ) {
$product = wc_get_product( $product );
var_dump($product);
return $url;
} );
Result:
/app/filters.php:175:boolean false
Following a suggestion in the comments from #howard-e, I was simply missing global $product;
Here is my full filter:
add_filter( 'dokan_get_edit_product_url', function( $product ) {
global $product;
$url = dokan_get_navigation_url() . 'edit-product?product_id=' . $product->get_id();
return $url;
} );
Obviously the format of the $url is unique to my set up, but worth posting the full code anyway.

woocommerce_before_calculate_totals hook stopped working after update to WC 3.0.1

I have updated to WC 3.0.1 from 2.6.14.
My original code is as follows:
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;
}
}
It is no longer updating the price in cart or minicart.
For overriding product price on cart in the latest version of Woocommerce (3.0.1) try using set_price( $price ) function in woocommerce this will help. Source here
add_action( 'woocommerce_before_calculate_totals', 'woocommerce_pj_update_price', 99 );
function woocommerce_pj_update_price() {
$custom_price = $_COOKIE["donation"]; // This will be your custom price
$target_product_id = 413; //Product ID
foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if($cart_item['data']->get_id() == $target_product_id){
$cart_item['data']->set_price($custom_price);
}
}
}
Works with a little change:
//OLD:
$value['data']->price = $custom_price;
//NEW:
$value['data']->set_price( $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 );
}
}
add_action( 'woocommerce_before_calculate_totals', 'add_custom_price', 10, 1);
function add_custom_price( $cart_obj ) {
// This is necessary for WC 3.0+
if ( is_admin() && ! defined( 'DOING_AJAX' ) )
return;
foreach ( $cart_obj->get_cart() as $key => $value ) {
$value['data']->set_price( 40 );
}
}
if i set $value['data']->set_price( 40 ) work fine, but:
foreach ( $cart_obj->get_cart() as $key => $value ) {
$price = 50;
$value['data']->set_price( $price );
}
Well, the problem is you are calling price directly at $value['data']->price.
Make it:
$value['data']->get_price()
and I think you problem will be fixed

Resources