What can we help you with?
Split cart items from the same order and ship via multiple shipping methods
In this article, we take a look at how you can split the cart items from the same order and ship via Multiple Shipping methods.
The below Code Works For the following Plugins :
Here in order to set up a marketplace make use of the WooCommerce plugins like Dokan and Product Vendors etc. which ease your process to set up Multi Vendor Marketplace.
The Problem
When we are trying to make the WooCommerce store into a marketplace, there are a number of problems that come into the picture. Whenever a customer adds various products from various vendors to the cart, the shipping options will be shown common for the customer. This is the default option in WooCommerce.
But what if one of the vendors wants to save some money, by not shipping the items and allowing local pickup. In some cases, the vendors might be closely located in the area of the customer and local pick up seems much more feasible.
Current WooCommerce versions ( from version 2.1) let WooCommerce split the order and be shipped and grouped separately, to ship individually using different shipping methods for different vendors. Hence each order can be packaged separately to be shipped. Now in order to group the items, you can do it product-wise, vendor wise or even based on category.
Now, here we focus on splitting the items with respect to each product vendor like this :
Each Order as a Separate Package to Ship
When you consider each order as a separate package to ship, then the structure of the package object looks like :
ship_via --> Associate shipping method with the package. contents --> Array of cart_contents from WC_Cart object.
The remaining code looks like :
//'ship_via' => array( 'flat_rate' ), 'contents' => $vendor_items, 'contents_cost' => array_sum( wp_list_pluck( $vendor_items, 'line_total' ) ), 'applied_coupons' => WC()->cart->applied_coupons, 'destination' => array( 'country' => WC()->customer->get_shipping_country(), 'state' => WC()->customer->get_shipping_state(), 'postcode' => WC()->customer->get_shipping_postcode(), 'city' => WC()->customer->get_shipping_city(), 'address' => WC()->customer->get_shipping_address(), 'address_2' => WC()->customer->get_shipping_address_2()
Once the items are added into the cart, then the package is filtered using the hook woocommerce_cart_shipping_packages. We then add the packages to be filtered here by then to be split for shipping options.
add_filter( 'woocommerce_cart_shipping_packages', array( &$this, 'th_woocommerce_cart_shipping_packages') );
Here the multi vendor plugins like Product vendors, allow associating each product with the multiple vendors, and hence here each product is associated with each vendor.
Let us take a look at the code which helps to split the package vendor wise. In this code, an array of vendors is created and is mapped with the array of packages or cart items.
$vendor_items_map = array(); foreach ( WC()->cart->get_cart() as $item ) { $product_id = $item['product_id']; // Get vendors for each product. $vendors = get_product_vendors( $product_id ); if ( $item['data']->needs_shipping() ) { if($vendors) { foreach( $vendors as $vendor ) { // Expecting/assuming there is only one Vendor assigned per product. Hm. $vendor_items_map[$vendor->ID][] = $item; break; } } // No vendor associated with the item. else { $vendor_items_map['0'][] = $item; } } }
Next we make an array of packages wherein the index of each package will be associated with each individual vendor. The package contents are associated with the array of items from vendor_items_map. Remember that the items that are split, will be a part of the single order.
foreach($vendor_items_map as $key => $vendor_items) { $packages[] = array( //'ship_via' => array( 'flat_rate' ), 'contents' => $vendor_items, 'contents_cost' => array_sum( wp_list_pluck( $vendor_items, 'line_total' ) ), 'applied_coupons' => WC()->cart->applied_coupons, 'destination' => array( 'country' => WC()->customer->get_shipping_country(), 'state' => WC()->customer->get_shipping_state(), 'postcode' => WC()->customer->get_shipping_postcode(), 'city' => WC()->customer->get_shipping_city(), 'address' => WC()->customer->get_shipping_address(), 'address_2' => WC()->customer->get_shipping_address_2() ) ); }
Example
If we have two vendors who are selling in the marketplace. Both Vendor 1 and Vendor 2 are located at different locations. Suppose a customer purchases products from vendor 1 and vendor 2. Upon processing the orders, since both the vendors are from different locations, the shipping charges might differ. Hence processing the default way might not come handy and also cost a lot for the customer as well as the vendor. To simplify the same, we can provide the customer with the option to choose a viable shipping option for each package.
If you want to display the Shipping vendor’s name, then you can use the hook in template cart-shipping.php and then replace the vendor name instead of a random numbering method. Hence here is the complete code to split cart items from the same order and ship via multiple shipping methods.
Github Snippet :
define("PV_ATTRIBUTE", "vendor"); if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly /** * Check if WooCommerce is active */ if ( in_array( 'woocommerce/woocommerce.php', apply_filters( 'active_plugins', get_option( 'active_plugins' ) ) ) ) { if ( ! class_exists( 'TH_Shipping_Options' ) ) { class TH_Shipping_Options { /** * Constructor for your shipping class * * @access public * @return void */ public function __construct() { // take care of anything else that needs to be done immediately upon plugin instantiation, here in the constructor. add_filter( 'woocommerce_cart_shipping_packages', array( &$this, 'th_woocommerce_cart_shipping_packages') ); // Overriding template to introduce vendor names along with standard labels across shipping packages. //add_filter( 'woocommerce_locate_template', array( $this, 'th_woocommerce_locate_template' ), 10, 3 ); } function th_woocommerce_locate_template( $template, $template_name, $template_path ) { if('cart/cart-shipping.php' == $template_name) { $path = plugin_dir_path( __FILE__ ) . '/woocommerce/templates/' . $template_name; return file_exists( $path ) ? $path : $template; } return $template; } function th_woocommerce_cart_shipping_packages( $packages ) { // Reset the packages $packages = array(); $vendor_items_map = array(); foreach ( WC()->cart->get_cart() as $item ) { $product_id = $item['product_id']; $vendors = get_product_vendors( $product_id ); if ( $item['data']->needs_shipping() ) { if($vendors) { foreach( $vendors as $vendor ) { // Expecting/assuming there is only one Vendor assigned per product. Hm. $vendor_items_map[$vendor->ID][] = $item; break; } } // No product vendor associated with item. else { $vendor_items_map['0'][] = $item; } } } foreach($vendor_items_map as $key => $vendor_items) { $packages[] = array( //'ship_via' => array( 'flat_rate' ), 'contents' => $vendor_items, 'contents_cost' => array_sum( wp_list_pluck( $vendor_items, 'line_total' ) ), 'applied_coupons' => WC()->cart->applied_coupons, 'destination' => array( 'country' => WC()->customer->get_shipping_country(), 'state' => WC()->customer->get_shipping_state(), 'postcode' => WC()->customer->get_shipping_postcode(), 'city' => WC()->customer->get_shipping_city(), 'address' => WC()->customer->get_shipping_address(), 'address_2' => WC()->customer->get_shipping_address_2() ) ); } return $packages; } } // finally instantiate our plugin class and add it to the set of globals $GLOBALS['th_shipping_options_init'] = new TH_Shipping_Options(); } // Start up this plugin add_action( 'init', 'TH_Shipping_Options' ); function TH_Shipping_Options() { global $TH_Shipping_Options; $TH_Shipping_Options = new TH_Shipping_Options(); } }
To Conclude :
Hence here is how you can split cart items from the same order and ship via multiple shipping methods when you are selling on a multi-vendor market place