CRM migration
Field-level mapping, validation, and rollback between Civicrm and Odoo CRM. We move data and schema; workflows are rebuilt natively in Odoo CRM.
Civicrm
Source
Odoo CRM
Destination
Compatibility
8 of 15
objects map 1:1 between Civicrm and Odoo CRM.
Complexity
BStandard
Timeline
5-7 weeks
Overview
CiviCRM and Odoo CRM share an open-source DNA but differ fundamentally in their data model and target sector. CiviCRM is purpose-built for nonprofits: Contributions, Memberships, Grants, Events, and Cases are first-class entities. Odoo CRM is one module inside a broader ERP suite where the partner (contact) model and opportunity pipeline are primary, and nonprofit-specific objects require the Odoo非营利应用 (nonprofit app) or custom fields. We bridge this gap by mapping CiviCRM subtypes (Individual, Household, Organization) to Odoo Partner types, Contributions to a custom crm.donation model, Memberships to Odoo Subscriptions or custom membership fields, and CiviCase chains to Odoo Project tasks with activity history preserved. ECK (Entity Construction Kit) entities migrate as Odoo custom models requiring schema pre-creation. We do not migrate CiviMail mailing records, CiviRules automated processes, or CMS-coupled extensions; we deliver a written inventory of these for the customer's admin to address post-migration.
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 Civicrm 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.
Civicrm
Contact (Individual)
Odoo CRM
Partner (individual type)
1:1CiviCRM Individual contacts map to Odoo res.partner with is_company=False and individual_type set to 'natural_person'. Standard name fields (first_name, last_name), addresses (with location_type: work, home, other), emails (with location_type), phones (with phone_type), and the contact's preferred_language migrate directly. We resolve the CiviCRM preferred_language field to res.partner lang field at import time.
Civicrm
Contact (Organization)
Odoo CRM
Partner (company type)
1:1CiviCRM Organization contacts map to Odoo res.partner with is_company=True. Organization name maps to name (not legal_name separately), and the legal_name field from CiviCRM maps to Odoo's company_registry if a registry number is present. Organization subtypes (Employer, Government, Non-profit) map to Odoo customer_rank and supplier_rank flags based on the organization's contribution history.
Civicrm
Contact (Household)
Odoo CRM
Partner (contact subtype)
lossyCiviCRM Household contacts have no native Odoo equivalent because Odoo's res.partner model treats households as Organizations with individual members. We create a parent Partner record for the household and attach individual members as child contacts with partner_share=True. The household address migrates to the parent partner; member relationship types (e.g., 'head of household') map to Odoo res.partner relation types that we pre-create during schema setup.
Civicrm
Activity
Odoo CRM
Project Task Activity Log
1:1CiviCRM Activities (calls, emails, meetings, tasks) map to Odoo Project tasks in a dedicated 'CiviCRM Activity History' project. Each activity type (Meeting, Phone Call, Email, Task) becomes a task with the original activity subject, details body, date, and assignee preserved. We use the Odoo Project API to create tasks in batch, with the project_id resolved before task import begins.
Civicrm
Group and GroupContact
Odoo CRM
Odoo Tag + Project Team
1:1CiviCRM Groups with static membership (GroupContact records) map to Odoo Tags on the Partner record. For groups larger than 500 members where tag proliferation becomes unwieldy, we create a dedicated Odoo Project team or partner category hierarchy and add members to that team. Dynamic (smart) groups cannot be reproduced without re-running the filter logic; we document the filter criteria for the customer's admin to implement in Odoo.
Civicrm
Contribution
Odoo CRM
Custom: crm.donation (or Account Move lines)
lossyOdoo has no native donation or contribution module in the base CRM. We create a custom crm.donation model with fields mapping to financial_type, total_amount, currency, receive_date, payment_instrument, and contribution_status. For organizations already using Odoo Accounting, we alternatively map Contributions to Account Move lines (invoices/payments) linked to the partner, which provides native financial reporting. The customer chooses the model during scoping, and we pre-create the custom model via Odoo data migration before any records load.
Civicrm
Membership
Odoo CRM
Custom membership fields on Partner or Subscription product
lossyCiviMember records (type, status, start_date, end_date, source, membership_price_set tier) map to custom fields on the Odoo res.partner: membership_type__c, membership_status__c, membership_start_date__c, membership_end_date__c. For organizations with complex membership tier structures, we create Odoo Product.product variants representing each tier and link them to the partner via sale order lines for historical tracking. We preserve the membership status timeline (new, current, grace, expired) as a status history JSON blob in a long-text field.
Civicrm
Event and Participant
Odoo CRM
Odoo Event app (calendar.event) or Project tasks
lossyCiviCRM Events map to Odoo Calendar events with event_type, start_date, end_date, and location preserved. CiviCRM participant roles (Attendee, Speaker, Volunteer, Host) map to custom event.registration fields or tags. If the Odoo Events app is not installed in the destination database, we fall back to Project tasks with a dedicated 'Events' project to preserve event records. Price sets for paid events require Odoo Product and Sale Order line mapping before event registrations migrate.
Civicrm
CiviCase
Odoo CRM
Odoo Project + Tasks
1:1CiviCase records map to Odoo Project with a dedicated project per case_type. Each case's activity chain (the set of CiviCRM Activities linked to the case) migrates as sequential tasks within the Odoo project. We enumerate every active case_type during scoping, extract its status option values, and create corresponding Odoo stage records in the project. The Odoo project name carries the original CiviCRM case_id for audit traceability. Case statuses are type-scoped in CiviCRM and must be extracted per case_type XML before mapping.
Civicrm
Relationship
Odoo CRM
Partner relation type + related Partner
1:1CiviCRM relationship types (employee-employer, spousal, household member, etc.) map to Odoo res.partner relation types that we pre-create during schema setup. Both the source and target contact must exist in Odoo before the relationship can be established, so we load contact records before relationship records. The bidirectional flag on CiviCRM relationship types translates to a symmetric Odoo relation type where applicable.
Civicrm
Tag
Odoo CRM
Partner Tag (res.partner.category)
1:1CiviCRM Tags are flat entity-level labels that attach to Contacts, Activities, or other records. We map them to Odoo res.partner.category entries for Contact tags, and to Project task tags for Activity tags. Multi-entity tagging is split into separate tag migrations per entity type since Odoo tags are model-scoped.
Civicrm
Grant (CiviCRM extension)
Odoo CRM
Custom: crm.grant or Project milestone
lossyGrants is an optional CiviCRM extension present in some nonprofit instances. We create a custom crm.grant model in Odoo with grant_type, amount_requested, amount_granted, status, and deadline fields, linked to the applying Organization partner. For organizations that use Odoo Project for grant-funded work, we alternatively map grants to project milestones with the grant amount as the milestone value.
Civicrm
ECK Entity (Entity Construction Kit)
Odoo CRM
Custom Odoo Model (ir.model + ir.model.fields)
lossyECK allows arbitrary custom entity types in CiviCRM with user-defined schemas. These have no standard mapping to Odoo. We pre-create a corresponding custom model in Odoo (via ir.model and ir.model.fields) for each ECK entity type, preserving all custom property fields and any contact lookups. ECK entity data migrates as records in the new custom model after the standard contact migration completes. The customer identifies ECK usage during scoping.
Civicrm
Attachment / File
Odoo CRM
IrAttachment linked to Partner or Project
1:1CiviCRM stores file attachments in the database (civicrm_file / civicrm_entity_file) or on the filesystem. We extract the file content and re-associate with the destination record using the original CiviCRM entity table name and entity_id as an external reference stored in the ir_attachment description field. Files linked to Contacts attach to the migrated Odoo partner; files linked to Cases attach to the migrated Odoo project task.
Civicrm
Custom Fields (CustomValue / Custom_* multi-record)
Odoo CRM
Odoo custom fields (ir.model.fields with custom=True)
lossyCiviCRM single-record custom fields on contacts (custom groups with is_multiple=False) map to custom fields on res.partner in Odoo. Multi-record custom groups (Custom_* prefixed tables) map to Odoo one2many lines on a custom model that we create per custom group. We query the total custom group count during scoping; sites with more than 20 custom groups exceed MySQL's 61-join limit in CiviCRM APIv4 and require entity-by-entity export rather than bulk wildcard queries.
| Civicrm | Odoo CRM | Compatibility | |
|---|---|---|---|
| Contact (Individual) | Partner (individual type)1:1 | Fully supported | |
| Contact (Organization) | Partner (company type)1:1 | Fully supported | |
| Contact (Household) | Partner (contact subtype)lossy | Fully supported | |
| Activity | Project Task Activity Log1:1 | Fully supported | |
| Group and GroupContact | Odoo Tag + Project Team1:1 | Fully supported | |
| Contribution | Custom: crm.donation (or Account Move lines)lossy | Fully supported | |
| Membership | Custom membership fields on Partner or Subscription productlossy | Fully supported | |
| Event and Participant | Odoo Event app (calendar.event) or Project taskslossy | Fully supported | |
| CiviCase | Odoo Project + Tasks1:1 | Fully supported | |
| Relationship | Partner relation type + related Partner1:1 | Fully supported | |
| Tag | Partner Tag (res.partner.category)1:1 | Fully supported | |
| Grant (CiviCRM extension) | Custom: crm.grant or Project milestonelossy | Fully supported | |
| ECK Entity (Entity Construction Kit) | Custom Odoo Model (ir.model + ir.model.fields)lossy | Fully supported | |
| Attachment / File | IrAttachment linked to Partner or Project1:1 | Fully supported | |
| Custom Fields (CustomValue / Custom_* multi-record) | Odoo custom fields (ir.model.fields with custom=True)lossy | 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.
Civicrm gotchas
Server-to-server migration requires CMS settings file portability
Multi-record custom groups can hit MySQL's 61-join limit
No native bulk export — data portability is API- or database-dependent
CiviCase statuses are per-case-type — not a global status list
Hosted Spark tier has no documented API rate limit — performance varies by plan
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 Odoo module selection
We audit the source CiviCRM instance for contact volume by subtype (Individual, Household, Organization), custom field count and multi-record set count, active case_types and their status option values, contribution history volume, membership tiers and price sets, ECK entity types in use, and API vs direct database access feasibility. We pair this with an Odoo module inventory: whether the Odoo nonprofit app is available, whether Odoo Accounting is present, and which Odoo custom models need pre-creation. The discovery output is a written migration scope with object-level mapping, a custom model design document, and an Odoo module recommendation.
Custom model pre-creation and Odoo schema design
We pre-create all required custom models in the destination Odoo database before any data migration begins. This includes the crm.donation or crm.grant model if applicable, custom membership fields on res.partner, ECK-equivalent custom models with the correct ir.model and ir.model.fields definitions, and Odoo partner relation types that map CiviCRM relationship types. Custom models are deployed into an Odoo Sandbox or development environment first for validation. We also configure the CiviCRM Activity History project and any case-type project structures in Odoo Project during this phase.
Sandbox migration and reconciliation
We run a full migration into an Odoo test environment using production-like data volumes. The customer's Odoo admin reconciles record counts across all objects (partners by type, activities, contributions, memberships, cases), spot-checks 30-50 records against the CiviCRM source, and validates that contribution totals match by financial_type. Any field mapping corrections, custom field type adjustments, or case-type project configurations happen in this phase. The customer signs off the mapping before production migration begins.
Owner and relationship reconciliation
CiviCRM Contacts, Activities, and Cases reference owner IDs (CiviCRM users or CMS users). We extract every distinct owner referenced in the source data and map them by email to Odoo User accounts. Any CiviCRM owner without a matching Odoo User goes to a reconciliation queue for the customer's admin to provision before record import resumes. Contact relationships (employee-employer, household member, spousal) require both partner records to exist before the relationship can be established, so partner loading precedes relationship loading.
Production migration in dependency order
We run production migration in strict record-dependency order: Odoo custom models (ir.model, ir.model.fields), Odoo Users (manual provisioning validated), Partners by type (Organizations first, then Individuals, then Households via parent-child structure), Relationship records, Custom fields on partners, Contributions or Account Move lines, Membership records, Events and participants, Project structures for CiviCase, Case task chains, Activities in the Activity History project, Attachments (ir_attachment), Tags, and ECK entities last because they often have partner lookups. Each phase emits a row-count reconciliation report before the next phase begins.
Cutover, delta migration, and CiviMail and automation handoff
We freeze writes to CiviCRM during cutover, run a final delta migration for any records modified during the migration window, then enable Odoo as the system of record. We deliver a written inventory of active CiviRules automated processes, CiviMail mailing records, and CMS-coupled extensions that cannot migrate. The inventory includes the CiviRules trigger, conditions, and actions with a recommended Odoo automated action equivalent for the customer's admin to rebuild. We support a one-week hypercare window where we resolve reconciliation issues. We do not rebuild CiviRules, CiviMail, or workflow automations as part of the migration scope.
Platform deep dives
Civicrm
Source
Strengths
Weaknesses
Odoo CRM
Destination
Strengths
Weaknesses
Complexity grading
Standard CRM migration. All 8 core objects map 1:1 between Civicrm and Odoo CRM.
Overall complexity
Standard migration
Derived from compatibility, mapping clarity, API constraints, and data volume across Civicrm and Odoo CRM.
Object compatibility
All 8 core objects map 1:1 between Civicrm and Odoo CRM.
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
Civicrm: Not publicly documented — Spark tier has no published limit; self-hosted performance is infrastructure-dependent.
Data volume sensitivity
Civicrm 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 Civicrm to Odoo CRM migration scoping. Not seeing yours? Book a call.
Walk through your Civicrm 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 Civicrm
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.