WooCommerce: Split cart items from the same order and ship via multiple shipping methods

First things first :).
Product Vendors is a WordPress plugin which will enable marketplace capabilities for WooCommerce.

The Problem to Solve.

Let me explain about the problem we have faced after turning a WooCommerce store to the marketplace. When the customer adds a number of products from various vendors to the cart, shipping options will be shown common for all those cart items. That is the default way WooCommerce handles it. But, there is a high possibility that customer wants to save some money by opting for a local pickup from Vendor A, who is located in the same area and choose a different shipping method for Vendor B.

Prior to WooCommerce 2.1, this problem was not solvable straight as each order had to be shipped using a single shipping method with a single total price. But, from 2.1 onwards, those cart items can be split and grouped separately as different packages and can be shipped individually by using different shipping methods. These packages can be created by various  logical groupings of cart items, as like product vendor, product category,  cost etc.

However, we can focus on how the package can be split into packaging items with respect to each Product Vendor. Before getting into implementation details, you can take a look at the screenshot below this article to see how the shipping section of cart page will look like. after the vendor-wise split of packages.

Each Order is a Package!

Each order is considered as the package from WooCommerce 2.1 onwards.  The structure of package object is described below.

ship_via --> Associate shipping method with the package.
contents --> Array of cart_contents from WC_Cart object.

Remaining fields are self-explanatory.

    //'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 items are added to the cart, the package can be filtered using the hook woocommerce_cart_shipping_packages. We will have the package object ready for the split at this point.

add_filter( 'woocommerce_cart_shipping_packages', array( &$this, 'th_woocommerce_cart_shipping_packages') );

Product Vendors plugin allows associating each product with multiple vendors. I am still unsure about the actual benefit of such flexibility. But, for our use case, each product will be only associated with the single vendor.

Below part of the code is specific to Vendor-wise split. Basically, a map will be built, which will have a mapping between Vendor ID and an array of cart items associated with the vendor.

    $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;
            }
        }
    }

Here is the heart of the package split. An array of packages will be built where each index will have a package associated with individual vendor. Each package contents will be associated with the array of items from vendor_items_map. Note that all these items split into packages will be 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()
            )
        );    
    }

The Product Vendor Use Case.

See the below list of items from a cart page. Here, the customer purchased multiple items from various vendors. Travel Box and Jute Bag belongs to Vendor 1 and Red Jacket belongs to Vendor 2. Paint doesn’t belong to any Vendor which is directly sold by marketplace owner.

Cart

Cart

And here is how shipping section will look like. Notice the vendor-wise split of shipping options. The customer is free to select desired shipping method for each vendor.

There is a catch here. The labels for each package will be displayed as Shipping #1, Shipping #2, etc. At this point of time (WooCommerce 2.3.4), the product packaging code doesn’t look matured as there is any hook present in the template cart-shipping.php, to replace vendor name instead of currently numbered shipping package labels.  But, how in the below screenshot you are able to see vendor names across package labels!? Simple..I have overridden the template cart-shipping in my plugin code and changed it.

Cart Total

Cart Total

Solved!

Go Gist!

Here is the GitHub snippet of complete code for reference.


We have a number of high-quality WooCommerce plugins available at Our Shop.


Comments (26)

  • orensaban1993
    orensaban1993

    Hii
    I’m trying to implement it on my website, but with no success.
    I need to change the “class a” “class b” or should I leave it as is?
    any other idea for why it wouldn’t work?

    osawoodart.com

    please help me :):)
    Thank you!

    April 29, 2018 at 11:37 am
    • Anindo
      Anindo

      Hi,

      Ideally, you do not need to change anything on this.
      Just use the snippet as it is. However, make sure that the product is mapped with the vendor perfectly.

      Also, if you are still getting this, just approach us via ticket. Our team will surely help you.

      May 2, 2018 at 4:44 pm
  • amibhop
    amibhop

    This is a wonderful explanation,but doesnt work if i use yith multi vendore plugin.how can we fix this

    November 20, 2017 at 8:40 am
  • picadelli
    picadelli

    Hi, this solution looks really interesting and I wish it would work like a flaw with my theme but somehow it doesn’t. Once I added the code to the functions.php of my theme and when I want to add a product to the shopping cart of my shop it keeps trying to add but it cannot. Can you help me out with this please? Thanks ahead!

    November 7, 2017 at 7:14 pm
    • Anindo
      Anindo

      Hi,

      Can you let us know which plugin are you using?

      November 8, 2017 at 3:31 pm
    • picadelli
      picadelli

      Hello,

      Thanks for your fast response. I am using the Woocommerce Product Vendors as markeplace plugin, for shipping the Flexible Shipping plugin from WP Desk and as shopping cart item counter the Woocommerce Menu Cart plugin. Product Vendors works perfect so I dearly hope, that your code is able to split the orders of future vendors on my marketplace as well. To diplay more clearly what happens with and without the code, I made screen captures:

      This is without your code added:

      https://www.useloom.com/share/3ff07e8096404b5e86164a9ee9826caa

      And this is with your code added:

      https://www.useloom.com/share/77fc47740b694ccb9c3368ea2adc87a7
      -> I am not able to even add the item to the shopping cart :-/

      I have spend hours and hours already on searching for the last three days, but yours seems to be the only ‘non plugin’ solution (which is perfect) for splitting the cart per vendor for Product Vendors so please, please help me out on this- you’re basically my last hope…

      Thanks so much!

      November 9, 2017 at 8:11 am
    • Anindo
      Anindo

      Hi,

      Since you are not using our plugin, we won’t be able to directly check your problem. However, we can guide you to find the cause. When you are using our code, then kindly open the console of the browser once and then check the error. Kindly let me know what are you getting there.

      November 9, 2017 at 3:26 pm
    • picadelli
      picadelli

      Hi,

      sorry, was not aware that you offer a marketplace plugin as well. :-/ This page was the first search result while I was searching for ‘woocommerce product vendors split cart’ so I didn’t think this was related to another plugin. Thus I even more appreciate your support for my problem. 🙂 Here is what shows up in console when trying to add an item to the cart while running your code:

      https://www.dropbox.com/s/av0a9jmeu5pkaq6/console.jpg?dl=0

      -> I don’t understand a word. :-/ Hope you do.

      Thanks!

      November 9, 2017 at 7:20 pm
  • Jason
    Jason

    Can this same logic be used to split the cart up based on different shipping addresses?

    I need to ship to different shipping addresses and have th eUPS Print label plugin generate packages base don the addresses selcted during checkout.

    September 12, 2017 at 6:21 am
    • Lorenzo
      Lorenzo

      Hi Jason,

      Unfortunately, we won’t be able to provide such a code snippet. Also we do not have any mechanism to use multiple shipping addresses in our UPS plugin. But if required you can contact your developer and see if its possible to modify this code snippet to your requirements.

      September 12, 2017 at 4:33 pm
  • dave22
    dave22

    Hi

    I have WooCommerce v3.1.1 and Woocommerce Product Vendors v2.0.34 (latest versions at time of writing).

    I can’t find any trace of the get_product_vendors() method — I’m getting the same error that others have mentioned.

    Do you know if it has been deprecated/superseded?
    Many thanks

    July 30, 2017 at 8:18 am
  • engr.asifali2014
    engr.asifali2014

    Hello!
    Please can anyone help me regarding same problem but little difference i explain below.
    I have a tour booking woocommerce website.
    http://www.freewebmall.com
    I want to split the orders from shopping cart when any customer purchase bulk tours from cart and pay once.
    He will receive separate email with order#no for each tour not one email and one order no for all tour.

    June 24, 2017 at 3:28 pm
    • Anindo
      Anindo

      Hi,

      Thanks for reaching out to us.
      Using the existing plugins this cannot be achieved as our plugins use the existing order completion email from WooCommerce. And as per WooCommerce, this email is generated one per order.

      We highly regret the inconvenience. However, this seems to be an interesting feature which our team can take up in future.

      June 26, 2017 at 11:12 am
  • pradeep.wadhwa14
    pradeep.wadhwa14

    I get this error when adding this code to the function.php file
    Fatal error: Call to undefined function get_product_vendors

    Let me know what I am doing wrong ?

    May 4, 2017 at 6:05 pm
    • Anindo
      Anindo

      Hi Pradeep,

      The function get_product_vendors() is suppplied by WooThemes for their Product Vendor plugin.
      It will throw the error if the plugin is not activated or not present in your website.

      Kindly check this.

      May 8, 2017 at 10:12 am
  • kevin.sartain
    kevin.sartain

    Hi I am trying to Split cart items from different vendors, I have added the snipit above to my functions.php but all I get when I go to a cart is a blank page with just “cart” every thing else is blank??? have I missed something?

    April 8, 2017 at 8:03 pm
  • anas.akhtar01
    anas.akhtar01

    I get this error when adding this code to the function.php file

    Fatal error: Call to undefined function get_product_vendors

    I am using the product vendor plugin and adding it below the theme function.php file guide me if i am doing it wrong.

    January 22, 2017 at 1:01 pm
    • anas.akhtar01
      anas.akhtar01

      One more thing i want to add is can i use this code for WC Vendors plugin too ?

      January 22, 2017 at 1:02 pm
    • Anindo
      Anindo

      Hi Anas,

      This code snippet works is not related to the type of multi-vendor plugin.
      So it will will work with WC-Vendors too.

      January 23, 2017 at 12:42 am
    • Anindo
      Anindo

      Hi Anas,

      We regret the problem faced by you.
      Kindly raise the issue immediately to our support team. They will surely look into it and help you resolve the problem.

      January 23, 2017 at 12:41 am
  • Sam

    Thanks Besty, very helpful post…

    It works only for cart and checkout, can we call it on thank you page or order detail page?
    Thank you page or order detail page still display total shipping cost.
    After splitting the package, is it possible to create separate order for each package?

    January 17, 2017 at 11:43 pm
    • Anindo
      Anindo

      Hi Sam,

      Thanks for reaching out to us.

      Yes, currently, the snippet works on the cart and the checkout page.
      For showing it in the thank you page, you need to modify the snippet a little. Taking developer’s help on this is recommended.

      January 18, 2017 at 10:52 am
    • Sam

      Hello Anindo,
      Thanks for the update on displaying the package-cost on thank you page,
      can you please give me rough idea, how we can do it?

      January 19, 2017 at 6:16 am
  • jason

    ?so i add this code to what file? functions.php

    January 17, 2017 at 11:01 am
    • Anindo
      Anindo

      Hi Jason,

      Yes, you can add this code to Appereance –> Editor –> functions.php.

      And you are done!

      January 17, 2017 at 5:18 pm

Leave a Reply