1

Inspired by this answer code, I've write the code below which works to add an error notice in WooCommerce checkout under a condition (they add a non-shippable product, (id'd by shipping class) to the cart and are outside of my local delivery zones (i.e. their postal code falls into the rest of the world zone) and only pickup is available (identified by only shipping method available).

add_action('woocommerce_after_checkout_validation', 'get_zone_info', 10 );
function get_zone_info( ) {
    // Get cart shipping packages
    $shipping_packages =  WC()->cart->get_shipping_packages();

    // Get the WC_Shipping_Zones instance object for the first package
    $shipping_zone = wc_get_shipping_zone( reset( $shipping_packages ) );

    $zone_id   = $shipping_zone->get_id(); // Get the zone ID
    $zone_name = $shipping_zone->get_zone_name(); // Get the zone name

    $shipping_class_target = 87;
    $in_cart = 0;
    foreach ( WC()->cart->get_cart_contents() as $key => $values ) {
        if ( $values[ 'data' ]->get_shipping_class_id() == $shipping_class_target ) {
            $in_cart = 1;
            break;
        }
    }

    if ( $zone_name=='Locations not covered by your other zones' && $in_cart==1 || $zone_id==0 && $in_cart==1 ) {
        //wc_print_notice( __( '<p>Count_ships: ' .$counter_ship. ' Cart id: ' . $in_cart .  ' | Zone id: ' . $zone_id . ' | Zone name: ' . $zone_name . '</p>', 'woocommerce' ), 'success' );
        wc_print_notice( __( '<b>ONLY PICKUP IS AVAILABLE</b><br>To have courier delivery, please goto your cart and remove products which cannot be shipped', 'woocommerce' ), 'success' );
    } else {
        //donothing
    }
}

I have two Issues:

1- The notice check doesn't retrigger each time the address changes

2- I use the Multi-Step Checkout Pro for WooCommerce by Silkypress and want this notice to come up during the ORDER section...however the above notice doesn't work at all when I activate this plugin.
I've contacted them for support, but appreciate any help.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
Brad M
  • 11
  • 2
  • As for the message, replace the current line with this `wc_add_notice( __( 'ONLY PICKUP IS AVAILABLE
    To have courier delivery, please goto your cart and remove products which cannot be shipped', 'woocommerce' ), 'error' );`. You must set the notice type to **`error`** to prevent the order from being completed.
    – Vincenzo Di Gaetano Feb 12 '21 at 20:40

1 Answers1

2

Alternatively, you can think differently, automatically disabling all shipping methods except local pickup (if there is at least one product in the cart with the shipping class id equal to the one specified).

Based on:

Then you can use a custom function to check if there are products in the cart with a specific shipping class id:

// check if the products in the cart have a specific shipping class id
function check_product_in_cart_according_shipping_class_id() {

    $shipping_class_target = 87;
    $in_cart = false;

    // check if in the cart there is at least one product with the shipping class id equal to "87"
    foreach ( WC()->cart->get_cart() as $cart_item ) {
        $product = $cart_item['data'];
        $shipping_class = $product->get_shipping_class_id();
        if ( $shipping_class == $shipping_class_target ) {
            $in_cart = true;
            return $in_cart;
        }
    }
    return $in_cart;
}

And with another custom function you get the list of products that cannot be shipped (and therefore those that have the shipping class id equal to 87):

// gets products that have a specific shipping class
function get_product_in_cart_according_shipping_class_id() {

    $shipping_class_target = 87;
    $product_list = '<ul>';
    
    // check if in the cart there is at least one product with the shipping class id equal to "87"
    foreach ( WC()->cart->get_cart() as $cart_item ) {
        $product = $cart_item['data'];
        $shipping_class = $product->get_shipping_class_id();
        if ( $shipping_class == $shipping_class_target ) {
            $sku = $product->get_sku();
            $name = $product->get_name();
            $qty = $cart_item['quantity'];
            $product_list .= '<li>' . $qty . ' x ' . $name . ' (' . $sku . ')</li>';
        }
    }
    $product_list .= '</ul>';
    return $product_list;
}

Then disable all shipping methods except local pickup:

// disable all shipping methods except local pickup based on product shipping class
add_filter( 'woocommerce_package_rates', 'disable_shipping_methods_based_on_product_shipping_class', 10, 2 );
function disable_shipping_methods_based_on_product_shipping_class( $rates, $package ) {

    $in_cart = check_product_in_cart_according_shipping_class_id();

    // if it is present, all shipping methods are disabled except local pickup
    if ( $in_cart ) {
        foreach( $rates as $rate_key => $rate ) {
            if ( $rate->method_id != 'local_pickup' ) {
                unset($rates[$rate_key]);
            }
        }
    }
    
    return $rates;

}

Finally, it adds the custom notice on the cart and checkout page.
I created two functions because on the cart page the notice needs to update when a product is added or removed and using the woocommerce_before_calculate_totals hook would cause multiple notices in the checkout.

In the cart:

// add custom notice in cart
add_action( 'woocommerce_before_calculate_totals', 'add_custom_notice_in_cart' );
function add_custom_notice_in_cart() {
    // only on the cart page
    if ( ! is_cart() ) {
        return;
    }
    $in_cart = check_product_in_cart_according_shipping_class_id();
    if ( $in_cart ) {
        $products = get_product_in_cart_according_shipping_class_id();
        wc_clear_notices();
        wc_add_notice( sprintf( __( 'Only <strong>Local Pickup</strong> shipping method is available. These products cannot be shipped:<br>%s', 'woocommerce' ), $products ), 'notice' );
    }
}

enter image description here

In the checkout:

// add custom notice in checkout
add_action( 'woocommerce_checkout_before_customer_details', 'add_custom_notice_in_checkout' );
function add_custom_notice_in_checkout() {
    $in_cart = check_product_in_cart_according_shipping_class_id();
    if ( $in_cart ) {
        $products = get_product_in_cart_according_shipping_class_id();
        wc_clear_notices();
        wc_add_notice( sprintf( __( 'Only <strong>Local Pickup</strong> shipping method is available. These products cannot be shipped:<br>%s', 'woocommerce' ), $products ), 'notice' );
    }
}

enter image description here

The code has been tested and works. Add it to your active theme's functions.php.

Vincenzo Di Gaetano
  • 3,892
  • 3
  • 13
  • 32
  • Thank you both for the suggestions! I don't want to stop the user from placing the order as they may want to pickup items which can't ship...I just want them to know why they can't ship the whole order incase they missed the fact that not all items can ship out! Also, I have limited the shipping options already, so users will only see 1 option (local delivery) if they've added a non-shipable item to the cart...its just a matter of letting them know why they don't see overnight shipping options which is stumping me especially if they change their address once in the checkout... – Brad M Feb 13 '21 at 17:19
  • @BradM I have integrated my answer based on your comment. – Vincenzo Di Gaetano Feb 14 '21 at 10:26