CRM migration
Field-level mapping, validation, and rollback between Function 365 and Odoo CRM. We move data and schema; workflows are rebuilt natively in Odoo CRM.
Function 365
Source
Odoo CRM
Destination
Compatibility
11 of 12
objects map 1:1 between Function 365 and Odoo CRM.
Complexity
BStandard
Timeline
48–72 hours
Overview
Function 365 (Microsoft Dynamics 365 CRM) stores data across the Dynamics 365 data model: Contact, Account, Opportunity, and Activity entities with system fields like ownerid and statecode. Odoo CRM consolidates contacts and companies into res.partner (using partner_type to distinguish individuals from organizations), uses crm.lead for both raw leads and qualified opportunities (distinguished by type field), and stores activities as mail.message and mail.activity records linked by res_id. We map Contact rows to res.partner with partner_type='contact', Account rows to res.partner with partner_type='company', and Opportunity rows to crm.lead with type='opportunity'. Function 365's activityParty records (calls, emails, meetings) become mail.activity entries with activity_type_id matching Odoo's standard types. Owner resolution matches by email against res.users. Workflows, business rules, and plugin steps in Dynamics 365 do not migrate — we export workflow definitions as a SharePoint list your Odoo administrator can reference during Flow rebuild. The migration runs via Odoo's xmlrpc/JSON-RPC API against your target Odoo instance, with batch processing for large record sets.
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 Function 365 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.
Function 365
Contact
Odoo CRM
res.partner
1:1Function 365 Contact rows become res.partner records with partner_type set to 'contact'. The contact's primary account lookup (parentcustomerid in Dynamics 365) maps to the parent_id field on the res.partner record, establishing the relationship between individual contacts and their employing organizations. Email addresses, phone numbers, street addresses, city, state, and postal code fields map directly to their corresponding res.partner field names, preserving all standard contact information during the transformation process.
Function 365
Account
Odoo CRM
res.partner
1:1Function 365 Account rows become res.partner records with partner_type='company'. The company name maps to name, website maps to website, and industry code maps to industry. Child Contact records under an Account receive parent_id pointing to the newly created company partner, preserving the account-to-contact hierarchy in Odoo's partner structure.
Function 365
Opportunity
Odoo CRM
crm.lead
1:1Function 365 Opportunity rows map to crm.lead records where type='opportunity'. The accountid lookup resolves to the target res.partner (company) record. Estimatedvalue maps to planned_revenue; closeprobability maps to priority as a percentage. The original opportunityid is preserved in x_source_opportunity_id for reconciliation.
Function 365
Lead
Odoo CRM
crm.lead
1:1Function 365 Lead rows map to crm.lead records where type='lead'. LeadSource maps to source_id (linking to crm.tracking.source). The lead's companyname field is used to either match an existing res.partner company or create a new one before the lead converts to opportunity.
Function 365
PhoneCall / Email / Meeting
Odoo CRM
mail.activity
1:1Function 365 activityparty records (phonecall, email, appointment) become mail.activity entries. The activity_type_id is resolved by matching the Dynamics 365 activity type code: 1=Call, 2=Email, 3=Meeting. The res_id links to the target crm.lead or res.partner; the user_id links to the activity's owner.
Function 365
Annotation (Notes/Attachments)
Odoo CRM
ir.attachment
1:1Function 365 Annotation records with notetype set to 'annotation' migrate as ir.attachment entries linked to the target model (crm.lead or res.partner) through the res_model and res_id fields. The original document body content and filename are preserved intact during the migration, and Odoo's standard attachment storage path is utilized on the destination instance for all migrated annotation files.
Function 365
SystemUser
Odoo CRM
res.users
1:1Function 365 systemuser records are matched to Odoo res.users by email address (internalid). Unmatched users are flagged before migration; your team either creates the Odoo user first or assigns records to a fallback user. The user's full name populates the res.users name field.
Function 365
BusinessUnit
Odoo CRM
crm.team
1:1Function 365 BusinessUnit rows map to crm.team records in Odoo, with each business unit becoming a separate CRM team. The business unit's name and member list (derived from systemuser positions within the business unit) populate the team name and team member structure in Odoo. All Opportunity records owned by a business unit are reassigned to the corresponding crm.team based on the opportunity's owning business unit lookup.
Function 365
Stage (ProcessStage)
Odoo CRM
crm.stage
1:1Dynamics 365 pipeline stages defined in the ProcessStage entity are mapped value-by-value to Odoo crm.stage records within each corresponding crm.team. The stage's statecode (Active, Won, or Lost) maps to the folded and stage_type flags on the crm.stage record. The original sort order from Dynamics 365's ProcessStage stepid is preserved to maintain the kanban column sequence in Odoo's pipeline view.
Function 365
Custom Entity (x_ tables)
Odoo CRM
Custom x_ model in Odoo
1:1Function 365 custom entities (tables created in the Power Platform or solution layers) require Odoo custom model creation via the ORM. We create the model with the appropriate _name, add x_ prefix fields matching the source schema, and set up the many2one relations to standard Odoo models before data loads.
Function 365
LeadToOpportunityProcess (workflow)
Odoo CRM
crm.lead convert wizard
1:1Dynamics 365's LeadToOpportunityProcess plugin does not have a direct Odoo equivalent. The conversion logic (creating an Opportunity from a Lead, linking the Contact, setting initial stage) must be replicated using Odoo's crm.lead convert wizard after migration. We document the source conversion rules as a rebuild reference.
Function 365
Connection (record relationships)
Odoo CRM
mail.followers / res.partner relations
many:1Function 365 Connections representing N:N record relationships used for customer partnerships and stakeholder tracking are merged into Odoo's mail.followers model for record following functionality, and partner_id relations on crm.lead for stakeholder association. Only connections that represent 'stakeholder' roles migrate as partner_ids on the opportunity record, ensuring that relevant business relationships are preserved in the target Odoo system.
| Function 365 | Odoo CRM | Compatibility | |
|---|---|---|---|
| Contact | res.partner1:1 | Fully supported | |
| Account | res.partner1:1 | Fully supported | |
| Opportunity | crm.lead1:1 | Fully supported | |
| Lead | crm.lead1:1 | Fully supported | |
| PhoneCall / Email / Meeting | mail.activity1:1 | Fully supported | |
| Annotation (Notes/Attachments) | ir.attachment1:1 | Fully supported | |
| SystemUser | res.users1:1 | Fully supported | |
| BusinessUnit | crm.team1:1 | Fully supported | |
| Stage (ProcessStage) | crm.stage1:1 | Fully supported | |
| Custom Entity (x_ tables) | Custom x_ model in Odoo1:1 | Fully supported | |
| LeadToOpportunityProcess (workflow) | crm.lead convert wizard1:1 | Fully supported | |
| Connection (record relationships) | mail.followers / res.partner relationsmany: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.
Function 365 gotchas
AI-assisted notes are proprietary — verify clinical-record export coverage
NHS Number format must be preserved exactly
Implementation specialist time is paid extra at £55/session
GDPR consent timestamps are regulatory artefacts
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
Extract Function 365 data via Dataverse API
We authenticate against your Dynamics 365 tenant using an Azure AD application registration (oauth2 with client credentials flow) and pull data via the Dataverse Web API. For each entity (Contact, Account, Opportunity, Lead, ActivityPointer, Annotation), we paginate usingodata.nextLink to handle large result sets. The extraction runs against a read-only application user to avoid disrupting active sessions. We log record counts per entity and flag any API throttling responses for retry with exponential backoff. The export produces a structured JSONL file per entity, timestamped and checksummed for integrity verification.
Audit Odoo target schema and create custom models
Before any data loads, we inspect the target Odoo instance using xmlrpc to read ir.model and ir.model.fields. We identify which standard fields already exist and which x_ custom fields need creation. For each Dynamics 365 custom entity, we generate a Python model definition and a CSV data file that creates the model via Odoo's module loading mechanism. We also query existing crm.stage and crm.team records to map the target pipeline structure. The schema plan is delivered as a human-readable document that your Odoo admin reviews and approves before FlitStack creates any fields.
Run a sample migration with field-level diff
A representative sample — typically 100–300 records per major entity — migrates first using Odoo's xmlrpc/jsonrpc endpoint. We write each record, then query it back and generate a diff showing source field value vs. destination field value. The diff report highlights: value-mapping mismatches (e.g., industry codes that didn't resolve), owner resolution failures (unmatched emails), and truncation issues on text fields with Odoo's character limits. Your team reviews the diff and approves field mapping adjustments before the full migration runs.
Execute full migration with delta-pickup window
The full dataset loads in batches of 500 records per entity using Odoo's create() method via the ORM API. Batch processing avoids transaction timeout on large inserts. After the initial load completes, we open a 24–48 hour delta-pickup window during which any records modified in Dynamics 365 are re-extracted and updated in Odoo using write() with the original source GUID preserved in x_ fields. A final reconciliation query counts source records vs. destination records per entity and surfaces any remaining delta records. The audit log records every create and write operation with timestamp and operator.
Deliver reconciliation report and rollback playbook
The final deliverable is a reconciliation report showing: record counts per entity (source vs. destination), owner resolution rate (percentage of records with a matched Odoo user), delta records remaining, and field-level error summary. If reconciliation fails any SLA threshold, FlitStack runs a one-click rollback that deletes the migrated records and reverts the Odoo database to its pre-migration snapshot. The workflow export (Power Automate definitions) and lead-conversion mapping reference are delivered as SharePoint document attachments in the final report package.
Platform deep dives
Function 365
Source
Strengths
Weaknesses
Odoo CRM
Destination
Strengths
Weaknesses
Complexity grading
Standard CRM migration. All 8 core objects map 1:1 between Function 365 and Odoo CRM.
Overall complexity
Standard migration
Derived from compatibility, mapping clarity, API constraints, and data volume across Function 365 and Odoo CRM.
Object compatibility
All 8 core objects map 1:1 between Function 365 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
Function 365: Not publicly documented.
Data volume sensitivity
Function 365 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 Function 365 to Odoo CRM migration scoping. Not seeing yours? Book a call.
Walk through your Function 365 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 Function 365
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.