skip to Main Content

I use the following code to display the delivery method "local pickup" only for one city (Paris). I use the code together with the plugin "States, Cities, and Places for WooCommerce" (https://wordpress.org/plugins/states-cities-and-places-for-woocommerce/) and everything works correctly.

add_filter( 'woocommerce_package_rates', 'shipping_package_rates_filter_callback', 100, 2 );

function shipping_package_rates_filter_callback( $rates, $package ) {
 // Define the allowed cities for local pickup
 $allowed_cities = array( 'Paris' );
 // The defined method id
 $pickup_method_id = 'local_pickup:3';

 // Get the shipping city for the current order
 $shipping_city = isset( $package['destination']['city'] ) ? strtolower( $package['destination']['city'] ) : '';

 // Check if the shipping city is in the allowed cities and the pickup method is available
 if ( in_array( $shipping_city, $allowed_cities ) && isset( $rates[$pickup_method_id] ) ) {
     return $rates;
 } else {
     // Hide the pickup method
     unset( $rates[$pickup_method_id] );
     return $rates;
 }
}

add_action( 'woocommerce_checkout_update_order_review', 'refresh_shipping_methods', 10, 1 );

function refresh_shipping_methods( $post_data ){
 $bool = true;

 // Define the allowed cities for local pickup
 $allowed_cities = array( 'Paris' );

 // Check if the shipping city is in the allowed cities
 $shipping_address = WC()->customer->get_shipping();
 if ( isset( $shipping_address['city'] ) && in_array( strtolower( $shipping_address['city'] ), $allowed_cities ) ) {
     $bool = false;
 }

 // Mandatory to make it work with shipping methods
 foreach ( WC()->cart->get_shipping_packages() as $package_key => $package ){
     WC()->session->set( 'shipping_for_package_' . $package_key, $bool );
 }
 WC()->cart->calculate_shipping();
}

My problem is that I would like the "local pickup" delivery method to be checked by default when choosing the city of Paris.Although I put this method in the first position, the default checked method is one of the methods that appears before selecting the city (the one in the 2nd place). I tried to modify the following code, but it doesn’t seem to work. How could I solve the problem?

add_action( 'woocommerce_before_checkout_form', 'ahir_select_free_shipping_by_default' );

function ahir_select_free_shipping_by_default() {
    if ( isset(WC()->session) && ! WC()->session->has_session() )
        WC()->session->set_customer_session_cookie( true );

    // Check if "free shipping" is already set
    if ( strpos( WC()->session->get('chosen_shipping_methods')[0], 'free_shipping' ) !== false )
        return;

    // Loop through shipping methods
    foreach( WC()->session->get('shipping_for_package_0')['rates'] as $key => $rate ){
        if( $rate->method_id === 'free_shipping' ){
            // Set "Free shipping" method
            WC()->session->set( 'chosen_shipping_methods', array($rate->id) );
            return;
        }
    }
}

2

Answers


  1. Chosen as BEST ANSWER

    I don't know if I did it right, but I found a solution to make it work the way I want. Based on the answer here https://stackoverflow.com/a/67083089/21533506 I added the following code:

        add_filter( 'woocommerce_package_rates', 'disable_shipping_method_based_on_location', 10, 2 );
    function disable_shipping_method_based_on_location( $rates, $package ) {
    
        
        $city = $package['destination']['city'];
    
        // If the city is Paris, hide flat_rate:2, flat_rate:4 and local_pickup:3
        if ( 'Paris' === $city ) {
            unset( $rates['flat_rate:2'] );
            unset( $rates['flat_rate:4'] );
        } else { // Otherwise, hide flat_rate:6, flat_rate:7 and local_pickup:3 for all other localities
            unset( $rates['flat_rate:6'] );
            unset( $rates['flat_rate:7'] );
            unset( $rates['local_pickup:3'] );
        }
     
        return $rates;
    

    After that I added the following code to hide all delivery methods until the address is filled in:

        add_filter('woocommerce_package_rates', 'hide_shipping_until_address');
    function hide_shipping_until_address($rates) {
        $address = WC()->customer->get_shipping();
        $city = isset($address['city']) ? $address['city'] : '';
        $state = isset($address['state']) ? $address['state'] : '';
    
        if (empty($city) || empty($state)) {
            foreach ($rates as $rate_id => $rate) {
                unset($rates[$rate_id]);
            }
        }
    
        return $rates;
    }
    

    After that I modified the code from here https://stackoverflow.com/a/77896304/21533506 to display the local fetch in the first position:

        add_filter( 'woocommerce_package_rates', 'filter_woocommerce_package_rates', 100, 2 );
    function filter_woocommerce_package_rates( $rates, $package ) {
        $free_shipping_exists = false; // Initialize free shipping flag
    
        // Loop through shipping rates for the current shipping package
        foreach ( $rates as $rate_key => $rate ) {
            // If method is free shipping, set free shipping flag to true
            if ( 'free_shipping' === $rate->method_id ) {
                $free_shipping_exists = true;
                break;
            }
        }
    
        // If free shipping exists, hide other shipping methods except local pickup
        if ( $free_shipping_exists ) {
            foreach ( $rates as $rate_key => $rate ) {
                // If method is not local pickup and not free shipping, unset it
                if ( 'local_pickup' !== $rate->method_id && 'free_shipping' !== $rate->method_id ) {
                    unset( $rates[$rate_key] );
                }
                // If method is local pickup, set cost to zero
                elseif ( 'local_pickup' === $rate->method_id ) {
                    $rates[$rate_key]->cost = 0;
                    $rates[$rate_key]->taxes = array_fill_keys( array_keys( $rates[$rate_key]->taxes ), 0 );
                }
            }
        }
    
        return $rates;
    }
    

  2. To set/define the default shipping method when the city is "Paris", try the following that uses woocommerce_shipping_chosen_method filter hook:

    add_filter( 'woocommerce_shipping_chosen_method', 'set_default_chosen_shipping_method', 10, 3 );
    function set_default_chosen_shipping_method( $default, $rates, $chosen_method ){ 
        $allowed_cities = array('Paris');
        $matched_city   = false;
    
        foreach( WC()->cart->get_shipping_packages() as $key => $package ) {
            if ( isset($package['destination']['city']) 
            && in_array( ucfirst($package['destination']['city']), $allowed_cities ) ) {
                $matched_city = true;
                break;
            }
        }
    
        if( ! $matched_city ) {
            return $default;
        }
    
        // Loop through shipping methods
        foreach( $rates as $rate_key => $rate ){
            if( $rate->method_id === 'local_pickup' ){
                return $rate_key;
            }
        }
        return $default;
    }
    

    Code goes in functions.php file of your child theme (or in a plugin). It could work.


    Note that in your existing code, for the 2 first functions, you should replace strtolower( with ucfirst( if you want to get it working with the defined city(s).

    add_filter( 'woocommerce_package_rates', 'shipping_package_rates_callback', 100, 2 );
    function shipping_package_rates_callback( $rates, $package ) {
        $pickup_cities  = array('Paris'); // The defined allowed cities
        $pickup_rate_id = 'local_pickup:3'; // The defined method id
    
        // Get the shipping city for the current order
        $shipping_city = isset($package['destination']['city']) ? ucfirst($package['destination']['city']) : '';
    
        // Check if the shipping city is in the allowed cities and the pickup method is available
        if ( ! in_array($shipping_city, $pickup_cities) && isset($rates[$pickup_rate_id]) ) {
            unset($rates[$pickup_rate_id]); // Hide the pickup method
        }
        return $rates;
    }
    
    add_action( 'woocommerce_checkout_update_order_review', 'refresh_shipping_methods', 10, 1 );
    function refresh_shipping_methods( $post_data ){
        $pickup_cities  = array('Paris'); // The defined allowed cities
        $bool = true;
    
        // Check if the shipping city is in the allowed cities
        if ( in_array( ucfirst(WC()->customer->get_shipping_city()), $pickup_cities ) ) {
            $bool = false;
        }
        // Mandatory to make it work with shipping methods
        foreach ( WC()->cart->get_shipping_packages() as $package_key => $package ){
            WC()->session->set( "shipping_for_package_{$package_key}", $bool );
        }
        WC()->cart->calculate_shipping();
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search