Operators handbook: bol.com sync
A per-website integration with bol.com's Retailer API. It is a module
(bol.com, id bol) that requires checkout: switch it on on the
website's Modules tab. While it is off, its settings are hidden and
nothing syncs, whatever the settings say.
Two directions, each switchable on its own:
- Stock, CMS → bol. Your stock levels are pushed onto your bol offers so they stop selling what you no longer have.
- Orders, bol → CMS. Bol orders are imported as normal orders, so you fulfil them next to your own.
Out of scope, deliberately: it never creates offers or products on bol, and it never pushes prices. Your bol listings and their pricing stay yours to manage in bol's own back office. This integration only moves stock up and orders down. Imported orders do carry bol's price: what the customer actually paid there, which is often not the shop's own price.
Before you switch it on
SKU is the EAN. This is the whole matching rule, in both directions.
A bol offer and a bol order line are matched to your catalog by looking
up sku on a product variant first, then on a product.
So the prerequisite is not a setting, it is data: every product you sell on bol must carry its EAN in the SKU field. Get that wrong and the sync does not fail loudly, it simply matches nothing, which is why the dry run below exists.
What operators set
Websites → Module settings → bol.com (shown while the module is on):
| Setting | What it does |
|---|---|
| Client ID / secret | Your bol Retailer API credentials. Nothing syncs without them. |
| Fulfilment method | FBR (you ship) or FBB (bol ships from their warehouse). |
| Push stock to bol | The CMS → bol half. |
| Pull orders from bol | The bol → CMS half. |
The client secret is write-only. Once saved it is never shown again, not even to a super admin; the panel only reports whether one is set. Leave the field blank to keep the existing secret and type a new one to replace it. Nobody can read it back out of the admin, so store it wherever you keep your other credentials too.
Test connection checks the credentials without changing anything.
There is no row until you save the panel once, and no row means no credentials. A website that never saved this panel has no bol integration and nothing runs for it. The module switch is the master switch: off, nothing runs and the panel is hidden.
Run a dry run first
The panel's dry run reports exactly what a real sync would change, in the CMS and on bol, having written nothing. It uses the same resolvers the real run uses, so it cannot disagree with what would actually happen.
It answers the two questions that matter before a first sync:
- Which offers would get a stock push, and to what level.
- Which bol orders would import, how many lines each, at what total, and which EANs matched nothing in your catalog.
That last one is the reason to bother. An unmatched line still imports (see below), so a wrong SKU produces a quiet mess rather than an error, and the dry run is where you see it while it is still free to fix.
How the sync runs
A scheduler tick every 15 minutes fans out to every website with the module on and credentials saved. The Sync now button runs the same job immediately.
Both halves need a running cms-worker; the work goes through Messenger,
so a stopped worker means the Status card simply stops advancing.
The Status card shows the last stock sync, the last order sync, the last status, and the last error if there was one.
Stock push
For each offer, the stock to report is resolved from the matching variant or product:
- Stock is not tracked on that product? Nothing is pushed. Bol's own number stands untouched. This is the right behaviour for a made-to-order or unlimited item, and it means turning stock tracking off is also how you exclude something from the push.
- No match by SKU? Nothing is pushed. Same outcome, different cause.
- Otherwise, the level is pushed only when it differs from what was last pushed, so a steady catalogue costs almost no API calls.
Order import
Each new bol order (one already imported is skipped) becomes an ordinary order, with lines matched by EAN.
Matched lines are priced at what the customer paid on bol, not at the shop's catalog price: bol's unit price is incl. VAT, so it is kept exactly and the VAT is taken out of it at the product's rate. Only when bol sends no price does a line fall back to the catalog price. The product's tax class is snapshotted like on any other order.
An unmatched line still imports. It falls back to bol's gross unit price under the zero-rate tax class, so the order total still reconciles with what bol charged the customer. What it loses is the VAT for that line, so the import leaves a note on the order naming the EANs that matched nothing: check the VAT on that order before it goes into the VAT return. Treat unmatched EANs in the dry run as work to do, not as noise.
With the VAT engine on, each new bol order is taxed by it: bol's price stays exactly, the engine decides where the sale is taxed and takes the VAT out. An order the engine refuses is still imported (the sale happened), taxed per product, with a note saying why. Bol orders are invoiced like any other order.
Re-pricing orders imported at the catalog price
Orders imported before the importer used bol's price were booked at the shop's catalog price. A console command fetches each of a website's bol orders from bol again and corrects its lines and totals to what the customer paid:
bin/console bol:reprice-orders <website-id> # dry run: lists what would change
bin/console bol:reprice-orders <website-id> --commit # writes the corrections
It is not a re-import: the orders keep their status, notes and stock, nothing is deducted twice, and running it again leaves corrected lines alone. bol only serves orders it still retains (roughly three months); older orders are reported and left as they are.
What this does not do
- No offer or product creation on bol. Listings are created in bol's back office.
- No price push. Your bol prices are set on bol.
- No cancellation or return handling. Only open orders come across.
- Nothing is deleted. An offer that disappears from bol leaves its local row behind.