skip to Main Content

I’m using the most recent versions of WordPress and WooCommerce.

I’m aware this might seem weird out of context, but I wanna add a hook to my functions.php that removes a certain product by ID from an order after the order has been placed.

Here is what I am working with so far:

add_action('woocommerce_new_order', 'custom_process_order', 10, 1);
function custom_process_order($order_id) {
    $order = new WC_Order( $order_id );
    $items = $order->get_items();
    foreach ($items as $item) {
        if ($item->get_id()==10766) {
          $order->remove_item( $item->get_id() );
        }
    }
    return $order_id;
}

I am trying to remove the product with the ID 10766 from the order if said product is part of the order.
I took this from a different, older post here so the code itself might not be perfect. It definitely isn’t working.

Update after some experimentation:
With the help of Cameron Hurd, I tried the following code:

add_action('woocommerce_new_order', 'custom_process_order', 10, 1);

function custom_process_order($order_id) {
    $order = new WC_Order( $order_id );
    $items = $order->get_items();
    foreach ($items as $item) {
        if ($item->get_id() === 10766) {
            $order->remove_item( $item->get_id() );
        }
    }

    $order->save_items();

    return $order_id;
}

This seemed promising, but pressing the checkout button will submit an empty order while also throwing an empty WooCommerce error message leaving the user on the checkout page.

After this I had 2 ideas:
1.) Changing remove_item to wc_delete_order_item. I really don’t know the difference and changing it didn’t seem to make a difference either.
2.) Changing the hook so it happens after the order is in the back end. I tried changing it from woocommerce_new_order to woocommerce_thankyou. This however broke the thank you page in addition to doing nothing to the order.

This is what I’m sitting with now:

add_action('woocommerce_checkout_create_order', 'custom_process_order', 10, 1);

function custom_process_order($order_id) {
    $order = new WC_Order( $order_id );
    $items = $order->get_items();
    foreach ($items as $item) {
        $current_item_id = $item->get_id();
        if ( $current_item_id === 10766) {
            $order->remove_item($current_item_id);
        }
    }

    $order->save_items();

    return $order_id;
}

Still looking for a working answer.

2

Answers


  1. Browsing the code from abstracts/abstract-wc-order.php, where the remove_item method is located… I see that items are added to an array called items_to_delete. That variable shows up again in save_items, where each item class to be deleted has its delete method invoked.

    <?php
    // abstracts/abstract-wc-order.php
    
    abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
    
        /**
         * Remove item from the order.
         *
         * @param int $item_id Item ID to delete.
         * @return false|void
         */
        public function remove_item( $item_id ) {
            $item      = $this->get_item( $item_id, false );
            $items_key = $item ? $this->get_items_key( $item ) : false;
    
            if ( ! $items_key ) {
                return false;
            }
    
            // Unset and remove later.
            $this->items_to_delete[] = $item;
            unset( $this->items[ $items_key ][ $item->get_id() ] );
        }
    
        // ...
    
        /**
         * Save all order items which are part of this order.
         */
        protected function save_items() {
            $items_changed = false;
    
            foreach ( $this->items_to_delete as $item ) {
                $item->delete();
                $items_changed = true;
            }
            $this->items_to_delete = array();
    
            // ...
        }
    
        // ...
    }
    

    Therefore, you might try calling $order->save_items() after the foreach loop in your custom action:

    add_action('woocommerce_new_order', 'custom_process_order', 10, 1);
    
    function custom_process_order($order_id) {
        $order = new WC_Order( $order_id );
        $items = $order->get_items();
        foreach ($items as $item) {
            if ($item->get_id() === 10766) {
                $order->remove_item( $item->get_id() );
            }
        }
    
        $order->save_items();
    
        return $order_id;
    }
    
    Login or Signup to reply.
  2. You could use the woocommerce_checkout_order_processed action hook,
    where you then specify the product id.

    So you get:

    function custom_process_order( $order_id ) {
        if( ! $order_id ) return;
    
        // get order object
        $order = new WC_Order( $order_id );
        
        // get order items = each product in the order
        $items = $order->get_items();
    
        foreach ( $items as $item ) {
            $product = wc_get_product( $item['product_id'] );
            
            if ( $product->get_id() == 10766 ) {
                $order->remove_item( $item->get_id() );
            }
        }
        
        // Calculate
        $order->calculate_totals();
        
        // Save
        $order->save();
    }
    add_action( 'woocommerce_checkout_order_processed', 'custom_process_order', 10, 1 );
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search