Commit 9b7a70cf0dd for woocommerce
commit 9b7a70cf0ddf2b67677dd0127a6f211ee86fa456
Author: Lucio Giannotta <lucio.giannotta@a8c.com>
Date: Fri Jun 19 20:26:25 2026 +0200
Fix variation creation to respect custom attribute term order (#65501)
When generating all variations for a variable product, the combinations
were built from attribute options read via the cached get_the_terms()
path (wc_get_object_terms in read_attributes), which returns terms
alphabetically and ignores the attribute's configured custom
(menu_order) order. As a result "Create All Variations" created
variations alphabetically even when the attribute used custom ordering.
Build the combinations for taxonomy-backed variation attributes from
wc_get_product_terms() -- the same order-aware source the storefront
already uses -- so generated variations follow the attribute's configured
order. Local (non-taxonomy) attributes keep their existing behaviour.
Adds a regression test that fails on trunk and passes with the fix.
Closes #63757
diff --git a/plugins/woocommerce/changelog/fix-63757-variation-custom-order b/plugins/woocommerce/changelog/fix-63757-variation-custom-order
new file mode 100644
index 00000000000..25a5f9319bb
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-63757-variation-custom-order
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Respect the attribute's custom term order when creating all product variations.
diff --git a/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php b/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php
index 5de82de01b6..de9b861600d 100644
--- a/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php
+++ b/plugins/woocommerce/includes/data-stores/class-wc-product-data-store-cpt.php
@@ -1562,7 +1562,19 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
return $count;
}
- $attributes = wc_list_pluck( array_filter( $product->get_attributes(), 'wc_attributes_array_filter_variation' ), 'get_slugs' );
+ $variation_attributes = array_filter( $product->get_attributes(), 'wc_attributes_array_filter_variation' );
+ $attributes = array();
+
+ foreach ( $variation_attributes as $attribute_key => $attribute ) {
+ if ( $attribute->is_taxonomy() && taxonomy_exists( $attribute->get_name() ) ) {
+ // Respect the attribute's configured term order (e.g. custom "menu_order") instead of
+ // the alphabetical order returned from the cached object terms. See get_slugs(), which
+ // preserves the order of the options read in read_attributes() via wc_get_object_terms().
+ $attributes[ $attribute_key ] = wc_get_product_terms( $product->get_id(), $attribute->get_name(), array( 'fields' => 'slugs' ) );
+ } else {
+ $attributes[ $attribute_key ] = $attribute->get_slugs();
+ }
+ }
if ( empty( $attributes ) ) {
return $count;
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/product/data-store.php b/plugins/woocommerce/tests/legacy/unit-tests/product/data-store.php
index 722fe5ebf41..a139f73d1d4 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/product/data-store.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/product/data-store.php
@@ -1054,6 +1054,48 @@ class WC_Tests_Product_Data_Store extends WC_Unit_Test_Case {
$this->assertEquals( 0, $count );
}
+ /**
+ * @testdox Creating all variations follows the attribute's custom (menu_order) term order, not alphabetical order.
+ */
+ public function test_create_all_product_variations_respects_custom_attribute_order() {
+ // Custom order is XS, S, M, L; alphabetical order by term name would be L, M, S, XS.
+ $attribute_data = WC_Helper_Product::create_attribute( 'size', array( 'XS', 'S', 'M', 'L' ) );
+ $term_ids = $attribute_data['term_ids'];
+ foreach ( $term_ids as $index => $term_id ) {
+ update_term_meta( $term_id, 'order', $index + 1 );
+ }
+
+ $attribute = new WC_Product_Attribute();
+ $attribute->set_id( $attribute_data['attribute_id'] );
+ $attribute->set_name( $attribute_data['attribute_taxonomy'] );
+ $attribute->set_options( $term_ids );
+ $attribute->set_visible( true );
+ $attribute->set_variation( true );
+
+ $product = new WC_Product_Variable();
+ $product->set_name( 'Custom Order Variable Product' );
+ $product->set_attributes( array( $attribute ) );
+ $product_id = $product->save();
+
+ $data_store = WC_Data_Store::load( 'product' );
+ $data_store->create_all_product_variations( wc_get_product( $product_id ) );
+ $data_store->sort_all_product_variations( $product_id );
+
+ $created_order = array();
+ foreach ( wc_get_product( $product_id )->get_children() as $variation_id ) {
+ $variation = wc_get_product( $variation_id );
+ $created_order[] = $variation->get_attributes()['pa_size'];
+ }
+
+ $this->assertEquals(
+ array( 'xs', 's', 'm', 'l' ),
+ $created_order,
+ 'Variations should be created in the attribute custom (menu_order) order, not alphabetical order.'
+ );
+
+ WC_Helper_Product::delete_attribute( $attribute_data['attribute_id'] );
+ }
+
/**
* Test WC_Product_Data_Store_CPT::create_all_product_variations
*/