Commit a54159e07fb for woocommerce
commit a54159e07fb2fdd0efde19060484cfe8476deca4
Author: Samrat Biswas <samrat.biswas@automattic.com>
Date: Fri Jul 3 03:20:09 2026 -0700
Add setting to enable/disable backorder notifications (#65891)
* Add setting to enable/disable backorder notifications
Adds a woocommerce_notify_backorder setting (Settings > Products >
Inventory > Notifications) and a woocommerce_should_send_backorder_notification
filter, mirroring the existing low stock / out of stock notifications.
Defaults to enabled, so existing stores are unaffected on upgrade.
Closes #31365
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Add woocommerce_notify_backorder to inventory settings test expectations
The new backorder notification setting added to the inventory section was
missing from the expected settings array in
test_get_inventory_settings_returns_all_settings, causing the assertEquals
to fail.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Add tests for backorder notification setting and filter gates
Cover the new gating in WC_Emails::backorder(): the email is sent when
woocommerce_notify_backorder is enabled, and suppressed when the option is
disabled or when the woocommerce_should_send_backorder_notification filter
returns false.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Remove stray blank line in backorder filter docblock
Align the woocommerce_should_send_backorder_notification docblock with the
existing low_stock/no_stock filter docblocks, which have no blank line
before @since.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Fix PHPStan error in backorder notification gate
The new woocommerce_should_send_backorder_notification filter calls
$args['product']->get_id(), but PHPStan typed $args['product'] as a bare
object (no get_id()), failing CI. Narrow the existing guard from
is_object() to an instanceof WC_Product check so the product is properly
typed. This also resolves the two previously-baselined object:: method
errors (get_stock_quantity, get_formatted_name) on the same method, which
are removed from phpstan-baseline.neon.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Samrat Biswas <samrat.biswas@a8c.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-authored-by: Ján Mikláš <neosinner@gmail.com>
diff --git a/plugins/woocommerce/changelog/31365-add-backorder-notification-setting b/plugins/woocommerce/changelog/31365-add-backorder-notification-setting
new file mode 100644
index 00000000000..1915a0908ed
--- /dev/null
+++ b/plugins/woocommerce/changelog/31365-add-backorder-notification-setting
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Add a setting to enable or disable backorder stock notification emails, plus a woocommerce_should_send_backorder_notification filter, mirroring the existing low stock and out of stock notifications.
diff --git a/plugins/woocommerce/includes/admin/settings/class-wc-settings-products.php b/plugins/woocommerce/includes/admin/settings/class-wc-settings-products.php
index a61dc47e687..f3fe6fd4419 100644
--- a/plugins/woocommerce/includes/admin/settings/class-wc-settings-products.php
+++ b/plugins/woocommerce/includes/admin/settings/class-wc-settings-products.php
@@ -289,6 +289,16 @@ class WC_Settings_Products extends WC_Settings_Page {
'id' => 'woocommerce_notify_no_stock',
'default' => 'yes',
'type' => 'checkbox',
+ 'checkboxgroup' => '',
+ 'autoload' => false,
+ 'class' => 'manage_stock_field',
+ ),
+
+ array(
+ 'desc' => __( 'Enable backorder notifications', 'woocommerce' ),
+ 'id' => 'woocommerce_notify_backorder',
+ 'default' => 'yes',
+ 'type' => 'checkbox',
'checkboxgroup' => 'end',
'autoload' => false,
'class' => 'manage_stock_field',
diff --git a/plugins/woocommerce/includes/class-wc-emails.php b/plugins/woocommerce/includes/class-wc-emails.php
index 3ec1c534a11..bea9a7b3073 100644
--- a/plugins/woocommerce/includes/class-wc-emails.php
+++ b/plugins/woocommerce/includes/class-wc-emails.php
@@ -1197,13 +1197,28 @@ class WC_Emails {
$order = wc_get_order( $args['order_id'] );
if (
! $args['product'] ||
- ! is_object( $args['product'] ) ||
+ ! $args['product'] instanceof WC_Product ||
! $args['quantity'] ||
! $order
) {
return;
}
+ if ( 'no' === get_option( 'woocommerce_notify_backorder', 'yes' ) ) {
+ return;
+ }
+
+ /**
+ * Determine if the current product should trigger a backorder notification.
+ *
+ * @param bool $send Whether the backorder notification should be sent.
+ * @param int $product_id The backordered product id.
+ * @since 11.0.0
+ */
+ if ( false === apply_filters( 'woocommerce_should_send_backorder_notification', true, $args['product']->get_id() ) ) {
+ return;
+ }
+
$stock_before = $args['quantity'] + $args['product']->get_stock_quantity();
$backordered_quantity = $args['quantity'] - max( 0, $stock_before );
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 3f9e35eca02..09ad470e704 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -11556,18 +11556,6 @@ parameters:
count: 1
path: includes/class-wc-emails.php
- -
- message: '#^Call to an undefined method object\:\:get_formatted_name\(\)\.$#'
- identifier: method.notFound
- count: 1
- path: includes/class-wc-emails.php
-
- -
- message: '#^Call to an undefined method object\:\:get_stock_quantity\(\)\.$#'
- identifier: method.notFound
- count: 1
- path: includes/class-wc-emails.php
-
-
message: '#^Cannot call method get_id\(\) on WC_Order\|WC_Order_Refund\|false\.$#'
identifier: method.nonObject
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-emails-tests.php b/plugins/woocommerce/tests/php/includes/class-wc-emails-tests.php
index d8e4261b443..69f4a628c7d 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-emails-tests.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-emails-tests.php
@@ -163,4 +163,71 @@ class WC_Emails_Tests extends \WC_Unit_Test_Case {
$this->assertStringContainsString( 'Test meta key', $content );
$this->assertStringContainsString( 'test_meta_value', $content );
}
+
+ /**
+ * Build a valid set of arguments for WC_Emails::backorder().
+ *
+ * @return array
+ */
+ private function get_backorder_args() {
+ $product = WC_Helper_Product::create_simple_product();
+ $order = \Automattic\WooCommerce\RestApi\UnitTests\Helpers\OrderHelper::create_order();
+
+ return array(
+ 'product' => $product,
+ 'quantity' => 2,
+ 'order_id' => $order->get_id(),
+ );
+ }
+
+ /**
+ * @testdox backorder() sends a notification when the setting is enabled (default).
+ */
+ public function test_backorder_sends_when_setting_enabled() {
+ update_option( 'woocommerce_notify_backorder', 'yes' );
+ $args = $this->get_backorder_args();
+
+ $mailer = tests_retrieve_phpmailer_instance();
+ $before = count( $mailer->mock_sent );
+ ( new WC_Emails() )->backorder( $args );
+ $after = count( $mailer->mock_sent );
+
+ $this->assertSame( $before + 1, $after, 'Backorder notification should be sent when the setting is enabled.' );
+ }
+
+ /**
+ * @testdox backorder() does not send a notification when the setting is disabled.
+ */
+ public function test_backorder_does_not_send_when_setting_disabled() {
+ update_option( 'woocommerce_notify_backorder', 'no' );
+ $args = $this->get_backorder_args();
+
+ $mailer = tests_retrieve_phpmailer_instance();
+ $before = count( $mailer->mock_sent );
+ ( new WC_Emails() )->backorder( $args );
+ $after = count( $mailer->mock_sent );
+
+ $this->assertSame( $before, $after, 'Backorder notification must not be sent when the setting is disabled.' );
+
+ update_option( 'woocommerce_notify_backorder', 'yes' );
+ }
+
+ /**
+ * @testdox backorder() does not send a notification when the woocommerce_should_send_backorder_notification filter returns false.
+ */
+ public function test_backorder_does_not_send_when_filter_returns_false() {
+ update_option( 'woocommerce_notify_backorder', 'yes' );
+ $args = $this->get_backorder_args();
+
+ add_filter( 'woocommerce_should_send_backorder_notification', '__return_false' );
+
+ $mailer = tests_retrieve_phpmailer_instance();
+ $before = count( $mailer->mock_sent );
+ ( new WC_Emails() )->backorder( $args );
+ $after = count( $mailer->mock_sent );
+
+ remove_filter( 'woocommerce_should_send_backorder_notification', '__return_false' );
+
+ $this->assertSame( $before, $after, 'Backorder notification must not be sent when the filter returns false.' );
+ }
}
diff --git a/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-products-test.php b/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-products-test.php
index c461549dc3b..d44e67d03ac 100644
--- a/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-products-test.php
+++ b/plugins/woocommerce/tests/php/includes/settings/class-wc-settings-products-test.php
@@ -128,6 +128,7 @@ class WC_Settings_Products_Test extends WC_Settings_Unit_Test_Case {
'woocommerce_hold_stock_minutes' => 'number',
'woocommerce_notify_low_stock' => 'checkbox',
'woocommerce_notify_no_stock' => 'checkbox',
+ 'woocommerce_notify_backorder' => 'checkbox',
'woocommerce_stock_email_recipient' => 'text',
'woocommerce_notify_low_stock_amount' => 'number',
'woocommerce_notify_no_stock_amount' => 'number',