Commit 85ff2fd2394 for woocommerce

commit 85ff2fd239407f3a2433ad39aa4eec3d8a508fbb
Author: Chris Lilitsas <1105590+xristos3490@users.noreply.github.com>
Date:   Tue Oct 13 17:32:20 2026 +0300

    Stop back-in-stock emails for out-of-stock sibling variations when one variation is restocked (#69500)

    * fix: skip BIS emails for out-of-stock variations when a sibling restocks

    * fix: keep variation sign-ups in the notifications product filter

    * fix: guard filtered get_children() and deprecate unused eligibility service in ListTable

diff --git a/plugins/woocommerce/changelog/fix-wooplug-7906-bis-sibling-variation-emails b/plugins/woocommerce/changelog/fix-wooplug-7906-bis-sibling-variation-emails
new file mode 100644
index 00000000000..49a16c8b714
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-wooplug-7906-bis-sibling-variation-emails
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Stop sending back-in-stock emails for out-of-stock variations when a sibling variation is restocked.
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Admin/ListTable.php b/plugins/woocommerce/src/Internal/StockNotifications/Admin/ListTable.php
index 33e3d6dd011..870fca366c7 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Admin/ListTable.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Admin/ListTable.php
@@ -4,6 +4,7 @@ declare( strict_types = 1 );

 namespace Automattic\WooCommerce\Internal\StockNotifications\Admin;

+use Automattic\WooCommerce\Enums\ProductType;
 use Automattic\WooCommerce\Internal\DataStores\StockNotifications\StockNotificationsDataStore;
 use Automattic\WooCommerce\Internal\StockNotifications\Enums\NotificationStatus;
 use Automattic\WooCommerce\Internal\StockNotifications\Notification;
@@ -69,7 +70,9 @@ class ListTable extends \WP_List_Table {
 	/**
 	 * Eligibility service.
 	 *
-	 * @var EligibilityService
+	 * @deprecated 11.3.0 No longer used by this class. Will be removed in a future version.
+	 *
+	 * @var EligibilityService|null
 	 */
 	public $eligibility_service;

@@ -78,6 +81,8 @@ class ListTable extends \WP_List_Table {
 	 *
 	 * @internal
 	 *
+	 * @deprecated 11.3.0 No longer used by this class. Will be removed in a future version.
+	 *
 	 * @param EligibilityService $eligibility_service Eligibility service.
 	 */
 	final public function init( EligibilityService $eligibility_service ) {
@@ -377,8 +382,10 @@ class ListTable extends \WP_List_Table {
 			$filter  = absint( wp_unslash( $_GET['customer_stock_notifications_product_filter'] ) ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
 			$product = wc_get_product( $filter );
 			if ( $product instanceof \WC_Product ) {
-				$target_ids               = $this->eligibility_service->get_target_product_ids( $product );
-				$query_args['product_id'] = $target_ids;
+				$children                 = $product->get_children();
+				$query_args['product_id'] = $product->is_type( ProductType::VARIABLE )
+					? array_merge( array( $product->get_id() ), is_array( $children ) ? $children : array() )
+					: array( $product->get_id() );
 			} else {
 				NotificationsPage::add_notice( __( 'Invalid product selected.', 'woocommerce' ), 'error' );
 			}
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Utilities/EligibilityService.php b/plugins/woocommerce/src/Internal/StockNotifications/Utilities/EligibilityService.php
index 1e77ce88e85..d97ffaa6b85 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Utilities/EligibilityService.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Utilities/EligibilityService.php
@@ -122,9 +122,9 @@ class EligibilityService {
 	/**
 	 * Get the product IDs that need to be checked for stock notifications.
 	 *
-	 * For simple products, this returns just the product ID. For variable products,
-	 * it returns both the parent product ID and the IDs of all variations whose stock
-	 * is managed by the parent product.
+	 * For simple products, this returns just the product ID. For variable products that
+	 * manage stock, it also returns the IDs of the variations that share the parent's stock.
+	 * Variations of a parent that does not manage stock keep their own stock status.
 	 *
 	 * This is used in two key scenarios:
 	 * 1. Checking if a product has any active notifications
@@ -137,7 +137,7 @@ class EligibilityService {
 	 */
 	public function get_target_product_ids( WC_Product $product ): array {
 		$lookup_ids = array( $product->get_id() );
-		if ( $product->is_type( ProductType::VARIABLE ) ) {
+		if ( $product->is_type( ProductType::VARIABLE ) && $product->get_manage_stock() ) {
 			$children_ids = $this->stock_management_helper->get_managed_variations( $product );
 			$lookup_ids   = array_merge( $lookup_ids, $children_ids );
 		}
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/AsyncTasks/NotificationsProcessorTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/AsyncTasks/NotificationsProcessorTests.php
index d13651bf482..583aeb5fbe8 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/AsyncTasks/NotificationsProcessorTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/AsyncTasks/NotificationsProcessorTests.php
@@ -210,6 +210,38 @@ class NotificationsProcessorTests extends \WC_Unit_Test_Case {
 		$this->assertEquals( NotificationStatus::SENT, $notification_on_variation->get_status() );
 	}

+	/**
+	 * Test process_batch on a parent that does not manage stock leaves out-of-stock variations alone.
+	 */
+	public function test_process_batch_variable_without_parent_stock_skips_out_of_stock_siblings() {
+		$variable   = WC_Helper_Product::create_variation_product();
+		$variations = array_map( 'wc_get_product', $variable->get_children() );
+		$this->assertFalse( $variable->get_manage_stock() );
+
+		$restocked = $variations[0];
+		$restocked->set_stock_status( ProductStockStatus::IN_STOCK );
+		$restocked->save();
+		$sibling = $variations[1];
+		$sibling->set_stock_status( ProductStockStatus::OUT_OF_STOCK );
+		$sibling->save();
+
+		\WC_Product_Variable::sync( $variable->get_id() );
+		$variable = wc_get_product( $variable->get_id() );
+		$this->assertEquals( ProductStockStatus::IN_STOCK, $variable->get_stock_status() );
+
+		$notification = new Notification();
+		$notification->set_product_id( $sibling->get_id() );
+		$notification->set_user_id( 1 );
+		$notification->set_status( NotificationStatus::ACTIVE );
+		$notification->save();
+
+		$this->sut->process_batch( $variable->get_id() );
+
+		$notification = new Notification( $notification->get_id() );
+		$this->assertEquals( NotificationStatus::ACTIVE, $notification->get_status() );
+		$this->assertEmpty( $notification->get_date_notified() );
+	}
+
 	/**
 	 * Test process_batch method bail out when product is not in stock.
 	 */
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Utilities/EligibilityServiceTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Utilities/EligibilityServiceTests.php
index 8e0434a8427..7f9b7536984 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Utilities/EligibilityServiceTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Utilities/EligibilityServiceTests.php
@@ -101,7 +101,13 @@ class EligibilityServiceTests extends \WC_Unit_Test_Case {
 		$product = WC_Helper_Product::create_simple_product();
 		$this->assertEquals( array( $product->get_id() ), $this->sut->get_target_product_ids( $product ) );

-		$variable           = WC_Helper_Product::create_variation_product();
+		$variable = WC_Helper_Product::create_variation_product();
+		$this->assertFalse( $variable->get_manage_stock() );
+		$this->assertEquals( array( $variable->get_id() ), $this->sut->get_target_product_ids( $variable ), 'Variations of a parent that does not manage stock keep their own stock status.' );
+
+		$variable->set_manage_stock( true );
+		$variable->set_stock_quantity( 10 );
+		$variable->save();
 		$managed_variations = array_filter(
 			array_map(
 				function ( $variation ) {
@@ -115,6 +121,7 @@ class EligibilityServiceTests extends \WC_Unit_Test_Case {
 			)
 		);

+		$this->assertNotEmpty( $managed_variations );
 		$this->assertEquals( array( $variable->get_id(), ...$managed_variations ), $this->sut->get_target_product_ids( $variable ) );

 		$variation = $variable->get_children()[0];