Commit bf5534ac49f for woocommerce

commit bf5534ac49f7439dc85f5f6bc9226eebd5931c8b
Author: Jorge A. Torres <jorge.torres@automattic.com>
Date:   Mon Oct 19 23:41:53 2026 -0500

    [HPOS] Prevent deletion of order items when placeholder post is deleted via `wp_delete_post()` (#69573)

diff --git a/plugins/woocommerce/changelog/wooplug-7591-hpos-placeholder-deletion b/plugins/woocommerce/changelog/wooplug-7591-hpos-placeholder-deletion
new file mode 100644
index 00000000000..b4b4f57b5bc
--- /dev/null
+++ b/plugins/woocommerce/changelog/wooplug-7591-hpos-placeholder-deletion
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Keep order items intact on HPOS stores when an order's placeholder post is deleted through WordPress functions.
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index da3620e3110..978bbbd236d 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -59496,7 +59496,7 @@ parameters:
 		-
 			message: '#^Access to property \$post_type on an unknown class Automattic\\WooCommerce\\Internal\\DataStores\\Orders\\WP_Post\.$#'
 			identifier: class.notFound
-			count: 2
+			count: 1
 			path: src/Internal/DataStores/Orders/DataSynchronizer.php

 		-
diff --git a/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php b/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php
index 43d5bb047fd..147eb413462 100644
--- a/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php
+++ b/plugins/woocommerce/src/Internal/DataStores/Orders/DataSynchronizer.php
@@ -979,7 +979,7 @@ ORDER BY orders.id ASC
 	 * @internal For exclusive usage of WooCommerce core, backwards compatibility not guaranteed.
 	 */
 	public function maybe_prevent_deletion_of_post( $delete, $post ) {
-		if ( self::PLACEHOLDER_ORDER_POST_TYPE !== $post->post_type && $this->custom_orders_table_is_authoritative() && $this->data_store->order_exists( $post->ID ) ) {
+		if ( $this->custom_orders_table_is_authoritative() && $this->data_store->order_exists( $post->ID ) ) {
 			$delete = false;
 		}

diff --git a/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php b/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php
index 7a92c84e386..04c2cd54b0f 100644
--- a/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/DataStores/Orders/DataSynchronizerTests.php
@@ -278,6 +278,52 @@ class DataSynchronizerTests extends \HposTestCase {
 		);
 	}

+	/**
+	 * @testdox When HPOS is authoritative and sync is disabled, deleting a post with the ID of an existing order is prevented and the order keeps its items.
+	 *
+	 * @testWith ["shop_order_placehold"]
+	 *           ["post"]
+	 *
+	 * @param string $post_type Post type of the post sharing the order's ID.
+	 */
+	public function test_post_deletion_is_prevented_while_order_exists_in_hpos( string $post_type ): void {
+		global $wpdb;
+
+		$this->toggle_cot_authoritative( true );
+		$this->disable_cot_sync();
+
+		$order_id = OrderHelper::create_order()->get_id();
+		$wpdb->update( $wpdb->posts, array( 'post_type' => $post_type ), array( 'ID' => $order_id ) );
+		clean_post_cache( $order_id );
+		$this->assertSame( $post_type, get_post_type( $order_id ) );
+
+		$count_items_query = $wpdb->prepare( "SELECT COUNT(*) FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id = %d", $order_id );
+		$items_before      = (int) $wpdb->get_var( $count_items_query ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+		$this->assertGreaterThan( 0, $items_before );
+
+		$result = wp_delete_post( $order_id, true );
+
+		$this->assertFalse( $result, 'Deleting the post should fail.' );
+		$this->assertSame( $post_type, get_post_type( $order_id ), 'The post should still exist.' );
+		$this->assertSame( $items_before, (int) $wpdb->get_var( $count_items_query ), 'The order should keep its items.' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
+	}
+
+	/**
+	 * @testdox When HPOS is authoritative, a placeholder post whose order no longer exists in HPOS can be deleted.
+	 */
+	public function test_orphaned_placeholder_post_can_be_deleted(): void {
+		$this->toggle_cot_authoritative( true );
+		$this->disable_cot_sync();
+
+		$order_id = OrderHelper::create_order()->get_id();
+		$this->direct_delete_cot_order( $order_id );
+
+		$result = wp_delete_post( $order_id, true );
+
+		$this->assertInstanceOf( \WP_Post::class, $result, 'Deleting an orphaned placeholder post should succeed.' );
+		$this->assertNull( get_post( $order_id ), 'The orphaned placeholder post should be gone.' );
+	}
+
 	/**
 	 * @testdox When sync is disabled and the posts table is authoritative, deleting an order generates a deletion record in the meta table if the order exists in the backup table.
 	 *