- Choose one owner for every important business field.
- Other systems may copy a value, but must not silently become its second owner.
- Separate commands from notifications so data does not bounce forever.
- Conflicts need an explicit policy, not last-write-wins by accident.
- Document ownership in language understood by commerce, finance and operations.
The integration the client actually needed
The requirement was larger than installing an Odoo connector. Commercial product content, translations, selling metadata and storefront prices needed to move from Medusa towards Odoo. Physical stock needed to move from Odoo into commerce. Orders, fulfilment, tracking and invoice information crossed the boundary in their own directions.
Medusa gives a strong commerce model, workflows, subscribers and Admin extension points. It does not know the client’s Odoo model, identifiers, units, locale mapping or ownership decisions. We built that translation layer, the directional workflows and an Admin health surface that separates totals, linkage quality, stock drift and explicit reconciliation. This is the kind of project-specific integration that the choice of an extensible engine made possible.
The real problem is not ‘keeping everything in sync’
Teams often begin an ERP integration by saying that Medusa and Odoo must stay in sync. That sounds reasonable but avoids the most important question: if the two systems disagree, which one is correct? Copying data in both directions without an ownership rule creates two competing masters. The last message to arrive wins, even when it contains older or less authoritative information.
A source of truth is the system allowed to make the final decision for a particular business fact. There is rarely one source of truth for the whole company. Odoo may own accounting and fulfilment status, while Medusa owns the online basket and checkout. Product content may belong to a PIM or to Odoo, while regional presentation belongs to the storefront. Ownership must be decided field by field and action by action.
How a harmless update loop begins
Suppose Odoo changes a product title and sends it to Medusa. Medusa saves the title and emits its normal ‘product updated’ event. An integration sees that event and sends the same title back to Odoo. Odoo records another update and sends it to Medusa again. Nothing is logically changing, yet the systems can continue producing messages, audit noise and unnecessary work.
The same pattern becomes dangerous with stock, price or order status. A stale value can overwrite a newer one, or an automatic correction can reverse a legitimate manual decision. The answer is not merely to add a short delay and ignore events that arrive within it. Time-based suppression can also hide a real human edit. The design needs to know why a change happened and whether the receiving system is authorised to send it back.
Create an ownership map before writing integration code
We use a table that business and technical teams can review together. Each row names a fact—product identifier, description, price, available stock, customer address, order status, shipment reference, refund status—and identifies its owner. It also states who may propose a change, which system distributes copies and what happens during disagreement.
For example, Odoo might own physical stock because warehouse movements happen there. Medusa may display a copy for checkout performance but should not invent a new warehouse quantity. Medusa may own the customer’s current checkout address, while the completed order sends a fixed snapshot to Odoo. That snapshot should not change later because the customer edits their account. Clear ownership turns vague ‘sync’ work into a set of understandable contracts.
Distinguish a request to change from news that something changed
A command asks an owner to perform an action: ‘cancel this order’ or ‘reserve this stock’. A notification reports a fact that already happened: ‘the order was cancelled’ or ‘stock is now 12’. Mixing the two makes integrations hard to reason about. A receiving system may treat news as a fresh instruction and repeat the action.
We name messages so their intent is clear and attach an origin reference. When Odoo publishes a stock level, Medusa records that the update came from Odoo and does not send the copied value back as a merchant-authored change. When Medusa asks Odoo to cancel an order, it waits for Odoo’s accepted or rejected result rather than marking fulfilment complete on its own. This vocabulary makes responsibility visible in both code and operations.
Decide what happens when the systems disagree
‘Last update wins’ is a conflict policy, but usually an accidental and weak one. Network delays mean the latest arrival may contain the oldest fact. Better policies depend on the field. The owner can always win; a newer version from the owner can win; some changes can be merged; or a disagreement can stop automation and request human review.
The integration carries stable identifiers and, where useful, a version or source timestamp. It never overwrites a known record merely because a mutable label happens to match. For financially important changes, recording both the requested value and the accepted result gives the team a reliable explanation when the systems disagree.
Make ownership visible to operations
Support should not need to know which service or database table contains a field. An order view can state that fulfilment is controlled by Odoo, show the last accepted update and explain when the next retry will occur. If a user tries to edit an ERP-owned value in Medusa, the interface can disable the control or clearly present the action as a request to Odoo.
Every failed exchange should answer: which system owns the fact, what value each system currently holds, which message last moved it and what action is safe. Manual overrides need permission, a reason and an expiry or reconciliation rule. Otherwise an emergency correction becomes a permanent third source of truth known only to one operator.
Questions to answer before connecting Medusa and Odoo
For every field, name the owner, the systems allowed to propose changes and the conflict policy. For every message, state whether it is a request or a notification. Use stable identifiers, preserve the origin of automated changes and make repeated delivery safe. Test delayed and duplicated messages, changes arriving in reverse order and a manual edit made while synchronisation is unavailable.
Most importantly, have commerce, warehouse, finance and customer-service owners approve the map. Integration architecture is not successful because two databases contain equal values during a demo. It is successful when the organisation knows who may change a fact, can explain disagreement and can recover without data bouncing indefinitely between systems.
Common questions
No. Ownership depends on the fact. Odoo often owns accounting, stock and fulfilment, while Medusa owns the online checkout. Decide field by field.
They can propose changes, but one explicit conflict policy must decide the accepted value. Uncontrolled two-way editing usually becomes last-write-wins by accident.
Preserve the origin and identity of changes, distinguish commands from notifications and stop copied updates from being emitted again as new user-authored changes.
