phpwoocommerceproductcartshipping-method

Allow Free shipping If there is only a specific product type or if subtotal reaches a minimal amout


I have a script that create a progress bar to show "Spend another XX € for free shipping!" based on a minimal amount to be spent, but I would like to avoid this text if there are ONLY products in cart from a specific product type. This because people could also buy an "Appointment" product, that are not physical products, but are under the custom product type "Appointment".

Here is my current code:

add_filter( 'woocommerce_package_rates', 'ecommercehints_free_shipping_for_specific_products', 10, 2 );
function ecommercehints_free_shipping_for_specific_products($rates, $package, $order) {
    // Loop through order items
    foreach ( $order->get_items() as $item ) {
        // Get an instance of the WC_Product Object from the WC_Order_Item_Product
        $product = $item->get_product();
        $cart_total = WC()->cart->get_subtotal();

        if( $product->is_type('Appointment')  ){
            echo 'You\'ve unlocked free shipping!';
        } else if ( $cart_total < 50 ) {
            unset( $rates['free_shipping:21'] ); // The shipping method radio button value
        }
        break; // We stop the loop and keep the first item
    }
    return $rates;
}   

// Show 'Spend another X amount' on checkout.
add_filter( 'woocommerce_checkout_before_order_review', 'ecommercehints_checkout_page_progress_bar', 10 );
function ecommercehints_checkout_page_progress_bar() {
    $cart_total = WC()->cart->get_subtotal();
    $cart_remaining = 50 - $cart_total;
    $product = $item->get_product();

    if ($cart_total < 50 ) {
        echo 'Spend another ' . get_woocommerce_currency_symbol() . $cart_remaining . ' for free shipping!<br><progress id="freeshippingprogress" max="50" value="'.$cart_total.'"></progress>';
    } else if ( $product->is_type('Appointment') {
        echo 'You\'ve unlocked free shipping!';
    }      
}

Solution

  • There are some mistakes in your code like:

    Also, you need to be sure that "Appointment" is a valid product type. In general, product types are always in lowercase, so I used "appointment" (in lowercase) below.

    Try the following revised code:

    // Conditional function that checks if there are only "appointment" product types in cart
    function has_only_appointments() {
        $others_found = false; // Initializing
    
        // Loop through cat items
        foreach ( WC()->cart->get_cart() as $item ) {
            if ( ! $item['data']->is_type('appointment') ) {
                $others_found = true;
                break;
            }
        }
        return !$others_found;
    }
    
    // Filter shipping  methods
    add_filter( 'woocommerce_package_rates', 'ecommercehints_free_shipping_for_specific_products', 10, 2 );
    function ecommercehints_free_shipping_for_specific_products( $rates, $package ) {
        // check if there are only "appointment" product types in cart
        if( has_only_appointments()  ){
            return $rates;
        }
        
        // If there is shippable products remove free shipping if cart subtotal is under specific amount 
        if ( WC()->cart->get_subtotal() < 50 ) {
            unset( $rates['free_shipping:21'] ); // The targeted Free shipping method rate ID
        }
        return $rates;
    }
    
    // Show 'Spend another X amount' on checkout.
    add_filter( 'woocommerce_checkout_before_order_review', 'ecommercehints_checkout_page_progress_bar', 10 );
    function ecommercehints_checkout_page_progress_bar() {
        $subtotal = WC()->cart->get_subtotal();
    
        // check if there are only "appointment" product types in cart or If cart subtotal is up to 50
        if( has_only_appointments( WC()->cart->get_cart() ) || $subtotal >= 50 ){
            echo __("You've unlocked free shipping!", "woocommerce");
        } elseif  ( $subtotal < 50 ) {
            printf( __('Spend another %s for free shipping!', 'woocommerce'), wc_price( 50 - $subtotal ) );
            printf('<br><progress id="freeshippingprogress" max="50" value="%d"></progress>', intval($subtotal) );
        }    
    }
    

    It should work…

    Important: Empty your cart to refresh the shipping methods cached data.


    Addition (for virtual products):

    Now as your custom product type "Appointments" is not a "Physical" product, it should be "virtual" by default…

    So you can try the following instead:

    // Conditional function that checks if there are only virtual products in cart
    function has_only_virtual_products() {
        $physical_found = false; // Initializing
    
        // Loop through cat items
        foreach ( WC()->cart->get_cart() as $item ) {
            if ( ! $item['data']->is_virtual() ) {
                $others_found = true;
                break;
            }
        }
        return !$others_found;
    }
    
    // Filter shipping  methods
    add_filter( 'woocommerce_package_rates', 'ecommercehints_free_shipping_for_specific_products', 10, 2 );
    function ecommercehints_free_shipping_for_specific_products( $rates, $package ) {
        // check if there are only virtual products in cart
        if( has_only_virtual_products()  ){
            return $rates;
        }
        
        // If there is shippable products remove free shipping if cart subtotal is under specific amount 
        if ( WC()->cart->get_subtotal() < 50 ) {
            unset( $rates['free_shipping:21'] ); // The targeted Free shipping method rate ID
        }
        return $rates;
    }
    
    // Show 'Spend another X amount' on checkout.
    add_filter( 'woocommerce_checkout_before_order_review', 'ecommercehints_checkout_page_progress_bar', 10 );
    function ecommercehints_checkout_page_progress_bar() {
        $subtotal = WC()->cart->get_subtotal();
    
        // check if there are only virtual products in cart or If cart subtotal is up to 50
        if( has_only_virtual_products() || $subtotal >= 50 ){
            echo __("You've unlocked free shipping!", "woocommerce");
        } elseif  ( $subtotal < 50 ) {
            printf( __('Spend another %s for free shipping!', 'woocommerce'), wc_price( 50 - $subtotal ) );
            printf('<br><progress id="freeshippingprogress" max="50" value="%d"></progress>', intval($subtotal) );
        }    
    }
    

    It could work…

    Important: Empty your cart to refresh the shipping methods cached data.