Search code examples
phpwordpresswoocommerceshipping-methodproduct-quantity

Hide some shipping methods if cart has more than 4 items from specific shipping class


Here's the scenario:

I ship trays of drinks using UPS. However, it becomes very difficult to pack 5 trays of drinks into boxes. So I would like to disable UPS shipping method and only display Flat Rate Shipping if a customer orders 5 or more trays of drinks. I have about 7 different drinks, but I can add these drinks to a shipping class to simplify the code.

I want to expand this code to include the quantity of products in a specific the class or perhaps the number of times a product in the shipping class appears. So if the cart has 5 or more of a product in this specific shipping class, it should remove the shipping methods I've specified under array.

How do I expand this code to also include quantity of products?

// If we find the shipping class & quantity of product in the shipping class is equal to or greater than 5.

add_filter( 'woocommerce_package_rates', 'hide_shipping_method_based_on_shipping_class', 10, 2 );
function hide_shipping_method_based_on_shipping_class( $rates, $package )
{
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    // Shipping Class To Find
    $class = 182;

    // Number Of Shipping Class Items In Cart
    $amount = 5;

    // Shipping Methods To Hide
    $method_key_ids = array('wf_shipping_ups:07', 'wf_shipping_ups:08', 'wf_shipping_ups:11', 'wf_shipping_ups:54', 'wf_shipping_ups:65', 'wf_shipping_ups:70', 'wf_shipping_ups:74', 'free_shipping:2', 'request_shipping_quote');

    // Checking In Cart Items
    foreach( $package['contents'] as $item ) {
        // If We Find The Shipping Class and Number of Items
        if( $item['data']->get_shipping_class_id() == $class && count($package['contents']) >= $amount ){
            foreach( $method_key_ids as $method_key_id ){
                unset($rates[$method_key_id]); // Remove Targeted Methods
            }
            break; // Stop The Loop
        }
    }
    return $rates;
}

Edit - Addition:

Since I purchased the "WooCommerce UPS Shipping Plugin with Print Label" from PluginHive, I have access to their "Manage Shipping Methods" plugin which allows me to do the following:

Set multiple rules to exclude various shipping methods from various shipping classes. Break the sequence on first occurrence.

The rules I have set-up are as follows:

  1. For Class 150 (Break on First Occurrence) - Unset:

wf_shipping_ups:07, wf_shipping_ups:08, wf_shipping_ups:11, wf_shipping_ups:54, wf_shipping_ups:65, wf_shipping_ups:70, wf_shipping_ups:74, free_shipping:2, request_shipping_quote.

  1. For Class 151 - Unset: flat_rate:20, flat_rate:21.

I've created a third class 182 in the above code for the products I want to target. It should be treated as class 151 only if less than 5 items of the class are added to the cart.

But it should be treated as Class 150 if 5 or more items are added to the cart.

That's my dilemma.

Potential Solution - Addition:

I figured out how to solve my problem. The code @LoicTheAztec assisted me with lets me unset shipping methods for a given shipping class if the product quantity in the cart is 5 or more.

What I need to do now is unset two other shipping methods (flat_rate:20 and flat_rate:21) which is causing the conflict, for the same shipping class (182) but this time for a product quantity in the cart of 4 or less (=<).

Then I can use the existing plugin to create the following rules:

Break on First Occurrence (Check)

  1. For Class 150 - Unset:

wf_shipping_ups:07, wf_shipping_ups:08, wf_shipping_ups:11, wf_shipping_ups:54, wf_shipping_ups:65, wf_shipping_ups:70, wf_shipping_ups:74, free_shipping:2, request_shipping_quote.

  1. For Class 182 - Unset:

Nothing - Because Both Codes Will Create The Logic

  1. For Class 151 - Unset:

flat_rate:20, flat_rate:21.

This should resolve the conflict caused by the plugin.

The million dollar question is...can I somehow use @LoicTheAztec 's solution to set some sort of minimum quantity?


Solution

  • The following will hide specific defined shipping methods if total items from specific shipping class are 5 or more:

    add_filter( 'woocommerce_package_rates', 'hide_shipping_method_based_on_shipping_class', 10, 2 );
    function hide_shipping_method_based_on_shipping_class( $rates, $package ) {
        $targeted_class_ids = array(182); // Shipping Class To Find
        $allowed_max_qty    = 4; // Max allowed quantity for the shipping class
        $shipping_rates_ids = array( // Shipping Method rates Ids To Hide
            'wf_shipping_ups:07',
            'wf_shipping_ups:08',
            'wf_shipping_ups:11',
            'wf_shipping_ups:54',
            'wf_shipping_ups:65',
            'wf_shipping_ups:70',
            'wf_shipping_ups:74',
            'free_shipping:2',
            'request_shipping_quote'
        );
        
        $related_total_qty  = 0;
    
        // Checking cart items for current package
        foreach( $package['contents'] as $key => $cart_item ) {
            if( in_array( $cart_item['data']->get_shipping_class_id(), $targeted_class_ids ) ){
                $related_total_qty += $cart_item['quantity'];
            }
        }
        
        // When total allowed quantity is more than allowed (for items from defined shipping classes)
        if ( $related_total_qty > $allowed_max_qty ) {
            // Hide related defined shipping methods
            foreach( $shipping_rates_ids as $shipping_rate_id ) {
                if( isset($rates[$shipping_rate_id]) ) {
                    unset($rates[$shipping_rate_id]); // Remove Targeted Methods
                }
            }
        }
        return $rates;
    }
    

    Code goes in functions.php file of your active child theme (or active theme). Untested it should works.

    Refresh the shipping caches:

    1. This code is already saved on your functions.php file.
    2. In a shipping zone settings, disable / save any shipping method, then enable back / save.

      You are done and you can test it.

    Handling number of items instead of items cumulated quantity:

    Replace:

    $related_total_qty += $cart_item['quantity'];
    

    by

    $related_total_qty++;