8

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.

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?

2
  • 1
    I don't know the complete answer, but for sure you are missing the 3rd parameter of add_product( $product, $qty = 1, $args = array() ). You need to pass $args = array('variation' => $something );. I am not yet sure how that $something needs to be formatted. Commented Sep 13, 2015 at 2:16
  • @helgatheviking Ah, you are quite right: I was passing an array as a third parameter (in the second example), but I hadn't noticed that it needed the 'variation' key. I've added that ($variationsArray['variation'] = $membership['attributes'];) and now the item appears 'correctly' in the order (as in, it's the right variation of the right product), but the price is still wrong. No idea what I'm missing! Commented Sep 13, 2015 at 9:42

3 Answers 3

8

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();
}
Sign up to request clarification or add additional context in comments.

3 Comments

That's cool you figured out out. I remember asking that the regular add to cart support direct addition by variation id and got shot down, so never would have thought to do this.
@helgatheviking I've actually had to do this a couple of different ways for a couple of different projects, including adding a variation directly via AJAX (similar to the way you mentioned). I need to bookmark this, or else I'm definitely going to forget how to do it next time.
This is the third time this exact answer has been useful for me. if only i could upvote your answer again.
1

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);

Comments

0

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();
}

1 Comment

Hi @Tyler it gives me error that Fatal error: Call to a member function get_title() on a non-object. can you please help me solve this problem?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.