Select Page

I recently had a customer that used WooCommerce(WC) as an e-commerce engine, however, the entire shipping functionality was not needed.

WC has some backend settings that allow you disable shipping entirely, and in this case, that option was checked. What this setting does is removes the shipping calculator from the cart page, and also removes the shipping fields from the checkout page.

However, the customer did want to use the shipping fields, as they processed shipping outside of their online store. Thus, if the buyer wanted to have their items shipped to a different shipping address then these fields would need to be available.

After a little bit of research within WC core, I cam up with a simple solution:

 
add_filter( 'woocommerce_order_needs_shipping_address', 'maybe_display_shipping_address', 10, 3);
function maybe_display_shipping_address( $needs_shipping, $hide, $order ) {
return true;
}
add_filter( 'woocommerce_product_needs_shipping', 'maybe_each_product_needs_shipping', 10, 2);
function maybe_each_product_needs_shipping( $is_virtual, $product ){
return true;
}
add_filter( 'woocommerce_cart_needs_shipping_address', 'maybe_cart_needs_shipping_address', 10, 1);
function maybe_cart_needs_shipping_address( $needs_shipping_address ){
return true;
}

There is a set of filters that will allow us to trick WC into thinking we need to have the shipping fields, even when shipping is disabled and there are no shipping methods. (although these is one caveat, and I will show you in a bit)

The first filter we want to utilize is:

/**
* Checks if an order needs display the shipping address, based on shipping method.
*
* @return boolean
*/
public function needs_shipping_address() {
if ( 'no' === get_option( 'woocommerce_calc_shipping' ) ) {
return false;
}
$hide = apply_filters( 'woocommerce_order_hide_shipping_address', array( 'local_pickup' ), $this );
$needs_address = false;
foreach ( $this->get_shipping_methods() as $shipping_method ) {
if ( ! in_array( $shipping_method['method_id'], $hide ) ) {
$needs_address = true;
break;
}
}
return apply_filters( 'woocommerce_order_needs_shipping_address', $needs_address, $hide, $this );
}

This filter comes from abstract-wc-order.php file in WC core, and because it is a function in which a filter is applied, we can modify it to our convenience.
Our filter now looks like this:

add_filter( 'woocommerce_order_needs_shipping_address', 'maybe_display_shipping_address', 10, 3);
function maybe_display_shipping_address( $needs_shipping, $hide, $order ) {
return true;
}

The second filter we need to take a look at is:

/**
* Should the shipping address form be shown.
*
* @return bool
*/
function needs_shipping_address() {

$needs_shipping_address = false;

if ( $this->needs_shipping() === true && ! wc_ship_to_billing_address_only() ) {
$needs_shipping_address = true;
}

return apply_filters( 'woocommerce_cart_needs_shipping_address', $needs_shipping_address );
}

With this filter we can show the shipping form and allow the shipping details to be saved within the checkout ( even though it is intended for the cart, it is actually used in the checkout screen).
Here is our code to modify it:

add_filter( 'woocommerce_cart_needs_shipping_address', 'maybe_cart_needs_shipping_address', 10, 1);
function maybe_cart_needs_shipping_address( $needs_shipping_address ){
return true;
}

And the last filter we are looking for has to do with each product that our buyer has within their cart/checkout/order.
By default, when shipping is disabled, it is telling WC that each product is a virtual product. BUT, when the checkbox within each product is NOT checked, you wind up with a simple product that does not need shipping. So we must look for the filter to enable this per product, and here it is:

/**
* Checks if a product needs shipping.
*
* @return bool
*/
public function needs_shipping() {
return apply_filters( 'woocommerce_product_needs_shipping', $this->is_virtual() ? false : true, $this );
}

Basically we just need to tell WC that the product does need shipping.
Here is our modified filter:

add_filter( 'woocommerce_product_needs_shipping', 'maybe_each_product_needs_shipping', 10, 2);
function maybe_each_product_needs_shipping( $is_virtual, $product ){
return true;
}

We could go a little further, just in case there was actual virtual products in our inventory, but in this case, just returning true solves our issue.

To put is all together, and place within theme functions, or a plugin file, it would look like this:

add_filter( 'woocommerce_order_needs_shipping_address', 'maybe_display_shipping_address', 10, 3);
function maybe_display_shipping_address( $needs_shipping, $hide, $order ) {
return true;
}
add_filter( 'woocommerce_product_needs_shipping', 'maybe_each_product_needs_shipping', 10, 2);
function maybe_each_product_needs_shipping( $is_virtual, $product ){
return true;
}
add_filter( 'woocommerce_cart_needs_shipping_address', 'maybe_cart_needs_shipping_address', 10, 1);
function maybe_cart_needs_shipping_address( $needs_shipping_address ){
return true;
}

Let me know what you think, or if you have any improvements, in the comments below.

There are no products

Get a FREE 15 minute consultation!

Stuck on deciding what is best for your website?  Need someone to bounce ideas off of?

Great first step! I will contact you via email soon.