# DataFirefly Odoo Connector — installation and configuration guide

> This guide walks through the installation, configuration and day-to-day operation of the DataFirefly Odoo Connector plugin for Shopware 6.6 and 6.7. By the end, your store will be syncing products,…

- Page: <https://www.datafirefly.com/en/documentation/df-odoo-connector/>
- Language: en
- Last updated: 2026-06-29
- Other languages: [fr](https://www.datafirefly.com/documentation/df-odoo-connector/index.md), [es](https://www.datafirefly.com/es/documentation/df-odoo-connector/index.md), [de](https://www.datafirefly.com/de/documentation/df-odoo-connector/index.md), [it](https://www.datafirefly.com/it/documentation/df-odoo-connector/index.md), [pl](https://www.datafirefly.com/pl/documentation/df-odoo-connector/index.md), [nl](https://www.datafirefly.com/nl/documentation/df-odoo-connector/index.md), [pt](https://www.datafirefly.com/pt/documentation/df-odoo-connector/index.md)
- Index: <https://www.datafirefly.com/en/documentation/llms.txt>

This guide walks through the installation, configuration and day-to-day operation of the **DataFirefly Odoo Connector** plugin for Shopware 6.6 and 6.7. By the end, your store will be syncing products, stock, customers and orders with your Odoo instance over native XML-RPC, with no external dependency and no third-party API surcharge.

## Overview

The plugin builds a two-way bridge between Shopware and Odoo by speaking Odoo's XML-RPC protocol directly (stable since version 8). No module to install on the Odoo side, no paid middleware, no SaaS broker in between.

| Entity | Odoo → Shopware (pull) | Shopware → Odoo (push) |
| --- | --- | --- |
| Products (product.template) | ✅ | ✅ |
| Stock (qty_available / free_qty) | ✅ | — |
| Categories (product.category) | ✅ | ✅ |
| Customers (res.partner) | — | ✅ with child addresses |
| Orders (sale.order) | — | ✅ with optional confirmation and invoice |

## Requirements

- **Shopware** 6.6.x or 6.7.x (all minor versions).
- **PHP** 8.2, 8.3 or 8.4.
- **PHP extensions**: curl, xml, simplexml (shipped by default on virtually all hosts).
- **Odoo** 12, 13, 14, 15, 16, 17 or 18, Community or Enterprise. Odoo.sh, Odoo Online (SaaS) and self-hosted instances all work the same way.
- A dedicated Odoo user for the API (recommended) with read/write access on the models used (product.template, product.product, res.partner, sale.order, stock.warehouse, product.category, res.country, account.tax).

## Installation

### Via admin upload

1. Download `DfOdooConnector-v1.0.0.zip` from your customer account.
2. In Shopware admin: _Extensions → My extensions → Upload extension_.
3. Select the ZIP and click _Install_.
4. Enable the extension using the toggle.

### Via SSH console

```
cd /path/to/shopware
cp DfOdooConnector-v1.0.0.zip custom/plugins/
cd custom/plugins && unzip DfOdooConnector-v1.0.0.zip
sudo -u www-data setsid php bin/console plugin:refresh
sudo -u www-data setsid php bin/console plugin:install --activate DfOdooConnector
sudo -u www-data setsid php bin/console cache:clear
```

Rebuild the administration to load the Vue 3 module:

```
sudo -u www-data setsid php bin/build-administration.sh
```

**Note** — Installation creates two tables: `df_odoo_mapping` (persistent Shopware ↔ Odoo mappings) and `df_odoo_log` (operation journal). No existing table is modified.

## Odoo-side configuration

### Create a dedicated user

We strongly recommend creating a dedicated Odoo user for the integration rather than using a personal administrator account. This makes auditing the connector's actions much easier and lets you revoke its access independently.

1. In Odoo: _Settings → Users & Companies → Users_.
2. Create a user named for instance `Shopware Bridge`.
3. Give it the required rights: Inventory (user), Sales (administrator), Invoicing (user if you enable invoice creation), Contacts (user).

### Generate an API key

1. Log into Odoo as this new user.
2. Click the avatar top-right → _Preferences_.
3. Tab _Account_ → _API Keys_ → _New API Key_.
4. Give it a descriptive name (e.g. `Shopware Connector`) and copy the generated value.

**Important** — The API key is shown only once. If you lose it, you'll need to generate a new one. Store it in a password manager before closing the modal.

## Shopware-side configuration

### Fill in the connection

In the Shopware admin: _Settings → System → Plugins → Df Odoo → Settings_, or via the side menu _Settings → Df Odoo → Settings_.

- **Odoo URL**: full URL of your instance without trailing slash, e.g. `https://myaccount.odoo.com`.
- **Database name**: visible in the Odoo URL after `?db=`, or in _Settings → Technical → Database_.
- **User**: login of the dedicated user, usually their email address.
- **Odoo API key**: the value you copied at the previous step.
- **Timeout**: 30 seconds by default, enough for most cases.

### Test the connection

Click _Test connection_ top-right. If everything is fine, a green notification displays Odoo's version and the user ID (uid). If the connection fails, Odoo's error message is shown as-is.

**Tip** — The connection test can also be called from the console to script a health check:
`curl -X POST -H "Authorization: Bearer ADMIN_TOKEN" https://yourshopware.com/api/_action/df-odoo/test-connection`

## Sync directions

Each entity has its own selector: _disabled_, _Odoo → Shopware_ (pull), _Shopware → Odoo_ (push), or _both directions_. The defaults are:

- **Products**: both directions
- **Stock**: Odoo → Shopware (Odoo is the source of truth)
- **Customers**: Shopware → Odoo
- **Orders**: Shopware → Odoo
- **Categories**: disabled (enable manually depending on your setup)

## Product synchronization

### Matching strategies

Three strategies are configurable:

- **SKU** (recommended): Shopware `productNumber` ↔ Odoo `default_code`.
- **Odoo ID**: relies solely on the persistent mapping table. Useful if your SKUs are volatile.
- **Barcode** (EAN): Shopware `ean` ↔ Odoo `barcode`. Requires EANs on both sides.

Once linked, two products stay paired through the `df_odoo_mapping` table, even if the SKU later changes.

### Pull from Odoo

The scheduled task reads `product.template` records modified since the last run (`write_date` field) and creates or updates the matching Shopware products. Synced fields are: name, SKU, sale price, cost price, short description, long description, weight, volume, active flag, category, taxes.

### Push to Odoo

Active Shopware master products (with `parentId = null`) are pushed to Odoo as `product.template` records of type _product_ (storable item). Shopware variants are rolled up under their parent product.

**Change detection** — Before each write, a SHA-1 hash of the payload is compared to the one stored in the mapping. If nothing changed, the write is skipped and the operation is journaled as `skipped`. This avoids hammering Odoo on successive cron runs.

## Stock synchronization

Stock is always pulled from Odoo (never the other way around). Every 15 minutes, the scheduled task reads `product.product` variants in batches of 100 by parent template id, aggregates `qty_available` or `free_qty` (globally configurable), then updates the `stock` field of each Shopware product in a single DAL request.

**qty_available vs free_qty** — `qty_available` reflects the physical stock in the warehouse. `free_qty` subtracts quantities already reserved on undelivered orders. _free_qty_ is generally preferable for an e-commerce site as it prevents overselling.

## Category synchronization

Disabled by default. Enable it if your category tree should stay in sync with Odoo's. The `parent_id` hierarchy is preserved on both sides. Like products, a content hash avoids unnecessary writes.

## Customer synchronization

Shopware customers are pushed as Odoo `res.partner` records with:

- **Email-based deduplication**: before any creation, an existing partner with the same email and `parent_id = false` is searched. If found, it is updated rather than duplicated.
- **company_type**: _company_ if the company field of the billing address is filled, _person_ otherwise.
- **Child addresses**: the default billing address is created as a child partner with `type='invoice'`, the default shipping address as a child partner with `type='delivery'`.
- **Intra-community VAT**: reported on the `vat` field of the main partner.
- **Country and state**: resolved by ISO code with an in-memory request-scoped cache.

## Order synchronization

### Real-time at checkout

If the option _Push each order on placement_ is enabled, an event subscriber listens to `CheckoutOrderPlacedEvent` and pushes the order to Odoo immediately after checkout completion. The customer is created in Odoo if necessary (via customer sync), then the order is created as a `sale.order` with:

- `partner_id` resolved via the customer mapping.
- `order_line` in Odoo tuple syntax: `[0, 0, {name, product_uom_qty, price_unit, product_id}]`.
- An extra line for shipping fees if the shipping `totalPrice` is greater than zero.
- `company_id`, `warehouse_id`, `pricelist_id` according to the configured defaults.

**Checkout is never blocked** — If Odoo is unreachable, the error is logged in `df_odoo_log` and the order is retried within 10 minutes by the `df_odoo.order_sync` scheduled task, which scans unmapped orders from the last 7 days.

### State filter

The _Order state filter_ setting restricts which orders are pushed:

- **All**: every placed order is sent (recommended in B2C with immediate payment).
- **Paid only**: only orders whose payment state is _paid_ are pushed. Prevents abandoned carts with manual payment from polluting Odoo.
- **Paid or shipped**: also pushes orders shipped before payment (B2B with payment terms).

### Automatic confirmation and invoice

Two options drive what happens on the Odoo side once the order is created:

- **Confirm order**: calls `action_confirm` on the `sale.order`, which goes straight to _confirmed_ state instead of staying as _quotation_.
- **Create invoice**: calls `_create_invoices` to immediately generate a validated invoice. The created invoice's ID is stored as an `invoice`-type mapping.

## Multi sales channel

All plugin settings can be overridden per sales channel. At the top of the _Settings_ page, Shopware's native selector lets you switch between _All channels_ and a specific channel.

Typical use cases:

- A B2C channel pushing to a main Odoo and a B2B channel pushing to a separate Odoo.
- A production channel with _push_ direction and a staging channel with direction _disabled_.
- Different Odoo IDs (warehouse, sales team, pricelist) per channel.

## Scheduled tasks

| Internal name | Frequency | Action |
| --- | --- | --- |
| `df_odoo.product_sync` | 1 hour | Pull then push products according to the configured direction. Pull only looks at records modified since -2h. |
| `df_odoo.stock_sync` | 15 minutes | Pull stock from Odoo for all already-mapped products. |
| `df_odoo.customer_sync` | 1 hour | Push active customers not yet mapped (up to 100 per run). |
| `df_odoo.order_sync` | 10 minutes | Push orders from the last 7 days not yet mapped (up to 50 per run). |

Force a task to run from the console:

```
sudo -u www-data setsid php bin/console scheduled-task:run-single df_odoo.product_sync
```

**Shopware worker** — For scheduled tasks to fire automatically, the Shopware messenger worker must be running (cron `messenger:consume` or systemd unit). Check in _Settings → System → Queue_ that the _scheduled_task_ queue is being consumed regularly.

## Admin module

A **Df Odoo** section appears under _Settings → Plugins_ with four pages.

### Dashboard

Live counters (active mappings per entity, last 24h activity per status), manual sync buttons per entity (pull and push), connection status banner, list of recent errors and shortcuts to the other pages.

### Settings

Full form with the sales channel selector. _Test connection_ and _Save_ buttons in the action bar.

### Logs

Every operation is journaled in `df_odoo_log` with its status (_success_, _error_, _warning_, _skipped_), direction, entity, duration in milliseconds and full message. Combinable filters by status, entity type and direction. Server-side pagination.

**Debug mode** — _Skipped_ operations are only written to the journal when debug mode is enabled in the settings. Use it temporarily to investigate a behavior, then disable in production to avoid bloating the table.

### Mappings

Read-only view on the `df_odoo_mapping` table with search, filter by entity type and sort by last-sync date. Handy to check that a given product is mapped to the expected Odoo ID.

## Admin REST API

All endpoints require standard admin authentication (Bearer token).

| Method | Endpoint | Parameters |
| --- | --- | --- |
| POST | `/api/_action/df-odoo/test-connection` | `salesChannelId` (optional) |
| POST | `/api/_action/df-odoo/sync/products` | `direction=pull\|push`, `salesChannelId` |
| POST | `/api/_action/df-odoo/sync/stock` | `salesChannelId` |
| POST | `/api/_action/df-odoo/sync/customers` | `salesChannelId` |
| POST | `/api/_action/df-odoo/sync/orders` | `salesChannelId`, `limit` (1-500) |
| POST | `/api/_action/df-odoo/sync/categories` | `direction=pull\|push` |
| GET | `/api/_action/df-odoo/stats` | — |
| GET | `/api/_action/df-odoo/logs` | `status`, `entityType`, `direction`, `page`, `perPage` |

Example call to force a product push:

```
curl -X POST
  -H "Authorization: Bearer ADMIN_TOKEN"
  -d "direction=push"
  https://yourshopware.com/api/_action/df-odoo/sync/products
```

## Tables and stored data

The plugin creates two MySQL tables:

- `df_odoo_mapping` — persistent mappings (Shopware id ↔ Odoo id) with sync hash and optional payload. A row is unique on (entity type, Shopware id) and on (entity type, Odoo id).
- `df_odoo_log` — operation journal with status, duration, message, payload, sales channel id.

No standard Shopware table is modified.

## Uninstall

From the admin: _Extensions → My extensions → Df Odoo → Uninstall_.

A modal offers two options:

- **Keep user data** checked: the `df_odoo_mapping` and `df_odoo_log` tables are preserved, along with the system settings. Handy for a later reinstall.
- **Keep user data** unchecked: both tables are dropped on uninstall. The Odoo instance is never touched.

## Troubleshooting

### "Odoo configuration incomplete" on connection test

One of the four required fields (URL, database, user, API key) is empty. Make sure you selected the right sales channel before saving.

### "401 Unauthorized" or "access denied"

The API key was revoked on the Odoo side, or the user lacks the required rights on the targeted model. Regenerate an API key and check the Odoo user's rights (especially _Inventory → User_ and _Sales → Administrator_).

### "Connection refused" or timeout

The Odoo URL is not reachable from the Shopware server. Make sure the firewall allows outbound HTTPS to the Odoo domain. Increase the timeout if your Odoo instance is slow to respond.

### Products are not syncing

Check the journal (_Logs_ page) for error entries. Enable debug mode to also track _skipped_ operations and find out whether the content hash means nothing actually changed.

### Orders are not pushed at checkout

Make sure the _Push each order on placement_ option is enabled and the _Orders_ direction is set to _Shopware → Odoo_. If the state filter is set to _Paid only_ and payment is asynchronous, the scheduled task pushes the order a few minutes after the payment lands.

## Known limitations

- Odoo variant attributes (`product.attribute`) are not yet mapped automatically. Shopware variants are rolled up as Odoo parent products (`product.template`). Odoo variants you create manually remain correctly linked through their parent template. Native handling is planned for version 1.1.
- Order-line discounts are reported as an adjusted `price_unit`, not as Odoo's `discount` field.
- Shopware payment and shipping methods are not mapped to Odoo `journal_id` — Odoo's defaults are used.

## Support

For any question, contact the DataFirefly team via the contact form on [datafirefly.com](https://www.datafirefly.com/contact/). Please attach the journal export (_Logs_ page → export button coming soon) or at least a screenshot of the failing line, plus the exact Shopware, PHP and Odoo versions.
