I have been looking at lots of snippets to enable a shipping method for a specific user role only. So anyone who is NOT part of this user role should not see it, including guests who are not logged in.
The snippets like below don't achieve this and I am struggling to reverse the logic. I want to avoid having to manually input every shipping method to the snippet, which is obviously not future proof due to when new shipping methods are added.
add_filter( 'woocommerce_package_rates', 'hide_specific_shipping_method_based_on_user_role', 100, 2 );
function hide_specific_shipping_method_based_on_user_role( $rates, $package ) {
// Here define the shipping rate ID to hide
$targeted_rate_id = '15'; // The shipping rate ID to hide
$targeted_user_roles = array('clubadmin'); // The user roles to target (array)
$current_user = wp_get_current_user();
$matched_roles = array_intersect($targeted_user_roles, $current_user->roles);
if( ! empty($matched_roles) && isset($rates[$targeted_rate_id]) ) {
unset($rates[$targeted_rate_id]);
}
return $rates;
}
Original Post: Woocommerce Shipping method based on user role
Author: LoicTheAztec
By default, this shipping method should be disabled, unless the user fulfills a certain user role
In the $rate_ids array you add 1 or more IDs for the respective shipping methods
So you get:
function filter_woocommerce_package_rates( $rates, $package ) {
// Set the rate IDs in the array
$rate_ids = array( 'local_pickup:1', 'free_shipping:2' );
// NOT the required user role, remove shipping method(s)
if ( ! current_user_can( 'administrator' ) ) {
// Loop trough
foreach ( $rates as $rate_id => $rate ) {
// Checks if a value exists in an array
if ( in_array( $rate_id, $rate_ids ) ) {
unset( $rates[$rate_id] );
}
}
}
return $rates;
}
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 10, 2 );
Note: to find the correct $rate_ids you can use the second part from this answer - (The "for debugging purposes" part)
Related
I'm using the following answer code:
Show or hide shipping methods based on user meta data in WooCommerce
Which I have adapted to my needs:
add_filter( 'woocommerce_package_rates', 'shipping_methods_based_on_wholesale_customer', 10, 2 );
function shipping_methods_based_on_wholesale_customer( $rates, $package ){
$is_wholesale = get_user_meta( get_current_user_id(), 'wholesale_customer', true );
// Set the shipping methods rate ids in the arrays:
if( $is_wholesale ) {
$shipping_rates_ids = array('flat_rate:10', 'flat_rate:7'); // To be removed for NON Wholesale users
} else {
$shipping_rates_ids = array('flat_rate:13', 'flat_rate:15'); // To be removed for Wholesale users
}
// Loop through shipping rates from the current shipping package
foreach( $rates as $rate_key => $rate ) {
if ( in_array( $rate_key, $shipping_rates_ids) ) {
unset( $rates[$rate_key] );
}
}
return $rates;
}
While the original answer generally works, it doesn't for me. It's removing 'flat_rate:13' and 'flat_rate:15' but for BOTH wholesale customers as for guests. But it should only remove for wholesale customers.
Now, guests are seeing the wrong shipment costs: 'flat_rate:10' and 'flat_rate:7'
So since it seems to be a matter of debugging, I downloaded : JSM's Show User Metadata and checked a wholesale customer and it says:
wp_capabilities -> array(
0 => array (
'wholesale_customer' => true,
'bbp_participant' => true,
),
)
Is there anyone running into the same issue? is there any other way I can debug so that it becomes clear where it goes wrong?
My initial problem is that AJAX add-to-cart button on Woocommerce doesn't seem to work on Private products (which we're only displaying to a selection of customers): the wheel appears on the add-to-cart button, the page reloads, and the product is added to cart but there's no notice that it has, so it's quite confusing.
After lots of research, all I could find is this thread https://wordpress.org/support/topic/add-to-cart-redirecting-only-on-private-products-2/ which led me to believe it's most likely a bug?
I tried to come up with a work-around: disable AJAX add-to-cart and revert to default behaviour on Private products only so we can still display some kind of notification that the product has been added to cart. I've thought of this code as a starting point:
add_action( 'wp_enqueue_scripts', 'bbloomer_disable_woocommerce_cart_fragments', 11 );
function bbloomer_disable_woocommerce_cart_fragments() {
if ( is_front_page() ) wp_dequeue_script( 'wc-cart-fragments' );
}
Would this work? And how would I modify it so the condition is "if is private product"?
You could solve the problem by thinking the other way around. Instead of showing private products only for specific users you could hide them for all other user roles (leaving the products with the "publish" status).
This way the Ajax problem does not arise.
If the user has one of the roles defined in the $user_roles array of the following function, it hides all products in the $product_ids_to_hide array:
// hide products based on user role
add_filter( 'woocommerce_product_is_visible', 'hide_products_by_user_role', 10, 2 );
function hide_products_by_user_role( $visible, $product_id ) {
// only if the user is logged in
if ( ! is_user_logged_in() ) {
return $visible;
}
// set the product ids to hide
$product_ids_to_hide = array( 12, 14 );
// sets the user roles for which products are to be hidden
$user_roles = array( 'custom_role_1', 'custom_role_2' );
$user = wp_get_current_user();
if ( array_intersect( $user_roles, $user->roles ) && in_array( $product_id, $product_ids_to_hide ) ) {
return false;
}
return $visible;
}
To reply to your comment:
If you want to allow the user to modify the product ids to be hidden based on the user role you can create a .csv file to be uploaded to FTP with the list of product ids, one per line.
If your client in the future wants to remove or add a product id, he can do so simply by overwriting the .csv file with an FTP import or directly in the file manager (if the hosting allows it).
The file I created as an example will be called product-ids-to-hide.csv and will need to be uploaded to the same directory as the functions.php file. No header is required. Here is an example:
349
235
456
745
and this will be the new updated function:
// hide products based on user role
add_filter( 'woocommerce_product_is_visible', 'hide_products_by_user_role', 10, 2 );
function hide_products_by_user_role( $visible, $product_id ) {
// only if the user is logged in
if ( ! is_user_logged_in() ) {
return $visible;
}
// sets the user roles for which products are to be hidden
$user_roles = array( 'custom_role_1', 'custom_role_2' );
$user = wp_get_current_user();
// only if the user has at least one role present in the array
if ( empty( array_intersect( $user_roles, $user->roles ) ) ) {
return $visible;
}
// get an array with product ids
$product_ids_to_hide = file( realpath( __DIR__ . '/product-ids-to-hide.csv' ), FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES );
if ( in_array( $product_id, $product_ids_to_hide ) ) {
return false;
}
return $visible;
}
The code has been tested and works. Add it to your active theme's functions.php.
It's straightforward to required registration globally in WooCommerce settings but for most of the products we sell, it's not necessary and I would rather restrict the logged in users.
However for one product I would like to require registration.
Something like
$product_id = $product->get_id();
if ($product_id !== 1024) {
remove_action( 'woocommerce_before_checkout_registration_form', 'action_woocommerce_checkout_registration_form', 10, 1 );
}
but it's obviously not that simple. Any ideas?
This answer assumes that WooCommerce > Settings > Accounts & privacy > Allow customers to place orders without an account is enabled
You can then use the following code, that will ensure that when there is 1 or more productID's in the shopping cart, registration during checkout will be required
function filter_woocommerce_checkout_registration_required( $bool_value ) {
// Several can be added, separated by a comma
$product_ids = array ( 30, 813 );
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
// Product ID from cart item in specific array
if ( in_array( $cart_item['product_id'], $product_ids ) ) {
// Registration_required
$bool_value = true;
// Break loop
break;
}
}
return $bool_value;
}
add_filter( 'woocommerce_checkout_registration_required', 'filter_woocommerce_checkout_registration_required', 10, 1 );
In Woocommerce I am using WooCommerce Wholesale Pro Suite (from IgniteWoo) and Flat Rate Box Shipping plugins to add B2B to our eshop.
I am trying to disable the Flat Rate Box Shipping for specific user roles, guests and customers. I found this code after searching online:
add_filter( 'woocommerce_package_rates', 'hide_shipping_for_user_role', 10, 2 );
function hide_shipping_for_user_role( $rates, $package ) {
// Role ID to be excluded
$excluded_role = "wholesale_customer";
// Shipping rate to be excluded
$shipping_id = 'table_rate_shipping_free-shipping';
// Get current user's role
$user = wp_get_current_user();
if ( empty( $user ) ) return false;
if( in_array( $excluded_role, (array) $user->roles ) && isset( $rates[ $shipping_id ] ) )
unset( $rates[ $shipping_id ] );
return $rates;
}
What should I use in place of "wholesale_customer" and in place of "table_rate_shipping_free-shipping", so the Flat Rate Box Shipping is not showing, for guests and customers roles?
Any help is appreciated.
Update 2:
You may have to "Enable debug mode" in general shipping settings under "Shipping options" tab, to disable temporarily shipping caches.
For info: The shipping method ID for "Flat rate boxes" is flat_rate_boxes.
The following code will disable "Flat rate boxes" Shipping Methods For "Guests" (non logged in users) and "customer" user role:
add_filter( 'woocommerce_package_rates', 'hide_specific_shipping_method_based_on_user_role', 30, 2 );
function hide_specific_shipping_method_based_on_user_role( $rates, $package ) {
## --- Your settings --- ##
$excluded_role = "customer"; // User role to be excluded
$shipping_id = 'flat_rate_boxes'; // Shipping rate to be removed
foreach( $rates as $rate_key => $rate ){
if( $rate->method_id === $shipping_id ){
if( current_user_can( $excluded_role ) || ! is_user_logged_in() ){
unset($rates[$rate_key]);
break;
}
}
}
return $rates;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Don't forget to enable back shipping cache.
I run an online store with WooCommerce and sell normal products and books. I want to reach the following:
If somebody has a cart value of < 50 he should pay for shipping
If somebody has a cart value of > 50 shipping should be free
If somebody adds a book to the cart shipping is always free
I try this with the following code:
function custom_free_per_class( $return, $package ) {
// Setup an array of shipping classes that allow Flat Rate Shipping
$shippingclass_array = array( 'bookshipping');
// loop through the cart checking the shipping classes
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$shipping_class = get_the_terms( $values['product_id'], 'product_shipping_class' );
if ( isset( $shipping_class[0]->slug ) && in_array( $shipping_class[0]->slug, $shippingclass_array ) ) {
return true;
break;
}//if
}//foreach
}//function
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'custom_free_per_class', 10, 2 );
add_filter( 'woocommerce_package_rates', 'hide_shipping_when_free_is_available', 10, 2 );
/**
* Hide shipping rates when free shipping is available
*
* #param array $rates Array of rates found for the package
* #param array $package The package array/object being shipped
* #return array of modified rates
*/
function hide_shipping_when_free_is_available( $rates, $package ) {
// Only modify rates if free_shipping is present
if ( isset( $rates['free_shipping'] ) ) {
// To unset all methods except for free_shipping, do the following
$free_shipping = $rates['free_shipping'];
$rates = array();
$rates['free_shipping'] = $free_shipping;
}
return $rates;
}
I set it up in the WooCommerce backend that free shipping should be available after a cart value of 50 has been reached. But however this does not work. The above code works - so free shipping is guaranteed when somebody adds a book, but this seems to hinder the other intention (cart > 50) from working. If I remove the code
add_filter( 'woocommerce_shipping_free_shipping_is_available', 'custom_free_per_class', 10, 2 );
the functionality with adding free shipping when 50 is reached works, but surely books are not free anymore.
Des anybody has an idea what is going wrong here?
I would really appreciate it if anyone can help me.
Thanks!
I solved it by myself. The input parameter for the function is not ($return, $package) - it is just ($is_available).