CRM migration
Field-level mapping, validation, and rollback between OpenCRM and Odoo CRM. We move data and schema; workflows are rebuilt natively in Odoo CRM.
OpenCRM
Source
Odoo CRM
Destination
Compatibility
7 of 12
objects map 1:1 between OpenCRM and Odoo CRM.
Complexity
BStandard
Timeline
3-5 weeks
Overview
Moving from OpenCRM to Odoo CRM is a migration from a VtigerCRM-derived single-module CRM to a modular ERP suite where CRM is one application among dozens. OpenCRM stores Contacts linked to Companies via foreign-key, and Deals attached to either entity; Odoo models the same data using res.partner (with address_type for Contact vs Company), crm.lead for pipeline opportunities, and ir.attachment for files. The structural difference is that Odoo CRM lives inside the same database as accounting, inventory, and project modules, so data that stays siloed in OpenCRM (Contacts, Companies, Deals) can be joined against accounting records, purchase orders, and project tasks in Odoo — but only if the schema is designed correctly before import. We export from OpenCRM via the UI CSV mechanism (there is no public REST endpoint), stage and cleanse the data, then load through Odoo's xmlrpc/jsonrpc API. We do not migrate OpenCRM Workflows or automation rules as code; we deliver a written inventory of every active rule for the customer's Odoo admin to rebuild using Odoo Studio action rules and automated actions.
Every standard and custom field arrives verified.
AI proposes the map; you confirm before any record moves.
Parent–child, lookups, and ownership stay linked.
Calls, emails, meetings — with original timestamps.
Documents, uploads, and inline notes move with the record.
Why teams make this switch
Leaving
What's pushing teams away
Choosing
What's pulling them in
Object mapping
Each row shows how a OpenCRM object lands in Odoo CRM, including any object-level transformations, lookup resolution, or schema-design dependencies.
Typical mapping — final map is confirmed during the sample migration step.
OpenCRM
Company
Odoo CRM
res.partner (address_type = contact, is_company = true)
1:1OpenCRM Company records map to Odoo res.partner with is_company set to True. The company name maps to name, and all standard address fields (street, city, postcode, country) map directly. We use the OpenCRM record ID as an external identifier stored in a custom ref field on res.partner to enable safe upserts and cross-reference audit after migration. Companies must be imported before Contacts to satisfy Odoo's partner_id foreign-key on the contact partner record.
OpenCRM
Contact
Odoo CRM
res.partner (address_type = contact, is_company = false, parent_id = Company)
1:manyOpenCRM Contact records map to Odoo res.partner with is_company set to False and parent_id pointing to the Company res.partner created in the previous step. The parent_id is resolved using the OpenCRM record ID that we stored as the external reference on the Company partner. If a Contact has no parent Company in OpenCRM, we create an is_company partner record for it and flag it in the reconciliation report. Title, phone, mobile, email, and website map directly; any custom contact fields map to custom res.partner fields created during schema provisioning.
OpenCRM
Deal
Odoo CRM
crm.lead
1:1OpenCRM Deal records map to Odoo crm.lead with type = opportunity. The deal value maps to expected_revenue, the expected close date maps to date_deadline, and the owner maps to user_id by email matching. Deals attached to a Contact map with partner_id pointing to the Contact res.partner; Deals attached to a Company map with partner_id pointing to the Company res.partner. We resolve the Odoo partner_id using the OpenCRM record ID stored as the external reference on the res.partner.
OpenCRM
Pipeline Stage
Odoo CRM
crm.stage within crm.team pipeline
lossyOpenCRM pipeline stage names are tenant-defined and rarely align with Odoo's default crm.stage values (New, Qualified, Proposal, Negotiation, Won, Lost). We produce a stage-mapping table during scoping, present it to the customer for confirmation, and apply the mapping during crm.lead import so each record lands in the correct Odoo stage. Stage probabilities are mapped from OpenCRM to Odoo stage_sequence and probability fields. If the customer uses multiple OpenCRM pipelines, each maps to a separate crm.team in Odoo with its own stage sequence.
OpenCRM
Activity (Call, Meeting, Task)
Odoo CRM
mail.activity
1:1OpenCRM Activities map to Odoo mail.activity records linked via res_model = crm.lead or res.partner and res_id pointing to the migrated record. Activity type (call, meeting, task) maps to Odoo's activity_type_id, with the original activity name preserved in the activity note. Dates and timestamps are normalised to UTC before import. We resolve the Odoo user_id for activity assignment by email matching against the Odoo res.users table.
OpenCRM
Note
Odoo CRM
mail.message
1:1OpenCRM Notes attached to Contacts, Companies, or Deals map to Odoo mail.message records with message_type = comment. We link each message via res_model and res_id to the migrated crm.lead or res.partner using the OpenCRM record ID cross-reference. Note body migrates as plain text; rich formatting in OpenCRM Notes is simplified on import. Author attribution maps to the Odoo user by email.
OpenCRM
Attachment
Odoo CRM
ir.attachment
1:1OpenCRM file attachments stored against Contacts, Companies, or Deals are extracted to a file store during staging and then loaded into Odoo ir.attachment with res_model pointing to crm.lead or res.partner and res_id pointing to the migrated record ID. We generate reference links in the attachment name to the source OpenCRM record for audit traceability. Files are ingested via Odoo's base_import module or direct file write to the Odoo filestore depending on volume.
OpenCRM
User / Owner
Odoo CRM
res.users
1:1OpenCRM Owner records map to Odoo res.users by email matching. The OpenCRM owner reference on Contact, Company, and Deal is resolved to an Odoo user_id at migration time. Any OpenCRM Owner without a matching Odoo user is held in a reconciliation queue for the customer's Odoo admin to provision before the record import resumes.
OpenCRM
Custom Field
Odoo CRM
Custom field on res.partner or crm.lead
lossyOpenCRM custom fields on Contacts, Companies, and Deals are discovered during scoping, typed (text, number, date, picklist), and provisioned in Odoo via ir.model.fields or Odoo Studio before data import begins. Custom field values are included in the CSV staging files and mapped field-by-field during the import transform. Picklist values in OpenCRM are mapped to Odoo selection fields or many2one relations depending on the data.
OpenCRM
Tag / Label
Odoo CRM
crm.tag
lossyOpenCRM tags on Contacts and Companies map to Odoo crm.tag records with a many2many relation to crm.lead. Tags stored as flat string arrays in OpenCRM are split and deduplicated, and crm.tag records are created before the crm.lead import. The tag-many2many relation is established during the crm.lead insert using the tag names as the matching key.
OpenCRM
Workflow / Automation
Odoo CRM
Written inventory only
lossyOpenCRM Workflow rules are not migrated as executable code. We inspect each active OpenCRM workflow, document its trigger (record creation, field change, stage transition), conditions, and actions, and deliver a written inventory recommending Odoo Automated Actions (ir.actions.server) or Studio action rule equivalents. The customer's Odoo admin rebuilds the automations post-migration using Odoo Studio or the developer interface.
OpenCRM
Historical timestamps
Odoo CRM
crm.lead.create_date, write_date, mail.activity.date_deadline
1:1OpenCRM record creation and modification timestamps are preserved on the migrated Odoo records. crm.lead.create_date is set from the OpenCRM Deal creation date; mail.activity.date_deadline preserves the original activity due date. We normalise all timestamps to UTC during the staging phase to avoid timezone offset issues when loading into Odoo.
| OpenCRM | Odoo CRM | Compatibility | |
|---|---|---|---|
| Company | res.partner (address_type = contact, is_company = true)1:1 | Fully supported | |
| Contact | res.partner (address_type = contact, is_company = false, parent_id = Company)1:many | Fully supported | |
| Deal | crm.lead1:1 | Fully supported | |
| Pipeline Stage | crm.stage within crm.team pipelinelossy | Fully supported | |
| Activity (Call, Meeting, Task) | mail.activity1:1 | Fully supported | |
| Note | mail.message1:1 | Fully supported | |
| Attachment | ir.attachment1:1 | Fully supported | |
| User / Owner | res.users1:1 | Fully supported | |
| Custom Field | Custom field on res.partner or crm.leadlossy | Fully supported | |
| Tag / Label | crm.taglossy | Fully supported | |
| Workflow / Automation | Written inventory onlylossy | Fully supported | |
| Historical timestamps | crm.lead.create_date, write_date, mail.activity.date_deadline1:1 | Fully supported |
Gotchas + challenges
Platform-specific issues from each side, plus the pair-specific challenges that don't show up on either platform's page on its own.
OpenCRM gotchas
Bulk import without CRM ID or ExternalID creates duplicate records
Import ordering dependency: Companies before Contacts
No documented public REST API for programmatic export
Pipeline stage names are tenant-defined and require manual mapping
Odoo CRM gotchas
Odoo.sh version gating blocks assisted migrations from trial
Enterprise modules fail to install on Community after database restore
Custom module view inheritance breaks between Odoo major versions
Custom fields risk losing their application context on Community
API access for Community is gated behind the Custom Plan
Pair-specific challenges
Migration approach
Discovery and scoping
We export full-column CSV files from OpenCRM for every supported object (Companies, Contacts, Deals, Activities, Notes, Attachments) via the UI export mechanism. We audit the export to identify custom field names, active workflow rules, pipeline stage names, and tag usage. We also inventory the Odoo destination instance: edition (Community, Online, or Enterprise), installed apps, and existing partner and lead records. The discovery output is a written migration scope with object list, record counts per object, custom field inventory, and stage-mapping table for customer confirmation.
Odoo schema provisioning
We provision the Odoo destination schema before any data loads. This includes creating custom fields on res.partner and crm.lead via ir.model.fields, provisioning crm.team pipelines with customer-confirmed stage sequences, creating crm.tag records from OpenCRM tags, and confirming the res.users table has an active user for every OpenCRM Owner. Schema provisioning happens in an Odoo Sandbox or development environment first. We also configure the OpenCRM ExternalID field on the destination to store the source record ID for cross-reference and future upsert operations.
Data staging and cleansing
We parse the OpenCRM CSV exports and stage them for transformation. Cleansing steps include: deduplication on email address for Contacts and Company name for Companies, date format normalisation to ISO 8601 (UTC), address field standardisation, and identification of orphaned records (Contacts without a parent Company, Deals without a valid linked entity). Any data quality issues are reported to the customer for resolution before import begins. We do not silently drop records; every record that cannot be mapped is flagged in the reconciliation report.
Sandbox migration and validation
We run a full migration into the Odoo Sandbox environment using production-like data volume. The customer's Odoo administrator reconciles record counts (Partners in, Contacts in, Leads in, Activities in), spot-checks 25-50 random records against the OpenCRM source, and validates that parent_id linkages on res.partner are correct and that crm.lead stage assignments match the stage-mapping table. Sign-off on the sandbox migration is required before production cutover begins. Any mapping corrections are made in the transform scripts and validated in sandbox before the production run.
Production migration in dependency order
We run production migration in strict record-dependency order: res.users validation first ( Owners must exist in Odoo before records can reference them), then res.partner records for Companies (is_company = True), then res.partner records for Contacts with parent_id resolved, then crm.lead for Deals, then mail.activity for Activities, then mail.message for Notes, then ir.attachment for Files, and finally crm.tag relations on crm.lead. Each phase emits a row-count reconciliation report before the next phase begins. We freeze OpenCRM writes during the production cutover window and run a final delta migration of any records modified during the window before enabling Odoo as the system of record.
Cutover, validation, and automation handoff
We deliver a post-migration validation report comparing OpenCRM record counts to Odoo record counts per object, and a field-level spot-check on 50 random records. We deliver the OpenCRM Workflow and Automation inventory document to the customer's Odoo administrator for rebuild using Odoo Studio Automated Actions. We support a five-business-day hypercare window for reconciliation issues raised by the sales team. We do not rebuild OpenCRM workflows as Odoo automated actions inside the migration scope; that work is handled by the customer's Odoo admin or a separate Odoo implementation engagement.
Platform deep dives
OpenCRM
Source
Strengths
Weaknesses
Odoo CRM
Destination
Strengths
Weaknesses
Complexity grading
Standard CRM migration. 3 of 8 objects need a mapping; the rest are 1:1.
Overall complexity
Standard migration
Derived from compatibility, mapping clarity, API constraints, and data volume across OpenCRM and Odoo CRM.
Object compatibility
3 of 8 objects need a mapping; the rest are 1:1.
Field mapping clarity
Field mapping is derived from defaults — final spec confirmed during the sample migration.
Timeline complexity
8-object category — typical timelines run 2–7 days end-to-end.
API constraints
OpenCRM: Not publicly documented.
Data volume sensitivity
OpenCRM doesn't expose a bulk API — REST + parallelization used for high-volume runs.
Estimator
Rule-based pricing — no per-record fees, no manual quotes. Migrations over 2M records are scoped individually.
Step 1
Pick a category, then your source and destination platforms.
Category
FAQ
Answers to the questions buyers ask most during OpenCRM to Odoo CRM migration scoping. Not seeing yours? Book a call.
Walk through your OpenCRM to Odoo CRM migration with a real engineer — 30 minutes, free, written quote within 24 hours.
Book a free 30 minute consultationAdjacent paths
Other ways to leave OpenCRM
Other ways to arrive at Odoo CRM
Ready when you are
Tell us record counts and timeline. We'll come back with a written quote inside 1 business day — no commitment, no sales pitch.