For some custom functionality in Woocommerce, I need to copy order items with all metadata from one order to another order programatically. The source order is of type WC_Order while destination order type is WC_Subscription which extends WC_Order and is a custom order type.

I have tried with a following function but it doesn't work. Although the order total is updated which matches to source order but the items in order are not added.

function wc_copy_order_items( &$from_order, &$to_order ) {
    if ( ! is_a( $from_order, 'WC_Abstract_Order' ) || ! is_a( $to_order, 'WC_Abstract_Order' ) ) {
        throw new InvalidArgumentException( _x( 'Invalid data. Orders expected aren\'t orders.', 'In wc_copy_order_items error message. Refers to origin and target order objects.', 'woocommerce-subscriptions' ) );

    $from_order_all_items = $from_order->get_items( array( 'line_item', 'fee', 'shipping' ) );
    foreach( $from_order_all_items as $item ) {
        $to_order->add_item( $item );


I have also tried with woocommerce admin function wc_save_order_items by hooking into woocommerce_process_shop_order_meta while saving source order from admin but that also results in only update in totals of destination order and do not add items.

wc_save_order_items( $source_order_id, $_POST );

Basically, I want to copy all the data including all meta data and custom fields from source order to destination order. I've managed to copy other metadata by somehow but I can't get it right for copying order items with item metadata.

Please help me to find why above function doesn't work and guide me for a solution to copy order items between two orders.


Just change the $item_id to $to_order_item_id and you don't even have to touch the order_item_meta since it is still associated with the proper $item_id. So your foreach from above would be ...

foreach($order->get_items() as $item_id=>$item) {
    wc_update_order_item($item_id, array('order_id'=>$to_order_item_id));

Then you could even calculate the new totals of both orders after by using ...

$original_order = new WC_Order($original_order_id);
$order = new WC_Order($to_order_item_id);

I also discovered sometimes woocommerce will not let you change the order item data so I ended up doing the same as above but with a straight to the database update instead ...

global $wpdb;
$table = 'wp_woocommerce_order_items';
$data = array('order_id'=>$to_order_item_id);
foreach($order->get_items() as $item_id=>$item) {
    $where = array('order_item_id'=>$item_id);
    $updated = $wpdb->update($table, $data, $where);
    if($updated === false) {
        echo 'sorry dawg, there was an error';
    else {
        echo 'you got the soup!';

Ok, you need this for custom WOO functionality.

But WC_Subscription is from the WOO Subscription plugin.

It feels wrong to duplicate an order, can't you just connect it somewhere, and where you want, collect the data from the original order when you need it?

I can see what you're missing.

When you do $order->get_items(), it collects all the products in that order, this does not include all (product) item meta data.

For that you have to add something like this:

foreach ( $order->get_items() as $item_id => $item ) {

  // get item_meta
  $from_order_item_meta_data = wc_get_order_item_meta( $item_id, '_set_correct_meta_key_here', true );

  // save item meta data
  wc_update_order_item_meta($to_order_item_id, '_set_correct_meta_key_here', $from_order_item_meta_data);

You cannot use this directly in your function, the example above is just to show you the methods wc_get_order_item_meta() & wc_update_order_item_meta();

Regards, Bjorn

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.