skip to Main Content

I’m trying to update the quantities of an ordered product and when doing so I would like the order to reflect the actual cost. What I find happens is that the Product Cost is reduced to match the total and the order total is never actually updated. I’ve supplied a simple sample below:

function prefix_update_woo_order() {

    $order_id       = 123; // This needs to be a real order or there will be errors
    $order_item_id  = 5; // This needs to be a real order item ID or there will be errors.

    $order       = new WC_Order( $order_id );
    $order_items = $order->get_items();
    $order_items[ $order_item_id ]->set_quantity( 2 );

    $order->calculate_taxes();
    $order->calculate_totals();
    $order->save();

}
add_action( 'admin_init', 'prefix_update_woo_order' );

For example, a “Beanie with Logo” product is on sale for $18.00 and I originally buy 1. I want to programmitically update the order item to a quantity of 2 instead of 1 after the order has been placed. I would expect the total to be $36.00 but what I’m finding is that the product cost changes to match the total price. Instead of a cost of $18.00 for a “Beanie with Logo” the quantity is updated to 2 and the cost is reduced to $9.00ea.

In short, what I want to do is update an existing order items quantity and have the totals updated to reflect the new quantity cost, discount, taxes. What methods do I need to use to achieve this?

4

Answers


  1. Chosen as BEST ANSWER

    This is what I've come up with. I've tried to use the wc_format_decimal() where applicable. Seems like a lot of work to simply update an order items quantity but it is what it is.

    The bottom comment is unnecessary but if you're using the Cost of Goods plugin then that will take care of that.

    /**
     * Update the order item quantity and totals
     * @param Integer $order_id
     * @param Integer $order_item_id
     * @param Integer $quantity         - Quantity to set
     * 
     * @return void
     */
    function prefix_update_woo_order( $order_id, $order_item_id, $quantity ) {
    
        // Get Order, Item, and Product Data
        $order          = new WC_Order( $order_id );
        $order_items    = $order->get_items();
        $line_item      = $order_items[ $order_item_id ];
        $variation_id   = $line_item->get_variation_id();
        $product_id     = $line_item->get_product_id();
        $product        = wc_get_product( $variation_id ? $variation_id : $product_id );
        $quantity_old   = $line_item->get_quantity();
    
        // Calculate Old and New Discounts
        $discount = wc_format_decimal( $line_item->get_subtotal() - $line_item->get_total(), '' );
        if( ! empty( $discount ) ) {
            $discount_per_qty = wc_format_decimal( ( $discount / $quantity_old ), '' );
            $discount = wc_format_decimal( ( $discount_per_qty * $quantity ), '' );
        }
    
        // Set Quantity and Order Totals
        $line_item->set_quantity( $quantity );
        $total = wc_get_price_excluding_tax( $product, array( 'qty' => $line_item->get_quantity() ) );  // Also see `wc_get_price_excluding_tax()`
        $line_item->set_subtotal( $total );             // Without Discount
        $line_item->set_total( $total - $discount );    // With Discount
    
        // Save Everything
        $line_item->save();
        wc_save_order_items( $order_id, $order_items );
    
        /**
         * If using the 'Cost of Goods' Plugin
         * - - - - - -
         * $cog         = $line_item->get_meta( '_wc_cog_item_cost', true );
         * $new_cog     = wc_format_decimal( ( $quantity * $cog ), '' );
         * $line_item->update_meta_data( '_wc_cog_item_total_cost', $new_cog );
         * wc_cog()->set_order_cost_meta( $order_id, true );
         */
    
    }
    

  2. i have not tried what you are trying to achieve before. What i see in your code is that $order_items is an array of Items Objects produced from WC_Order::get_items(), but i dont see the WC_Order Instance being notified that the order items have changed. I would expect a method like $order->update_cart($order_items); I believe i found some usefull links for more research
    https://hotexamples.com/examples/-/WC_Order/-/php-wc_order-class-examples.html
    woocommerce – programmatically update cart item quantity

    Sorry, i was not much of help!

    Login or Signup to reply.
  3. Use the follows code snippet to do the above task –

    function prefix_update_woo_order() {
    
        $order_id       = 123; // This needs to be a real order or there will be errors
        $order_item_id  = 5; // This needs to be a real order item ID or there will be errors.
    
        $order       = wc_get_order( $order_id );
        if( $order && !wc_get_order_item_meta( $order_item_id, '_order_item_data_updated', true ) ) {
            $item_price = wc_get_order_item_meta( $order_item_id, '_line_total', true );
            $updated_item_quantity = 2;
            wc_update_order_item_meta( $order_item_id, '_qty', $updated_item_quantity );
            wc_update_order_item_meta( $order_item_id, '_line_total', $item_price * $updated_item_quantity );
    
            $order->calculate_totals();
            $order->save();
            // set flag
            wc_add_order_item_meta( $order_item_id, '_order_item_data_updated', true, true );
        }
    
    }
    add_action( 'admin_init', 'prefix_update_woo_order' );
    

    Codes goes to your active theme’s functions.php

    Login or Signup to reply.
  4. Hello I think this code will change your problem

    add_action( 'admin_init', 'test_order_update_order' );
    function test_order_update_order() {
        $order_id       = 80; // This needs to be a real order or there will be errors
        $order_item_id  = 11; // This needs to be a real order item ID or there will be errors.
        $quantity = 2;  //quantity which you want to set.
        $order       = new WC_Order( $order_id );
        $order_items = $order->get_items();
        foreach ( $order_items as $key => $value ) {
            if ( $order_item_id == $key ) {
               $product_value = $value->get_data();
               $product_id    = $product_value['product_id']; 
            }
        }
        $product = wc_get_product( $product_id );
        $price = $product->get_price();
        $price = ( int ) $quantity * $price;
        $order_items[ $order_item_id ]->set_quantity( 2 );
        $order_items[ $order_item_id ]->set_subtotal( $price );
        $order->calculate_taxes();
        $order->calculate_totals();
        $order->save();
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search