# Supplier Feed Import & Dropshipping for PrestaShop 8 & 9

> Complete guide to the Supplier Feed Import & Dropshipping module for PrestaShop 8 and 9: automatic analysis, multiple sources per field, packed variant splitting, second combination axis, related products and large catalogs.

- Page: <https://www.datafirefly.com/en/documentation/import-fournisseurs-dropshipping-prestashop/>
- Language: en
- Last updated: 2026-09-18
- Other languages: [fr](https://www.datafirefly.com/documentation/import-fournisseurs-dropshipping-prestashop/index.md), [es](https://www.datafirefly.com/es/documentation/import-fournisseurs-dropshipping-prestashop/index.md), [de](https://www.datafirefly.com/de/documentation/import-fournisseurs-dropshipping-prestashop/index.md), [it](https://www.datafirefly.com/it/documentation/import-fournisseurs-dropshipping-prestashop/index.md), [pl](https://www.datafirefly.com/pl/documentation/import-fournisseurs-dropshipping-prestashop/index.md), [pt](https://www.datafirefly.com/pt/documentation/import-fournisseurs-dropshipping-prestashop/index.md), [nl](https://www.datafirefly.com/nl/documentation/import-fournisseurs-dropshipping-prestashop/index.md)
- Index: <https://www.datafirefly.com/en/documentation/llms.txt>

## Overview

The **Supplier Feed Import & Dropshipping** module (technical name `dfsupplierfeed`) automatically imports and syncs your suppliers' catalogs into PrestaShop 8 and 9. It handles multiple suppliers and feeds in CSV, XML and JSON, applies your margin rules, builds combinations, links products together, syncs stock via cron, and arbitrates duplicate EAN13 codes through a per-supplier priority.

The module does not replace PrestaShop's native CSV import (built for a single manual load): it industrializes _recurring_ imports from several sources, with automatic margins, combinations and synchronization.

## Installation

1. In the back office, go to **Modules > Module Manager**, then **Upload a module**.
2. Select the `dfsupplierfeed.zip` file and confirm.
3. Once installed, click **Configure**.

On install, the module creates five tables (`dfsf_supplier`, `dfsf_feed`, `dfsf_rule`, `dfsf_product`, `dfsf_log`) and generates a unique cron token.

## Interface overview

- **Dashboard** — counters and a warning about feeds whose import is in progress.
- **Suppliers** — suppliers and priorities.
- **Feeds** — feeds, analysis, field mapping and options.
- **Margin rules** — retail price computation rules.
- **Logs** — detailed import history.
- **Settings & Cron** — general settings, large catalogs, cron URLs.

## Step 1 — Create your suppliers

In the **Suppliers** tab, add a supplier with:

- **Name** of the supplier.
- **Priority** — an integer, `1` being the highest. It settles EAN13 duplicates.
- **Active** — an inactive supplier is skipped by the cron.
- **Create the native PrestaShop supplier** — recommended: it also fills the purchase cost in `product_supplier`.

Assign the best priorities (lowest numbers) to your most reliable or cheapest suppliers: they are the ones that will "own" shared products.

## Step 2 — Create the feed and let the module analyse it

In the **Feeds** tab, create the feed with its supplier, its source type (remote _URL_ or _local file_ inside the shop directory) and its format. Save, then click the **magnifier** button on the feed row.

The module downloads a sample and shows the `items_path` detected for XML and JSON feeds, the list of every field actually present with sample values, and a complete pre-filled mapping, editable before it is applied.

Numbered photo columns (`image_1`, `image_2`...) are grouped automatically, packed size columns, colour columns and related-reference columns are recognised, and column names are identified in English, French, Spanish, German and Italian with a sanity check on the values.

Always check the proposal before applying it. Many suppliers ship a recommended retail price where the module expects a **purchase cost**: your margin would then be applied on top of an already marged price.

## Step 3 — The field mapping

The mapping is a JSON object linking the feed's columns or nodes to normalized fields. The 21 canonical fields are:

`name` `reference` `ean13` `mpn` `cost` `quantity` `description` `description_short` `category` `manufacturer` `weight` `tax_rate` `image` `images` `group_reference` `attributes` `variants_stock` `variants_ean` `variants_reference` `variant_attribute` `related`

Only `reference` or `ean13` is mandatory: those are the two matching keys. A row with neither is rejected.

### Several sources for one field

A field value may be a list. A supplier spreading its photos over several columns maps like this:

```
{
  "fields": {
    "reference": "id",
    "name": "name",
    "cost": "wholesale_price",
    "images": ["image_1", "image_2", "image_3", "image_4"]
  }
}
```

For the `images` field every filled column is imported. For any other field the first non-empty value is kept, which lets you write a fallback chain: `"cost": ["sale_price", "net_price"]`.

### CSV

Link each field to a **column header**, or to a **column index** starting at 0 when the headers are unusable. The delimiter is detected automatically, multiline quoted fields are handled, and both `1 234,56` and `1,234.75` are accepted.

```
{
  "fields": {
    "name": "product_name",
    "reference": "sku",
    "ean13": "ean",
    "cost": "price",
    "quantity": "stock",
    "category": "category",
    "image": "image_url"
  }
}
```

A row whose column count does not match the header is **rejected** and counted as an error. That is the only safe way to handle an unescaped delimiter inside a text field: without the check, every following value would be shifted and imported silently.

### XML

`items_path` points at the repeated node, at any depth. Field paths are relative to that node, and `@name` reads an attribute.

```
{
  "items_path": "products/product",
  "fields": {
    "reference": "@sku",
    "name": "title",
    "ean13": "ean",
    "cost": "pricing/wholesale",
    "quantity": "stock/quantity",
    "image": "images/image"
  }
}
```

Since paths are relative to the item, a value living only on an ancestor node cannot be read: there is no `..` navigation.

### JSON

`items_path` uses dot notation down to the items array. A numeric segment reads an array entry, so `images.0` is the first image. Leave `items_path` empty if the file starts directly with `[`.

```
{
  "items_path": "data.products",
  "fields": {
    "reference": "sku",
    "name": "name",
    "ean13": "barcode",
    "cost": "prices.cost",
    "quantity": "inventory.available",
    "image": "images.0"
  }
}
```

The Feeds tab holds sixteen commented examples covering the most common structures.

## Step 4 — Define your margins

In the **Margin rules** tab, each rule computes the tax-excluded retail price from the tax-excluded purchase cost:

- **Percent** — `cost × (1 + value/100)`.
- **Coefficient** — `cost × value`.
- **Fixed addition** — `cost + value`.

An optional **psychological rounding** is then applied: `x.99`, `x.95`, `x.90` or round up to integer.

### Scope and resolution

A rule can target a supplier, a category, both, or be global. The most specific one wins, in this order: supplier + category, then supplier only, then category only, then the global rule. Category rules also apply to child categories. With no rule at all, the **default margin** from the settings is used.

## EAN priority between sources

When the same `ean13` appears in several feeds:

- the supplier with the best priority **owns** the product;
- other sources are **skipped** for that reference;
- if a better-priority supplier later brings that EAN, it **automatically takes over**.

## Combinations

Two feed shapes are supported.

### One line per variant

Enable **Build combinations** and map `group_reference` (identical for every variant of one product) and `attributes` (the options, for example `Size:M|Colour:Red`). Separators `|`, `,` and `;` are accepted between pairs, `:` and `=` between name and value.

### One line per product, sizes packed in a column

This is the most common shape among textile and lingerie wholesalers:

```
sizes_stock : EU 70C | FR 85C:4,EU 70D | FR 85D:2,EU 75A | FR 90A:1
ean_codes   : EU 70C | FR 85C:5901741925360,EU 70D | FR 85D:5901741925377
```

Enable **Split packed variants** and map `variants_stock`, plus `variants_ean` and `variants_reference` when the feed provides them. The module splits the line into one combination per size and matches stock, EAN and reference **by label**. Three settings sit next to the checkbox: the **attribute group name** (`Taille` by default), the **separator between entries** (`,`) and the **separator between label and value** (`:`).

The label/value split happens on the **last** occurrence of the separator, so a label such as `EU 70C | FR 85C` stays readable. Pairs built this way never go through string parsing, which rules out collisions with separators present inside the labels.

Tick the box **before** the first import. If you import without it first, the products are created with no `group_reference`: enabling the split afterwards means the module cannot find those parents and creates new ones, doubling your catalog. If it happens, delete the created products and clear the cursors from the Maintenance panel.

### A second axis from a feed column

Many suppliers ship the colour in a separate column while the sizes are packed. Map `variant_attribute` to that column:

```
{
  "fields": {
    "reference": "id",
    "name": "name",
    "cost": "wholesale_price",
    "variant_attribute": "color",
    "variants_stock": "sizes_stock",
    "variants_ean": "ean_codes"
  }
}
```

Every combination of the product then gains a second axis, under the **attribute group for the extra column** set on the feed (`Couleur` by default). You get Size and Colour combinations, usable by faceted filters.

Since each feed line is a product in a single colour, the Colour group holds one value per product. Colourways are not merged into a single page with a colour selector: to navigate between them, use the related products below.

## Related products

When the feed lists the other colourways or the associated models in a column of references, tick **Import related products** and map `related`:

```
{
  "fields": {
    "reference": "id",
    "related": "other_colors"
  }
}
```

The column holds a comma separated list of supplier references. The links are created as **PrestaShop accessories**, so they show up in your theme's related products block.

Resolution happens **once the feed has been read from end to end**, because a reference very often points at a product appearing later in the file. Three behaviours to know:

- a reference pointing at the product itself is skipped, which is common since many suppliers list the whole group on every member;
- a reference pointing at a product absent from the feed is skipped **without counting as an error**: on a category-filtered export this routinely accounts for a fifth of the references;
- existing accessories are **never deleted**, so links you added by hand survive the import. In exchange, a grouping changed by the supplier leaves old links in place.

The number of links created appears in the end-of-import message and in a dedicated log column.

## What the feed may overwrite

Five checkboxes per feed decide which fields are synchronized: **prices**, **stock**, **name**, **descriptions**, **images**. By default only prices and stock are checked.

If you rewrite the product pages for SEO, uncheck name and descriptions after the first import, otherwise the next cron pass will overwrite your work.

## Products removed from the supplier catalog

Each feed picks its behaviour: leave untouched, set stock to zero, disable, or both. The action runs at the end of a **completed full import**, and only on the products that feed had created or linked.

In dropshipping, setting the stock to zero is the safest choice: the product can no longer be sold but keeps its URL and its ranking.

## Categories and currencies

The `category` field accepts a plain name or a full path, for example `Home > Office > Chairs`. The separator is configurable per feed, and the **Create missing categories** option creates the absent levels. A path starting with the separator, such as `/WOMEN/Lingerie`, is read correctly.

If the supplier invoices in another currency, select it on the feed: costs are converted to the shop default currency before margins are applied.

## Large catalogs

Feeds are read in streaming: the memory used does not depend on the file size. Processing is sliced into resumable batches. Two settings, in the **Settings & Cron** tab:

- **Checkpoint every N items** (2000 by default).
- **Time budget per pass** (120 s by default) — the next cron call resumes at the exact same item.

A very large catalog simply needs several cron passes and completes on its own. The downloaded file is cached while the import is unfinished.

Splitting multiplies the volume: a feed of 7,300 products with variants produces over 33,000 combinations, so roughly 41,000 objects created on the first full import. Plan for several passes and test on a staging shop before production.

## Running an import manually

- **Full import** (play icon) — updates linked products and creates the missing ones if the feed allows it.
- **Stock sync** (refresh icon) — updates only prices and quantities of already-linked products.

From the back office a pass is capped at 45 seconds so the web server does not time out.

## Automating with cron

```
# Hourly stock sync
0 * * * * curl -sL "https://yourshop.tld/index.php?fc=module&module=dfsupplierfeed&controller=cron&token=YOUR_TOKEN&mode=stock" > /dev/null

# Nightly full import
30 3 * * * curl -sL "https://yourshop.tld/index.php?fc=module&module=dfsupplierfeed&controller=cron&token=YOUR_TOKEN&mode=full" > /dev/null
```

Optional parameters: `&id_feed=N` to process a single feed, `&budget=600` to allow a longer run.

If you regenerate the token in the settings, update your cron jobs: the old URL will return a 403 error.

## General settings

- **New products active immediately** — disabled by default.
- **Deactivate products out of stock at the supplier**, reactivated when stock returns.
- **Default margin** when no rule matches.
- **Log retention** and automatic purge.
- **On uninstall** — delete the data or keep everything.
- **Clear feed cache and cursors** in the Maintenance panel.

## Monitoring and logs

The **Logs** tab lists every run: feed, mode, items processed, created, updated, skipped, errors, missing, related links created, a completion flag with the resume point, execution time and the details of the first errors.

## Troubleshooting

### "Malformed CSV row: 23 columns instead of 22"

The row contains an unescaped delimiter or quote inside a text field. It is rejected to avoid importing shifted values.

### "Feed file not found or outside shop directory"

For a file source, the path must point to a readable file inside the shop directory.

### The analysis finds no items

Set `items_path` manually in the mapping, then run the analysis again.

### Products are created but invisible on the front office

This is the default behaviour: created products are disabled.

### Combinations are not created

Check that the matching box is ticked, that the required fields are mapped (`group_reference` and `attributes`, or `variants_stock`), and that the separators match the file.

### My catalog doubled after enabling the split

The import ran before the box was ticked. Delete the products created by that feed, clear the cursors, then run again.

### Few or no related links created

Links are only resolved at the end of a **completed** full import: on a large feed processed in several passes, they appear on the last one. Also check that the references in the column match the field mapped to `reference`.

### Prices look too high or too low

Check whether the feed provides tax-inclusive costs, whether the currency is correct, which margin rule applies, and that the field mapped to `cost` really is a purchase cost.

### The import never finishes

That is normal on a very large feed: it progresses in passes. The Status column gives the resume item.

## Compatibility

- PrestaShop 8.0 to 9.x, PHP 7.4 to 8.3.
- No PrestaShop core override.
- Multistore: created products are associated with the shops of the current context.
- Interface translated into English and French.
