P
Portaim
Docs menu

WooCommerce conversion

What Portaim does with a WooCommerce product export, column by column, when it converts the file to Shopify's layout on upload, and what does not carry over.

What is recognised

The CSV produced by WooCommerce’s built-in exporter (Products, then Export), with any column selection, in English. A file is treated as a WooCommerce export when it has the columns Type, SKU, Name, Published and Regular price, plus at least two more that only WooCommerce writes, such as Visibility in catalog, In stock?, Tax status, Parent or Attribute 1 name.

Not recognised: exports from third-party plugins (WP All Export, Store Exporter Deluxe) and exports with translated headers. Those files still import; they go through the ordinary mapping step like any supplier file. A WordPress XML export is not one of the accepted file types.

The conversion is deterministic. The same file always produces the same result, and the uploaded file itself is never modified.

Row types

WooCommerce typeResult
simpleOne product with one default variant.
variableThe product shell: title, description, vendor, type, tags, status and gallery. Its own price is ignored; its SKU links the variations and can supply the handle, and its stock is used only where a variation’s Stock says parent.
variationOne variant of its parent: options, SKU, price, stock, weight, tax, shipping and its own image.
variable with no variations in the fileSkipped, with a notice.
groupedSkipped. The member products are ordinary rows and import on their own.
externalSkipped.
virtual flagVariant does not require shipping.
downloadable flagProduct imports; the download files do not.

Variations are linked to their parent by the Parent column (id:123 or a SKU). When the column is missing or its cell is blank, a variation attaches to the nearest variable row above it, which is how WooCommerce writes the file.

Handle

WooCommerce’s export has no URL slug, so the handle is made from a Slug column when one exists, else from the product name, else from the SKU: lowercase, ASCII, hyphens, cut at 255 characters. Two different products that produce the same handle get -2, -3 appended; products are never merged because they share a name. Original WooCommerce URLs are not preserved unless a slug column is present.

Options

Attributes become options only when they vary between a product’s variations:

  • an attribute with the same value on every variation becomes a tag (Material: Cotton), not an option;
  • attributes listed on the parent but used by no variation become tags too;
  • a variation without a value for an axis gets the parent’s default, else the first value listed;
  • more than three varying attributes: the first two stay as Option 1 and 2, the rest are combined into Option 3 (Degree, Color = 20°, Black), so every variant stays purchasable;
  • an option name containing / is rewritten with , , because Shopify’s importer rejects it.

Two variations with the same option values are both kept and flagged in the Fix step, where you resolve them.

Column by column

Product fields

WooCommerceShopifyRule
NameTitleTrimmed.
Short description + DescriptionBody (HTML)Short description first. WordPress block markers and shortcodes are removed; the rest of the HTML passes through.
BrandsVendorFirst brand listed.
CategoriesTypeThe last segment of the first category: Home > Audio → Audio.
CategoriesTagsEvery segment of every category.
TagsTagsAppended after the category tags.
Published, Visibility in catalogStatus, Published1 or true → active and published. 0, -1, 2 → draft and unpublished. hidden visibility → unpublished.
ImagesImage Src, Image PositionIn the listed order, duplicates removed, up to 250.

Variant fields

WooCommerceShopifyRule
SKUVariant SKUTrimmed; may be empty.
Regular price, Sale price + sale datesVariant Price, Compare At PriceAn active sale: price = sale, compare-at = regular. No sale or an expired one: price = regular. Sales scheduled for the future are ignored, with a notice.
Stock, In stock?, Backorders allowed?Inventory Tracker, Qty, PolicyA numeric stock is tracked; blank stock stays untracked. Backorders allowed → policy continue.
Tax statusVariant Taxabletaxable → true; none, shipping → false.
Weight (kg) and similarVariant Grams, Weight UnitConverted to grams; the unit is read from the header.
Type flag virtualRequires Shippingfalse.
Images on a variationVariant ImageThe first URL.

Not carried over

ID, Is featured?, Position, Sold individually?, Low stock amount, Allow customer reviews?, Purchase note, Shipping class, Tax class, dimensions, upsells and cross-sells, grouped products, external URL and button text, download settings, Attribute N visible and global, and every Meta: column including SEO plugin fields. Product category (Shopify’s taxonomy), SEO title and description, barcodes and cost are not produced because WooCommerce’s export has no matching columns; map or add them in Portaim if you need them.

Inside Portaim

  • The row quota is charged on the rows in the uploaded file, not the converted rows.
  • The converted columns are Shopify’s own, so they are mapped one to one by the Shopify preset, with no model involved. A saved mapping for the same layout takes precedence.
  • One image host at a time is probed with a plain request. A host that answers with an error, typically a 403 from hotlink protection, produces a warning, because Shopify would fail to download every image from it while reporting the import as successful.
  • The conversion card on the mapping page lists what was skipped, changed and left for the Fix step, grouped by what the converter did, with the affected products under each. Use the original file instead discards the conversion.

The conversion card on the mapping page: recognised as a WooCommerce product export, 16 rows turned into 6 products, 12 variants and 10 images, with an image-host warning, the skipped products and the changes listed

Caution Images are not re-hosted. Shopify downloads them from your WordPress site at import time, so the site must stay online and must serve files to non-browser requests until the import is done.

Not finding what you need? Email [email protected], or check the FAQ and the changelog.