Search code examples
phpwordpresswoocommercecartshipping-method

Allow only Local Pickup for specific products outside specific Shipping Zone in WooCommerce


I have several products in the store, however one product (flammable) can only be shipped via a specific shipping company; Unfortunately that company can't reach the whole country. So in case the customer buys the flammable product and it's outside the coverage of the only company that can ship the product, he should not see any other shipping option except local pickup.

So far I have this code (courtesy of different StackOverFlow answers):

function filter_woocommerce_package_rates( $rates, $package ) {
    // Shipping zone
    //echo 'entrando';
    $shipping_zone = wc_get_shipping_zone( $package );

    $product_ids = array( 2267 ); // HERE set the product IDs in the array
    $method_id = 'weight_based_shipping:38'; // HERE set the shipping method ID that I want to hide
    $found = false;
    
    // Get zone ID
    $zone_id = $shipping_zone->get_id();
    
    //echo $shipping_zone;
    //echo $zone_id;
    
    // NOT equal
    if ( $zone_id != 8 ) {
        // Unset a single rate/method for a specific product
        foreach( $package['contents'] as $cart_item ) {
        if ( in_array( $cart_item['product_id'], $product_ids ) ){
            $found = true;
            break;
        }
        }
         if ( $found )
            unset( $rates[$method_id] );
    }

    return $rates;
}
add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 10, 2 );

however I don't know why is not working. Even the 'echo' is not working.


Solution

  • Updated - Try following instead (code is commented):

    // SETTINGS BELOW: Custom function that handle your settings
    function custom_shipping_settings() {
        return array(
            'product_ids' => array(2267), // Define the products that need to be in a separated shipping package (local pickup)
            'allowed_zones_ids' => array(8), // Define the allowed zones IDs
        );
    }
    
    
    // Splitting cart items into 2 shipping packages
    add_filter( 'woocommerce_cart_shipping_packages', 'split_shipping_packages' );
    function split_shipping_packages( $packages ) {
        extract(custom_shipping_settings()); // Load and extract settings
    
        $customer       = WC()->customer;
        $destination    = array(
            'country'   => $customer->get_shipping_country(),
            'state'     => $customer->get_shipping_state(),
            'postcode'  => $customer->get_shipping_postcode(),
            'city'      => $customer->get_shipping_city(),
            'address'   => $customer->get_shipping_address(),
            'address_2' => $customer->get_shipping_address_2()
        );
        $package_dest   = array( 'destination' => $destination );
        $zone           = wc_get_shipping_zone( $package_dest );
    
        if ( ! in_array( $zone->get_id(), $allowed_zones_ids ) ) {
    
            // Reset packages and initialize variables
            $packages = $splitted_cart_items = array();
    
            // Loop through cart items
            foreach ( WC()->cart->get_cart() as $item_key => $item ) {
                if ( is_a($item['data'], 'WC_Product') && $item['data']->needs_shipping() ) {
                    if ( ! array_intersect( array($item['product_id'], $item['variation_id']), $product_ids ) ) {
                        $splitted_cart_items[0][$item_key] = $item; // Regular items
                    } else {
                        $splitted_cart_items[1][$item_key] = $item; // Special separated items
                    }
                }
            }
    
            if ( count($splitted_cart_items) < 2 )
                return $packages;
    
            // Loop through spitted cart items
            foreach ( $splitted_cart_items as $key => $items ) {
                // Set each cart items group in a shipping package
                $packages[$key] = array(
                    'contents'        => $items,
                    'contents_cost'   => array_sum( wp_list_pluck( $items, 'line_total' ) ),
                    'applied_coupons' => WC()->cart->get_applied_coupons(),
                    'user'            => array( 'ID' => get_current_user_id() ),
                    'destination'     => $destination,
                );
            }
        }
        return $packages;
    }
    
    // Force local pickup for specific splitted shipping package (for the defined products)
    add_filter( 'woocommerce_package_rates', 'filter_wc_package_rates', 10, 2 );
    function filter_wc_package_rates( $rates, $package ) {
        extract(custom_shipping_settings()); // Load and extract settings
    
        $zone  = wc_get_shipping_zone( $package ); // Get current shipping zone
        $found = false;
    
        // For all others shipping zones than allowed zones
        if ( ! in_array( $zone->get_id(), $allowed_zones_ids ) ) {
            // Loop through cart items for current shipping package
            foreach( $package['contents'] as $item ) {
                // Look for defined specific product Ids
                if ( array_intersect( array($item['product_id'], $item['variation_id']), $product_ids ) ) {
                    $found = true; // Flag as found
                    break; // Stop the loop
                }
            }
    
            // If any defined product is in cart
            if ( $found ) {
                // Loop through shipping rates
                foreach ( $rates as $rate_key => $rate ) {
                    // Hide all shipping methods keeping "Local pickup"
                    if ( 'local_pickup' !== $rate->method_id ) {
                        unset( $rates[$rate_key] );
                    }
                }
            }
        }
        return $rates;
    }
    

    Code goes in functions.php file of your active child theme (or active theme). Tested and 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.