State restriction - wordpress

I have some items that we are not allowed to send to California, how can I restrict people who have "CA" in their address, Woocomerce cart? I have this code but for some reason, it doesn't work
// REMOVE ITEMS THAT CAN'T SHIP TO CALIFORNIA/INTERNATIONAL
if( !empty($shipping_state) && $shipping_state == 'CA'){
$removed_item = false;
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
if ( has_term('not-carb-compliant-cant-ship-to-california', 'product_tag', $cart_item['product_id'] ) ) {
WC()->cart->remove_cart_item($cart_item_key);
$removed_an_item = true;
}
}
if ( $removed_an_item ) {
$notice = 'This tem is not currently available in your region and has been removed from your cart.';
wc_print_notice( $notice , 'error' );
}
}
}

Related

Hide "ship to a different address" in Woocommerce based on user roles and specific products in cart

I'm trying to hide the "ship to a different address" on WooCommerce checkout page for 2 user roles, if their cart contains any of the specified product ID's.
My code is working for a single product ID:
/** Shipping Address */
function filter_woocommerce_cart_needs_shipping_address( $needs_shipping ) {
// The targeted products & roles
$targeted_variation_id = 414;
$targeted_user_role = 'team';
$targeted_user_role2 = 'team2';
// Flag
$found = false;
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
if ( in_array( $targeted_variation_id, array( $cart_item['product_id'], $cart_item['variation_id'] ) ) ) {
$found = true;
break;
}
}
// True & user Role is
if ( $found && current_user_can( $targeted_user_role )) {
$needs_shipping = false;
}
// True & only 1 item in cart
if ( $found && current_user_can( $targeted_user_role2 )) {
$needs_shipping = false;
}
return $needs_shipping;
}
add_filter( 'woocommerce_cart_needs_shipping_address', 'filter_woocommerce_cart_needs_shipping_address', 10, 1 );
But then when I try to create an array of multiple IDs, I can't achieve the desired result, this is my attempt:
**Multiple Variants**
/** Shipping Address */
function filter_woocommerce_cart_needs_shipping_address( $needs_shipping ) {
// The targeted products & roles
$targeted_variation_id = array(414,617);
$targeted_user_role = 'team';
$targeted_user_role2 = 'team2';
// Flag
$found = false;
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
if ( in_array( $targeted_variation_id, array( $cart_item['product_id'], $cart_item['variation_id'] ) ) ) {
$found = true;
break;
}
}
// True & user Role is
if ( $found && current_user_can( $targeted_user_role )) {
$needs_shipping = false;
}
// True & only 1 item in cart
if ( $found && current_user_can( $targeted_user_role2 )) {
$needs_shipping = false;
}
return $needs_shipping;
}
add_filter( 'woocommerce_cart_needs_shipping_address', 'filter_woocommerce_cart_needs_shipping_address', 10, 1 );
Anyone able to help figure out how to get it to work with multiple ID's and not just one?
The mistakes in your code is in the following line:
if ( in_array( $targeted_variation_id, array( $cart_item['product_id'], $cart_item['variation_id'] ) ) )
$targeted_variation_id has been changed to an array,
while the searched value must be a string when using in_array()
It also seems more logical to check the user role first, before iterate all cart items
So you get:
function filter_woocommerce_cart_needs_shipping_address( $needs_shipping ) {
// Retrieve the current user object
$user = wp_get_current_user();
// Add your user roles, several can be entered, separated by a comma
$user_roles = array( 'team1', 'team2', 'administrator' );
// Targeted product IDs, several can be entered, separated by a comma
$targeted_product_ids = array( 30, 813, 414, 617 );
// User role is found
if ( array_intersect( $user_roles, (array) $user->roles ) ) {
// Loop through cart contents
foreach ( WC()->cart->get_cart_contents() as $cart_item ) {
// Get product ID
$product_id = $cart_item['variation_id'] > 0 ? $cart_item['variation_id'] : $cart_item['product_id'];
// Checks if a value exists in an array
if ( in_array( $product_id, $targeted_product_ids ) ) {
$needs_shipping = false;
break;
}
}
}
return $needs_shipping;
}
add_filter( 'woocommerce_cart_needs_shipping_address', 'filter_woocommerce_cart_needs_shipping_address', 10, 1 );

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 );

Remove other products if certain product category is present on WooCommerce checkout

I am creating a landing page for the customers and with a specific products that has a category of landing-page.
I want the other products that is currently on the cart page to be removed when the category landing-page is present on the cart.
Here's the snippet. Right now, it removes all the products in it because of the $woocommerce->cart->empty_cart().
add_action('woocommerce_checkout_before_customer_details', 'check_if_landing_page_category_is_on_cart');
function check_if_landing_page_category_is_on_cart() {
global $woocommerce;
$categories = array('landing-page');
$has_category = false;
foreach ( WC()->cart->get_cart() as $cart_item ) {
// Check for product categories
if ( has_term( $categories, 'product_cat', $cart_item['product_id'] ) ) {
$woocommerce->cart->empty_cart();
$has_category = true;
break;
}
}
if ( $has_category ) {
?>
<style>
.coupon-form {
display: none;
}
</style>
<?php
}
}
Any advice?
You can use WC_Cart::remove_cart_item() opposite WC_Cart::empty_cart()
So you get:
function action_woocommerce_checkout_before_customer_details() {
// Add categories. Multiple can be added, separated by a comma
$categories = array( 'landing-page' );
// Initialize
$cart_item_keys = array();
$has_category = false;
// 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 ) {
// Get product id
$product_id = $cart_item['product_id'];
// NOT certain category
if ( ! has_term( $categories, 'product_cat', $product_id ) ) {
// Push to array
$cart_item_keys[] = $cart_item_key;
} else {
$has_category = true;
}
}
// NOT empty & has category is true
if ( ! empty ( $cart_item_keys ) && $has_category ) {
// Loop through all cart item keys that do not contain the category
foreach ( $cart_item_keys as $cart_item_key ) {
// Remove product from cart
WC()->cart->remove_cart_item( $cart_item_key );
}
}
}
}
add_action( 'woocommerce_checkout_before_customer_details', 'action_woocommerce_checkout_before_customer_details', 10, 0 );

How to find out the cheapest shipping method and change the shipping label in woocommerce?

So here's what I need to do: find out the cheapest shipping method and change the "Free Shipping" method label to "Free Shipping" + "Cheapest method label". This way the customer will be able to know what is the shipping method used for the free shipping.
First I thought about global variables.. then used the wp_options, but I am not certain if the wp_options are user shared or user-specific data.
Would you have any good ideas or corrections on the following code, which by the way isn't working? Thanks!
add_filter( 'woocommerce_shipping_chosen_method', 'wf_default_shipping_method', 10 );
function wf_default_shipping_method( $method ) {
$the_cheapest_cost = 1000000;
$packages = WC()->shipping()->get_packages()[0]['rates'];
foreach ( array_keys( $packages ) as $key ) {
if ( ( $packages[$key]->cost > 0 ) && ( $packages[$key]->cost < $the_cheapest_cost ) ) {
$the_cheapest_cost = $packages[$key]->cost;
$transport_label = $packages[$key]->label;
$method_id = $packages[$key]->id;
}
}
if(get_option('atransport')){
update_option('atransport', $transport_label);
}else{
add_option('atransport', $transport_label);
}
return $method_id;
}
add_filter( 'woocommerce_package_rates', 'change_shipping_methods_label_names', 20, 2 );
function change_shipping_methods_label_names( $rates, $package ) {
$transport = get_option('atransport');
foreach( $rates as $rate_key => $rate ) {
if ( __( 'Free Shipping', 'woocommerce' ) == $rate->label ){
$rates[$rate_key]->label = __( 'FreeShipping -' . $transport, 'woocommerce' ); // New label name
}
}
delete_option('atransport');
return $rates;
}
You can iterate a loop of $rates and get the cheapest method. then based on the found cheapest method you can change the label. code will go in your active theme functions.php file.
add_filter( 'woocommerce_package_rates', 'hide_other_shipping_when_free_is_available', 100, 1 );
function hide_other_shipping_when_free_is_available( $rates ) {
$cheapest_method = '';
// Loop through shipping rates
if ( is_array( $rates ) ) {
foreach ( $rates as $key => $rate ) {
// Set variables when the rate is cheaper than the one saved
if ( empty( $cheapest_method ) || $rate->cost < $cheapest_method->cost ) {
$cheapest_method = $rate;
}
}
}
// Return the cheapest rate when possible
if ( ! empty( $cheapest_method ) ) {
$free = array();
foreach ( $rates as $rate_id => $rate ) {
if ( $rate->id === $cheapest_method->id ) {
if ( __( 'Free Shipping', 'woocommerce' ) == $rate->label ){
$rates[ $rate_id ] = $rate;
// Here we append the labbelled saved price formated display
$rates[ $rate_id ]->label = 'Free Shipping '.$rates[ $rate_id ]->label;
break; // stop the loop
}
}
}
return $rates;
}
return $rates;
}
Tested and works

WooCommerce Hide Payment Gateway for Custom Attribute

Just starting in my WordPress/WooCommerce journey.
I found a question on here from a while back asking about restricting payment gateways for a specific attribute selection. I've tried to integrate this into my site and I cannot get it working! I've added the Attribute and Terms, and the slugs are as follows - pay_now_deposit (attr) and pay_now / deposit (terms).
I'm looking to restrict Klarna payments for those paying by deposit, and the code I have is below -
function conditional_payment_gateways( $available_gateways ) {
$in_cart = false;
foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
// See if there is an attribute called 'pa_size' in the cart
// Replace with whatever attribute you want
if (array_key_exists('pa_pay_now_deposit', (array) $values['data']->get_attributes() ) ) {
foreach ($values['data']->get_attributes() as $attribute => $variation);
// Replace 'small' with your value.
if ($variation == 'deposit') $in_cart = true; //edited
}
}
if ( $in_cart ) {
unset($available_gateways['klarna_payments']);
}
else {
unset($available_gateways['cod']);
}
return $available_gateways;
}
Any advice on where I'm going wrong would be greatly appreciated!
Try to add a break statement.
add_filter('woocommerce_available_payment_gateways', 'conditional_payment_gateways', 10, 1);
function conditional_payment_gateways( $available_gateways ) {
$in_cart = false;
foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
if ( array_key_exists( 'pa_pay_now_deposit', (array) $values['data']->get_attributes() ) ) {
foreach ( $values['data']->get_attributes() as $attribute => $variation ){
if ( $variation == 'deposit' ){
$in_cart = true; //edited
break;
}
}
}
if( $in_cart ){
break;
}
}
if ( $in_cart ) {
unset( $available_gateways['klarna_payments'] );
}else {
unset( $available_gateways['cod'] );
}
return $available_gateways;
}

Resources