Commit 763c11f6d56 for woocommerce
commit 763c11f6d56ca1d6f03cbca5fdf75d4cfd63230b
Author: Ahmed Ismail <105542236+ahmedevsaad@users.noreply.github.com>
Date: Thu Nov 12 17:49:59 2026 +0200
Update the order modified date when only meta-stored order data changes (#69777)
With posts storage, record every order save in post_modified, as HPOS
already does, so item, total and custom meta changes move the date.
Squashed from 95324e29f36, 000597eaaba and 70aafdf7601 when rebasing
onto trunk.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
diff --git a/plugins/woocommerce/changelog/fix-34798-parent-modified-date b/plugins/woocommerce/changelog/fix-34798-parent-modified-date
new file mode 100644
index 00000000000..c0123fc0951
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-34798-parent-modified-date
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Update the order modified date every time an order is saved on stores using posts storage, including item and custom field changes, as HPOS does.
diff --git a/plugins/woocommerce/includes/data-stores/abstract-wc-order-data-store-cpt.php b/plugins/woocommerce/includes/data-stores/abstract-wc-order-data-store-cpt.php
index 8dde869ba80..70acacb6d0e 100644
--- a/plugins/woocommerce/includes/data-stores/abstract-wc-order-data-store-cpt.php
+++ b/plugins/woocommerce/includes/data-stores/abstract-wc-order-data-store-cpt.php
@@ -260,6 +260,19 @@ abstract class Abstract_WC_Order_Data_Store_CPT extends WC_Data_Store_WP impleme
wp_update_post( array_merge( array( 'ID' => $order->get_id() ), $post_data ) );
}
$order->read_meta_data( true ); // Refresh internal meta data, in case things were hooked into `save_post` or another WP hook.
+ } else {
+ // Record the save even when only meta or items changed, matching the HPOS data store.
+ $modified = time();
+ $order->set_date_modified( $modified );
+ $GLOBALS['wpdb']->update(
+ $GLOBALS['wpdb']->posts,
+ array(
+ 'post_modified' => get_date_from_gmt( gmdate( 'Y-m-d H:i:s', $modified ) ),
+ 'post_modified_gmt' => gmdate( 'Y-m-d H:i:s', $modified ),
+ ),
+ array( 'ID' => $order->get_id() )
+ );
+ clean_post_cache( $order->get_id() );
}
$this->update_post_meta( $order );
$order->apply_changes();
diff --git a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-order-data-store-cpt-test.php b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-order-data-store-cpt-test.php
index c1140d06c25..64046e91f86 100644
--- a/plugins/woocommerce/tests/php/includes/data-stores/class-wc-order-data-store-cpt-test.php
+++ b/plugins/woocommerce/tests/php/includes/data-stores/class-wc-order-data-store-cpt-test.php
@@ -14,9 +14,15 @@ use Automattic\WooCommerce\Tests\Helpers\InterleavedStatusOrdersTrait;
* @group order-query-tests
*/
class WC_Order_Data_Store_CPT_Test extends WC_Unit_Test_Case {
+
use CogsAwareUnitTestSuiteTrait;
use InterleavedStatusOrdersTrait;
+ /**
+ * A modified date in the past, used to check that saving an order moves it.
+ */
+ private const PAST_MODIFIED_GMT = '2020-01-01 00:00:00';
+
/**
* Store the COT state before the test class.
*
@@ -1727,6 +1733,112 @@ class WC_Order_Data_Store_CPT_Test extends WC_Unit_Test_Case {
}
}
+ /**
+ * Create an order whose stored modified date is in 2020.
+ *
+ * @return WC_Order The order, read back from the database.
+ */
+ private function create_order_modified_in_the_past(): WC_Order {
+ $order = OrderHelper::create_order();
+ $GLOBALS['wpdb']->update(
+ $GLOBALS['wpdb']->posts,
+ array(
+ 'post_modified' => get_date_from_gmt( self::PAST_MODIFIED_GMT ),
+ 'post_modified_gmt' => self::PAST_MODIFIED_GMT,
+ ),
+ array( 'ID' => $order->get_id() )
+ );
+ clean_post_cache( $order->get_id() );
+
+ $order = wc_get_order( $order->get_id() );
+ $this->assertSame( strtotime( self::PAST_MODIFIED_GMT ), $order->get_date_modified()->getTimestamp(), 'The order should start with the backdated modified date.' );
+
+ return $order;
+ }
+
+ /**
+ * Assert that both the order object and the stored order have a modified date after the backdated one.
+ *
+ * @param WC_Order $order The saved order.
+ */
+ private function assert_date_modified_moved( WC_Order $order ): void {
+ $this->assertGreaterThan( strtotime( self::PAST_MODIFIED_GMT ), $order->get_date_modified()->getTimestamp(), 'The order object should have the new modified date.' );
+
+ $stored = wc_get_order( $order->get_id() );
+ $this->assertGreaterThan( strtotime( self::PAST_MODIFIED_GMT ), $stored->get_date_modified()->getTimestamp(), 'The stored order should have the new modified date.' );
+ $this->assertSame(
+ $stored->get_date_modified()->getTimestamp(),
+ strtotime( get_post_field( 'post_modified_gmt', $order->get_id(), 'raw' ) . ' UTC' ),
+ 'post_modified_gmt should match the order modified date.'
+ );
+ }
+
+ /**
+ * @testdox Saving an order whose changes are all stored in meta, like new totals, updates its modified date.
+ */
+ public function test_update_with_meta_only_changes_updates_date_modified(): void {
+ $order = $this->create_order_modified_in_the_past();
+
+ $order->set_total( (float) $order->get_total() + 5 );
+ $order->save();
+
+ $this->assert_date_modified_moved( $order );
+ }
+
+ /**
+ * @testdox Saving an order after changing only custom meta updates its modified date.
+ */
+ public function test_update_with_custom_meta_only_updates_date_modified(): void {
+ $order = $this->create_order_modified_in_the_past();
+
+ $order->update_meta_data( '_test_custom_meta', 'value' );
+ $order->save();
+
+ $this->assert_date_modified_moved( $order );
+ $this->assertSame( 'value', wc_get_order( $order->get_id() )->get_meta( '_test_custom_meta' ), 'The custom meta should be saved.' );
+ }
+
+ /**
+ * @testdox Saving an order after renaming one of its items updates its modified date.
+ */
+ public function test_update_after_item_rename_updates_date_modified(): void {
+ $order = $this->create_order_modified_in_the_past();
+
+ $items = $order->get_items();
+ $item = reset( $items );
+ $item->set_name( 'Renamed item' );
+ $order->save();
+
+ $this->assert_date_modified_moved( $order );
+ $this->assertSame( 'Renamed item', WC_Order_Factory::get_order_item( $item->get_id() )->get_name(), 'The item rename should be saved.' );
+ }
+
+ /**
+ * @testdox Saving an order after adding a zero-amount fee updates its modified date.
+ */
+ public function test_update_after_adding_zero_fee_updates_date_modified(): void {
+ $order = $this->create_order_modified_in_the_past();
+
+ $fee = new WC_Order_Item_Fee();
+ $fee->set_name( 'Free gift wrap' );
+ $fee->set_total( '0' );
+ $order->add_item( $fee );
+ $order->save();
+
+ $this->assert_date_modified_moved( $order );
+ }
+
+ /**
+ * @testdox Saving an order without changes updates its modified date, as the HPOS data store does.
+ */
+ public function test_update_without_changes_updates_date_modified(): void {
+ $order = $this->create_order_modified_in_the_past();
+
+ $order->save();
+
+ $this->assert_date_modified_moved( $order );
+ }
+
/**
* @testdox CPT order queries return matching orders when the "$filter" filter replaces an unknown status with a registered one.
*
diff --git a/plugins/woocommerce/tests/php/includes/wc-stock-functions-tests.php b/plugins/woocommerce/tests/php/includes/wc-stock-functions-tests.php
index 2704cea10b1..97589c059f2 100644
--- a/plugins/woocommerce/tests/php/includes/wc-stock-functions-tests.php
+++ b/plugins/woocommerce/tests/php/includes/wc-stock-functions-tests.php
@@ -554,4 +554,32 @@ class WC_Stock_Functions_Tests extends \WC_Unit_Test_Case {
$this->assertIsIntAndEquals( $site_wide_low_stock_amount, wc_get_low_stock_amount( $var1 ) );
}
+
+ /**
+ * @testdox Reducing stock for an order updates the modified date of the product that holds the stock.
+ */
+ public function test_reducing_order_stock_updates_product_date_modified(): void {
+ $product = WC_Helper_Product::create_simple_product();
+ $product->set_manage_stock( true );
+ $product->set_stock_quantity( 10 );
+ $product->save();
+
+ $past = '2020-01-01 00:00:00';
+ $GLOBALS['wpdb']->update(
+ $GLOBALS['wpdb']->posts,
+ array(
+ 'post_modified' => $past,
+ 'post_modified_gmt' => $past,
+ ),
+ array( 'ID' => $product->get_id() )
+ );
+ clean_post_cache( $product->get_id() );
+
+ $order = WC_Helper_Order::create_order( 0, $product );
+ wc_reduce_stock_levels( $order );
+
+ $product = wc_get_product( $product->get_id() );
+ $this->assertSame( 6, $product->get_stock_quantity() );
+ $this->assertGreaterThan( strtotime( $past ), $product->get_date_modified()->getTimestamp() );
+ }
}