CRM migration
Field-level mapping, validation, and rollback between Opal CRM and Odoo CRM. We move data and schema; workflows are rebuilt natively in Odoo CRM.
Opal CRM
Source
Odoo CRM
Destination
Compatibility
10 of 15
objects map 1:1 between Opal CRM and Odoo CRM.
Complexity
BStandard
Timeline
3-5 weeks
Overview
Moving from Opal CRM to Odoo CRM is a structural migration from a field-sales niche tool to a full modular ERP with CRM capabilities. Opal CRM organizes data around field-sales workflows with Tour Planning, lead engagement, and quotation approval, but has no publicly documented REST API or bulk export endpoint. We work around this by coordinating a direct data extraction with the customer during discovery, validating export completeness against UI record counts, and reconstructing any flattened Tour Plan expense data before loading into Odoo. We map Opal Leads to Odoo crm.lead, Opal Sales Representatives to Odoo res.users, Opal Quotations to Odoo sale.order, and Opal Tour Plans to a combination of crm.lead (for visit scheduling) and account.move entries (for expenses). Quotation workflow approval states and custom role permissions are preserved as custom fields and documented for manual Odoo configuration. We do not migrate Opal automations, Tour Plan approval logic, or quotation workflow rules as code; these require rebuilding in Odoo Studio or via custom modules 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 Opal CRM 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.
Opal CRM
Lead
Odoo CRM
crm.lead
1:1Opal CRM Leads map directly to Odoo crm.lead records. We set type=lead for unqualified prospects and type=opportunity once the lead has a quoted value or assigned opportunity in Opal. The crm.lead name field maps from Opal lead name, email maps to email_from, phone maps to phone, and the lead source attribution from Opal becomes a custom tag or stage origin field on crm.lead. We preserve any custom field values Opal exports asChar or asText fields mapped to equivalent crm.lead custom fields created via Odoo Studio before import.
Opal CRM
Sales Representative
Odoo CRM
res.users
1:1Opal CRM Sales Reps map to Odoo res.users. The res.users login maps from the Opal rep email, name maps from the rep display name, and access rights are set to sales / user: the default Odoo CRM access for a sales representative. Role permissions (Admin, Manager, Sales Rep) in Opal do not export as structured data; we capture the role assignment per user and document it in the handover notes so the Odoo admin assigns the equivalent access rights (Settings > Users > Access Rights) after migration.
Opal CRM
Company
Odoo CRM
res.partner
1:1Opal CRM Companies attached to Leads map to Odoo res.partner with partner_type set to company. The company name maps to the partner name field, domain maps to website, and any contact person within the company becomes a separate res.partner record with parent_id pointing to the company partner. We use the company name or domain as the dedupe key during import to avoid creating duplicate partner records.
Opal CRM
Tour Plan
Odoo CRM
crm.lead (visit) + account.move (expenses)
1:manyTour Plans store both itinerary data and expense line items, which require a two-part migration. The tour itinerary (dates, locations, assigned rep) migrates as a crm.lead record with type=opportunity and a custom visit_date field, treating each Tour Plan as a planned field visit. Individual expense line items are extracted and migrated as account.move records (expense entries) with analytic account tags so the customer's accountant can reconcile them post-migration. If Opal exports Tour Plans as a flat record with expense items collapsed into a notes field, we decompress the expense data and reconstruct structured transaction rows before Odoo load.
Opal CRM
Quotation
Odoo CRM
sale.order
1:1Opal CRM Quotations map to Odoo sale.order. The quotation header fields (customer reference, date, validity, payment terms) map to sale.order fields; line items (product, quantity, unit price, tax) map to sale.order.line. We resolve the Opal customer reference to a res.partner id and the Opal product name or SKU to a product.product id before inserting. Opal's quotation workflow approval state is captured in a custom field opal_workflow_state__c on the sale.order and documented for the admin to translate into Odoo's sale.advance.payment.inv workflow or a custom approval module.
Opal CRM
Quotation Line Item
Odoo CRM
sale.order.line
1:1Opal quotation line items map to Odoo sale.order.line. We map product reference (name or SKU) to product_product, quantity to product_uom_qty, unit price to price_unit, and any discount percentage to discount. Tax handling depends on whether Opal stores tax codes or tax amounts: tax codes are mapped to account_tax ids in Odoo; amounts are stored as price_unit inclusive of tax with a flag note. If Opal does not export product SKUs, we match by product name string to the nearest Odoo product_product record and flag any unmatched lines for manual mapping.
Opal CRM
Pipeline Stage
Odoo CRM
crm.stage
lossyOpal CRM pipeline stage names and ordering migrate to Odoo crm_stage records within the CRM app. We set stage.name from the Opal stage label, stage.sequence for ordering, and stage.is_won or is_closed based on whether the Opal stage represents a closed-won or closed-lost state. Stage probability percentages migrate to stage.probability. If Opal exports pipeline stage as a string property on the lead rather than a separate object, we query all distinct values and create crm_stage records during Odoo configuration before lead import begins.
Opal CRM
Activity: Call
Odoo CRM
mail.activity (type=call)
1:1Opal CRM call engagement logs map to Odoo mail.activity records with activity_type_id set to the built-in Call type (or a custom call type created in Odoo Settings > Technical > Activity Types). Call duration maps to a custom duration field on the activity; call disposition or outcome maps to note or a custom picklist field. We link the activity to the corresponding crm.lead (WhoID) via activity.res_model=crm.lead and activity.res_id pointing to the migrated lead id. Activity date maps from the Opal call timestamp.
Opal CRM
Activity: Email
Odoo CRM
mail.message
1:1Opal CRM email engagements map to Odoo mail.message records on the crm.lead. The email subject becomes message_subject, body becomes body_html (stored as HTML fragment), and sender maps from the Opal rep email to mail.message.author_id. We set message.message_type=email and link to the crm.lead via res_model=crm.lead and res_id. If Opal exports email content as plain text, we wrap it in HTML before storing in Odoo's message body field.
Opal CRM
Activity: Meeting
Odoo CRM
calendar.event
1:1Opal CRM meeting engagements map to Odoo calendar.event records. The meeting title maps to name, start datetime maps to start, end datetime maps to stop, location maps to location, and attendee list maps to partner_ids (resolved via email to res.partner ids). We link the calendar.event to the crm.lead via custom context field or an activity record so that the meeting appears in the lead's activity timeline. Recurrence patterns from Opal do not migrate and are noted in the handover document for manual Odoo calendar recurrence setup.
Opal CRM
Activity: Task
Odoo CRM
project.task
1:1Opal CRM task engagements map to Odoo project.task records linked to a default CRM project (project_id set to the CRM project's internal id). Task title maps to name, due date maps to date_deadline, priority maps from Opal priority to task priority, and description maps to description. Assigned user maps from Opal rep to res.users. Tasks without a due date are imported with no date_deadline set. If the customer's Odoo instance does not have Project installed, we fall back to storing tasks as mail.activity records instead.
Opal CRM
Custom Properties
Odoo CRM
ir.model.fields (x_*)
lossyOpal CRM custom fields discovered during discovery do not have a publicly documented schema, so we identify them by comparing the Opal export header row against the known Opal standard field set. Any non-standard columns are treated as custom. We create matching custom fields in Odoo via Settings > Technical > Database Structure > Models, appending x_ prefix to the field name, matching the field type (char, text, integer, float, date, select, multiselect) based on the export data. Custom fields are created before any data import to ensure the Odoo import wizard accepts the mapped values without type errors.
Opal CRM
Role Permissions
Odoo CRM
res.groups membership
lossyOpal CRM role assignments (Admin, Manager, Sales Rep) do not export as structured permission records. We capture the role assignment per user from the Opal team member list and document it in the handover notes. The Odoo admin recreates the permission set by assigning res.groups (Settings > Users > Access Rights) or by creating custom access rights groups via Settings > Technical > Access Rights > User Types. The mapping table we deliver lists each migrated user, their Opal role, and the recommended Odoo group (e.g., Opal Admin > Sales / Manager, Opal Sales Rep > Sales / User).
Opal CRM
Lead Source Attribution
Odoo CRM
utm.source + utm.medium
lossyIf Opal CRM tracks lead source (form, campaign, event, manual) as a property on the lead record, we map it to Odoo's utm.source and utm.medium tracking fields linked to the crm.lead record. We create corresponding utm.source records (one per distinct Opal source value) during Odoo configuration and link each migrated lead to its source via the lead.referrer_id field. This enables the customer to continue using Odoo's native campaign attribution reporting post-migration.
Opal CRM
Performance / Points
Odoo CRM
Custom field or report only
1:1Opal CRM Basic and Standard plans include a sales performance tracking feature using a points or rewards system. This is a platform-specific gamification concept with no Odoo standard equivalent. We capture any point balance, rank, or reward data as a custom field on the crm.lead or res.users record and document the current values in the handover report. The customer's admin decides whether to continue using the gamification model via Odoo gamification challenges or to retire it as a reporting artifact.
| Opal CRM | Odoo CRM | Compatibility | |
|---|---|---|---|
| Lead | crm.lead1:1 | Fully supported | |
| Sales Representative | res.users1:1 | Fully supported | |
| Company | res.partner1:1 | Fully supported | |
| Tour Plan | crm.lead (visit) + account.move (expenses)1:many | Fully supported | |
| Quotation | sale.order1:1 | Fully supported | |
| Quotation Line Item | sale.order.line1:1 | Fully supported | |
| Pipeline Stage | crm.stagelossy | Fully supported | |
| Activity: Call | mail.activity (type=call)1:1 | Fully supported | |
| Activity: Email | mail.message1:1 | Fully supported | |
| Activity: Meeting | calendar.event1:1 | Fully supported | |
| Activity: Task | project.task1:1 | Fully supported | |
| Custom Properties | ir.model.fields (x_*)lossy | Mapping required | |
| Role Permissions | res.groups membershiplossy | Mapping required | |
| Lead Source Attribution | utm.source + utm.mediumlossy | Fully supported | |
| Performance / Points | Custom field or report only1: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.
Opal CRM gotchas
No publicly documented API for bulk data export
Tour Plan expense data may flatten during export
Quotation workflow state is not a standard CRM field
Free tier limits and trial expiry not visible in export
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 extraction method confirmation
We audit the Opal CRM account to understand record volumes (Leads, Quotations, Tour Plans, Sales Reps), data quality (duplicate rate, null field frequency), and the extraction method available. Since Opal CRM has no public API, we coordinate with the customer to generate a manual export file or request direct database access. We validate the export against the Opal UI record counts for each object type and flag any data that appears incomplete or truncated. We also confirm the target Odoo version, installed modules (CRM, Sale Management, Project, Accounting), and hosting method (Community self-hosted or Enterprise cloud) to determine the correct API endpoints and import method.
Schema design and Odoo configuration
We design the destination schema in Odoo before any data import. This includes creating any custom fields identified in the Opal export (via Settings > Technical > Database Structure > Models), configuring crm_stage pipeline stages to match the Opal pipeline stage names, setting up utm.source and utm.medium records for lead attribution tracking, and confirming the sale.order workflow states. If the customer needs Tour Plan itinerary data stored in Odoo, we create a custom visit_date field on crm.lead and an expense tracking configuration in the accounting module. The Odoo admin reviews and approves the schema design before we proceed to staging migration.
Staging migration and reconciliation
We run a full migration into the customer's Odoo staging environment (a test database or sandbox) using production-like data volumes. The customer reconciles record counts for each object type against the Opal source, spot-checks 25-50 random records for field-level accuracy, and validates that Tour Plan expense data has been correctly reconstructed. Any mapping corrections, missing custom fields, or Odoo configuration adjustments happen at this stage. We do not proceed to production migration until the customer provides written sign-off on the staging results.
Owner reconciliation and user provisioning
We extract every distinct Opal Sales Rep referenced on Leads, Quotations, and Tour Plans and match by email against the Odoo destination's res.users table. Any Opal rep without a matching Odoo user account is placed in a reconciliation queue. The customer's Odoo admin provisions the missing user accounts with the correct access rights based on our role-mapping table. OwnerId references are required on crm.lead and sale.order imports in Odoo, so this step must complete before record import begins. Inactive Odoo users can be used as owners with a note flagging them for later deactivation.
Production migration in dependency order
We run the production migration in record dependency order: res.users (validated), res.partner (from Opal companies and contacts), crm_stage (pipeline stages), crm.lead (from Opal Leads with utm tracking linked), sale.order (from Opal Quotations with workflow state preserved as custom field), account.move (expense entries extracted from Tour Plans), mail.activity and mail.message (activity history), project.task (tasks), and finally any custom field data appended to the migrated records. Each phase emits a row-count reconciliation report before the next phase begins. We freeze Opal CRM write access during the final migration window to prevent new records from being created after the delta extract.
Cutover, validation, and automation rebuild handoff
We run a final delta migration of any records created or modified in Opal CRM during the cutover window, then disable Opal CRM access and set Odoo as the system of record. We deliver the complete migration record including the Tour Plan expense reconstruction log, quotation workflow state mapping table, role-permission mapping table, and the custom field creation manifest. We provide a written inventory of any Opal automation logic (quotation approval workflows, Tour Plan approval sequences) that requires manual rebuild in Odoo Studio or via custom module development. We support a one-week hypercare window for reconciliation issues but do not rebuild Odoo workflows as standard scope.
Platform deep dives
Opal CRM
Source
Strengths
Weaknesses
Odoo CRM
Destination
Strengths
Weaknesses
Complexity grading
Standard CRM migration. All 8 core objects map 1:1 between Opal CRM and Odoo CRM.
Overall complexity
Standard migration
Derived from compatibility, mapping clarity, API constraints, and data volume across Opal CRM and Odoo CRM.
Object compatibility
All 8 core objects map 1:1 between Opal CRM 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
Opal CRM: Not publicly documented..
Data volume sensitivity
Opal CRM 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 Opal CRM to Odoo CRM migration scoping. Not seeing yours? Book a call.
Walk through your Opal CRM 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 Opal CRM
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.