Commit 59cc1bb36cb for woocommerce
commit 59cc1bb36cbc74d3671762bfd595188ef2ea3a20
Author: Chris Lilitsas <1105590+xristos3490@users.noreply.github.com>
Date: Wed Sep 23 23:09:32 2026 +0300
Revert "Report stock notification save failures instead of silent success" (#69020)
* revert: "Report stock notification save failures instead of silent success" (#68812)
* fix: keep WC_Data_Store::create()/update() out of the PHPStan baseline after revert
* fix: stop treating a zero-row stock notification update as a failed write
diff --git a/plugins/woocommerce/changelog/revert-68812-stock-notification-zero-row-update b/plugins/woocommerce/changelog/revert-68812-stock-notification-zero-row-update
new file mode 100644
index 00000000000..25f232d56cd
--- /dev/null
+++ b/plugins/woocommerce/changelog/revert-68812-stock-notification-zero-row-update
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Stop reporting an error when saving a back in stock notification whose row did not need to change.
diff --git a/plugins/woocommerce/changelog/wooplug-7732-stock-notification-save-errors b/plugins/woocommerce/changelog/wooplug-7732-stock-notification-save-errors
deleted file mode 100644
index cd38099eeb1..00000000000
--- a/plugins/woocommerce/changelog/wooplug-7732-stock-notification-save-errors
+++ /dev/null
@@ -1,4 +0,0 @@
-Significance: patch
-Type: fix
-
-Surface failed database writes when saving a back in stock notification instead of reporting success.
diff --git a/plugins/woocommerce/includes/class-wc-data-store.php b/plugins/woocommerce/includes/class-wc-data-store.php
index c6d0ad58f86..4d263ef4336 100644
--- a/plugins/woocommerce/includes/class-wc-data-store.php
+++ b/plugins/woocommerce/includes/class-wc-data-store.php
@@ -181,10 +181,10 @@ class WC_Data_Store {
*
* @since 3.0.0
* @param WC_Data $data WooCommerce data instance.
- * @return mixed Whatever the underlying data store returns. Most core data stores return nothing; some return a WP_Error on failure.
+ * @return void
*/
public function create( &$data ) {
- return $this->instance->create( $data );
+ $this->instance->create( $data );
}
/**
@@ -192,10 +192,10 @@ class WC_Data_Store {
*
* @since 3.0.0
* @param WC_Data $data WooCommerce data instance.
- * @return mixed Whatever the underlying data store returns. Most core data stores return nothing; some return a WP_Error on failure.
+ * @return void
*/
public function update( &$data ) {
- return $this->instance->update( $data );
+ $this->instance->update( $data );
}
/**
diff --git a/plugins/woocommerce/src/Internal/DataStores/StockNotifications/StockNotificationsDataStore.php b/plugins/woocommerce/src/Internal/DataStores/StockNotifications/StockNotificationsDataStore.php
index 6d0b2afc26b..de0da1bcfba 100644
--- a/plugins/woocommerce/src/Internal/DataStores/StockNotifications/StockNotificationsDataStore.php
+++ b/plugins/woocommerce/src/Internal/DataStores/StockNotifications/StockNotificationsDataStore.php
@@ -190,7 +190,7 @@ CREATE TABLE $meta_table_name (
);
if ( false === $insert ) {
- return new \WP_Error( 'db_insert_error', __( 'Could not insert stock notification into the database.', 'woocommerce' ) );
+ return new \WP_Error( 'db_insert_error', 'Could not insert stock notification into the database.' );
}
$notification_id = (int) $wpdb->insert_id;
@@ -260,7 +260,7 @@ CREATE TABLE $meta_table_name (
global $wpdb;
if ( 0 === $notification->get_id() ) {
- return new \WP_Error( 'invalid_stock_notification', __( 'Invalid notification ID.', 'woocommerce' ) );
+ return new \WP_Error( 'invalid_stock_notification', 'Invalid notification ID.' );
}
$changes = $notification->get_changes();
@@ -293,7 +293,7 @@ CREATE TABLE $meta_table_name (
);
if ( false === $result ) {
- return new \WP_Error( 'db_update_error', __( 'Could not update stock notification in the database.', 'woocommerce' ) );
+ return new \WP_Error( 'db_update_error', 'Could not update stock notification in the database.' );
}
}
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Frontend/MyAccountEndpoint.php b/plugins/woocommerce/src/Internal/StockNotifications/Frontend/MyAccountEndpoint.php
index a0adbdfde78..ce30000bd0d 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Frontend/MyAccountEndpoint.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Frontend/MyAccountEndpoint.php
@@ -536,7 +536,12 @@ class MyAccountEndpoint {
$notification->set_cancellation_source( NotificationCancellationSource::USER );
$notification->set_date_cancelled( time() );
- if ( is_wp_error( $notification->save() ) ) {
+ $notification->save();
+
+ // `WC_Data_Store::update()` drops the data store's return value, so a failed write
+ // reaches us as a successful save. Read the row back to confirm it really changed.
+ $saved = Factory::get_notification( $notification->get_id() );
+ if ( ! $saved instanceof Notification || NotificationStatus::CANCELLED !== $saved->get_status() ) {
return new \WP_Error( 'wc_bis_cancel_failed', __( 'We could not cancel that back in stock notification. Please try again.', 'woocommerce' ) );
}
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Notification.php b/plugins/woocommerce/src/Internal/StockNotifications/Notification.php
index 796a5e24da5..d4f41a52260 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Notification.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Notification.php
@@ -370,13 +370,9 @@ class Notification extends \WC_Data {
}
if ( $this->get_id() ) {
- $result = $this->data_store->update( $this );
+ $this->data_store->update( $this );
} else {
- $result = $this->data_store->create( $this );
- }
-
- if ( is_wp_error( $result ) ) {
- return $result;
+ $this->data_store->create( $this );
}
return $this->get_id();
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/MyAccountEndpointTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/MyAccountEndpointTests.php
index 1e488ad0054..3c80440b3fe 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/MyAccountEndpointTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/MyAccountEndpointTests.php
@@ -582,6 +582,50 @@ class MyAccountEndpointTests extends \WC_Unit_Test_Case {
$this->assertCount( 1, \wc_get_notices( 'error' ) );
}
+ /**
+ * A failed database write reports an error instead of telling the customer the
+ * notification was cancelled.
+ */
+ public function test_cancel_reports_an_error_when_the_save_fails(): void {
+ $user_id = $this->factory->user->create( array( 'role' => 'customer' ) );
+ \wp_set_current_user( $user_id );
+ $notification = $this->create_notification( $user_id, NotificationStatus::ACTIVE );
+
+ $this->simulate_action_request( MyAccountEndpoint::ACTION_CANCEL, $notification->get_id(), true );
+
+ $neutralize_update = array( $this, 'neutralize_notification_update' );
+ add_filter( 'query', $neutralize_update );
+ try {
+ $this->run_action_expecting_redirect();
+ } finally {
+ remove_filter( 'query', $neutralize_update );
+ }
+
+ $this->assertCount( 1, \wc_get_notices( 'error' ) );
+ $this->assertEmpty( \wc_get_notices( 'success' ) );
+
+ $reloaded = Factory::get_notification( $notification->get_id() );
+ $this->assertInstanceOf( Notification::class, $reloaded );
+ $this->assertSame( NotificationStatus::ACTIVE, $reloaded->get_status() );
+ }
+
+ /**
+ * Make an UPDATE against the stock notifications table match no rows, so the data
+ * store reports the failure the way a real database error would.
+ *
+ * @param string $query The query about to run.
+ * @return string
+ */
+ public function neutralize_notification_update( $query ) {
+ global $wpdb;
+
+ if ( is_string( $query ) && 0 === stripos( $query, 'UPDATE' ) && false !== stripos( $query, $wpdb->prefix . 'wc_stock_notifications' ) ) {
+ return $query . ' AND 1 = 0';
+ }
+
+ return $query;
+ }
+
/**
* User A cannot cancel user B's notification even when the nonce validates against B's action name
* (WordPress nonces bind to the current user, so this effectively asserts the ownership check).
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/NotificationTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/NotificationTests.php
index 251b2330f3f..202e941b437 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/NotificationTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/NotificationTests.php
@@ -3,12 +3,8 @@
declare( strict_types = 1 );
namespace Automattic\WooCommerce\Tests\Internal\StockNotifications;
-use Automattic\WooCommerce\Internal\DataStores\StockNotifications\StockNotificationsDataStore;
-use Automattic\WooCommerce\Internal\DataStores\StockNotifications\StockNotificationsMetaDataStore;
use Automattic\WooCommerce\Internal\StockNotifications\Notification;
use Automattic\WooCommerce\Internal\StockNotifications\Config;
-use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus;
-use Automattic\WooCommerce\Internal\Utilities\DatabaseUtil;
/**
* NotificationTests data tests.
@@ -300,79 +296,4 @@ class NotificationTests extends \WC_Unit_Test_Case {
$this->assertInstanceOf( \WP_Error::class, $result );
$this->assertSame( 'User Email is invalid.', $result->get_error_message() );
}
-
- /**
- * @testdox save() should return the data store error when the insert fails.
- */
- public function test_save_returns_data_store_error_when_create_fails(): void {
- $this->use_failing_data_store();
-
- $notification = new Notification();
- $notification->set_product_id( 1 );
- $notification->set_user_email( 'foo@bar.com' );
-
- $result = $notification->save();
-
- $this->assertInstanceOf( \WP_Error::class, $result );
- $this->assertSame( 'db_insert_error', $result->get_error_code() );
- }
-
- /**
- * @testdox save() should return the data store error when the update fails.
- */
- public function test_save_returns_data_store_error_when_update_fails(): void {
- $notification = new Notification();
- $notification->set_product_id( 1 );
- $notification->set_user_email( 'foo@bar.com' );
- $notification->save();
-
- $this->use_failing_data_store();
-
- $stored = new Notification( $notification->get_id() );
- $stored->set_status( NotificationStatus::CANCELLED );
-
- $result = $stored->save();
-
- $this->assertInstanceOf( \WP_Error::class, $result );
- $this->assertSame( 'db_update_error', $result->get_error_code() );
- }
-
- /**
- * Swap the stock notification data store for one whose writes always fail.
- */
- private function use_failing_data_store(): void {
- $store = new class() extends StockNotificationsDataStore {
- /**
- * Fail every insert.
- *
- * @param Notification $notification The data object to create.
- * @return \WP_Error
- */
- public function create( &$notification ) {
- return new \WP_Error( 'db_insert_error', 'Could not insert stock notification into the database.' );
- }
-
- /**
- * Fail every update.
- *
- * @param Notification $notification The data object to update.
- * @return \WP_Error
- */
- public function update( &$notification ) {
- return new \WP_Error( 'db_update_error', 'Could not update stock notification in the database.' );
- }
- };
-
- $store->init(
- wc_get_container()->get( StockNotificationsMetaDataStore::class ),
- wc_get_container()->get( DatabaseUtil::class )
- );
-
- add_filter(
- 'woocommerce_stock_notification_data_store',
- function () use ( $store ) {
- return $store;
- }
- );
- }
}