Commit 4f557da6c9e for woocommerce
commit 4f557da6c9eec7ee48cefae5e293ed53bca66b7f
Author: Chris Lilitsas <1105590+xristos3490@users.noreply.github.com>
Date: Thu Oct 15 18:41:25 2026 +0300
Show complete variation details for back-in-stock sign-ups in admin, My Account, and emails (#69502)
* fix: show all variation attributes for back-in-stock sign-ups
* fix: keep fixed variation attributes and avoid repeated attribute in titles
* fix: keep the variation name in the back-in-stock email title and drop the stale PHPStan baseline entry
The email title keeps `get_name()` so it matches the `{product_name}` subject placeholder and the existing e2e expectation; only the admin list and My Account switch to the parent title since they render the attribute list next to it. The `\WC_Product` docblock fix made the baseline's "invalid type" entry for email_product_title() obsolete, so remove it.
* refactor: rename get_variation_attributes_with_posted to get_variation_attributes_with_posted_data
* fix: show variation attributes below the product title in the stock notifications list
diff --git a/plugins/woocommerce/changelog/fix-wooplug-7915-bis-variation-labels b/plugins/woocommerce/changelog/fix-wooplug-7915-bis-variation-labels
new file mode 100644
index 00000000000..61354cbabb4
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-wooplug-7915-bis-variation-labels
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Show all variation details for back-in-stock sign-ups in the admin list, My Account, and emails.
diff --git a/plugins/woocommerce/client/legacy/css/admin.scss b/plugins/woocommerce/client/legacy/css/admin.scss
index d25b6ff9f67..0c8825a46f4 100644
--- a/plugins/woocommerce/client/legacy/css/admin.scss
+++ b/plugins/woocommerce/client/legacy/css/admin.scss
@@ -1462,6 +1462,11 @@ body.woocommerce_page_wc-customer-stock-notifications {
.wrap {
margin-inline-start: 0;
}
+
+ // Show the variation attributes on their own line below the product title.
+ .column-product .description {
+ display: block;
+ }
}
body.post-type-shop_order,
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 4d1c34cf5fb..51e7dbd2a93 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -65340,12 +65340,6 @@ parameters:
count: 1
path: src/Internal/StockNotifications/Admin/ListTable.php
- -
- message: '#^Call to an undefined method Automattic\\WooCommerce\\Internal\\StockNotifications\\Admin\\ListTable\:\:get_product_formatted_variation_list\(\)\.$#'
- identifier: method.notFound
- count: 1
- path: src/Internal/StockNotifications/Admin/ListTable.php
-
-
message: '#^Call to an undefined method WC_Data_Store\:\:query\(\)\.$#'
identifier: method.notFound
@@ -65844,12 +65838,6 @@ parameters:
count: 1
path: src/Internal/StockNotifications/Emails/EmailTemplatesController.php
- -
- message: '#^Call to method get_name\(\) on an unknown class Automattic\\WooCommerce\\Internal\\StockNotifications\\Emails\\WC_Product\.$#'
- identifier: class.notFound
- count: 1
- path: src/Internal/StockNotifications/Emails/EmailTemplatesController.php
-
-
message: '#^Call to method get_price_html\(\) on an unknown class Automattic\\WooCommerce\\Internal\\StockNotifications\\Emails\\WC_Product\.$#'
identifier: class.notFound
@@ -65916,12 +65904,6 @@ parameters:
count: 1
path: src/Internal/StockNotifications/Emails/EmailTemplatesController.php
- -
- message: '#^Parameter \$product of method Automattic\\WooCommerce\\Internal\\StockNotifications\\Emails\\EmailTemplatesController\:\:email_product_title\(\) has invalid type Automattic\\WooCommerce\\Internal\\StockNotifications\\Emails\\WC_Product\.$#'
- identifier: class.notFound
- count: 1
- path: src/Internal/StockNotifications/Emails/EmailTemplatesController.php
-
-
message: '#^Parameter \#1 \$price of method WC_Product\:\:set_price\(\) expects string, int given\.$#'
identifier: argument.type
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Admin/ListTable.php b/plugins/woocommerce/src/Internal/StockNotifications/Admin/ListTable.php
index 870fca366c7..7c616190d17 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Admin/ListTable.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Admin/ListTable.php
@@ -205,8 +205,8 @@ class ListTable extends \WP_List_Table {
return;
}
- $name = $product->get_name();
- $formatted_variation_list = $this->get_product_formatted_variation_list( true );
+ $name = $product->get_title();
+ $formatted_variation_list = $notification->get_product_formatted_variation_list( true );
if ( $formatted_variation_list ) {
/* translators: product name, identifier */
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Emails/EmailTemplatesController.php b/plugins/woocommerce/src/Internal/StockNotifications/Emails/EmailTemplatesController.php
index e577a641d0d..fe18b1ffed9 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Emails/EmailTemplatesController.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Emails/EmailTemplatesController.php
@@ -63,7 +63,7 @@ class EmailTemplatesController {
/**
* Email product title.
*
- * @param WC_Product $product The product object.
+ * @param \WC_Product $product The product object.
* @param Notification $notification The notification object.
* @param bool $plain_text Whether the email is plain text.
*/
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Notification.php b/plugins/woocommerce/src/Internal/StockNotifications/Notification.php
index 31bbdd3c90f..6b93164eb8c 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Notification.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Notification.php
@@ -389,16 +389,11 @@ class Notification extends \WC_Data {
public function get_product_formatted_variation_list( bool $flat = false ) {
$product = $this->get_product();
- if ( ! $product || ! $product->is_type( array( 'variation' ) ) ) {
+ if ( ! $product instanceof \WC_Product_Variation ) {
return '';
}
- // Replace list with custom data.
- $attributes = $this->get_meta( 'posted_attributes' );
- if ( ! $attributes ) {
- $attributes = $product->get_attributes();
- }
-
+ $attributes = $this->get_variation_attributes_with_posted_data( $product );
if ( empty( $attributes ) ) {
return '';
}
@@ -436,12 +431,33 @@ class Notification extends \WC_Data {
return (string) $product->get_permalink();
}
- // Posted attributes hold only the values chosen for "Any" attributes, so merge them over the variation's own attributes to build a complete link.
- // Keys that no longer belong to the variation (e.g. a removed attribute) are dropped so they don't leak into the URL.
+ return (string) $product->get_permalink( array( 'variation' => $this->get_variation_attributes_with_posted_data( $product ) ) );
+ }
+
+ /**
+ * Get the variation's attributes, with the shopper's posted values filled in.
+ *
+ * Posted values only fill the variation's "Any" attributes (empty values); the variation's fixed values always win.
+ * Keys that no longer belong to the variation (e.g. a removed attribute) are dropped.
+ *
+ * @param \WC_Product_Variation $product The notification's variation.
+ * @return array
+ */
+ private function get_variation_attributes_with_posted_data( \WC_Product_Variation $product ): array {
$variation_attributes = $product->get_variation_attributes();
- $attributes = array_merge( $variation_attributes, array_intersect_key( $posted_attributes, $variation_attributes ) );
+ $posted_attributes = $this->get_meta( 'posted_attributes' );
+ if ( empty( $posted_attributes ) || ! is_array( $posted_attributes ) ) {
+ return $variation_attributes;
+ }
+
+ $any_attributes = array_filter(
+ $variation_attributes,
+ static function ( $value ) {
+ return '' === $value;
+ }
+ );
- return (string) $product->get_permalink( array( 'variation' => $attributes ) );
+ return array_merge( $variation_attributes, array_intersect_key( $posted_attributes, $any_attributes ) );
}
/**
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/NotificationTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/NotificationTests.php
index cd986de2bf0..1eac24435a2 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/NotificationTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/NotificationTests.php
@@ -94,6 +94,57 @@ class NotificationTests extends \WC_Unit_Test_Case {
$this->assertEquals( '<dl class="variation"><dt>size:</dt><dd>small</dd><dt>colour:</dt><dd>red</dd></dl>', $formatted_variation_attributes );
}
+ /**
+ * @testdox Should list the variation's own attributes alongside the posted "Any" attributes.
+ */
+ public function test_get_product_formatted_variation_list_merges_posted_attributes(): void {
+ $variable_product = \WC_Helper_Product::create_variation_product();
+ // The first variation only sets "size"; "colour" is left as "Any".
+ $variation_id = $variable_product->get_children()[0];
+
+ $notification = new Notification();
+ $notification->set_product_id( $variation_id );
+ $notification->set_user_email( 'test@example.com' );
+ $notification->save();
+
+ $this->assertSame( 'size: small', $notification->get_product_formatted_variation_list( true ), 'Without posted attributes only the fixed attributes should be listed' );
+
+ // Posted attributes only hold the "Any" values; "attribute_pa_stale" is not a variation attribute and must be dropped.
+ $notification->update_meta_data(
+ 'posted_attributes',
+ array(
+ 'attribute_pa_colour' => 'red',
+ 'attribute_pa_stale' => 'gone',
+ )
+ );
+ $notification->save();
+
+ $this->assertSame( 'size: small, colour: red', $notification->get_product_formatted_variation_list( true ), 'The fixed attribute should be kept and the posted "Any" attribute added' );
+ }
+
+ /**
+ * @testdox Should keep the variation's fixed attribute value when a different value was posted for it.
+ */
+ public function test_get_product_formatted_variation_list_keeps_fixed_attribute_over_posted(): void {
+ $variable_product = \WC_Helper_Product::create_variation_product();
+ // The first variation fixes "size" to "small"; "colour" is left as "Any".
+ $variation_id = $variable_product->get_children()[0];
+
+ $notification = new Notification();
+ $notification->set_product_id( $variation_id );
+ $notification->set_user_email( 'test@example.com' );
+ $notification->update_meta_data(
+ 'posted_attributes',
+ array(
+ 'attribute_pa_size' => 'large',
+ 'attribute_pa_colour' => 'red',
+ )
+ );
+ $notification->save();
+
+ $this->assertSame( 'size: small, colour: red', $notification->get_product_formatted_variation_list( true ), 'The fixed attribute should win over the posted value, while the "Any" attribute is filled in' );
+ }
+
/**
* Test the get_product_permalink method.
*/