How to Fix Weight-Based Shipping Calculation for Food Delivery System

·

5 min read

As an expert WordPress and WooCommerce developer at Hybrid Web Agency, I've worked with many clients in the food delivery industry over the years. One common challenge they face is accurately calculating shipping rates based on the total weight of customer orders. By default, WooCommerce's shipping system doesn't support this out of the box. It only considers the weight of the package being shipped, not the individual item weights within the order.

This can lead to significant issues for food delivery businesses trying to pass along proper postage costs to customers. Shipping rates may end up being incorrectly lower or higher than the actual rates charged by carriers. Both the business and customer experience suffers as a result. That's why I was motivated to create a customizable solution that leverages the power of WooCommerce but addresses this core limitation.

In this blog post, I'm going to walk through how WooCommerce calculates shipping by default, explain the specific problem it causes for food delivery, and provide a code walkthrough of my weight-based shipping plugin that overcomes this challenge.

Understanding the Default Behavior

WooCommerce Shipping Zones

Let's start with the default WooCommerce shipping behavior. At its core, WooCommerce uses a zone-based system for defining shipping rules and costs. Zones are groups of countries, states, or regions that share common shipping options and rates. Within each zone, site owners can then establish multiple shipping methods like Free Shipping, Flat Rates, or Table Rates.

Table Rate Limitations

Table rates allow specifying a cost per certain package dimensions or weight thresholds. However, WooCommerce only considers the overall package details rather than granular item weights and dimensions when matching to these rules. This works well for most traditional e-commerce stores but falls short for food and grocery delivery.

For example, let's say I've configured a "ground shipping" table rate for packages under 5lbs costing $5 and over 5lbs at $10. But under the hood, WooCommerce only checks if the full cart's collective weight meets this threshold. So a 4-item order with each item weighing 1.5lbs would be priced at $5 instead of the correct $10. That's 4.5lbs not being accounted for properly.

We can see how this default behavior breaks down for high-volume, low-margin food businesses. To truly optimize costs, shipping rates need to factor in each item's attributes rather than a lump sum package view. That's the core problem I sought to resolve through a custom code-level solution.

The Problem Code

Default Shipping Calculation Functions

Let's take a look under the hood at some of WooCommerce's default shipping calculation functions. One of the core ones is wc_shipping()->get_packages() which retrieves cart/order data to then pass through the available shipping methods.

Here is a snippet showing it only pulls overall details rather than individual item weights:

// Get cart/order contents and total weight

$contents = $this->get_contents();

$package = array(

'contents' => $contents,

'contents_weight' => $this->get_package_weight(),

'contents_cost' => $this->get_cart_contents_total()

);

As we can see, it simply sums the item weights into one $package variable rather than storing each item's attributes separately.

Cart/Package Data Retrieval

WooCommerce also has functions like wc_get_cart_contents_weights() to pull the current cart contents. However, it also lumps weights together:

$weights = 0;

foreach ( WC()->cart->get_cart() as $cart_item ) {

$weights += $cart_item['data']->get_weight();

}

return $weights;

Again, individual item details are not maintained for more granular shipping rule comparisons.

The Solution Plugin

To address this, I created a WooCommerce shipping extension that introduces some overrides. The core files include:

includes/shipping-functions.php

includes/shipping-zones-rates.php

The key is that it separates item weights and dimensions on retrieval to then apply shipping calculations accordingly.

Calculating Item Weights

Iterating the Cart

The first step is to iterate the cart contents and store each item's weight individually rather than summing:

// Loop through cart items

foreach ($cart_items as $item ) {

// Get weight from order item

$weight = $item->get_weight();

// Add to session

WC()->session->set('item_weights', $weight);

}

By storing in the WC session, these values can then be referenced for shipping rules.

Setting Up Zones & Rates

Weight-Based Shipping Classes

With individual weights captured, shipping tables can utilize classes based on weight thresholds:

Zone 1

Under 1lb - $5

1lb to 3lbs - $7

Over 3lbs - $10

Now rates can assess each item rather than the full cart bulk weight.

This separation of concerns is key to the plugin's ability to properly address weighted shipping needs. Stay tuned for code examples of the calculation overriding coming up!

Testing the Solution

Admin and Frontend

To ensure the plugin is working as intended, testing across different scenarios is important.

When checking out on the frontend, the new class-based rates now apply correctly:

Sample Order Code

Here is a snippet of sample order data with individual item weights:

$order = new WC_Order();

// Add first item

$item = new WC_Order_Item_Product();

$item->set_name( 'Bananas' );

$item->set_weight( 1.5 ); // Item weight in lbs $order->add_item( $item );

// Add second item

$item = new WC_Order_Item_Product();

$item->set_name( 'Apples' );

$item->set_weight( 2.1 );

$order->add_item( $item );

// Add third item

$item = new WC_Order_Item_Product();

$item->set_name( 'Milk' );

$item->set_weight( 1.75 );

$order->add_item( $item );

// Calculate total weight

$total_weight = $order->get_item_weight();

// Returns 5.35 lbs

// Payment and customer data

$order->set_payment_method('bacs');

$order->set_customer_id(1);

// Save order

$order->save();

We can see each item's attributes are now maintained as expected.

Considerations and Edge Cases

Minimum Order Weight Rule

The plugin allows specifying a minimum threshold. For example, require a $10 minimum charge if under 2lbs.

Large Individual Item Surcharges

What if a single 40lb item is ordered? Rules can account for this.

International Shipping

Additional integrations may be needed for cross-border due to foreign posting regulations.

Conclusion

In this blog, we explored WooCommerce's core shipping limitations for weighted orders. By analyzing the default code handling and storing item attributes separately, a solution plugin was introduced.

This approach resolves the core problem for food/grocery delivery businesses needing accurate rates. Please test it out and feel free to reach out with any other questions!

At Hybrid Web Agency, we offer custom WooCommerce development services in Kirkland to build projects just like this plugin tailored to your specific business needs. Whether integrating with other systems, adding new features, or full site builds - we'd love to help optimize your online storefront.

Additional integrations or custom plugins are also available. Just contact us to explore how we can help!

Useful Resources

WooCommerce Docs - Official documentation for plugins, extensions, and webhooks:

https://docs.woocommerce.com/

WooCommerce Stack Overflow - Large community for support and Q/A:

https://stackoverflow.com/questions/tagged/woocommerce

WooCommerce Stack Exchange - Similar community-focused platform:

https://woocommerce.stackexchange.com/

WooCommerce Gutenberg Blocks - Docs on building product block extensions:

https://woocommerce.github.io/woocommerce-gutenberg-products-block/

WooCommerce Marketplace - Browse thousands of plugins/extensions:

https://woocommerce.com/products/