Fix/Fix new order triggers when creating/moving orders into draft or from draft to valid statuses (#49098)
* Prevent new order hook if moving or creating order status into a non-triggering status (drafts&failed refunded etc) when using CPT for orders. * Removed rogue error_log() * Lint * Prevent new order hook if moving or creating order status into a non-triggering status (drafts&failed refunded etc) when using HPOS for orders. * Added changelog * Fixed wrong if simplification * Unit tests for update methods * Renamed var * Renamed vars to snake_case. * Introduced remove_status_prefix() utility function. * Added default 'new' status to an Order that doesn't exist yet in the database for the update method in HPOS * Added default 'new' status to an Order that doesn't exist yet in the database for the update method in CPT * Linting. * Missing class import. * Added a new test for updating new processing Orders. * Linting. * Tests fix. * Tests fix. * Removed statuses from triggering the new order hook. * tweak * Test tweak * Test tweak * Test tweak * Test tweak * Test tweak * Tweak to rule out db querying failure.
This commit is contained in:
parent
3d148e2577
commit
1ba24e1937
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: fix
|
||||
|
||||
Resolved issues with new order hook triggers during transitions to and from draft statuses.
|
|
@ -626,7 +626,7 @@ abstract class WC_Abstract_Order extends WC_Abstract_Legacy_Order {
|
|||
*/
|
||||
public function set_status( $new_status ) {
|
||||
$old_status = $this->get_status();
|
||||
$new_status = 'wc-' === substr( $new_status, 0, 3 ) ? substr( $new_status, 3 ) : $new_status;
|
||||
$new_status = OrderUtil::remove_status_prefix( $new_status );
|
||||
|
||||
$status_exceptions = array( 'auto-draft', 'trash' );
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
* @package WooCommerce\Classes
|
||||
*/
|
||||
|
||||
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||
|
||||
if ( ! defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
@ -189,22 +191,37 @@ class WC_Order_Data_Store_CPT extends Abstract_WC_Order_Data_Store_CPT implement
|
|||
|
||||
// Also grab the current status so we can compare.
|
||||
$previous_status = get_post_status( $order->get_id() );
|
||||
// If the order doesn't exist in the DB, we will consider it as new.
|
||||
if ( ! $previous_status && $order->get_id() === 0 ) {
|
||||
$previous_status = 'new';
|
||||
}
|
||||
|
||||
// Update the order.
|
||||
parent::update( $order );
|
||||
|
||||
// Fire a hook depending on the status - this should be considered a creation if it was previously draft status.
|
||||
$new_status = $order->get_status( 'edit' );
|
||||
$current_status = $order->get_status( 'edit' );
|
||||
|
||||
if ( $new_status !== $previous_status && in_array( $previous_status, array( 'new', 'auto-draft', 'draft', 'checkout-draft' ), true ) ) {
|
||||
do_action( 'woocommerce_new_order', $order->get_id(), $order );
|
||||
} else {
|
||||
do_action( 'woocommerce_update_order', $order->get_id(), $order );
|
||||
// We need to remove the wc- prefix from the status for comparison and proper evaluation of new vs updated orders.
|
||||
$previous_status = OrderUtil::remove_status_prefix( $previous_status );
|
||||
$current_status = OrderUtil::remove_status_prefix( $current_status );
|
||||
|
||||
$draft_statuses = array( 'new', 'auto-draft', 'draft', 'checkout-draft' );
|
||||
|
||||
// This hook should be fired only if the new status is not one of draft statuses and the previous status was one of the draft statuses.
|
||||
if (
|
||||
$current_status !== $previous_status
|
||||
&& ! in_array( $current_status, $draft_statuses, true )
|
||||
&& in_array( $previous_status, $draft_statuses, true )
|
||||
) {
|
||||
do_action( 'woocommerce_new_order', $order->get_id(), $order ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
|
||||
return;
|
||||
}
|
||||
|
||||
do_action( 'woocommerce_update_order', $order->get_id(), $order ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method that updates all the post meta for an order based on it's settings in the WC_Order class.
|
||||
* Helper method that updates all the post meta for an order based on its settings in the WC_Order class.
|
||||
*
|
||||
* @param WC_Order $order Order object.
|
||||
* @since 3.0.0
|
||||
|
|
|
@ -468,7 +468,7 @@ class WC_REST_Orders_V2_Controller extends WC_REST_CRUD_Controller {
|
|||
}
|
||||
|
||||
// Format the order status.
|
||||
$data['status'] = 'wc-' === substr( $data['status'], 0, 3 ) ? substr( $data['status'], 3 ) : $data['status'];
|
||||
$data['status'] = OrderUtil::remove_status_prefix( $data['status'] );
|
||||
|
||||
// Format line items.
|
||||
foreach ( $format_line_items as $key ) {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
use Automattic\WooCommerce\Internal\DataStores\Orders\DataSynchronizer;
|
||||
use Automattic\WooCommerce\Internal\Utilities\Users;
|
||||
use Automattic\WooCommerce\Utilities\OrderUtil;
|
||||
use Automattic\WooCommerce\Utilities\StringUtil;
|
||||
|
||||
defined( 'ABSPATH' ) || exit;
|
||||
|
@ -147,9 +148,9 @@ function wc_get_is_pending_statuses() {
|
|||
*/
|
||||
function wc_get_order_status_name( $status ) {
|
||||
$statuses = wc_get_order_statuses();
|
||||
$status = 'wc-' === substr( $status, 0, 3 ) ? substr( $status, 3 ) : $status;
|
||||
$status = isset( $statuses[ 'wc-' . $status ] ) ? $statuses[ 'wc-' . $status ] : $status;
|
||||
return $status;
|
||||
$status = OrderUtil::remove_status_prefix( $status );
|
||||
|
||||
return $statuses[ 'wc-' . $status ] ?? $status;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -239,7 +239,7 @@ class Orders extends \WC_REST_Orders_Controller {
|
|||
}
|
||||
|
||||
// Format the order status.
|
||||
$data['status'] = 'wc-' === substr( $data['status'], 0, 3 ) ? substr( $data['status'], 3 ) : $data['status'];
|
||||
$data['status'] = OrderUtil::remove_status_prefix( $data['status'] );
|
||||
|
||||
// Format requested line items.
|
||||
$formatted_line_items = array();
|
||||
|
|
|
@ -2586,7 +2586,7 @@ FROM $order_meta_table
|
|||
* @param \WC_Order $order Order object.
|
||||
*/
|
||||
public function update( &$order ) {
|
||||
$previous_status = ArrayUtil::get_value_or_default( $order->get_data(), 'status' );
|
||||
$previous_status = ArrayUtil::get_value_or_default( $order->get_data(), 'status', 'new' );
|
||||
|
||||
// Before updating, ensure date paid is set if missing.
|
||||
if (
|
||||
|
@ -2621,8 +2621,15 @@ FROM $order_meta_table
|
|||
$order->apply_changes();
|
||||
$this->clear_caches( $order );
|
||||
|
||||
// For backwards compatibility, moving a draft order to a valid status triggers the 'woocommerce_new_order' hook.
|
||||
if ( ! empty( $changes['status'] ) && in_array( $previous_status, array( 'new', 'auto-draft', 'draft', 'checkout-draft' ), true ) ) {
|
||||
$draft_statuses = array( 'new', 'auto-draft', 'draft', 'checkout-draft' );
|
||||
|
||||
// For backwards compatibility, this hook should be fired only if the new status is not one of the draft statuses and the previous status was one of the draft statuses.
|
||||
if (
|
||||
! empty( $changes['status'] )
|
||||
&& $changes['status'] !== $previous_status
|
||||
&& ! in_array( $changes['status'], $draft_statuses, true )
|
||||
&& in_array( $previous_status, $draft_statuses, true )
|
||||
) {
|
||||
do_action( 'woocommerce_new_order', $order->get_id(), $order ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -228,4 +228,19 @@ final class OrderUtil {
|
|||
return $count_per_status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the 'wc-' prefix from status.
|
||||
*
|
||||
* @param string $status The status to remove the prefix from.
|
||||
*
|
||||
* @return string The status without the prefix.
|
||||
* @since 9.2.0
|
||||
*/
|
||||
public static function remove_status_prefix( string $status ): string {
|
||||
if ( strpos( $status, 'wc-' ) === 0 ) {
|
||||
$status = substr( $status, 3 );
|
||||
}
|
||||
|
||||
return $status;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -466,4 +466,74 @@ class WC_Order_Data_Store_CPT_Test extends WC_Unit_Test_Case {
|
|||
|
||||
remove_action( 'woocommerce_new_order', $callback );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testDox Updating an order status correctly triggers the "woocommerce_new_order" action.
|
||||
*/
|
||||
public function test_update_order_status_correctly_triggers_new_order_hook() {
|
||||
|
||||
$new_count = 0;
|
||||
|
||||
$callback = function () use ( &$new_count ) {
|
||||
++$new_count;
|
||||
};
|
||||
|
||||
add_action( 'woocommerce_new_order', $callback );
|
||||
|
||||
$order_data_store_cpt = new WC_Order_Data_Store_CPT();
|
||||
|
||||
$order = new WC_Order();
|
||||
$order->set_status( 'draft' );
|
||||
|
||||
$this->assertEquals( 0, $new_count );
|
||||
|
||||
$order->set_status( 'checkout-draft' );
|
||||
$order_data_store_cpt->update( $order );
|
||||
$order->save();
|
||||
$this->assertEquals( 0, $new_count );
|
||||
|
||||
$triggering_order_statuses = array( 'pending', 'on-hold', 'completed', 'processing' );
|
||||
|
||||
foreach ( $triggering_order_statuses as $k => $status ) {
|
||||
$current_status = $order->get_status( 'edit' );
|
||||
$order->set_status( $status );
|
||||
$order_data_store_cpt->update( $order );
|
||||
$order->set_status( 'checkout-draft' ); // Revert back to draft.
|
||||
$order->save();
|
||||
$this->assertEquals(
|
||||
$k + 1,
|
||||
$new_count,
|
||||
'Failed to trigger new order hook changing status: ' . $current_status . ' -> ' . $status
|
||||
);
|
||||
}
|
||||
|
||||
remove_action( 'woocommerce_new_order', $callback );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testDox Create a new order with processing status without saving and updating it should trigger the "woocommerce_new_order" action.
|
||||
*/
|
||||
public function test_update_new_processing_order_correctly_triggers_new_order_hook() {
|
||||
|
||||
$new_count = 0;
|
||||
|
||||
$callback = function () use ( &$new_count ) {
|
||||
++$new_count;
|
||||
};
|
||||
|
||||
add_action( 'woocommerce_new_order', $callback );
|
||||
|
||||
$order_data_store_cpt = new WC_Order_Data_Store_CPT();
|
||||
|
||||
$order = new WC_Order();
|
||||
$order->set_status( 'processing' );
|
||||
|
||||
$this->assertEquals( 0, $new_count );
|
||||
|
||||
$order_data_store_cpt->update( $order );
|
||||
|
||||
$this->assertEquals( 1, $new_count );
|
||||
|
||||
remove_action( 'woocommerce_new_order', $callback );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3461,4 +3461,66 @@ class OrdersTableDataStoreTests extends HposTestCase {
|
|||
remove_action( 'woocommerce_new_order', $callback );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testDox Updating an order status correctly triggers the "woocommerce_new_order" action.
|
||||
*/
|
||||
public function test_update_order_status_correctly_triggers_new_order_hook() {
|
||||
|
||||
$new_count = 0;
|
||||
|
||||
$callback = function () use ( &$new_count ) {
|
||||
++$new_count;
|
||||
};
|
||||
|
||||
add_action( 'woocommerce_new_order', $callback );
|
||||
|
||||
$order = new WC_Order();
|
||||
$order->set_status( 'draft' );
|
||||
|
||||
$this->assertEquals( 0, $new_count );
|
||||
|
||||
$order->set_status( 'checkout-draft' );
|
||||
$this->sut->update( $order );
|
||||
$order->save();
|
||||
$this->assertEquals( 0, $new_count );
|
||||
|
||||
$triggering_order_statuses = array( 'pending', 'on-hold', 'completed', 'processing' );
|
||||
|
||||
foreach ( $triggering_order_statuses as $status ) {
|
||||
$order->set_status( $status );
|
||||
$this->sut->update( $order );
|
||||
$order->set_status( 'checkout-draft' ); // Revert back to draft.
|
||||
$order->save();
|
||||
}
|
||||
|
||||
$this->assertEquals( 4, $new_count );
|
||||
|
||||
remove_action( 'woocommerce_new_order', $callback );
|
||||
}
|
||||
|
||||
/**
|
||||
* @testDox Create a new order with processing status without saving and updating it should trigger the "woocommerce_new_order" action.
|
||||
*/
|
||||
public function test_update_new_processing_order_correctly_triggers_new_order_hook() {
|
||||
|
||||
$new_count = 0;
|
||||
|
||||
$callback = function () use ( &$new_count ) {
|
||||
++$new_count;
|
||||
};
|
||||
|
||||
add_action( 'woocommerce_new_order', $callback );
|
||||
|
||||
$order = new WC_Order();
|
||||
$order->set_status( 'processing' );
|
||||
|
||||
$this->assertEquals( 0, $new_count );
|
||||
|
||||
$this->sut->update( $order );
|
||||
$order->save();
|
||||
|
||||
$this->assertEquals( 1, $new_count );
|
||||
|
||||
remove_action( 'woocommerce_new_order', $callback );
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue