在Woocommerce 3+中以编程方式创建带有行项目的订单

27

我需要使用程序方式创建一个Woocommerce订单,但是在使用“旧版”的Woocommerce时,这变成了一种非常麻烦的过程。

我必须手动插入各种数据库记录,并使用多个update_post_meta调用。

正在寻找更好的解决方案。

4个回答

49

使用最新版本的WooCommerce,可以尝试类似以下内容:

$address = array(
            'first_name' => 'Fresher',
            'last_name'  => 'StAcK OvErFloW',
            'company'    => 'stackoverflow',
            'email'      => 'test@test.com',
            'phone'      => '777-777-777-777',
            'address_1'  => '31 Main Street',
            'address_2'  => '', 
            'city'       => 'Chennai',
            'state'      => 'TN',
            'postcode'   => '12345',
            'country'    => 'IN'
        );

        $order = wc_create_order();
        $order->add_product( get_product( '12' ), 2 ); //(get_product with id and next is for quantity)
        $order->set_address( $address, 'billing' );
        $order->set_address( $address, 'shipping' );
        $order->add_coupon('Fresher','10','2'); // accepted param $couponcode, $couponamount,$coupon_tax
        $order->calculate_totals();

使用您的函数调用上面的代码,它将相应地工作。

请注意,它不适用于旧版本的WooCommerce,如2.1.12,它仅适用于WooCommerce 2.2及以上版本。

希望它有所帮助。


2
当您拥有订单对象时,使用此函数 get_class_methods( $order) ) 查看可用的函数非常有帮助! - Mattijs
好的。我该打什么电话下订单? - Kirby
如果我以这种方式创建订单,那么$order->get_formatted_billing_address()就无法正常工作。它不会应用我的过滤器。我该如何解决这个问题? - horace
我按照你的示例创建了订单,包括账单信息和送货信息。但是我使用了 woocommerce_formatted_address_replacements 过滤器钩子来格式化地址(例如在 [我的帐户] 页面上使用)。该过滤器会被普通商店订单调用,但对于我通过编程方式创建的订单则不会调用。此外,我还遇到了添加运费的问题。我应该如何获取要传递给 add_shipping() 方法的对象?如果您能在示例中添加这个内容就太好了 :) - horace
@kirby:wc_create_order本身就下单了,也就是将其添加到数据库中。(您可能已经明白了,但以后可以参考一下...) - jarnoan
显示剩余9条评论

15

2017-2023 适用于 WooCommerce 3 及以上版本

WooCommerce 3 引入了 CRUD 对象,订单项发生了很多变化...此外,一些 WC_Order 方法现在已被弃用,例如 add_coupon()

以下是一个函数,可以以编程方式创建一个包含所有必需数据(包括税费)的订单:

function create_wc_order( $data ){
    $gateways = WC()->payment_gateways->get_available_payment_gateways();
    $order    = new WC_Order();

    // Set Billing and Shipping adresses
    foreach( array('billing_', 'shipping_') as $type ) {
        foreach ( $data['address'] as $key => $value ) {
            if( $type === 'shipping_' && in_array( $key, array( 'email', 'phone' ) ) )
                continue;

            $type_key = $type.$key;

            if ( is_callable( array( $order, "set_{$type_key}" ) ) ) {
                $order->{"set_{$type_key}"}( $value );
            }
        }
    }

    // Set other details
    $order->set_created_via( 'programatically' );
    $order->set_customer_id( $data['user_id'] );
    $order->set_currency( get_woocommerce_currency() );
    $order->set_prices_include_tax( 'yes' === get_option( 'woocommerce_prices_include_tax' ) );
    $order->set_customer_note( isset( $data['order_comments'] ) ? $data['order_comments'] : '' );
    $order->set_payment_method( isset( $gateways[ $data['payment_method'] ] ) ? $gateways[ $data['payment_method'] ] : $data['payment_method'] );

    $calculate_taxes_for = array(
        'country'  => $data['address']['country'],
        'state'    => $data['address']['state'],
        'postcode' => $data['address']['postcode'],
        'city'     => $data['address']['city']
    );

    // Line items
    foreach( $data['line_items'] as $line_item ) {
        $args = $line_item['args'];
        $product = wc_get_product( isset($args['variation_id']) && $args['variation_id'] > 0 ? $$args['variation_id'] : $args['product_id'] );
        $item_id = $order->add_product( $product, $line_item['quantity'], $line_item['args'] );

        $item    = $order->get_item( $item_id, false );

        $item->calculate_taxes($calculate_taxes_for);
        $item->save();
    }

    // Coupon items
    if( isset($data['coupon_items'])){
        foreach( $data['coupon_items'] as $coupon_item ) {
            $order->apply_coupon(sanitize_title($coupon_item['code']));
        }
    }

    // Fee items
    if( isset($data['fee_items'])){
        foreach( $data['fee_items'] as $fee_item ) {
            $item = new WC_Order_Item_Fee();

            $item->set_name( $fee_item['name'] );
            $item->set_total( $fee_item['total'] );
            $tax_class = isset($fee_item['tax_class']) && $fee_item['tax_class'] != 0 ? $fee_item['tax_class'] : 0;
            $item->set_tax_class( $tax_class ); // O if not taxable

            $item->calculate_taxes($calculate_taxes_for);

            $item->save();
            $order->add_item( $item );
        }
    }

    // Set calculated totals
    $order->calculate_totals();
        
    if( isset($data['order_status']) ) {
        // Update order status from pending to your defined status and save data
        $order->update_status($data['order_status']['status'], $data['order_status']['note']);
        $order_id = $order->get_id();
    } else {
        // Save order to database (returns the order ID)
        $order_id = $order->save();
    }
    
    // Returns the order ID
    return $order_id;
}

代码放在您的活动子主题(或活动主题)的function.php文件中,或者放在插件文件中。


使用示例从数据数组中:

create_wc_order( array(
    'address' => array(
        'first_name' => 'Fresher',
        'last_name'  => 'StAcK OvErFloW',
        'company'    => 'stackoverflow',
        'email'      => 'test1@testoo.com',
        'phone'      => '777-777-777-777',
        'address_1'  => '31 Main Street',
        'address_2'  => '',
        'city'       => 'Chennai',
        'state'      => 'TN',
        'postcode'   => '12345',
        'country'    => 'IN',
    ),
    'user_id'        => '',
    'order_comments' => '',
    'payment_method' => 'bacs',
    'order_status'   => array(
        'status' => 'on-hold',
        'note'   => '',
    ),
    'line_items' => array(
        array(
            'quantity' => 1,
            'args'     => array(
                'product_id'    => 37,
                'variation_id'  => '',
                'variation'     => array(),
            )
        ),
    ),
    'coupon_items' => array(
        array(
            'code'         => 'summer',
        ),
    ),
    'fee_items' => array(
        array(
            'name'      => 'Delivery',
            'total'     => 5,
            'tax_class' => 0, // Not taxable
        ),
    ),
) );

你有处理过当你调用 add_product 方法时出现警告信息吗?该方法会显示如下信息:注意:自版本3.0起,woocommerce_order_add_product已被<strong>弃用</strong>!请改用 woocommerce_new_order_item 动作。 - Dan
@Dan 是的,但这与本主题无关(它是关于钩子的,在你的情况下取决于你想做什么)。也许你应该提出一个新问题... - LoicTheAztec

7
随着WC 2的发布,它变得更好了。 然而:
  • 我不想使用REST API,因为我直接从自己的WP插件中进行调用。 我认为在我的本地主机上进行curl没有用处
  • 'WooCommerce REST API Client Library' 对我没有用,因为它依赖于REST API,并且不支持创建订单调用
老实说,WooCom的API文档有限,可能他们仍在更新中。 目前,他们并没有告诉我如何创建新订单,需要哪些参数等等。
无论如何,我已经找到了使用REST API使用的类和函数来创建具有订单行(您的产品)的订单,并想要分享它!
我创建了自己的PHP类:
class WP_MyPlugin_woocommerce
{

public static function init()
{
    // required classes to create an order
    require_once WOOCOMMERCE_API_DIR . 'class-wc-api-exception.php';
    require_once WOOCOMMERCE_API_DIR . 'class-wc-api-server.php';
    require_once WOOCOMMERCE_API_DIR . 'class-wc-api-resource.php';
    require_once WOOCOMMERCE_API_DIR . 'interface-wc-api-handler.php';
    require_once WOOCOMMERCE_API_DIR . 'class-wc-api-json-handler.php';
    require_once WOOCOMMERCE_API_DIR . 'class-wc-api-orders.php';
}

public static function create_order()
{
    global $wp;

    // create order
    $server = new WC_API_Server( $wp->query_vars['wc-api-route'] );
    $order = new WC_API_Orders( $server );

    $order_id = $order->create_order( array
    (
        'order'             => array
        (
           'status'            => 'processing'
        ,  'customer_id'       =>  get_current_user_id()
        // ,   'order_meta'        => array
        //     (
        //        'some order meta'         => 'a value
        //     ,   some more order meta'    => 1
        //     )
        ,   'shipping_address'        => array
            (
                'first_name'          => $firstname
            ,   'last_name'           => $lastname
            ,   'address_1'           => $address
            ,   'address_2'           => $address2
            ,   'city'                => $city
            ,   'postcode'            => $postcode
            ,   'state'               => $state
            ,   'country'             => $country
            )

        ,   'billing_address'        => array(..can be same as shipping )

        ,   'line_items'        => array
            (
                array
                (
                    'product_id'         => 258
                ,   'quantity'           => 1
                )
            )
        )
    ) );
    var_dump($order_id);
    die();
}
}

重要提示:

  • The 'WOOCOMMERCE_API_DIR' constant points to '/woocommerce/includes/api/' in your plugin dir.
  • The order is assigned to a customer, in my case the current logged in user. Make sure your user has a role that has the capability to read, edit, create and delete orders. My role looks like this:

       $result = add_role(
        'customer'
    ,   __( 'Customer' )
    ,   array
        (
            'read'         => true
        // ,   'read_private_posts' => true
        // ,   'read_private_products' => true
        ,   'read_private_shop_orders' => true
        ,   'edit_private_shop_orders' => true
        ,   'delete_private_shop_orders' => true
        ,   'publish_shop_orders' => true
        // ,   'read_private_shop_coupons' => true
        ,   'edit_posts'   => false
        ,   'delete_posts' => false
        ,   'show_admin_bar_front' => false
        )
    );
    
  • If you want to look at the shop manager's rights, check

    var_dump(get_option( 'wp_user_roles'));

我的create_order函数能够很好地创建订单,并将商品行项目存储在order_items表中。

希望我对你有所帮助,这花费了我一些时间才做到正确。


请您帮忙查看一下我的问题:https://dev59.com/LZXfa4cB1Zd3GeqPl-uh。 - Manik

0
很多答案都展示了最好的过滤器(hook)使用方法。我搜索了好几天,因为当我使用'init'时,它会不断地产生更多的订单。在2分钟内,我就有了30个订单。无论如何,请使用这段代码,只会创建1个订单。另外,将$product = wc_get_product( '1001' )更改为您的产品ID。参考链接在第144行:https://github.com/dipolukarov/wordpress/blob/master/wp-content/plugins/woocommerce/classes/class-wc-checkout.php
function my_init2() {
    $order    = wc_create_order();
    $order_id = $order->get_id();
    
    $product = wc_get_product( '10001' );
    
    $address = array(
        'first_name' => 'John2',
        'last_name'  => 'Smith1',
        'email'      => 'johnsmith1@gmail.com',
    );
    //$order->date_created(2020-07-21 );
    $order->add_product( $product, 1 );
    $order->set_address( $address, 'billing' );
    $order->set_created_via( 'programatically' );
    $order->calculate_totals();
    $order->set_total( $product->get_price() );
    $order->update_status( 'wc-completed' );
    
    error_log( '$order_id: ' . $order_id );
    
}          
//add_action('admin_init','my_init2');
function my_run_only_once() {
 
    if ( get_option( 'my_run_only_once_20' ) != 'completed' ) {
  my_init2();
        update_option( 'my_run_only_once_20', 'completed' );
    }
}
add_action( 'admin_init', 'my_run_only_once' );


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接