Ask your WordPress questions! Pay money and get answers fast! (more info)

Woocommerce: Create Order for Each Item

  • SOLVED

When an order is placed using Woocommerce, each product (order item) is billed as part of the entire Order. An order can have an unlimited amount of products. I need a function that will create an order for each item instead of one order for all items.

Woocommerce: http://wordpress.org/extend/plugins/woocommerce/

Woocommerce docs: http://wcdocs.woothemes.com/

Woocommerce hooks: http://wcdocs.woothemes.com/codex/extending/hooks/



My initial reaction on process would be to remove_action(); the existing function that fires after checkout to create an order. And add a new action that fires the order creation function foreach item.


//-------Edit


As John Cotton pointed out this may be messier than I initial anticipated. At this point I will take quotes for other solutions to what is above.

I tried to cut out the meat of the order creation process, from the file woocommerce/classes/class-wc-checkout.php here http://pastie.org/5407412 to see how orders are currently being added.

//----------Edit 2

After a brief discussion on direction I have made some changes to allow for this change to be even remotely possible. The question I am now posing for a function is this:

I am looking for a function, that will add comments to a Woocommerce order, when a cpt changes status. Something like:

add_action( 'transition_post_status', 'add_woo_order_comment', 10, 3);
function add_woo_order_comment($new_status, $old_status, $post){
if ( in_array($post->post_type, 'event')){
global $woocommerce;
$woo_order = get_post_meta(post->ID, 'wpcf-order-number', true);
$order = woocommerce_order ( $woo_order );
$order->add_order_note(sprintf(__('Item #% status changed', 'woothemes'), post->ID));
}
}


I have never worked with the transition_post_status action before so I am not sure if this is possible, or if I am doing this right.

Answers (2)

2012-11-20

John Cotton answers:

I had quick look at the code and I think the woocommerce_checkout_action is your best bet for replacing.

However, it looks like a nightmare to me. The WooCommerce code is not broken out into neat little functions that you could call to do the work. So you'd either have to duplicate their code (bad idea) or repeatedly call process_checkout. But that function does all the cart/user validation including creating accounts if they are needed. If that fails on one element, then it comes back to the user having either created the order or not. But what if yours fails midway through with some "orders" placed and others not? Big mess!

An even if it all works, your customers are going to have multiple orders in their account history. Will they understand that?

I'm wondering why you want to do this. Perhaps if you explained, someone could offer you a better route to achieve your aims.


Kyle comments:

I see what you are saying, that sounds messy.

The customers never actually see their orders per se, so that's not an issue. To explain a bit, the site is setup to allow for both sellers and customers. The reason for wanting to break up the order, is that if a customer places an order for items with multiple sellers, each seller needs control over the item (not the order).

One potential workaround I had considered was taking the task out of Woocommerce's hands. The site is for booking services -- after a purchase an event is created for each item. So I could simply code the payment structure to not rely on Woocommerce once the order is placed and events created, and have them rely on something like 'event status' rather than 'order status' or something.

I would prefer to keep it in Woo, but it sounds like you think that isn't going to be possible.


Kyle comments:

In woocommerce/classes/class-wc-checkout.php I am looking at the process for creating new orders, is that where you meant for it being too messy to try and alter?


John Cotton comments:

<blockquote>So I could simply code the payment structure to not rely on Woocommerce once the order is placed and events created</blockquote>
From what you've said, that feels a more natural solution to me. Presumably you were thinking that each seller would have access to the order page for their items? I have a feeling I've heard of a WC extension that does something like that but I might be dreaming.

But you could code one - so hide orders from all of them but have a extra page on the dashboard that shows them the customer details (which will be the same for everyone) and the items from the order that are theirs. Not particularly difficult to do.

<blockquote>In woocommerce/classes/class-wc-checkout.php I am looking at the process for creating new orders, is that where you meant for it being too messy to try and alter?</blockquote>
Yes. It looks like you'd need a big chunk of what's in there.


John Cotton comments:

Gabriel has set out pretty much what I was saying so although grouping by seller is different than individual items (and would make a lot of sense), I think you've still got an issue with the WooCommerce code. If it was broken into small functional elements than the looping approach could work. But given the great big chunks of logic that are wrapped up in process_checkout I think you'd have difficulties maintaining. For example, any change to that function on a WooCommerce release (of which there have been many) would mean going into your code and re-applying.

Far better than you stand one step removed from WooCommerce and handle your own order presentation (because that's what it really boils down to - you want to hide parts of an order from some "admins".)


Kyle comments:

Thanks for the follow-up.

I am going to try the workaround I mentioned earlier of separating the order from Woocommerce after checkout, and having my payment gateway sync with the events that are created from each order item instead.

As of right now I don't see any tremendous pitfalls, except for the fact that tracking order status will be less 'neat'. The trade-off benefit of that fact being I don't touch Woocommerce's functions at all.

I will keep this question open for a bit extra, since at this point it is a $30 forum post, and I am still hoping to get some code to help solve this underlying problem.


John Cotton comments:

<blockquote>I will keep this question open for a bit extra, since at this point it is a $30 forum post, and I am still hoping to get some code to help solve this underlying problem.</blockquote>
By all means do, but I don't think the kind of code you're after is going to come for $30. Perhaps someone will prove me wrong, but I think it's a fairly complex thing you want to do and there isn't going to be 5 lines of code that will give you a result.


Kyle comments:

So I think this solution will work, the one thing I am trying to fix is the pitfall I stated before, about not being able to track items within Woocommerce anymore.

I am trying now to make a function that will add order notes to the order each item came from to help track events by the payment gateway a bit. I think that will complete this fix if it works


John Cotton comments:

<blockquote>I am trying now to make a function that will add order notes to the order each item came from to help track events by the payment gateway a bit. I think that will complete this fix if it works</blockquote>

You could use post meta (since an order is a post).

Use either meta_key names ('_event_x' = true) or values ('_event' = 'x') or a combination. Remember to use the underscore in the name to keep the meta hidden.


Kyle comments:

Hi, thanks for the reply. I posted a new function in the question above, but it doesn't seem to be working, I was wondering if you could tell why

2012-11-20

Gabriel Reguly answers:

Hi ktrusak,

Why do you need each seller to have access to a common order?

Would not that be the job of the site manager?

I mean, you will be receiving one payment for all items/services, so I guess someone should be in charge of checking if all items/services can be delivered?

Just my .02

Regards,
Gabriel


Kyle comments:

Hello, thanks for the reply

I think you are picturing the current situation which I am trying to change. As of right now, sellers may potentially have common orders because a customer pay purchase items from multiple sellers at once, on the same order.

What I am trying to do is avoid common orders by having an order created for each individual item purchased (and thus sellers would only be able to see their own orders). That way it is possible to check if each item/service is deliverable like you said, more easily because each item correlates to a unique order.


Gabriel Reguly comments:

Hmm, seems you will need an individual cart for each seller after the order is sent to checkout.

That can be confusing from a customer point of view, and even more would the customers be comfortable to pay for each seller individually?

Anyway, the solution would be:

1.extract all the cart items to a temporary place (a cart clone)
2.add back items from one seller
3.process the checkout
4.before thank you page, check if there are remaining items at the temporary place, if so then go back to 2
5.thank you page.

That way you could have orders with items grouped by seller, instead of 1 order for each item.

Regards,
Gabriel




Kyle comments:

Hmm, that is a very intersting approach, I hadn't considered the cart aspect of the equation. I will analyze this a bit to see where it leads. This is kind of like a checkout process loop, that cycles through the sellers, right? This code may be a bit above me, so I'll see if its feasible


Gabriel Reguly comments:

Hi ktrusak,

I have done some payment gateways for WooCommerce, and that was easy, far easier than doing the same payment gateways for WP-E-Commerce. So I can assure you that their code is of very good quality.

But those were not some $30 budget, hence here you only will get some hints or pointers to possible solutions.

(Your question is more a project than a question, like John already stated, albeit with another words.)

If you are comfortable with just one payment, you can break the order items into other orders just after payment using <em>woocommerce_thankyou</em> hook.

That is the most future proof solution, as you will be creating new orders by moving items from a paid order.

Actually, first I would create as many orders as the sellers in the original order and then would remove the items that are not associated with the seller of the order.

Creating orders is very straightforward, just do something like


$order_id = $this->get_request( 'order' );
$orginal_order = new WC_Order( $order_id );

$order_seller_1 = new WC_Order();
// save new order id
$order_seller_1_id = $order_seller_1->id;
// clone original order
$order_seller_1 = $orginal_order;
// restore new order id
$order_seller_1->id = $order_seller_1_id;
// remove items
.....
// rinse and repeat for all sellers

// remove original order or leave it as is for backup purposes.


There are some docs here: [[LINK href="http://wcdocs.woothemes.com/apidocs/class-WC_Order.html"]]http://wcdocs.woothemes.com/apidocs/class-WC_Order.html[[/LINK]]

Regards,
Gabriel


Kyle comments:

Hi Gabriel. Yes, I agree with you and John. This is definitely not a simple function as I had originally hoped. You guys have been a helpful.

What you are describing is very close to what I have in place right now. Instead of creating new orders as you have it I am creating 'events' and tying those into my payment gateway.


add_action( 'woocommerce_order_status_completed', 'create_new_event' );
function create_new_event($order_id) {
if ( is_user_logged_in() && class_exists('TribeEvents') )
{
global $current_user;
$current_user = wp_get_current_user();

// order object (optional but handy)
$order = new WC_Order( $order_id );

$items = get_post_meta($order_id, '_order_items', TRUE);

foreach ($items as $item) {
$meta = array();
foreach($item['item_meta'] as $meta_field)
{
$meta[$meta_field['meta_name']]=$meta_field['meta_value'];
}

$post = array(
'post_author' => $meta['Seller ID'],
'post_status' => 'pending',
'post_title' => $item['name'],
'post_type' => TribeEvents::POSTTYPE
);
$post_ID = wp_insert_post( $post );
....
....


And then there are many more lines with customer info, seller info, payment info etc. that is stored in each 'event'. Much of that code is to the credit of Hai Bui from this site on a different project for creating events from the Woocommerce Gravity Forms add-on.


Gabriel Reguly comments:

Hi ktrusak,

I wonder if is the function that is not being called or the order note that is not being added?

For the later, that is the correct code:


add_action( 'transition_post_status', 'add_woo_order_comment', 10, 3);

function add_woo_order_comment($new_status, $old_status, $post){
if ( in_array($post->post_type, 'event')){
$woo_order = get_post_meta(post->ID, 'wpcf-order-number', true);
$order = new WC_Order( $woo_order );
$order->add_order_note(sprintf(__('Item #% status changed', 'woothemes'), post->ID));
}
}


Regards,
Gabriel


Kyle comments:

Thanks for the reply,

I think the problem is with the function I've set up all together. It breaks my site (white screen)


Gabriel Reguly comments:

Sorry to hear that, good luck with it.