Search code examples
phpwoocommercehook-woocommercecartcheckout

Increase WooCommerce Cart Total Weight Based On Specific Shipping Class


I have some custom code in my functions.php file that changes the shipping class from 343 to 344 if more than one product with the shipping class 343 is in the cart during checkout. However, I need to add weight in grams to the total order as well, based on the same conditions.

Namely, when only 1 product with the class 343 is in the cart during checkout, I want to add 14 grams to the total weight of the order. In any other condition, namely if there is at least 1 product with shipping class 344 in the cart, OR more than 1 product with the shipping class 343 in the cart, I want to add 54 grams to the total weight of the order.

Currently, if there is only one product shipping class 343 in the cart during checkout, it's calculated as being in this shipping class. However, if two products with shipping class are in the cart during checkout, they're changed to shipping class 344. And I want to add the aforementioned weight to the order based on this same condition.

//This turns 2 letter-box items into 1 standard package
add_action( 'woocommerce_before_calculate_totals', 'adjust_shipping_class_based_on_quantity' );

function adjust_shipping_class_based_on_quantity( $cart ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return; // Exit

    if ( ! $cart->needs_shipping() ) 
        return; // Exit

    $count = 0; // Initialize

    // Count items from a specific shipping class
    foreach( $cart->get_cart() as $item ) {
        if ( $item['data']->get_shipping_class_id() === 343 ) {
            // Increment count for each item with shipping class ID 343
            $count += $item['quantity'];
        }
    }

    // Check that there are at least 2 items from the specific shipping class
    if ( $count < 2 ) 
        return; // Exit
    
    // Loop through cart items to change the shipping class of items with the targeted shipping class
    foreach( $cart->get_cart() as $item ) {
        if ( $item['data']->get_shipping_class_id() === 343 ) {
            $item['data']->set_shipping_class_id( 344 ); // Change the shipping class
        }
    }
}

While I'd yet come to the point where I could test any code conditional on the shipping class, I did try and see if this code below could add weight to the total order, but it caused an error instead.

add_action( 'woocommerce_cart_calculate_fees', 'add_extra_weight_to_order', 10, 1 );

function add_extra_weight_to_order( $cart ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) ) {
        return;
    }

    $extra_weight = 14; 

    $total_weight = $cart->get_cart_contents_weight();
    
    $total_weight += $extra_weight;

    $cart->set_cart_contents_weight( $total_weight );
}

How should I go about solving my problem?


Solution

  • Updated

    Note that WC_Cart set_cart_contents_weight() method doesn't exist, so this is why you get an error in your last function.

    The following, based on specific cart item shipping class ID quantity count:

    • will change this cart item shipping class ID,
    • will add to the cart total weight:
      • 14 grams, if there is only one item from a specific shipping class,
      • 52 grams for all other cases.
    // Utility function: Count items in cart from a specific shipping class Id
    function get_cart_shipping_class_count( $targeted_class_id, $cart = null ) {
        if ( ! $cart ) {
            $cart = WC()->cart;
        }
        $count = 0; // Initialize
    
        foreach( $cart->get_cart() as $item ) {
            $product = wc_get_product($item['variation_id'] > 0 ? $item['variation_id'] : $item['product_id']);
            if ( $product->get_shipping_class_id() === $targeted_class_id ) {
                $count += $item['quantity']; 
            }
        }
        return $count;
    }
    
    // Increase cart content weight conditionally (handles weight unit conversion)
    add_filter( 'woocommerce_cart_contents_weight', 'increase_conditionally_cart_contents_weight' );
    function increase_conditionally_cart_contents_weight( $weight ) {
        $qty_count   = get_cart_shipping_class_count( 343 ); // Get shipping class qty count
    
        $weight_unit = strtolower( get_option( 'woocommerce_weight_unit' ) ); // get weight unit
        $conv_rate   = $weight_unit === 'kg' ? 0.001 : 1; // Conversion rate from grams to kilos
    
        if ( $qty_count === 1 && WC()->cart->get_cart_contents_count() === 1 ) {
            $weight += 14 * $conv_rate;
        } else {
            $weight += 52 * $conv_rate;
        }
        return $weight;
    }
    
    // Change conditionally cart item shipping class
    add_action( 'woocommerce_before_calculate_totals', 'adjust_shipping_class_based_on_quantity' );
    function adjust_shipping_class_based_on_quantity( $cart ) {
        if ( is_admin() && ! defined( 'DOING_AJAX' ) )
            return; // Exit
    
        if ( ! $cart->needs_shipping() ) 
            return; // Exit
    
        // If there is only 1 items from the specific shipping class, we exit.
        if ( get_cart_shipping_class_count( 343, $cart ) < 2 ) 
            return; // Exit
        
        // Loop through cart items to change the shipping class of items with the targeted shipping class
        foreach( $cart->get_cart() as $item ) {
            if ( $item['data']->get_shipping_class_id() === 343 ) {
                $item['data']->set_shipping_class_id( 344 ); // Change the shipping class
            }
        }
    }
    

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

    Note: WC_Cart

    Related: WooCommerce: Change Shipping Class conditionally for specific Zone