From 4081b3359dbdc4f29c69fe9ffaba9f141862081f Mon Sep 17 00:00:00 2001 From: Stevche Radevski Date: Thu, 1 Aug 2024 13:23:49 +0200 Subject: [PATCH] feat: Add support for sorting export headers (#8386) * feat: Add support for sorting export headers * fix: Minor fixes to import flow --- .../__fixtures__/exported-products-comma.csv | 12 ++--- .../exported-products-semicolon.csv | 12 ++--- .../admin/__fixtures__/filtered-products.csv | 4 +- .../admin/__fixtures__/prices-with-region.csv | 4 +- .../__fixtures__/product-with-categories.csv | 10 ++-- .../product/admin/product-export.spec.ts | 1 - .../product/admin/product-import.spec.ts | 28 +++++----- .../product/helpers/normalize-for-import.ts | 16 +++--- .../product/helpers/normalize-v1-import.ts | 23 ++++++-- .../src/product/steps/generate-product-csv.ts | 54 ++++++++++++++++++- packages/core/utils/src/csv/jsontocsv.ts | 5 +- 11 files changed, 121 insertions(+), 48 deletions(-) diff --git a/integration-tests/http/__tests__/product/admin/__fixtures__/exported-products-comma.csv b/integration-tests/http/__tests__/product/admin/__fixtures__/exported-products-comma.csv index 811c0ad385..2c55514fd1 100644 --- a/integration-tests/http/__tests__/product/admin/__fixtures__/exported-products-comma.csv +++ b/integration-tests/http/__tests__/product/admin/__fixtures__/exported-products-comma.csv @@ -1,6 +1,6 @@ -Product Id,Product Title,Product Subtitle,Product Status,Product External Id,Product Description,Product Handle,Product Is Giftcard,Product Discountable,Product Thumbnail,Product Collection Id,Product Type Id,Product Weight,Product Length,Product Height,Product Width,Product Hs Code,Product Origin Country,Product Mid Code,Product Material,Product Created At,Product Updated At,Product Deleted At,Product Image 1,Product Image 2,Product Tag 1,Product Tag 2,Variant Id,Variant Title,Variant Sku,Variant Barcode,Variant Ean,Variant Upc,Variant Allow Backorder,Variant Manage Inventory,Variant Hs Code,Variant Origin Country,Variant Mid Code,Variant Material,Variant Weight,Variant Length,Variant Height,Variant Width,Variant Metadata,Variant Variant Rank,Variant Product Id,Variant Created At,Variant Updated At,Variant Deleted At,Variant Price USD,Variant Price EUR,Variant Price DKK,Variant Option 1 Name,Variant Option 1 Value,Variant Option 2 Name,Variant Option 2 Value -prod_01J3CRPNVGRZ01A8GH8FQYK10Z,Base product,,draft,,"test-product-description -test line 2",base-product,false,true,test-image.png,pcol_01J3CRPNT6A0G5GG34MWHWE7QD,ptyp_01J3CRPNV39E51BGGWSKT674C5,,,,,,,,,2024-07-22T08:25:06.158Z,2024-07-22T08:25:06.158Z,,test-image.png,test-image-2.png,123,456,variant_01J3CRPNW5J6EBVVQP1TN33A58,Test variant,,,,,false,true,,,,,,,,,,0,prod_01J3CRPNVGRZ01A8GH8FQYK10Z,2024-07-22T08:25:06.182Z,2024-07-22T08:25:06.182Z,,100,45,30,size,large,color,green -prod_01J3CRPNVGRZ01A8GH8FQYK10Z,Base product,,draft,,"test-product-description -test line 2",base-product,false,true,test-image.png,pcol_01J3CRPNT6A0G5GG34MWHWE7QD,ptyp_01J3CRPNV39E51BGGWSKT674C5,,,,,,,,,2024-07-22T08:25:06.158Z,2024-07-22T08:25:06.158Z,,test-image.png,test-image-2.png,123,456,variant_01J3CRPNW6NES6EN14X93F6YYB,Test variant 2,,,,,false,true,,,,,,,,,,0,prod_01J3CRPNVGRZ01A8GH8FQYK10Z,2024-07-22T08:25:06.182Z,2024-07-22T08:25:06.182Z,,200,65,50,size,small,color,green -prod_01J3CRPNYJTCAV1QKRF6H0BY3M,Proposed product,,proposed,,test-product-description,proposed-product,false,true,test-image.png,,ptyp_01J3CRPNV39E51BGGWSKT674C5,,,,,,,,,2024-07-22T08:25:06.256Z,2024-07-22T08:25:06.256Z,,test-image.png,test-image-2.png,new-tag,,variant_01J3CRPNYZ6VZ5FVJ7WHJABV54,Test variant,,,,,false,true,,,,,,,,,,0,prod_01J3CRPNYJTCAV1QKRF6H0BY3M,2024-07-22T08:25:06.271Z,2024-07-22T08:25:06.271Z,,100,45,30,size,large,color,green \ No newline at end of file +Product Id,Product Handle,Product Title,Product Status,Product Description,Product Subtitle,Product External Id,Product Thumbnail,Product Collection Id,Product Type Id,Product Created At,Product Deleted At,Product Discountable,Product Height,Product Hs Code,Product Image 1,Product Image 2,Product Is Giftcard,Product Length,Product Material,Product Mid Code,Product Origin Country,Product Tag 1,Product Tag 2,Product Updated At,Product Weight,Product Width,Variant Id,Variant Title,Variant Sku,Variant Upc,Variant Ean,Variant Hs Code,Variant Mid Code,Variant Manage Inventory,Variant Allow Backorder,Variant Barcode,Variant Created At,Variant Deleted At,Variant Height,Variant Length,Variant Material,Variant Metadata,Variant Option 1 Name,Variant Option 1 Value,Variant Option 2 Name,Variant Option 2 Value,Variant Origin Country,Variant Price DKK,Variant Price EUR,Variant Price USD,Variant Product Id,Variant Updated At,Variant Variant Rank,Variant Weight,Variant Width +prod_01J44RRJZ3M5F63NY82434RNM5,base-product,Base product,draft,"test-product-description +test line 2",,,test-image.png,pcol_01J44RRJXM6AM3YS5PMJDMH3YF,ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD,2024-07-31T16:07:55.102Z,,true,,,test-image.png,test-image-2.png,false,,,,,123,456,2024-07-31T16:07:55.102Z,,,variant_01J44RRJZW1T9KQB6XG7Q6K61F,Test variant,,,,,,true,false,,2024-07-31T16:07:55.133Z,,,,,,size,large,color,green,,30,45,100,prod_01J44RRJZ3M5F63NY82434RNM5,2024-07-31T16:07:55.133Z,0,, +prod_01J44RRJZ3M5F63NY82434RNM5,base-product,Base product,draft,"test-product-description +test line 2",,,test-image.png,pcol_01J44RRJXM6AM3YS5PMJDMH3YF,ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD,2024-07-31T16:07:55.102Z,,true,,,test-image.png,test-image-2.png,false,,,,,123,456,2024-07-31T16:07:55.102Z,,,variant_01J44RRJZW5GNQKT1FEDACEESW,Test variant 2,,,,,,true,false,,2024-07-31T16:07:55.133Z,,,,,,size,small,color,green,,50,65,200,prod_01J44RRJZ3M5F63NY82434RNM5,2024-07-31T16:07:55.133Z,0,, +prod_01J44RRK2GJJVMQQXT67TJCV08,proposed-product,Proposed product,proposed,test-product-description,,,test-image.png,,ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD,2024-07-31T16:07:55.213Z,,true,,,test-image.png,test-image-2.png,false,,,,,new-tag,,2024-07-31T16:07:55.213Z,,,variant_01J44RRK2WYHH0RDEK8BBGP7CY,Test variant,,,,,,true,false,,2024-07-31T16:07:55.228Z,,,,,,size,large,color,green,,30,45,100,prod_01J44RRK2GJJVMQQXT67TJCV08,2024-07-31T16:07:55.228Z,0,, \ No newline at end of file diff --git a/integration-tests/http/__tests__/product/admin/__fixtures__/exported-products-semicolon.csv b/integration-tests/http/__tests__/product/admin/__fixtures__/exported-products-semicolon.csv index b8aac43e12..dd78384423 100644 --- a/integration-tests/http/__tests__/product/admin/__fixtures__/exported-products-semicolon.csv +++ b/integration-tests/http/__tests__/product/admin/__fixtures__/exported-products-semicolon.csv @@ -1,6 +1,6 @@ -Product Id;Product Title;Product Subtitle;Product Status;Product External Id;Product Description;Product Handle;Product Is Giftcard;Product Discountable;Product Thumbnail;Product Collection Id;Product Type Id;Product Weight;Product Length;Product Height;Product Width;Product Hs Code;Product Origin Country;Product Mid Code;Product Material;Product Created At;Product Updated At;Product Deleted At;Product Image 1;Product Image 2;Product Tag 1;Product Tag 2;Variant Id;Variant Title;Variant Sku;Variant Barcode;Variant Ean;Variant Upc;Variant Allow Backorder;Variant Manage Inventory;Variant Hs Code;Variant Origin Country;Variant Mid Code;Variant Material;Variant Weight;Variant Length;Variant Height;Variant Width;Variant Metadata;Variant Variant Rank;Variant Product Id;Variant Created At;Variant Updated At;Variant Deleted At;Variant Price USD;Variant Price EUR;Variant Price DKK;Variant Option 1 Name;Variant Option 1 Value;Variant Option 2 Name;Variant Option 2 Value -prod_01J3CRPNVGRZ01A8GH8FQYK10Z;Base product;;draft;;"test-product-description -test line 2";base-product;false;true;test-image.png;pcol_01J3CRPNT6A0G5GG34MWHWE7QD;ptyp_01J3CRPNV39E51BGGWSKT674C5;;;;;;;;;2024-07-22T08:25:06.158Z;2024-07-22T08:25:06.158Z;;test-image.png;test-image-2.png;123;456;variant_01J3CRPNW5J6EBVVQP1TN33A58;Test variant;;;;;false;true;;;;;;;;;;0;prod_01J3CRPNVGRZ01A8GH8FQYK10Z;2024-07-22T08:25:06.182Z;2024-07-22T08:25:06.182Z;;100;45;30;size;large;color;green -prod_01J3CRPNVGRZ01A8GH8FQYK10Z;Base product;;draft;;"test-product-description -test line 2";base-product;false;true;test-image.png;pcol_01J3CRPNT6A0G5GG34MWHWE7QD;ptyp_01J3CRPNV39E51BGGWSKT674C5;;;;;;;;;2024-07-22T08:25:06.158Z;2024-07-22T08:25:06.158Z;;test-image.png;test-image-2.png;123;456;variant_01J3CRPNW6NES6EN14X93F6YYB;Test variant 2;;;;;false;true;;;;;;;;;;0;prod_01J3CRPNVGRZ01A8GH8FQYK10Z;2024-07-22T08:25:06.182Z;2024-07-22T08:25:06.182Z;;200;65;50;size;small;color;green -prod_01J3CRPNYJTCAV1QKRF6H0BY3M;Proposed product;;proposed;;test-product-description;proposed-product;false;true;test-image.png;;ptyp_01J3CRPNV39E51BGGWSKT674C5;;;;;;;;;2024-07-22T08:25:06.256Z;2024-07-22T08:25:06.256Z;;test-image.png;test-image-2.png;new-tag;;variant_01J3CRPNYZ6VZ5FVJ7WHJABV54;Test variant;;;;;false;true;;;;;;;;;;0;prod_01J3CRPNYJTCAV1QKRF6H0BY3M;2024-07-22T08:25:06.271Z;2024-07-22T08:25:06.271Z;;100;45;30;size;large;color;green \ No newline at end of file +Product Id;Product Handle;Product Title;Product Status;Product Description;Product Subtitle;Product External Id;Product Thumbnail;Product Collection Id;Product Type Id;Product Created At;Product Deleted At;Product Discountable;Product Height;Product Hs Code;Product Image 1;Product Image 2;Product Is Giftcard;Product Length;Product Material;Product Mid Code;Product Origin Country;Product Tag 1;Product Tag 2;Product Updated At;Product Weight;Product Width;Variant Id;Variant Title;Variant Sku;Variant Upc;Variant Ean;Variant Hs Code;Variant Mid Code;Variant Manage Inventory;Variant Allow Backorder;Variant Barcode;Variant Created At;Variant Deleted At;Variant Height;Variant Length;Variant Material;Variant Metadata;Variant Option 1 Name;Variant Option 1 Value;Variant Option 2 Name;Variant Option 2 Value;Variant Origin Country;Variant Price DKK;Variant Price EUR;Variant Price USD;Variant Product Id;Variant Updated At;Variant Variant Rank;Variant Weight;Variant Width +prod_01J44RRJZ3M5F63NY82434RNM5;base-product;Base product;draft;"test-product-description +test line 2";;;test-image.png;pcol_01J44RRJXM6AM3YS5PMJDMH3YF;ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD;2024-07-31T16:07:55.102Z;;true;;;test-image.png;test-image-2.png;false;;;;;123;456;2024-07-31T16:07:55.102Z;;;variant_01J44RRJZW1T9KQB6XG7Q6K61F;Test variant;;;;;;true;false;;2024-07-31T16:07:55.133Z;;;;;;size;large;color;green;;30;45;100;prod_01J44RRJZ3M5F63NY82434RNM5;2024-07-31T16:07:55.133Z;0;; +prod_01J44RRJZ3M5F63NY82434RNM5;base-product;Base product;draft;"test-product-description +test line 2";;;test-image.png;pcol_01J44RRJXM6AM3YS5PMJDMH3YF;ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD;2024-07-31T16:07:55.102Z;;true;;;test-image.png;test-image-2.png;false;;;;;123;456;2024-07-31T16:07:55.102Z;;;variant_01J44RRJZW5GNQKT1FEDACEESW;Test variant 2;;;;;;true;false;;2024-07-31T16:07:55.133Z;;;;;;size;small;color;green;;50;65;200;prod_01J44RRJZ3M5F63NY82434RNM5;2024-07-31T16:07:55.133Z;0;; +prod_01J44RRK2GJJVMQQXT67TJCV08;proposed-product;Proposed product;proposed;test-product-description;;;test-image.png;;ptyp_01J44RRJYAFEBZ2EY1KE1JM3XD;2024-07-31T16:07:55.213Z;;true;;;test-image.png;test-image-2.png;false;;;;;new-tag;;2024-07-31T16:07:55.213Z;;;variant_01J44RRK2WYHH0RDEK8BBGP7CY;Test variant;;;;;;true;false;;2024-07-31T16:07:55.228Z;;;;;;size;large;color;green;;30;45;100;prod_01J44RRK2GJJVMQQXT67TJCV08;2024-07-31T16:07:55.228Z;0;; \ No newline at end of file diff --git a/integration-tests/http/__tests__/product/admin/__fixtures__/filtered-products.csv b/integration-tests/http/__tests__/product/admin/__fixtures__/filtered-products.csv index 7f9808b17c..1b40f54ef2 100644 --- a/integration-tests/http/__tests__/product/admin/__fixtures__/filtered-products.csv +++ b/integration-tests/http/__tests__/product/admin/__fixtures__/filtered-products.csv @@ -1,2 +1,2 @@ -Product Id,Product Title,Product Subtitle,Product Status,Product External Id,Product Description,Product Handle,Product Is Giftcard,Product Discountable,Product Thumbnail,Product Collection Id,Product Type Id,Product Weight,Product Length,Product Height,Product Width,Product Hs Code,Product Origin Country,Product Mid Code,Product Material,Product Created At,Product Updated At,Product Deleted At,Product Image 1,Product Image 2,Product Tag 1,Variant Id,Variant Title,Variant Sku,Variant Barcode,Variant Ean,Variant Upc,Variant Allow Backorder,Variant Manage Inventory,Variant Hs Code,Variant Origin Country,Variant Mid Code,Variant Material,Variant Weight,Variant Length,Variant Height,Variant Width,Variant Metadata,Variant Variant Rank,Variant Product Id,Variant Created At,Variant Updated At,Variant Deleted At,Variant Price USD,Variant Price EUR,Variant Price DKK,Variant Option 1 Name,Variant Option 1 Value,Variant Option 2 Name,Variant Option 2 Value -prod_01J3CSN791SN1RN7X155Z8S9CN,Proposed product,,proposed,,test-product-description,proposed-product,false,true,test-image.png,,ptyp_01J3CSN76GCRSCDV9V489B5FWQ,,,,,,,,,2024-07-22T08:41:47.040Z,2024-07-22T08:41:47.040Z,,test-image.png,test-image-2.png,new-tag,variant_01J3CSN79CQ2ND94SRJSXMEMNH,Test variant,,,,,false,true,,,,,,,,,,0,prod_01J3CSN791SN1RN7X155Z8S9CN,2024-07-22T08:41:47.053Z,2024-07-22T08:41:47.053Z,,100,45,30,size,large,color,green \ No newline at end of file +Product Id,Product Handle,Product Title,Product Status,Product Description,Product Subtitle,Product External Id,Product Thumbnail,Product Collection Id,Product Type Id,Product Created At,Product Deleted At,Product Discountable,Product Height,Product Hs Code,Product Image 1,Product Image 2,Product Is Giftcard,Product Length,Product Material,Product Mid Code,Product Origin Country,Product Tag 1,Product Updated At,Product Weight,Product Width,Variant Id,Variant Title,Variant Sku,Variant Upc,Variant Ean,Variant Hs Code,Variant Mid Code,Variant Manage Inventory,Variant Allow Backorder,Variant Barcode,Variant Created At,Variant Deleted At,Variant Height,Variant Length,Variant Material,Variant Metadata,Variant Option 1 Name,Variant Option 1 Value,Variant Option 2 Name,Variant Option 2 Value,Variant Origin Country,Variant Price DKK,Variant Price EUR,Variant Price USD,Variant Product Id,Variant Updated At,Variant Variant Rank,Variant Weight,Variant Width +prod_01J44RRMJ7H2K9JAD9AQHA724B,proposed-product,Proposed product,proposed,test-product-description,,,test-image.png,,ptyp_01J44RRMF4J9SG6F7FG3B16A7F,2024-07-31T16:07:56.741Z,,true,,,test-image.png,test-image-2.png,false,,,,,new-tag,2024-07-31T16:07:56.741Z,,,variant_01J44RRMJJT9HSEGFJHTY2SX2P,Test variant,,,,,,true,false,,2024-07-31T16:07:56.754Z,,,,,,size,large,color,green,,30,45,100,prod_01J44RRMJ7H2K9JAD9AQHA724B,2024-07-31T16:07:56.754Z,0,, \ No newline at end of file diff --git a/integration-tests/http/__tests__/product/admin/__fixtures__/prices-with-region.csv b/integration-tests/http/__tests__/product/admin/__fixtures__/prices-with-region.csv index cd3c49388a..591f8a7d99 100644 --- a/integration-tests/http/__tests__/product/admin/__fixtures__/prices-with-region.csv +++ b/integration-tests/http/__tests__/product/admin/__fixtures__/prices-with-region.csv @@ -1,2 +1,2 @@ -Product Id,Product Title,Product Subtitle,Product Status,Product External Id,Product Description,Product Handle,Product Is Giftcard,Product Discountable,Product Thumbnail,Product Collection Id,Product Type Id,Product Weight,Product Length,Product Height,Product Width,Product Hs Code,Product Origin Country,Product Mid Code,Product Material,Product Created At,Product Updated At,Product Deleted At,Product Image 1,Product Image 2,Product Tag 1,Product Tag 2,Variant Id,Variant Title,Variant Sku,Variant Barcode,Variant Ean,Variant Upc,Variant Allow Backorder,Variant Manage Inventory,Variant Hs Code,Variant Origin Country,Variant Mid Code,Variant Material,Variant Weight,Variant Length,Variant Height,Variant Width,Variant Metadata,Variant Variant Rank,Variant Product Id,Variant Created At,Variant Updated At,Variant Deleted At,Variant Price USD,Variant Price Test Region [USD],Variant Option 1 Name,Variant Option 1 Value,Variant Option 2 Name,Variant Option 2 Value -prod_01J44BH2MFG9MM3EPEQ1SEBDCV,Product with prices,,draft,,test-product-description,product-with-prices,false,true,test-image.png,,,,,,,,,,,2024-07-31T12:16:37.517Z,2024-07-31T12:16:37.517Z,,test-image.png,test-image-2.png,123,456,variant_01J44BH2MXDYM80A5VCAMX17XH,Test variant,,,,,false,true,,,,,,,,,,0,prod_01J44BH2MFG9MM3EPEQ1SEBDCV,2024-07-31T12:16:37.533Z,2024-07-31T12:16:37.533Z,,100,45,size,large,color,green \ No newline at end of file +Product Id,Product Handle,Product Title,Product Status,Product Description,Product Subtitle,Product External Id,Product Thumbnail,Product Collection Id,Product Type Id,Product Created At,Product Deleted At,Product Discountable,Product Height,Product Hs Code,Product Image 1,Product Image 2,Product Is Giftcard,Product Length,Product Material,Product Mid Code,Product Origin Country,Product Tag 1,Product Tag 2,Product Updated At,Product Weight,Product Width,Variant Id,Variant Title,Variant Sku,Variant Upc,Variant Ean,Variant Hs Code,Variant Mid Code,Variant Manage Inventory,Variant Allow Backorder,Variant Barcode,Variant Created At,Variant Deleted At,Variant Height,Variant Length,Variant Material,Variant Metadata,Variant Option 1 Name,Variant Option 1 Value,Variant Option 2 Name,Variant Option 2 Value,Variant Origin Country,Variant Price Test Region [USD],Variant Price USD,Variant Product Id,Variant Updated At,Variant Variant Rank,Variant Weight,Variant Width +prod_01J44RRM579P1EY2ZNQVGN1THT,product-with-prices,Product with prices,draft,test-product-description,,,test-image.png,,,2024-07-31T16:07:56.325Z,,true,,,test-image.png,test-image-2.png,false,,,,,123,456,2024-07-31T16:07:56.325Z,,,variant_01J44RRM5J1569XJD39DM3PNFN,Test variant,,,,,,true,false,,2024-07-31T16:07:56.338Z,,,,,,size,large,color,green,,45,100,prod_01J44RRM579P1EY2ZNQVGN1THT,2024-07-31T16:07:56.338Z,0,, \ No newline at end of file diff --git a/integration-tests/http/__tests__/product/admin/__fixtures__/product-with-categories.csv b/integration-tests/http/__tests__/product/admin/__fixtures__/product-with-categories.csv index bc1484ce35..87499b7980 100644 --- a/integration-tests/http/__tests__/product/admin/__fixtures__/product-with-categories.csv +++ b/integration-tests/http/__tests__/product/admin/__fixtures__/product-with-categories.csv @@ -1,5 +1,5 @@ -Product Id,Product Title,Product Subtitle,Product Status,Product External Id,Product Description,Product Handle,Product Is Giftcard,Product Discountable,Product Thumbnail,Product Collection Id,Product Type Id,Product Weight,Product Length,Product Height,Product Width,Product Hs Code,Product Origin Country,Product Mid Code,Product Material,Product Created At,Product Updated At,Product Deleted At,Product Image 1,Product Image 2,Product Tag 1,Product Tag 2,Product Category 1,Variant Id,Variant Title,Variant Sku,Variant Barcode,Variant Ean,Variant Upc,Variant Allow Backorder,Variant Manage Inventory,Variant Hs Code,Variant Origin Country,Variant Mid Code,Variant Material,Variant Weight,Variant Length,Variant Height,Variant Width,Variant Metadata,Variant Variant Rank,Variant Product Id,Variant Created At,Variant Updated At,Variant Deleted At,Variant Price USD,Variant Price EUR,Variant Price DKK,Variant Option 1 Name,Variant Option 1 Value,Variant Option 2 Name,Variant Option 2 Value -prod_01J44E9HC8Y3HC7S8A7CX0W7N5,Base product,,draft,,"test-product-description -test line 2",base-product,false,true,test-image.png,pcol_01J44E9HAPFW5YMAJCNXT50KAS,ptyp_01J44E9HBBQ9QYY121WWWZ4QZR,,,,,,,,,2024-07-31T13:04:56.196Z,2024-07-31T13:04:56.196Z,,test-image.png,test-image-2.png,123,456,pcat_01J44E9HBRD8QT7Z1GW6R6FVCT,variant_01J44E9HCTS0E29TX7MEKYMQ4R,Test variant,,,,,false,true,,,,,,,,,,0,prod_01J44E9HC8Y3HC7S8A7CX0W7N5,2024-07-31T13:04:56.218Z,2024-07-31T13:04:56.218Z,,100,45,30,size,large,color,green -prod_01J44E9HC8Y3HC7S8A7CX0W7N5,Base product,,draft,,"test-product-description -test line 2",base-product,false,true,test-image.png,pcol_01J44E9HAPFW5YMAJCNXT50KAS,ptyp_01J44E9HBBQ9QYY121WWWZ4QZR,,,,,,,,,2024-07-31T13:04:56.196Z,2024-07-31T13:04:56.196Z,,test-image.png,test-image-2.png,123,456,pcat_01J44E9HBRD8QT7Z1GW6R6FVCT,variant_01J44E9HCTCY8821XHC8K0X7QW,Test variant 2,,,,,false,true,,,,,,,,,,0,prod_01J44E9HC8Y3HC7S8A7CX0W7N5,2024-07-31T13:04:56.218Z,2024-07-31T13:04:56.218Z,,200,65,50,size,small,color,green \ No newline at end of file +Product Id,Product Handle,Product Title,Product Status,Product Description,Product Subtitle,Product External Id,Product Thumbnail,Product Collection Id,Product Type Id,Product Category 1,Product Created At,Product Deleted At,Product Discountable,Product Height,Product Hs Code,Product Image 1,Product Image 2,Product Is Giftcard,Product Length,Product Material,Product Mid Code,Product Origin Country,Product Tag 1,Product Tag 2,Product Updated At,Product Weight,Product Width,Variant Id,Variant Title,Variant Sku,Variant Upc,Variant Ean,Variant Hs Code,Variant Mid Code,Variant Manage Inventory,Variant Allow Backorder,Variant Barcode,Variant Created At,Variant Deleted At,Variant Height,Variant Length,Variant Material,Variant Metadata,Variant Option 1 Name,Variant Option 1 Value,Variant Option 2 Name,Variant Option 2 Value,Variant Origin Country,Variant Price DKK,Variant Price EUR,Variant Price USD,Variant Product Id,Variant Updated At,Variant Variant Rank,Variant Weight,Variant Width +prod_01J44RRKH4HH2SANJ0S05YM853,base-product,Base product,draft,"test-product-description +test line 2",,,test-image.png,pcol_01J44RRKG4P1RZ3CKAMBS760TD,ptyp_01J44RRKGHPQMJ1CRMW7MEN3P1,pcat_01J44RRKGS2N86A8V37ZJD877K,2024-07-31T16:07:55.681Z,,true,,,test-image.png,test-image-2.png,false,,,,,123,456,2024-07-31T16:07:55.681Z,,,variant_01J44RRKHRQ7WJ902X4936GEK7,Test variant,,,,,,true,false,,2024-07-31T16:07:55.704Z,,,,,,size,large,color,green,,30,45,100,prod_01J44RRKH4HH2SANJ0S05YM853,2024-07-31T16:07:55.704Z,0,, +prod_01J44RRKH4HH2SANJ0S05YM853,base-product,Base product,draft,"test-product-description +test line 2",,,test-image.png,pcol_01J44RRKG4P1RZ3CKAMBS760TD,ptyp_01J44RRKGHPQMJ1CRMW7MEN3P1,pcat_01J44RRKGS2N86A8V37ZJD877K,2024-07-31T16:07:55.681Z,,true,,,test-image.png,test-image-2.png,false,,,,,123,456,2024-07-31T16:07:55.681Z,,,variant_01J44RRKHRAYY7Q7NTXNNMDA1S,Test variant 2,,,,,,true,false,,2024-07-31T16:07:55.704Z,,,,,,size,small,color,green,,50,65,200,prod_01J44RRKH4HH2SANJ0S05YM853,2024-07-31T16:07:55.704Z,0,, \ No newline at end of file diff --git a/integration-tests/http/__tests__/product/admin/product-export.spec.ts b/integration-tests/http/__tests__/product/admin/product-export.spec.ts index 3d20e18c91..bfcb0d9137 100644 --- a/integration-tests/http/__tests__/product/admin/product-export.spec.ts +++ b/integration-tests/http/__tests__/product/admin/product-export.spec.ts @@ -14,7 +14,6 @@ jest.setTimeout(50000) const compareCSVs = async (filePath, expectedFilePath) => { const asLocalPath = filePath.replace("http://localhost:9000", process.cwd()) let fileContent = await fs.readFile(asLocalPath, { encoding: "utf-8" }) - let fixturesContent = await fs.readFile(expectedFilePath, { encoding: "utf-8", }) diff --git a/integration-tests/http/__tests__/product/admin/product-import.spec.ts b/integration-tests/http/__tests__/product/admin/product-import.spec.ts index 14d90a1b1b..da9b5a35e5 100644 --- a/integration-tests/http/__tests__/product/admin/product-import.spec.ts +++ b/integration-tests/http/__tests__/product/admin/product-import.spec.ts @@ -112,11 +112,11 @@ medusaIntegrationTestRunner({ ) fileContent = fileContent.replace( - /prod_01J3CRPNVGRZ01A8GH8FQYK10Z/g, + /prod_01J44RRJZ3M5F63NY82434RNM5/g, baseProduct.id ) fileContent = fileContent.replace( - /variant_01J3CRPNW5J6EBVVQP1TN33A58/g, + /variant_01J44RRJZW1T9KQB6XG7Q6K61F/g, baseProduct.variants[0].id ) fileContent = fileContent.replace(/pcol_\w*\d*/g, baseCollection.id) @@ -225,16 +225,16 @@ medusaIntegrationTestRunner({ manage_inventory: true, prices: [ expect.objectContaining({ - currency_code: "usd", - amount: 100, + currency_code: "dkk", + amount: 30, }), expect.objectContaining({ currency_code: "eur", amount: 45, }), expect.objectContaining({ - currency_code: "dkk", - amount: 30, + currency_code: "usd", + amount: 100, }), ], options: [ @@ -252,16 +252,16 @@ medusaIntegrationTestRunner({ manage_inventory: true, prices: [ expect.objectContaining({ - currency_code: "usd", - amount: 200, + currency_code: "dkk", + amount: 50, }), expect.objectContaining({ currency_code: "eur", amount: 65, }), expect.objectContaining({ - currency_code: "dkk", - amount: 50, + currency_code: "usd", + amount: 200, }), ], options: [ @@ -326,16 +326,16 @@ medusaIntegrationTestRunner({ manage_inventory: true, prices: [ expect.objectContaining({ - currency_code: "usd", - amount: 100, + currency_code: "dkk", + amount: 30, }), expect.objectContaining({ currency_code: "eur", amount: 45, }), expect.objectContaining({ - currency_code: "dkk", - amount: 30, + currency_code: "usd", + amount: 100, }), ], options: [ diff --git a/packages/core/core-flows/src/product/helpers/normalize-for-import.ts b/packages/core/core-flows/src/product/helpers/normalize-for-import.ts index efbe98715e..5bedbe1f07 100644 --- a/packages/core/core-flows/src/product/helpers/normalize-for-import.ts +++ b/packages/core/core-flows/src/product/helpers/normalize-for-import.ts @@ -236,20 +236,24 @@ const normalizeVariantForImport = ( } const getNormalizedValue = (key: string, value: any): any => { - let res = stringFields.some((field) => key.startsWith(field)) - ? value?.toString() - : value + if (value === "\r") { + return "" + } + + if (stringFields.some((field) => key.startsWith(field))) { + return value?.toString() + } if (booleanFields.some((field) => key.startsWith(field))) { if (value === "TRUE") { - res = true + return true } if (value === "FALSE") { - res = false + return false } } - return res + return value } const snakecaseKey = (key: string): string => { diff --git a/packages/core/core-flows/src/product/helpers/normalize-v1-import.ts b/packages/core/core-flows/src/product/helpers/normalize-v1-import.ts index 7cb43ca6c1..9164c3a99c 100644 --- a/packages/core/core-flows/src/product/helpers/normalize-v1-import.ts +++ b/packages/core/core-flows/src/product/helpers/normalize-v1-import.ts @@ -106,22 +106,28 @@ export const normalizeV1Products = ( Object.entries(finalRes).forEach(([key, value]) => { if (key.startsWith("Price")) { delete finalRes[key] - finalRes[`Variant ${key}`] = value + if (value) { + finalRes[`Variant ${key}`] = value + } } if (key.startsWith("Option")) { delete finalRes[key] - finalRes[`Variant ${key}`] = value + if (value) { + finalRes[`Variant ${key}`] = value + } } if (key.startsWith("Image")) { delete finalRes[key] - finalRes[`Product Image ${key.split(" ")[1]}`] = value + if (value) { + finalRes[`Product Image ${key.split(" ")[1]}`] = value + } } if (key.startsWith("Sales Channel")) { delete finalRes[key] - if (key.endsWith("Id")) { + if (key.endsWith("Id") && value) { if (!salesChannelsMap.has(value)) { throw new MedusaError( MedusaError.Types.INVALID_DATA, @@ -134,6 +140,15 @@ export const normalizeV1Products = ( } } + if ( + key.startsWith("Product Category") && + (key.endsWith("Handle") || + key.endsWith("Name") || + key.endsWith("Description")) + ) { + delete finalRes[key] + } + // Note: Product categories from v1 are not imported to v2 }) diff --git a/packages/core/core-flows/src/product/steps/generate-product-csv.ts b/packages/core/core-flows/src/product/steps/generate-product-csv.ts index 0e5c3bcd69..ad5078fbe6 100644 --- a/packages/core/core-flows/src/product/steps/generate-product-csv.ts +++ b/packages/core/core-flows/src/product/steps/generate-product-csv.ts @@ -7,6 +7,56 @@ import { ModuleRegistrationName, convertJsonToCsv } from "@medusajs/utils" import { StepResponse, createStep } from "@medusajs/workflows-sdk" import { normalizeForExport } from "../helpers/normalize-for-export" +const prodColumnPositions = new Map([ + ["Product Id", 0], + ["Product Handle", 1], + ["Product Title", 2], + ["Product Status", 3], + ["Product Description", 4], + ["Product Subtitle", 5], + ["Product External Id", 6], + ["Product Thumbnail", 7], + ["Product Collection Id", 8], + ["Product Type Id", 9], +]) + +const variantColumnPositions = new Map([ + ["Variant Id", 0], + ["Variant Title", 1], + ["Variant Sku", 3], + ["Variant Upc", 4], + ["Variant Ean", 5], + ["Variant Hs Code", 6], + ["Variant Mid Code", 7], + ["Variant Manage Inventory", 8], + ["Variant Allow Backorder", 9], +]) + +const comparator = (a: string, b: string, columnMap: Map) => { + if (columnMap.has(a) && columnMap.has(b)) { + return columnMap.get(a)! - columnMap.get(b)! + } + if (columnMap.has(a)) { + return -1 + } + if (columnMap.has(b)) { + return 1 + } + return a.localeCompare(b) +} + +const csvSortFunction = (a: string, b: string) => { + if (a.startsWith("Product") && b.startsWith("Product")) { + return comparator(a, b, prodColumnPositions) + } + + if (a.startsWith("Variant") && b.startsWith("Variant")) { + return comparator(a, b, variantColumnPositions) + } + + return a.localeCompare(b) +} + export const generateProductCsvStepId = "generate-product-csv" export const generateProductCsvStep = createStep( generateProductCsvStepId, @@ -21,7 +71,9 @@ export const generateProductCsvStep = createStep( ) const normalizedData = normalizeForExport(products, { regions }) - const csvContent = convertJsonToCsv(normalizedData) + const csvContent = convertJsonToCsv(normalizedData, { + sortHeader: csvSortFunction, + }) const fileModule: IFileModuleService = container.resolve( ModuleRegistrationName.FILE diff --git a/packages/core/utils/src/csv/jsontocsv.ts b/packages/core/utils/src/csv/jsontocsv.ts index dbcd5318ef..56712fccdc 100644 --- a/packages/core/utils/src/csv/jsontocsv.ts +++ b/packages/core/utils/src/csv/jsontocsv.ts @@ -1,6 +1,8 @@ import { json2csv } from "json-2-csv" -export interface ConvertJsonToCsvOptions {} +export interface ConvertJsonToCsvOptions { + sortHeader?: boolean | ((aKey: string, bKey: string) => number) +} export const convertJsonToCsv = ( data: T[], @@ -8,6 +10,7 @@ export const convertJsonToCsv = ( ) => { return json2csv(data, { prependHeader: true, + sortHeader: options?.sortHeader ?? false, arrayIndexesAsKeys: true, expandNestedObjects: true, expandArrayObjects: true,