调用calculate_totals()触发订单总额为0的提前邮件通知如何解决?
解决WooCommerce拆分订单时提前触发邮件与总额计算问题
核心问题分析
拆分订单调用calculate_totals()时,内部会先执行calculate_taxes(true),该方法会自动调用save()——此时订单总额尚未完成计算(显示为0),且会触发订单状态变更的邮件通知;若跳过calculate_taxes()则会导致税费计算错误。
更优解决方案:临时使用草稿状态规避邮件触发
WooCommerce默认不会给草稿(draft)状态的订单发送通知邮件,因此可以通过以下步骤实现无提前邮件的正确计算:
- 创建新订单时先设置为
draft状态,避免计算过程中的save()触发邮件 - 完成订单项转移、总额与税费计算后,再将订单状态恢复为原订单的目标状态
- 此时触发的邮件会包含正确的订单总额与税费数据
修改后的代码实现
public static function add_hooks() { // 支付确认后拆分订单 add_action( 'woocommerce_order_status_pending_to_processing', [ __CLASS__, 'split_order' ], 10, 2 ); } public static function split_order( $order_id, WC_Order $order, $force = null ) { $items_to_split = array(); foreach ( $order->get_items() as $item_id => $item_data ) { if ( $item_data->meta_exists( '_SnapshotId' ) ) { $items_to_split[ $item_id ] = $item_data; } } if ( count( $items_to_split ) === 0 ) return; $target_status = $order->get_status(); $new_order = wc_create_order(); $new_order->set_customer_id( $order->get_customer_id() ); $new_order->set_payment_method( $order->get_payment_method() ); $new_order->set_created_via( 'split_order' ); // 临时设置为草稿状态,避免计算时触发邮件 $new_order->set_status( 'draft' ); $new_order->set_billing( $order->get_address( 'billing' ) ); $new_order->set_shipping( $order->get_address( 'shipping' ) ); // 转移订单项到新订单 foreach ( $items_to_split as $item_id => $item_data ) { /** @var WC_Order_Item_Product $item_data */ $new_order_item = new WC_Order_Item_Product(); $data = $item_data->get_data(); unset( $data['id'] ); unset( $data['order_id'] ); $new_order_item->set_props( $data ); $meta_data = array_map( function ( $n ) { return $n->get_data(); }, $item_data->get_meta_data() ); foreach ( $meta_data as $meta_data_entry ) { $new_order_item->add_meta_data( $meta_data_entry['key'], $meta_data_entry['value'] ); } $new_order->add_item( $new_order_item ); $order->remove_item( $item_id ); } // 完成计算,此时草稿状态不会触发邮件 $new_order->calculate_totals(); $order->calculate_totals(); // 恢复目标状态,此时触发的邮件包含正确数据 $new_order->set_status( $target_status ); $new_order->save(); }
备选方案:临时禁用邮件过滤器
如果草稿状态的方式不适用,还可以通过临时禁用WooCommerce邮件的过滤器实现:
// 计算前禁用所有邮件 add_filter( 'woocommerce_email_enabled', '__return_false' ); // 执行计算 $new_order->calculate_totals(); $order->calculate_totals(); // 恢复邮件功能 remove_filter( 'woocommerce_email_enabled', '__return_false' );
该方案会临时屏蔽所有WooCommerce邮件通知,适合需要严格控制邮件触发时机的场景。
内容的提问来源于stack exchange,提问作者shadryck
相关产品推荐
相关产品推荐

