phpwordpresswoocommercemethodsorders

Create a WooCommerce Order Programmatically adding normal products and gifted products


With the code below, I create an order programmatically in WooCommerce, adding products with price, and I am trying for specific products to set a zero price as they are gifts:

$order = wc_create_order(array('customer_id' => $currentUserId));

// Product for add with price 
foreach($cartProductInfo as $productIn) {
    $order->add_product( get_product($productIn['id']), $productIn['qty'] );
}

// Product for add with 0 price 
foreach($data['gift'] as $productIn) {      
    $order_item_id = $order->add_product( get_product($productIn['productId']), 1 );
    if ($order_item_id) {
        // Retrieve the order item
        $order_item = $order->get_item($order_item_id);

        // Set price to zero
        $order_item->set_props([
            'subtotal' => 0,
            'total'    => 0,
            'subtotal_tax' => 0,
            'total_tax'    => 0,
        ]);

        // Optionally, add meta to indicate it's a free gift
        $order_item->add_meta_data('is_free_gift', true);
        $order_item->add_meta_data('offer_type', $productIn['offer_type']);

        // Save the order item
        $order_item->save();
    }
} 
    
// calculate total.
$order->calculate_totals(false);    
$order->save();

But the total amount of products is not updating properly.

For the gifted products, after adding them to the order, I have set the price to zero.

Items Subtotal: This show correct price
Order Total: This show an incorrect calculated price

How to get the correct calculations, when adding items to an order, with some of them gifted?


Solution

  • You were near: Before adding the products that you want to offer as a gift (free), you need to set the product price to zero (without saving the product itself), then you can add the gifted product to the order.

    Also, they are some mistakes in your code, like get_product() function is now replaced by wc_get_product() since a while.

    So your code will be slightly different:

    // Create an order for specific customer
    $order = wc_create_order( array('customer_id' => $currentUserId) );
    
    // Loop through products Ids to be added as order items
    foreach($cartProductInfo as $productIn) {
        $_product = wc_get_product( $productIn['id'] ); // Get the WC_product object
        $order->add_product( $_product, $productIn['qty'] ); // Add the product to the order
    }
    
    // Loop through gifted products Ids to be added as order items
    foreach($data['gift'] as $productIn ) { 
        $free_product = wc_get_product( $productIn['id'] ); // Get the WC_product object
        $free_product->set_price(0); // Change the product price to zero
    
        // Add the gifted product (with zero price) to the order
        if ( $item_id = $order->add_product( $free_product, 1 ) ) {
            $item = $order->get_item( $item_id ); // Retrieve the order item
    
            // Add custom metadata to indicate that it's a gift (free)
            $item->add_meta_data( 'is_free_gift', true, true );
            $item->add_meta_data( 'offer_type', $productIn['offer_type'], true );
    
            $item->save();  // Save order item custom metadata
        }
    } 
        
    // calculate total and save (The save() method is already included)
    $order->calculate_totals(); 
    

    You don't need to use the save() method at the end as it's already included in WC_Abstract_Order calculate_totals() method.

    It will work showing the correct amounts, without recalculating the price of all sub-items.