WooCommerce: Creating an order programmatically with a variable product

I’m trying to create an order programmatically. Using wc_create_order() this is pretty straightforward:

$myProduct = new WC_Product(100);
$order = wc_create_order();
$order->add_product($myProduct, 1);
$order->calculate_totals();

This works as expected, and an order is created for a simple product with ID 100 for the correct amount.

Read More

However, if I try to do this with a variation, it doesn’t seem to behave correctly. After much trial and error, I got it to sort-of work this way:

$membershipProduct = new WC_Product_Variable(100);
$theMemberships = $membershipProduct->get_available_variations();

$trueProduct = new WC_Product(100);

$variationsArray = array();

foreach ($theMemberships as $membership) {
    if ($membership['sku'] == $chosenVariation) {
        $variationID = $membership['variation_id'];
        $variationsArray = $membership['attributes'];
    }
}

if ($variationID) {
    $trueProduct->variation_id = $variationID;
}

$order = wc_create_order();
$order->add_product($trueProduct, 1, $variationsArray);
$order->calculate_totals();

However, although it does create the order with the correct product and the correct variation ID, the total for the order is always 0 (which, coincidentally, is the price of the first variation).

Originally I was trying $order->add_product() with the object created from new WC_Product_Variable(), but that resulted in no products being added to the order at all, which leads me to believe it’s an issue with creating orders programmatically with variable products. However, following the WooCommerce source code for these calls, I can’t see what I’m doing wrong.

Is there something I’m missing, or a better way to create an order with a variable product?

Related posts

3 comments

  1. Solved it.

    Even though I could have sworn I’d tried (and failed) doing it this way, the answer was to not add the parent product ($trueProduct in the example), but to add the variation product by its ID.

    This may have failed previously because, as @helgatheviking noted, my $variationsArray was formatted incorrectly according to the source; I needed an array with a ['variation'] key to send the correct variation array of attributes.

    In total, my working code now looks like this:

    $membershipProduct = new WC_Product_Variable(100);
    $theMemberships = $membershipProduct->get_available_variations();
    
    $variationsArray = array();
    
    foreach ($theMemberships as $membership) {
        if ($membership['sku'] == $chosenVariation) {
            $variationID = $membership['variation_id'];
            $variationsArray['variation'] = $membership['attributes'];
        }
    }
    
    if ($variationID) {
        $varProduct = new WC_Product_Variation($variationID);
    
        $order = wc_create_order();
        $order->add_product($varProduct, 1, $variationsArray);
        $order->calculate_totals();
    }
    
  2. if you already have the variation_id you can just do this

    $product_variation = new WC_Product_Variation($variation_id);
    $order = wc_create_order();
    $args=array();
    foreach($product_variation->get_variation_attributes() as $attribute=>$attribute_value){
            $args['variation'][$attribute]=$attribute_value;
    }
    $order->add_product($product_variation, $product['quantity'], $args);
    
  3. Here is the solution that worked for me:

    function add_item_to_order( $order_id, $prod_id ) {
    
        $order      = wc_get_order( $order_id );
        $_product   = wc_get_product( $prod_id ); 
    
        // Set values
        $item = array();
        $item['product_id']        = $_product->id;
        $item['variation_id']      = isset( $_product->variation_id ) ? $_product->variation_id : '';
        $item['variation_data']    = $item['variation_id'] ? $_product->get_variation_attributes() : '';
        $item['name']              = $_product->get_title();
        $item['tax_class']         = $_product->get_tax_class();
        $item['qty']               = 1;
        $item['line_subtotal']     = wc_format_decimal( $_product->get_price_excluding_tax() );
        $item['line_subtotal_tax'] = '';
        $item['line_total']        = wc_format_decimal( $_product->get_price_excluding_tax() );
        $item['line_tax']          = '';
        $item['type']              = 'line_item';
    
        // Add line item
        $item_id = wc_add_order_item( $order_id, array(
            'order_item_name'       => $item['name'],
            'order_item_type'       => 'line_item'
        ) );
    
        // Add line item meta
        if ( $item_id ) {
            wc_add_order_item_meta( $item_id, '_qty', $item['qty'] );
            wc_add_order_item_meta( $item_id, '_tax_class', $item['tax_class'] );
            wc_add_order_item_meta( $item_id, '_product_id', $item['product_id'] );
            wc_add_order_item_meta( $item_id, '_variation_id', $item['variation_id'] );
            wc_add_order_item_meta( $item_id, '_line_subtotal', $item['line_subtotal'] );
            wc_add_order_item_meta( $item_id, '_line_subtotal_tax', $item['line_subtotal_tax'] );
            wc_add_order_item_meta( $item_id, '_line_total', $item['line_total'] );
            wc_add_order_item_meta( $item_id, '_line_tax', $item['line_tax'] );
            wc_add_order_item_meta( $item_id, '_line_tax_data', array( 
                                                                        'total' => array(), 
                                                                        'subtotal' => array() ) 
                                                                     );
            // Store variation data in meta
            if ( $item['variation_data'] && is_array( $item['variation_data'] ) ) {
                foreach ( $item['variation_data'] as $key => $value ) {
                    wc_add_order_item_meta( $item_id, str_replace( 'attribute_', '', $key ), $value );
                }
            }
    
        }
    
        $item['item_meta']       = $order->get_item_meta( $item_id );
        $item['item_meta_array'] = $order->get_item_meta_array( $item_id );
        $item                    = $order->expand_item_meta( $item );
        $order->calculate_totals();
    }
    

Comments are closed.