Commit d6760f74bc2 for woocommerce

commit d6760f74bc2d9fdf5781eefe8fea70dd5d47dbf5
Author: Chris Lilitsas <1105590+xristos3490@users.noreply.github.com>
Date:   Thu Jul 23 17:28:40 2026 +0300

    Fix: Prevent CSV-imported variations inheriting default category (#66255)

    * fix: prevent CSV-imported variations inheriting default category

    * fix: address review — preserve default_product_cat option and derive term ID from WP_Error

    * fix: address review — move variation import cleanup into finally and assert product_tag

diff --git a/plugins/woocommerce/changelog/31815-fix-variation-uncategorized-csv-import b/plugins/woocommerce/changelog/31815-fix-variation-uncategorized-csv-import
new file mode 100644
index 00000000000..3acd15fc720
--- /dev/null
+++ b/plugins/woocommerce/changelog/31815-fix-variation-uncategorized-csv-import
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Prevent product variations imported via CSV from inheriting the default "Uncategorized" product category.
diff --git a/plugins/woocommerce/includes/import/abstract-wc-product-importer.php b/plugins/woocommerce/includes/import/abstract-wc-product-importer.php
index 4846616e7cb..a30d06361ab 100644
--- a/plugins/woocommerce/includes/import/abstract-wc-product-importer.php
+++ b/plugins/woocommerce/includes/import/abstract-wc-product-importer.php
@@ -190,6 +190,15 @@ abstract class WC_Product_Importer implements WC_Importer_Interface {
 							'post_type' => 'product_variation',
 						)
 					);
+
+					// A placeholder created for this row by parse_id_field() is a
+					// simple product, so the product data store may have assigned it
+					// the default product category. Variations must not carry
+					// product_cat/product_tag terms, and WordPress does not clear
+					// them when the post type changes, so remove them here.
+					if ( $id && ! is_wp_error( $id ) ) {
+						wp_delete_object_term_relationships( $id, array( 'product_cat', 'product_tag' ) );
+					}
 				}

 				$product = wc_get_product_object( $data['type'], $id );
diff --git a/plugins/woocommerce/tests/php/includes/importer/class-wc-product-csv-importer-test.php b/plugins/woocommerce/tests/php/includes/importer/class-wc-product-csv-importer-test.php
index 631d9bc4778..fc1ba774760 100644
--- a/plugins/woocommerce/tests/php/includes/importer/class-wc-product-csv-importer-test.php
+++ b/plugins/woocommerce/tests/php/includes/importer/class-wc-product-csv-importer-test.php
@@ -256,6 +256,67 @@ class WC_Product_CSV_Importer_Test extends \WC_Unit_Test_Case {
 		wc_delete_attribute( $size_attr_id );
 	}

+	/**
+	 * @testdox Variations imported from a CSV that includes IDs do not inherit the default product category (issue #31815).
+	 */
+	public function test_imported_variations_do_not_inherit_default_product_category_31815() {
+		// Term creation during import requires the manage_product_terms capability.
+		wp_set_current_user( self::factory()->user->create( array( 'role' => 'administrator' ) ) );
+
+		// A default product category must be configured so that the simple-product
+		// placeholders created for the ID-bearing rows would otherwise be assigned it.
+		$inserted    = wp_insert_term( 'Uncategorized', 'product_cat' );
+		$default_cat = is_wp_error( $inserted )
+			? (int) $inserted->get_error_data( 'term_exists' )
+			: $inserted['term_id'];
+
+		// Preserve the previous option value so other tests reading it are unaffected.
+		$previous_default_cat = get_option( 'default_product_cat' );
+		update_option( 'default_product_cat', $default_cat );
+
+		$imported_ids = array();
+
+		try {
+			// Build the header-to-field mapping the way the admin import UI does.
+			$csv_file   = __DIR__ . '/variation-category-31815.csv';
+			$headers    = ( new WC_Product_CSV_Importer( $csv_file, array( 'parse' => false ) ) )->get_raw_keys();
+			$controller = new WC_Product_CSV_Importer_Controller();
+			$auto_map   = new ReflectionMethod( $controller, 'auto_map_columns' );
+			$auto_map->setAccessible( true );
+			$mapping = array_combine( $headers, $auto_map->invoke( $controller, $headers ) );
+
+			$importer     = new WC_Product_CSV_Importer(
+				$csv_file,
+				array(
+					'parse'   => true,
+					'mapping' => $mapping,
+				)
+			);
+			$data         = $importer->import();
+			$imported_ids = array_merge( $data['imported'], $data['imported_variations'] );
+
+			$this->assertCount( 2, $data['imported_variations'], 'Expected 2 variations to be imported.' );
+
+			foreach ( $data['imported_variations'] as $variation_id ) {
+				$variation = wc_get_product( $variation_id );
+				$this->assertInstanceOf( WC_Product_Variation::class, $variation );
+				$this->assertEmpty(
+					wp_get_object_terms( $variation_id, 'product_cat', array( 'fields' => 'ids' ) ),
+					'Imported variations must not be assigned any product category.'
+				);
+				$this->assertEmpty(
+					wp_get_object_terms( $variation_id, 'product_tag', array( 'fields' => 'ids' ) ),
+					'Imported variations must not be assigned any product tag.'
+				);
+			}
+		} finally {
+			foreach ( $imported_ids as $id ) {
+				WC_Helper_Product::delete_product( $id );
+			}
+			update_option( 'default_product_cat', $previous_default_cat );
+		}
+	}
+
 	/**
 	 * @testdox parse_float_field should respect the store's decimal separator setting (issue #38116).
 	 * @dataProvider provider_parse_float_field_decimal_separator
diff --git a/plugins/woocommerce/tests/php/includes/importer/variation-category-31815.csv b/plugins/woocommerce/tests/php/includes/importer/variation-category-31815.csv
new file mode 100644
index 00000000000..9f3f631c370
--- /dev/null
+++ b/plugins/woocommerce/tests/php/includes/importer/variation-category-31815.csv
@@ -0,0 +1,4 @@
+ID,Type,SKU,Name,Published,Categories,Regular price,Parent,Attribute 1 Name,Attribute 1 Value(s)
+90001,variable,VARBUG31815,Variation Category Bug Parent,1,Uncategorized,,,Color,"Red, Blue"
+90002,variation,,Variation Category Bug Parent - Red,1,,20,VARBUG31815,Color,Red
+90003,variation,,Variation Category Bug Parent - Blue,1,,20,VARBUG31815,Color,Blue