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.
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
- Download
DfOdooConnector-v1.0.0.zipfrom your customer account. - In Shopware admin: Extensions → My extensions → Upload extension.
- Select the ZIP and click Install.
- 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
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.
- In Odoo: Settings → Users & Companies → Users.
- Create a user named for instance
Shopware Bridge. - Give it the required rights: Inventory (user), Sales (administrator), Invoicing (user if you enable invoice creation), Contacts (user).
Generate an API key
- Log into Odoo as this new user.
- Click the avatar top-right → Preferences.
- Tab Account → API Keys → New API Key.
- Give it a descriptive name (e.g.
Shopware Connector) and copy the generated value.
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.
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↔ Odoodefault_code. - Odoo ID: relies solely on the persistent mapping table. Useful if your SKUs are volatile.
- Barcode (EAN): Shopware
ean↔ Odoobarcode. 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.
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 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 = falseis 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 withtype='delivery'. - Intra-community VAT: reported on the
vatfield 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_idresolved via the customer mapping.order_linein Odoo tuple syntax:[0, 0, {name, product_uom_qty, price_unit, product_id}].- An extra line for shipping fees if the shipping
totalPriceis greater than zero. company_id,warehouse_id,pricelist_idaccording to the configured defaults.
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_confirmon thesale.order, which goes straight to confirmed state instead of staying as quotation. - Create invoice: calls
_create_invoicesto immediately generate a validated invoice. The created invoice’s ID is stored as aninvoice-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
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.
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_mappinganddf_odoo_logtables 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’sdiscountfield. - 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.