Search code examples
phpwordpresswoocommercecartorders

Custom post type data and order item data in WooCommerce


I am using a plugin, WooCommerce Custom Post Type Manager, along with WooCommerce that enables the use of custom post types as products. This is working pretty well for the most part, but I want to be able to control inventory and I am able to see the problem. In the database under the order_item_meta table, the product_id = 0. Because the product_id is empty, there is no way to update the stock on a completed purchase.

I know that WooCommerce made some changes to where it searched if a post_type was a 'product' and if not, certain things failed. I am wondering if there is a filter hook or another way to add the product id with a custom function at checkout?

This is the function I am trying to create to control a custom type of inventory, we are selling event "seats". And no, even the regular "stock" does not work, probably because of the same reason.

function update_course_seats( $order_id ){
  $order = wc_get_order( $order_id );
  $items = $order->get_items();
  foreach ( $items as $item ) {
    $product_id = $item->get_product_id(); // this returns 0
    if($product_id != 0) {
      wc_update_order_item_meta( $item, '_seats', 10 ); // example number
    }
  }

//add_action( 'woocommerce_payment_complete', 'update_course_seats');

Solution

  • First you should use woocommerce_add_cart_item_data action hook, to store your Custom Post Type (CTP) Id on add to cart… But for that you will need to display, on your CTP pages, inside the add to cart form, a hidden field with the CTP Id like:

    <input type="hidden" name="ctpost_id" value="<?php echo get_the_id(); ?>">
    

    Now you can add this hooked function, that will add as custom cart item data your CTP Id:

    add_filter( 'woocommerce_add_cart_item_data', 'add_custom_cart_item_data', 10, 3 );
    function add_custom_cart_item_data( $cart_item_data, $product_id, $variation_id ){
        if( isset( $_POST['ctpost_id'] ) ) {
            $cart_item_data['ctpost_id'] = wc_clean( $_POST['ctpost_id'] );
        }
        return $cart_item_data;
    }
    

    The Second hook to be used is woocommerce_checkout_create_order_line_item action hook, to add custom order item meta data (or to make changes to order item data). This hook is triggered before payment gateways process during order creation.

    You can use WC_data add_meta_data() method to save your CTP Id as a custom CTP ID like:

    add_action( 'woocommerce_checkout_create_order_line_item', 'save_cpt_id_to_order_item_data', 10, 4 );
    function save_cpt_id_to_order_item_data( $item, $cart_item_key, $cart_item, $order ){
        if( isset($cart_item['ctpost_id']) && $cart_item['ctpost_id'] > 0 ) {
            // Add the custom CTP post ID
            $item->add_meta_data('_ctpost_id', $cart_item['ctpost_id'] );
        }
    
        // And here for example you add seats as custom cart item data 
        if( isset($cart_item['quantity']) ) {
            $item->add_meta_data( 'Seats', $cart_item['quantity'], true );
        }
    } 
    

    When the order will get paid, you will be able to update everything required, as you will have your CTP post ID as custom order item data.


    Then finally, you can use woocommerce_payment_complete as follow:

    add_action( 'woocommerce_payment_complete', 'action_payment_complete_callback', 10, 1 );
    function action_payment_complete_callback( $order_id ){
        $order = wc_get_order();
    
        // Loop through order items
        foreach ( $order->get_items() as $item_id => $item ) {
            $ctp_id = $item->get_meta('_ctpost_id'); // Get the CTP post ID
    
            // Your code goes here
        }
    }
    

    Or woocommerce_order_status_changed hooks like:

    add_action( 'woocommerce_order_status_changed', 'action_order_status_changed_callback', 10, 4 );
    function action_order_status_changed_callback( $order_id, $status_from, $status_to, $order ){
        if( in_array( $status_to, ['processing','completed'] ) ) {
            // Loop through order items
            foreach ( $order->get_items() as $item_id => $item ) {
                $ctp_id = $item->get_meta('_ctpost_id'); // Get the CTP post ID
    
                // Your code goes here
            }
        }
    }
    

    Related: Get Order items and WC_Order_Item_Product in Woocommerce 3.