Wialon–Odoo Integration: Orders, Mileage, Fuel

Siarhei Havarunou – CEO

Connect Wialon trips, odometers and fuel to Odoo: field ownership, a queue between the APIs, nightly reconciliation. From $500, quoted upfront.

Wialon and Odoo connected: orders, odometer readings and fuel through one queue

Odoo runs the business, Wialon runs the fleet, and between them sits a dispatcher retyping mileage into delivery orders. The integration that removes the retyping is not complicated, but it has three decisions that must be made before any code: who owns each field, what sits between the two APIs, and how you prove the sync did not silently stop. This guide makes all three, for the Odoo models fleets actually use.

The two APIs, so the shapes are clear

Odoo exposes an External API over XML-RPC — /xmlrpc/2/common to authenticate and /xmlrpc/2/object to call model methods through execute_kw — with a JSON-RPC endpoint offering the same surface. You address models by name and call their methods directly: search_read to query, create to insert, write to update. Since Odoo 14 the credential should be an API key rather than the user’s password, and that key should belong to an integration user with exactly the model access the sync needs, not to an administrator.

Wialon is the opposite shape: form-encoded POST to one endpoint, a service name in svc, JSON in params, a session id that dies after five idle minutes. Neither side knows anything about the other’s transaction model, which is the whole reason the middle of this article is about a queue.

What maps to what

Four mappings cover most fleets, and it is worth naming the Odoo models explicitly because choosing the wrong one is expensive to undo:

From WialonTo OdooNotes
Unitfleet.vehicleMatched on VIN or registration, never on name
Mileage counterfleet.vehicle.odometerOne row per reading; Odoo expects exactly this number
Service interval reachedfleet.vehicle.log.servicesOr a maintenance request, if that is where your workflow lives
Trip / geofence eventstock.picking or sale.order stageThe order moves, not a new record
Driver bindinghr.employee linkDecides whose timesheet or payroll line the hours attach to
Fuel transactionfleet.vehicle.log.fuel (OCA)Not in Odoo core — see below

Trips become delivery or service order updates: a geofence arrival closes the order stage, the departure opens the next one. Odometer readings land on fleet.vehicle.odometer, which already expects a value and a date and nothing else. Fuel transactions reconcile against tank levels before posting to accounting, because a fuel card statement is a claim and the tank is the evidence.

The fuel model deserves a sentence of its own. Odoo core Fleet does not carry a fuel log; fleet.vehicle.log.fuel comes from the community fleet_vehicle_log_fuel module. Decide early which way you are going — install the module and write structured fuel rows into it, or skip it and post fuel straight to accounting as vendor bills with the vehicle as an analytic dimension. Both work. Discovering the choice halfway through, after the reports have been built against a model that is not installed on the customer’s instance, does not.

Field ownership, drawn before any code

The mapping to draw on a whiteboard first is not the field list but the ownership list. VIN, registration and vehicle class belong to Odoo, because procurement and compliance live there. Position, sensor values and current driver belong to Wialon, because the hardware feeds there. Odometer is shared with an explicit precedence rule — Wialon for daily operations, Odoo’s verified reading wins after a maintenance inspection, because a technician with a torch beats a counter that has been reset by a tracker swap.

Two systems writing the same field without a precedence rule is the bug you will spend March debugging, and it never presents as a sync failure. It presents as a number that is right on Tuesday and wrong on Wednesday, with both systems’ logs showing a successful write.

Write the table down, put a column on it for the conflict rule, and have the customer sign it. Half the value of an integration project is delivered in that meeting.

Units, time and identity

Three unglamorous details cause most of the data-quality incidents in this kind of project.

Units of measure. Wialon reports in whatever the unit’s measurement settings say — kilometres or miles, litres or gallons — and that setting lives per unit, not per account. A fleet that acquired vehicles from two sources will happily have both, and a sync that assumes one will produce odometer rows that are wrong by a factor of 1.6 for part of the fleet. Read the measurement setting alongside the value and normalise on the way in.

Time. Wialon works in Unix timestamps, which are unambiguous. Odoo stores datetimes in UTC and renders them in the user’s timezone, which is also unambiguous until someone compares a rendered value to a raw one. Keep everything UTC through the queue, convert only at the display edge, and never let a date boundary — “trips on the 3rd” — be computed in two different places.

Identity. Match units to vehicles on VIN, or failing that on registration, and store the Wialon unit id on the Odoo record once matched. Never match on name: fleet names get edited, duplicated and suffixed with “(old)”, and a name-matched sync silently starts writing one vehicle’s mileage onto another. The first run should be a dry run that reports unmatched records on both sides for a human to resolve, and that report should stay available afterwards, because new vehicles arrive.

A queue between the APIs, always

Odoo’s External API and the Wialon Remote API never talk to each other directly. Between them sits a queue with three jobs: retries with backoff when either side limits or fails, idempotency keys so a retried delivery does not close an order twice, and a dead-letter store for the payloads that need a human. The resilient sync design is the full treatment; the short version is that a direct API-to-API script works until the first rate limit, and then it corrupts data at 2 a.m.

The idempotency key is worth being concrete about, because “make it idempotent” is advice nobody can act on. Build the key from the source facts — Wialon unit id, server timestamp of the event, event kind — and store it on the Odoo side, either in a dedicated integration model or in a field on the target record. Before writing, check the key; after writing, record it in the same transaction. That is the entire mechanism, and it converts “the network blipped and we posted 3,000 km twice” into a no-op.

Wialon’s side of the queue also has a shape worth respecting. Ten simultaneous requests per session, three simultaneous heavy ones, and a session that dies after five idle minutes: one worker per session, back off on error 10, re-login exactly once on error 1. The Python client guide has the client that does this.

Odoo’s side has a different constraint. It has no published request ceiling, but it has workers and per-request CPU limits, and a sync that opens thirty parallel connections to a two-worker instance will simply queue behind itself while the dispatcher’s screen stops loading. Size the concurrency to the customer’s deployment, and schedule anything heavy outside working hours.

Sync business events, not raw messages

Event granularity is the second decision, and getting it wrong is the most common reason these projects feel slow. Do not sync raw messages — thousands per hour that Odoo cannot use and will not index well. Sync business events: trip completed, geofence crossed, fuel anomaly detected, driver shift started, service interval reached. Each event maps to one Odoo write.

The rule of thumb is that an event is worth syncing if someone in Odoo would act on it. A position update is not; an arrival at a customer site is. The extraction side of that discipline — polling avl_evts rather than re-reading intervals on a timer — is covered in the Python client guide, and the reasons Wialon cannot simply call your endpoint are in why Wialon has no webhooks.

There is an Odoo-side version of the same discipline. Do not write into documents that have moved past the state where writing is legal — a confirmed invoice, a validated picking — because Odoo’s workflow will either refuse or accept in a way you did not intend. Write into a staging model the workflow reads from, or target the stage transition rather than the fields behind it.

The reconciliation that proves it works

A sync without a check is a hope. Once a night, a job compares record counts and key aggregates between Wialon and Odoo — trips per unit, odometer deltas, fuel totals — and alerts when the difference passes a threshold you set. Without it, a stopped integration looks exactly like a quiet week, and you discover the gap at month-end close.

Three thresholds are enough to start: a count difference per unit per day, an odometer delta that exceeds what a vehicle could physically cover, and any unit that reported nothing at all while its neighbours did. The last one catches tracker failures, which are far more common than integration failures and which the customer will otherwise blame on the integration.

This job is part of every integration we ship, not an add-on; it is also the first thing we add when taking over someone else’s.

How the work is phased

A first delivery is one direction, one mapping, one fleet: odometer from Wialon into fleet.vehicle.odometer, with matching, a queue and a reconciliation report. It is deliberately small because it proves the identity mapping against real data, which is where the surprises are.

The second phase adds the events that move documents, because those need the customer’s workflow decisions and those decisions take longer than the code. The third adds whatever else is on the whiteboard — fuel, maintenance, payroll hours — each one cheap once the queue and the matching exist.

Multi-company deployments change this shape and should be raised at the start rather than discovered: one Odoo database with several companies, or several Wialon resources under one account, means the matching table needs a company dimension and the integration user needs the right multi-company rules. Retrofitting that is a rewrite of the matching layer.

What it costs and how long it takes

Small connections — one mapping, one direction, one fleet — start at $500 for 10 hours at $50 an hour, quoted upfront against a written scope. Multi-system programs (Odoo plus BI, fuel cards, billing) run in phases over 8–12 weeks. The engagement models, all fixed before anything is built, are on the integration page — which also covers the same architecture for SAP, 1C, TMS and BI systems.

If your fleet runs on Odoo and your dispatcher still retypes, send a note: thirty minutes, free, and you will leave with a scope even if you build it yourself.

More from Asset Track

Let's connect

  • “Our client needed a data pipeline. It came back working, plus a few Wialon fixes we had not asked for. That client trusts us more now.”
    Faiz K. Customer Manager · Trakpro Limited