SW Shopware 6 Intermediate

DataFirefly Odoo Connector — installation and configuration guide

Connect Shopware 6.6 / 6.7 to Odoo 12 → 18 over native XML-RPC. Installation, API key setup, sync directions, scheduled tasks and troubleshooting.

Updated Module version 1.0.0

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 AccountAPI KeysNew 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_qtyqty_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 modeSkipped 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. 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.

Was this page helpful?

Still stuck? Contact support