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