Add a Shipping to an Order Programmatically in Woocommerce 3

Add a shipping to an order programmatically in Woocommerce 3

To handle this in Woocommerce 3+ use the following (from a WC_Order Order object $order):

## ------------- ADD SHIPPING PROCESS ---------------- ##

// Get the customer country code
$country_code = $order->get_shipping_country();

// Set the array for tax calculations
$calculate_tax_for = array(
'country' => $country_code,
'state' => '', // Can be set (optional)
'postcode' => '', // Can be set (optional)
'city' => '', // Can be set (optional)
);

// Optionally, set a total shipping amount
$new_ship_price = 5.10;

// Get a new instance of the WC_Order_Item_Shipping Object
$item = new WC_Order_Item_Shipping();

$item->set_method_title( "Flat rate" );
$item->set_method_id( "flat_rate:14" ); // set an existing Shipping method rate ID
$item->set_total( $new_ship_price ); // (optional)
$item->calculate_taxes($calculate_tax_for);

$order->add_item( $item );

$order->calculate_totals();

$order->update_status('on-hold');

// $order->save(); // If you don't update the order status

Tested an works.

Change shipping method programmatically from existing WooCommerce order

Here is the way to change an order "shipping" item programmatically for a new shipping method id (slug) to be defined:

// Here set your shipping method ID replacement
$new_method_id ='flat_rate';

// Get the the WC_Order Object from an order ID (optional)
$order = wc_get_order( $order_id );

// Array for tax calculations
$calculate_tax_for = array(
'country' => $order->get_shipping_country(),
'state' => $order->get_shipping_state(), // (optional value)
'postcode' => $order->get_shipping_postcode(), // (optional value)
'city' => $order->get_shipping_city(), // (optional value)
);

$changed = false; // Initializing

// Loop through order shipping items
foreach( $order->get_items( 'shipping' ) as $item_id => $item ){

// Retrieve the customer shipping zone
$shipping_zone = WC_Shipping_Zones::get_zone_by( 'instance_id', $item->get_instance_id() );

// Get an array of available shipping methods for the current shipping zone
$shipping_methods = $shipping_zone->get_shipping_methods();

// Loop through available shipping methods
foreach ( $shipping_methods as $instance_id => $shipping_method ) {

// Targeting specific shipping method
if( $shipping_method->is_enabled() && $shipping_method->id === $new_method_id ) {

// Set an existing shipping method for customer zone
$item->set_method_title( $shipping_method->get_title() );
$item->set_method_id( $shipping_method->get_rate_id() ); // set an existing Shipping method rate ID
$item->set_total( $shipping_method->cost );

$item->calculate_taxes( $calculate_tax_for );
$item->save();

$changed = true;
break; // stop the loop
}
}
}

if ( $changed ) {
// Calculate totals and save
$order->calculate_totals(); // the save() method is included
}

Tested and works


Related threads:

  • Add a shipping to an order programmatically in Woocommerce 3
  • Get orders shipping items details in WooCommerce 3

How can I create a WooCommerce order with an existing shipping method?

 function mwb_create_custom_order() {
global $woocommerce;

$address = array(
'first_name' => 'mwbtest',
'last_name' => 'mwb',
'company' => 'makewebbetter',
'email' => 'support@makewebbetter.com',
'phone' => '760-555-1212',
'address_1' => 'mwb',
'address_2' => '104',
'city' => 'San Diego',
'state' => 'Ca',
'postcode' => '92121',
'country' => 'US'
);
// Now we create the order
$order = wc_create_order();
$item = new WC_Order_Item_Shipping();

$item->set_method_id( 15 );
$item->set_method_title( 'Flate rate' );
$item->set_total(20);
$shipping_id = $item->save();
$order->add_item( $item );
$order->add_product( wc_get_product( '21' ), 1 );
$order->set_address( $address, 'billing' );
$order->calculate_totals();
}

add_action( 'init', 'mwb_create_custom_order');

How to get custom shipping method packages from WooCommerce

Because when using some 3rd Party shipping related plugins or some custom code, you can split the cart into multiple shipping packages, and so you can have multiple shipping methods (one per shipping package).

Imagine that you sell products that can be heavy or not… Imagine that you allow shipping by plane, by boat and by truck… Some items will be shippable by plane some others will not depending on their weight.

So when there are mixed items in cart (heavy and normal), the cart will be split into different shipping packages.

So that's why there is an array of chosen shipping methods and an array of shipping packages (even if you don't split your cart or don't use those plugins).

So there is only few WooCommerce shops that use multiple shipping packages, for mixed cart items.

Related threads for shipping methods on WooCommerce orders:

  • Add a shipping to an order programmatically in Woocommerce 3
  • Add update or remove WooCommerce shipping order items
  • Change shipping method programmatically from existing WooCommerce order
  • Get orders shipping items details in WooCommerce 3

Add update or remove WooCommerce shipping order items

To add or update shipping items use the following:

$order_id = 2343;
$order = wc_get_order($order_id);
$cost = 10;
$items = (array) $order->get_items('shipping');
$country = $order->get_shipping_country();

// Set the array for tax calculations
$calculate_tax_for = array(
'country' => $country_code,
'state' => '', // Can be set (optional)
'postcode' => '', // Can be set (optional)
'city' => '', // Can be set (optional)
);

if ( sizeof( $items ) == 0 ) {
$item = new WC_Order_Item_Shipping();
$items = array($item);
$new_item = true;
}

// Loop through shipping items
foreach ( $items as $item ) {
$item->set_method_title( __("Amazon shipping rate") );
$item->set_method_id( "amazon_flat_rate:17" ); // set an existing Shipping method rate ID
$item->set_total( $cost ); // (optional)

$item->calculate_taxes( $calculate_tax_for ); // Calculate taxes

if( isset($new_item) && $new_item ) {
$order->add_item( $item );
} else {
$item->save()
}
}
$order->calculate_totals();

It should better work…


To remove shipping items use te following:

$order_id = 2343;
$order = wc_get_order($order_id);
$items = (array) $order->get_items('shipping');

if ( sizeof( $items ) > 0 ) {
// Loop through shipping items
foreach ( $items as $item_id => $item ) {
$order->remove_item( $item_id );
}
$order->calculate_totals();
}

Related: Add a shipping to an order programmatically in Woocommerce 3

Custom add a shipping price in Woocommerce programmatically

First, you must choose shipping method ID.

Second, You need to put this code on your functions.php file inside your active theme directory. If your code is not working, please try to clear WooCommerce transient. In order to clear your WooCommerce transient, you can go to WooCommerce -> Status. Then click on Tools tab and you will see WooCommerce Transients. Click on the Clear Transients button.

add_filter( 'woocommerce_package_rates', 'override_ups_rates' );
function override_ups_rates( $rates ) {
foreach( $rates as $rate_key => $rate ){
// Check if the shipping method ID is UPS for example
if( ($rate->method_id == 'flexible_shipping_ups') ) {
// Set cost to zero
$rates[$rate_key]->cost = 5.50;
}
}
return $rates;
}

Custom shipping rates programmatically in Woocommerce

If you know the value of shipping_rate_id for each shipping method it means that in the WooCommerce plugin (WooCommerce > Settings > Shipping > Shipping Zones > Edit > Shipping Methods) you have already created and assigned a shipping cost (or free) for each individual shipping method.

So you don't need to change the cost of each one again.

Also for free shipping the shipping rate id will be free_shipping:17 instead of flat_rate:17.
Unless you have created a zero cost shipping method (flat rate instead free shipping).

The working code will be:

add_filter('woocommerce_package_rates', 'change_shipping_method_based_on_cart_total', 11, 2);
function change_shipping_method_based_on_cart_total( $rates, $package ) {

// set the shipping class id to exclude
$shipping_class_id = 150;

// initializes the total cart of products
$total_cart = 0;

foreach ( WC()->cart->get_cart() as $cart_item ) {
$product = $cart_item['data'];
// if the product has the shipping class id equal to "$shipping_class_id" it is excluded from the count
if ( $product->get_shipping_class_id() == $shipping_class_id ) {
continue;
}
$qty = $cart_item['quantity'];
$price = $cart_item['data']->get_price();
// add the product line total to the total
$total_cart += $price * $qty;

}

switch ( true ) {
case $total_cart < 10: // £4.99
// only the "flat_rate:12" shipping method will be enabled
unset($rates['flat_rate:13']);
unset($rates['flat_rate:14']);
unset($rates['flat_rate:15']);
unset($rates['flat_rate:17']); // or "unset( $rates['free_shipping:17'] );"
break;
case $total_cart >= 10 && $total_cart < 20: // £3.99
// only the "flat_rate:13" shipping method will be enabled
unset($rates['flat_rate:12']);
unset($rates['flat_rate:14']);
unset($rates['flat_rate:15']);
unset($rates['flat_rate:17']); // or "unset( $rates['free_shipping:17'] );"
break;
case $total_cart >= 20 && $total_cart < 30: // £2.99
// only the "flat_rate:14" shipping method will be enabled
unset($rates['flat_rate:12']);
unset($rates['flat_rate:13']);
unset($rates['flat_rate:15']);
unset($rates['flat_rate:17']); // or "unset( $rates['free_shipping:17'] );"
break;
case $total_cart >= 30 && $total_cart < 40: // £1.99
// only the "flat_rate:15" shipping method will be enabled
unset($rates['flat_rate:12']);
unset($rates['flat_rate:13']);
unset($rates['flat_rate:14']);
unset($rates['flat_rate:17']);
break;
case $total_cart >= 40: // free
// only the "flat_rate:17" or "free_shipping:17" shipping method will be enabled
unset($rates['flat_rate:12']);
unset($rates['flat_rate:13']);
unset($rates['flat_rate:14']);
unset($rates['flat_rate:15']);
break;
}

return $rates;
}

The code has been tested and works. The code goes into your theme's functions.php file.

Get orders shipping items details in WooCommerce 3

If you want to get the Order Items Shipping data, you need first to get them in a foreach loop (for 'shipping' item type) and to use WC_Order_Item_Shipping methods to access data

$order_id = 528; // For example

// An instance of
$order = wc_get_order($order_id);

// Iterating through order shipping items
foreach( $order->get_items( 'shipping' ) as $item_id => $item ){
$order_item_name = $item->get_name();
$order_item_type = $item->get_type();
$shipping_method_title = $item->get_method_title();
$shipping_method_id = $item->get_method_id(); // The method ID
$shipping_method_instance_id = $item->get_instance_id(); // The instance ID
$shipping_method_total = $item->get_total();
$shipping_method_total_tax = $item->get_total_tax();
$shipping_method_taxes = $item->get_taxes();
}

You can also get an array of this (unprotected and accessible) data using the WC_Data method get_data() inside this foreach loop:

$order_id = 528; // For example

// An instance of
$order = wc_get_order($order_id);

// Iterating through order shipping items
foreach( $order->get_items( 'shipping' ) as $item_id => $item ){
// Get the data in an unprotected array
$item_data = $item->get_data();

$shipping_data_id = $item_data['id'];
$shipping_data_order_id = $item_data['order_id'];
$shipping_data_name = $item_data['name'];
$shipping_data_method_title = $item_data['method_title'];
$shipping_data_method_id = $item_data['method_id'];
$shipping_data_instance_id = $item_data['instance_id'];
$shipping_data_total = $item_data['total'];
$shipping_data_total_tax = $item_data['total_tax'];
$shipping_data_taxes = $item_data['taxes'];
}

To finish you can use the following WC_Abstract_Order methods related to "Shipping data", like in this examples:

// Get an instance of the WC_Order object
$order = wc_get_order(522);

// Return an array of shipping costs within this order.
$order->get_shipping_methods(); // same thing than $order->get_items('shipping')

// Conditional function based on the Order shipping method
if( $order->has_shipping_method('flat_rate') ) {

// Output formatted shipping method title.
echo '<p>Shipping method name: '. $order->get_shipping_method()) .'</p>';

Set a custom shipping cost on every 10th Order in WooCommerce

First as Order IDs are not sequential because they are based on the post ID that is used also on Wordpress pages, posts and all other custom posts like Woocommerce products and coupons. So we need to enable a sequential count on your WooCommerce orders to make changes on every 10th orders.

The following will set shipping costs to zero every 10th orders, when order is placed before saving order data to database:

// Set a count based on placed orders for shipping items cost change
add_action( 'woocommerce_checkout_create_order', 'action_wc_checkout_create_order', 10, 2 );
function action_wc_checkout_create_order( $order, $data ) {
$orders_count = (int) get_option('wc_orders_count_for_shipping'); // get $order count for shipping item change

// Increase count for next order (starting count at 2 as this hook is triggered after shipping items hook)
set_option('wc_orders_count_for_shipping', $orders_count > 0 ? $orders_count + 1 : 2 );
}

// Set shipping cost to zero every 10-th orders when order is placed
add_action( 'woocommerce_checkout_create_order_shipping_item', 'action_wc_checkout_create_order_shipping_item', 10, 4 );
function action_wc_checkout_create_order_shipping_item( $item, $package_key, $package, $order ) {
$orders_count = (int) get_option('wc_orders_count_for_shipping');

// Every 10-th orders
if( $orders_count > 0 && ( $orders_count % 10 ) === 0 ) {
$item->set_total( '0' );
$item->set_taxes( [ 'total' => '0' ] );
$item->set_total_tax( '0' );
}
}

Code goes in functions.php file of your active child theme (or active theme). It should works.



Related Topics



Leave a reply



Submit