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
 	 */