Delete trashed orders after `EMPTY_TRASH_DAYS` as defined by WordPress (HPOS) (#41949)

* Add a test that fails if trashed orders are never deleted under HPOS

* Delete trashed orders after `EMPTY_TRASH_DAYS` as defined by WordPress

* add the changelog file

* appease the linter

* return early if HPOS is not authorative

* attempt to precede WordPress itself (which uses priority 10) to increase the probability of orders and their posts being handled solely by us

* appease the linter
This commit is contained in:
Leif Singer 2023-12-14 15:24:09 +01:00 committed by GitHub
parent 255f1ff919
commit 6f902e3dbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 101 additions and 1 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Delete trashed orders after `EMPTY_TRASH_DAYS` as defined by WordPress (HPOS)

View File

@ -80,6 +80,13 @@ class DataSynchronizer implements BatchProcessorInterface {
*/
private $error_logger;
/**
* The instance of the LegacyProxy object to use.
*
* @var LegacyProxy
*/
private $legacy_proxy;
/**
* The order cache controller.
*
@ -103,6 +110,7 @@ class DataSynchronizer implements BatchProcessorInterface {
self::add_action( 'woocommerce_refund_created', array( $this, 'handle_updated_order' ), 100 );
self::add_action( 'woocommerce_update_order', array( $this, 'handle_updated_order' ), 100 );
self::add_action( 'wp_scheduled_auto_draft_delete', array( $this, 'delete_auto_draft_orders' ), 9 );
self::add_action( 'wp_scheduled_delete', array( $this, 'delete_trashed_orders' ), 9 );
self::add_filter( 'updated_option', array( $this, 'process_updated_option' ), 999, 3 );
self::add_filter( 'added_option', array( $this, 'process_added_option' ), 999, 2 );
self::add_filter( 'deleted_option', array( $this, 'process_deleted_option' ), 999 );
@ -136,6 +144,7 @@ class DataSynchronizer implements BatchProcessorInterface {
$this->data_store = $data_store;
$this->database_util = $database_util;
$this->posts_to_cot_migrator = $posts_to_cot_migrator;
$this->legacy_proxy = $legacy_proxy;
$this->error_logger = $legacy_proxy->call_function( 'wc_get_logger' );
$this->order_cache_controller = $order_cache_controller;
$this->batch_processing_controller = $batch_processing_controller;
@ -966,6 +975,41 @@ ORDER BY orders.id ASC
do_action( 'woocommerce_scheduled_auto_draft_delete' );
}
/**
* Handles deletion of trashed orders after `EMPTY_TRASH_DAYS` as defined by WordPress.
*
* @since 8.5.0
*
* @return void
*/
private function delete_trashed_orders() {
if ( ! $this->custom_orders_table_is_authoritative() ) {
return;
}
$delete_timestamp = $this->legacy_proxy->call_function( 'time' ) - ( DAY_IN_SECONDS * EMPTY_TRASH_DAYS );
$args = array(
'status' => 'trash',
'limit' => self::ORDERS_SYNC_BATCH_SIZE,
'date_modified' => '<' . $delete_timestamp,
);
$orders = wc_get_orders( $args );
if ( ! $orders || ! is_array( $orders ) ) {
return;
}
foreach ( $orders as $order ) {
if ( $order->get_status() !== 'trash' ) {
continue;
}
if ( $order->get_date_modified()->getTimestamp() >= $delete_timestamp ) {
continue;
}
$order->delete( true );
}
}
/**
* Handle the 'woocommerce_feature_description_tip' filter.
*

View File

@ -25,13 +25,18 @@ class DataSynchronizerTests extends HposTestCase {
*/
public function setUp(): void {
parent::setUp();
$this->reset_legacy_proxy_mocks();
$container = wc_get_container();
$container->reset_all_resolved();
// Remove the Test Suites use of temporary tables https://wordpress.stackexchange.com/a/220308.
remove_filter( 'query', array( $this, '_create_temporary_tables' ) );
remove_filter( 'query', array( $this, '_drop_temporary_tables' ) );
OrderHelper::delete_order_custom_tables(); // We need this since non-temporary tables won't drop automatically.
OrderHelper::create_order_custom_table_if_not_exist();
OrderHelper::toggle_cot_feature_and_usage( false );
$this->sut = wc_get_container()->get( DataSynchronizer::class );
$this->sut = $container->get( DataSynchronizer::class );
}
/**
@ -530,6 +535,53 @@ class DataSynchronizerTests extends HposTestCase {
$this->assertNotContains( $order1->get_id(), $orders );
}
/**
* Test that trashed orders are deleted after the time set in `EMPTY_TRASH_DAYS`.
*/
public function test_trashed_order_deletion(): void {
$this->toggle_cot_authoritative( true );
$this->disable_cot_sync();
$order = new WC_Order();
$order->save();
// Ensure the placeholder post is there.
$placeholder = get_post( $order->get_id() );
$this->assertEquals( $order->get_id(), $placeholder->ID );
// Trashed orders should be deleted by the collection mechanism.
$order->get_data_store()->delete( $order );
$this->assertEquals( $order->get_status(), 'trash' );
$order->save();
// Run scheduled deletion.
do_action( 'wp_scheduled_delete' ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.HookCommentWrongStyle
// Refresh order and ensure it's *not* gone.
$order = wc_get_order( $order->get_id() );
$this->assertNotNull( $order );
// Time-travel into the future so that the time required to delete a trashed order has passed.
$this->register_legacy_proxy_function_mocks(
array(
'time' => function() {
return time() + DAY_IN_SECONDS * EMPTY_TRASH_DAYS + 1;
},
)
);
// Run scheduled deletion.
do_action( 'wp_scheduled_delete' ); // phpcs:ignore WooCommerce.Commenting.CommentHooks.HookCommentWrongStyle
// Ensure the placeholder post is gone.
$placeholder = get_post( $order->get_id() );
$this->assertNull( $placeholder );
// Refresh order and ensure it's gone.
$order = wc_get_order( $order->get_id() );
$this->assertFalse( $order );
}
/**
* @testDox When HPOS is enabled, the custom orders table is created.
*/