DEV Community

Cover image for How Consultants Map Existing Workflows to Odoo (Down to the Model Names)
Mohit
Mohit

Posted on

How Consultants Map Existing Workflows to Odoo (Down to the Model Names)

Most workflow mapping produces a diagram. Diagrams are the wrong deliverable.

What an implementation actually needs is a crosswalk: every step of your current process, sitting next to the Odoo model and field that will carry it, with the steps that have no home flagged and classified. That artefact is testable. A swimlane diagram is not — you cannot run a query against it to find out whether it is true.

Here is the method, including the checks that catch a bad mapping before it reaches a statement of work.

Step 1: capture the process as events, not boxes

Do not start with a workshop asking people to describe their process. You will get the official version.

Start by picking one real document and tracing it backwards. "Show me the last order you shipped" — then follow it: who typed it, what they typed it into, what triggered the next person, what they printed, where the paper went, what got re-keyed. For each step, record five things:

  • the trigger (a time, an email, a phone call, someone walking over)
  • the actor (a role, not a name)
  • the system of record at that moment
  • the decision being made, if any
  • the artefact produced (a document, a status change, a note in a spreadsheet) You want three to five traces per document type, ideally including one that went wrong. The exception traces are where the real requirements live. A process map built only from happy paths maps a business that doesn't exist.

Skip BPMN at this stage. Formal notation is excellent for communicating an agreed process and useless for discovering a disputed one, and every hour spent on notation is an hour not spent finding the fourth exception nobody mentioned.

Step 2: name the Odoo object for every step

This is the actual mapping work, and it is unglamorous. Each captured step gets a row.

Current process step Odoo model Fields that carry the meaning What usually goes wrong
Customer and their delivery sites res.partner parent_id, type (delivery / invoice) Sites modelled as separate customers, which breaks consolidated credit and statements
Quotation sale.order, state draftsent state, validity_date, pricelist_id Two legacy statuses both land on sent
Confirmed order sale.order, state = sale date_order, commitment_date "Confirmed" and "released to warehouse" are one state here, not two
Pick, pack, dispatch stock.picking + stock.move / stock.move.line picking_type_id, state, lot_id Assumed to need code; multi-step routes are configuration
Stock available to promise stock.quant quantity, reserved_quantity, location_id Legacy "available" rarely equals quantityreserved_quantity
Invoice account.move, move_type = out_invoice invoice_date, invoice_payment_term_id Proforma has no native state and gets faked badly
Volume or contract pricing product.pricelist.item min_quantity, compute_price, date_start / date_end Quoted as custom development when it is a pricelist rule
Works order mrp.production bom_id, state, qty_produced Byproducts and scrap left unmapped until go-live

Three rules make this table do real work:

If two current statuses map to the same model and state, you have lost a distinction. Decide now whether it comes back as a field, a saved filter, or not at all. Discovering it during user acceptance testing is how you end up with a custom field called x_status_2.

If one current step maps to two Odoo models, your step was hiding a handoff. That handoff is almost always where the process actually fails today. Split the row.

If a step maps to nothing, it goes to Step 3. Do not force it.

Step 3: classify every gap into one of four buckets

The value of the mapping is not the rows that match. It is the disciplined sorting of the rows that don't.

Bucket 1 — configuration. Exists in standard Odoo, needs setting up: multi-step routes, operation types, warehouses, pricelist items, payment terms, fiscal positions, units of measure. No code, no Studio, no cost beyond consulting time. Most first-draft gap lists are 60–70% this bucket once someone experienced reads them.

Bucket 2 — no-code automation. Odoo's automation rules cover a surprising amount: trigger on create or write or a time condition, then update a field, send a message, or run a small server action. Combined with Studio computed fields, this handles most "we need the system to notice X and do Y" requirements. Note the licensing constraint — Studio sits on the Custom plan, not Standard, so a business budgeted at Standard has no bucket 2 at all.

Bucket 3 — code. A custom module: _inherit = 'sale.order', new fields under a consistent prefix, an overridden method, tests. This is the bucket that scales your invoice and your upgrade risk, because every item in it must be re-tested on every version bump.

Bucket 4 — not in Odoo. Belongs in another system, or is a habit that dies with the migration. Write down which, and who agreed.

The discipline that matters: nothing is allowed into bucket 3 until someone has genuinely attempted it in buckets 1 and 2 and failed. In mappings we have reviewed, the single most common error is a requirement written as custom development because the person capturing it did not know that standard functionality already covered it — a pricelist rule quoted as a module, a multi-step delivery route quoted as a workflow engine.

Whether the build then runs in-house or through an Odoo implementation company, insist the bucket-3 list is signed before anyone quotes a fixed price. The count of items in that bucket, not the number of apps you are switching on, is what determines the number on the invoice.

Step 4: prove the mapping against real data

A mapping that has not been tested against your own records is a hypothesis. Two cheap checks turn it into a finding.

Load a real slice with stable identifiers. Import a few thousand real records using an id column carrying your legacy key as an External ID. Re-running the import then updates rather than duplicating, which means you can iterate the mapping instead of rebuilding the database each time you learn something.

Run the crosswalk check. Put the legacy status on the record as a temporary field, then count the pairs. In SQL, against a copy:

SELECT x_legacy_status, state, count(*)
FROM sale_order
WHERE x_legacy_status IS NOT NULL
GROUP BY 1, 2
ORDER BY 1, 3 DESC;
Enter fullscreen mode Exit fullscreen mode

Or through the ORM, which works without database access. Odoo 19 documents an external RPC API alongside a separate JSON-2 API; the long-standing XML-RPC path still reads cleanly:

import xmlrpc.client

common = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/common")
uid = common.authenticate(db, username, api_key, {})
models = xmlrpc.client.ServerProxy(f"{url}/xmlrpc/2/object")

pairs = models.execute_kw(
    db, uid, api_key,
    'sale.order', 'read_group',
    [[('x_legacy_status', '!=', False)]],
    {'fields': ['id'], 'groupby': ['x_legacy_status', 'state'], 'lazy': False},
)
Enter fullscreen mode Exit fullscreen mode

Read the output for two failure signatures. A legacy status spread across several Odoo states means your mapping rule is ambiguous. Two legacy statuses sharing one Odoo state means a distinction is about to disappear silently. Both are decisions somebody has to make; the query only tells you they exist. External API access also sits on the Custom plan, which is worth knowing before you promise this check.

What the finished mapping document contains

Not a diagram. Six things: the crosswalk table, the four bucket lists with an owner against each item, the traces you captured including the exceptions, the list of legacy statuses being deliberately dropped, the crosswalk query output, and a named business person's sign-off per document type.

Where this method falls down

It goes stale. A mapping is accurate on the day it is signed and drifts from then on, so if the project stalls for a quarter the exception traces need re-running rather than trusting.

It also invites over-investment. There is a genuine failure mode where three weeks disappear into beautifully complete documentation that nobody opens during the build, and the honest answer is that for a small single-company deployment, one afternoon of tracing per document type is enough.

And mapping is descriptive, not evaluative. It tells you where each current step lands in Odoo. It cannot tell you that a step should be deleted rather than migrated — that judgement comes from the people doing the work, which is the argument for having them in the room rather than sending them a diagram to approve.

Start with one document

Pick your highest-volume document type. Trace three real instances end to end, one of which went wrong. Write the crosswalk rows. Then sort the gaps into the four buckets and count bucket 3.

That number is your project. Everything else is scheduling.

Top comments (0)