CRM migration
Field-level mapping, validation, and rollback between Atomic CRM and Odoo CRM. We move data and schema; workflows are rebuilt natively in Odoo CRM.
Atomic CRM
Source
Odoo CRM
Destination
Compatibility
10 of 16
objects map 1:1 between Atomic CRM and Odoo CRM.
Complexity
BStandard
Timeline
3-5 weeks
Overview
Atomic CRM is an MIT-licensed React CRM template running on Supabase with no hosted option, no REST API surface, and custom fields added directly to Postgres tables. Odoo CRM is part of the Odoo ERP suite at $24.90 per user per month with a graphical admin UI, crm.lead for lead and opportunity tracking, and native integration with Odoo Sales, Invoicing, and Inventory. Moving data between them requires reading the live Supabase Postgres schema to discover every column, mapping pipeline stages and task types from App.tsx props (not the database), and configuring Odoo's crm.lead stage and team structure before any import. We resolve the Contacts-to-res.partner mapping (distinguishing individual contacts from company contacts on the same model), create the Odoo pipeline stages to match Atomic's deal categories, migrate engagement history as mail.message records, and deliver a written automation inventory for Odoo Studio rebuild. Workflows, email sequences, and reporting dashboards do not migrate as code.
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 Atomic 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.
Atomic CRM
Contact
Odoo CRM
res.partner
1:1Atomic CRM Contacts map to Odoo res.partner records. Contacts with a company link become partners of type company; contacts without a company link become individual partners. The primary key UUID in Supabase is not reused in Odoo; we generate new integer IDs and record the source UUID in a custom field src_uuid__c for audit traceability. Email, phone, and address fields map directly to Odoo's standard partner fields. Company sector in Atomic becomes a partner category or industry_id tag in Odoo.
Atomic CRM
Company
Odoo CRM
res.partner (company type)
1:1Atomic CRM Companies map to Odoo res.partner records with partner_type set to company. The one-to-many relationship between Companies and Contacts in Atomic maps to Odoo's native parent_id on res.partner, where the Contact record carries parent_id pointing to the Company partner. We create Company partners first during migration to satisfy the foreign key before importing Contact records. Company sector, size, website, revenue, and tax_identifier map to equivalent partner fields or custom fields created in Odoo Studio during schema setup.
Atomic CRM
Deal
Odoo CRM
crm.lead
1:1Atomic CRM Deals map to Odoo crm.lead records with type=opportunity. The deal name maps to name, the monetary value maps to planned_revenue, and the deal stage maps to stage_id which we resolve against Odoo's pre-configured pipeline stages. We pre-create Odoo pipeline stages matching Atomic's deal categories before migration. Any Atomic deal linked to a Contact maps with partner_id set to the corresponding res.partner ID, and partner_name is pulled from the Contact's name field.
Atomic CRM
Note
Odoo CRM
mail.message
1:1Atomic CRM Notes map to Odoo mail.message records. The note body becomes the message body, the source Contact or Deal reference becomes res_id and res_model pointing to the migrated res.partner or crm.lead record. Author attribution is resolved via the Contact email matching an Odoo res.users record. Odoo's Chatter thread on crm.lead displays these as the activity conversation history. Note timestamps are preserved in message_date for chronological ordering.
Atomic CRM
Task
Odoo CRM
crm.activity
1:1Atomic CRM Tasks map to Odoo crm.activity records linked to crm.lead via activity_type_id and res_id. Atomic task types (Call, Email, Meeting) defined as TypeScript props in App.tsx must be provided by the customer during scoping; we create corresponding activity types in Odoo CRM Settings before migration. Task status, assignee (hubspot_owner_id), due date, and description map to activity state, user_id, date_deadline, and note respectively. Owner resolution uses email matching against Odoo res.users.
Atomic CRM
Custom Fields
Odoo CRM
ir.model.field (Studio)
lossyAtomic CRM custom fields are Postgres columns added via Supabase Studio without documentation. We run a pre-migration schema diff against a reference Atomic CRM export to discover every column on every table, then pre-create equivalent fields in Odoo using Odoo Studio (or XML data file in developer mode). Field types are mapped from Postgres column types to Odoo field types (varchar to char, integer to integer, boolean to boolean, jsonb to many2many or char depending on content). Custom field mapping is the most time-sensitive part of the schema audit and must complete before any data import.
Atomic CRM
User
Odoo CRM
res.users
1:1Atomic CRM user management relies on Supabase Auth with auth UUIDs as owner references on Deals and Tasks. We extract every distinct owner reference and match by email address against the destination Odoo res.users table. Any Atomic owner without a matching Odoo user is placed in a reconciliation queue; the customer provisions the missing Odoo users before record migration resumes. Owner resolution is required for Deals and Tasks and must complete before those object imports begin.
Atomic CRM
Attachment metadata
Odoo CRM
ir.attachment
lossyAtomic CRM attachment metadata lives in Supabase but the actual files may live in Supabase Storage or an external S3 bucket. We check for the storage backend during scoping. If files are in Supabase Storage, we export the bucket contents and re-attach them in Odoo as ir.attachment records using Odoo's file storage (local or database attachment mode). Files stored outside Supabase (custom S3, external URLs) require separate coordination. Binary blob storage migration is scoped and priced separately from the base migration.
Atomic CRM
Tag
Odoo CRM
crm.tag or ir.module.category
lossyAtomic CRM stores tags as a many-to-many relationship table (likely contact_tag and deal_tag). Tags migrate to Odoo crm.tag records linked to crm.lead via the tag_ids many2many field. The customer chooses during scoping between Odoo crm.tag for loose labeling or a dedicated many2many field on res.partner for contact-level categorization. Tag names are deduplicated during import and new tag records are created in Odoo before the crm.lead import phase.
Atomic CRM
Deal Category (App.tsx prop)
Odoo CRM
crm.stage
lossyAtomic CRM deal categories and pipeline stages are defined as TypeScript props passed to the CRM root component, not stored in the Postgres database. The customer provides App.tsx (or equivalent) during scoping. We parse the deal category enum values, create corresponding crm.stage records in Odoo CRM with sequence and probability values, and map stage_id references during the Deal migration phase. Omitting this step results in Deals importing with no stage assignment.
Atomic CRM
Task Type (App.tsx prop)
Odoo CRM
crm.activity.type
lossyAtomic CRM task types (Call, Email, Meeting) are TypeScript enum props in App.tsx, not stored in the database. We request the customer share App.tsx during scoping and extract the task type enumeration. We create corresponding crm.activity.type records in Odoo CRM Settings with the same name and icon before the Task migration phase begins. Without this step, imported Tasks have no activity type assignment in Odoo.
Atomic CRM
Engagement: Email (historical)
Odoo CRM
mail.message (email type)
1:1If the Atomic CRM instance includes historical email forwarding logs stored in Supabase (mail forwarding feature introduced March 2026), these migrate as mail.message records with message_type=email linked to the relevant crm.lead or res.partner via res_id and res_model. Email subject, body, sender, and recipient map to subject, body, email_from, and email_to fields. We only migrate email metadata stored in Postgres tables; actual email transport remains external (the customer's mail server or Odoo's incoming mail gateway).
Atomic CRM
Engagement: Call log
Odoo CRM
crm.activity (call type)
1:1Call logs stored in Supabase as part of the Tasks table with a Call classification migrate as crm.activity records with activity_type_id set to the Call activity type resolved from Odoo. Call duration, disposition, and any notes in the task body map to Odoo's custom activity fields. Activity date is preserved from the original Supabase record timestamp.
Atomic CRM
Owner assignment
Odoo CRM
user_id on crm.lead
1:1Atomic CRM owner references on Deals and Tasks use Supabase Auth UUIDs. We match these to Odoo res.users by email address during the owner reconciliation phase. The resulting user_id is written to the crm.lead responsible_id field. Any unresolved owners are flagged in the reconciliation report and held until the customer provisions the corresponding Odoo user account. Owner resolution must complete before the Deal and Task import phases run.
Atomic CRM
Pipeline stage configuration
Odoo CRM
crm.stage + crm.team
lossyAtomic CRM deal pipelines are single Kanban views with configurable stage names. Odoo CRM supports multiple crm.team (sales teams) each with their own crm.stage sequence. We configure one crm.team per pipeline and assign stage_id values within each team. Stage probabilities are set on the stage records and honored in Odoo's revenue forecasting. This configuration is deployed in Odoo Studio or via XML data file before any crm.lead records are imported.
Atomic CRM
Contact-Company relationship
Odoo CRM
parent_id on res.partner
1:1Atomic CRM's foreign-key relationship between Contacts and Companies (one Company to many Contacts) maps to Odoo's parent_id on res.partner. The Company Contact in Odoo is a res.partner with partner_type=company and the individual Contact is a res.partner with parent_id pointing to it. We preserve the relationship direction during import by creating Company partners first, then resolving parent_id on each Contact record from the stored company_id in the Atomic CRM Contact row.
| Atomic CRM | Odoo CRM | Compatibility | |
|---|---|---|---|
| Contact | res.partner1:1 | Fully supported | |
| Company | res.partner (company type)1:1 | Fully supported | |
| Deal | crm.lead1:1 | Fully supported | |
| Note | mail.message1:1 | Fully supported | |
| Task | crm.activity1:1 | Fully supported | |
| Custom Fields | ir.model.field (Studio)lossy | Mapping required | |
| User | res.users1:1 | Fully supported | |
| Attachment metadata | ir.attachmentlossy | Fully supported | |
| Tag | crm.tag or ir.module.categorylossy | Fully supported | |
| Deal Category (App.tsx prop) | crm.stagelossy | Fully supported | |
| Task Type (App.tsx prop) | crm.activity.typelossy | Fully supported | |
| Engagement: Email (historical) | mail.message (email type)1:1 | Fully supported | |
| Engagement: Call log | crm.activity (call type)1:1 | Fully supported | |
| Owner assignment | user_id on crm.lead1:1 | Fully supported | |
| Pipeline stage configuration | crm.stage + crm.teamlossy | Fully supported | |
| Contact-Company relationship | parent_id on res.partner1: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.
Atomic CRM gotchas
No hosted SaaS version — migration target is a Postgres database
Custom fields are schema changes, not UI-configured properties
CRM component props define business logic that lives in code, not data
No native file attachment export — storage backend varies by deployment
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 Supabase schema audit
We audit the source Atomic CRM by connecting to the Supabase Postgres instance and running a schema diff across all tables (contacts, companies, deals, notes, tasks, and any junction tables). We document every column, data type, relationship, and RLS policy that could affect the migration. We also extract record counts, custom field presence, and any Supabase Storage usage. This audit output is the authoritative source schema used for all mapping decisions and must complete before any custom field configuration begins in Odoo.
Configuration file extraction from App.tsx
We request the customer share App.tsx (or the equivalent CRM root component) to extract deal category and task type enumerations that are defined as TypeScript props rather than stored in the database. If the customer cannot produce the file, we use Odoo's standard stage and activity type defaults. We pair this with an Odoo CRM edition assessment: Odoo Online ($24.90/user/mo) for managed hosting, Odoo Community for self-hosted, or Odoo Enterprise for advanced automation and Studio access. The configuration extraction and edition selection drive the entire Odoo schema setup phase.
Odoo CRM schema setup and pipeline configuration
We configure the Odoo CRM destination schema before any data import. This includes creating crm.team records (one per source pipeline), crm.stage records with probability and sequence values mapped from the customer's Atomic deal categories, crm.activity.type records for each task type, and all custom fields from the Supabase schema audit using Odoo Studio (or XML data file for Community). Pipeline stage assignments, team assignments, and activity type creation are deployed to a staging Odoo database first for verification. This step must complete before any record import begins.
Staging migration and reconciliation
We run a full migration into a staging Odoo database using production-like record volumes. The customer's CRM administrator reviews record counts across Contacts, Companies, Deals, Notes, and Tasks; spot-checks 25-50 records for field-level accuracy; and validates that pipeline stages and task types match the source configuration. Any field mapping corrections, missed custom fields, or stage configuration errors are resolved at this stage. The staging sign-off is required before the production migration begins. We do not proceed to production without explicit written sign-off from the customer's administrator.
Production migration in dependency order
We execute the production migration in strict dependency order. res.partner records (Companies) are created first with parent_id left null, then Contacts are created with parent_id resolved to the Company partner. crm.lead records (Deals) are created with partner_id and user_id resolved, stage_id resolved to the pre-configured pipeline stage, and planned_revenue from the deal value. mail.message records (Notes and email logs) are created linked to their parent records. crm.activity records (Tasks and calls) are created with activity_type_id, user_id, and res_id resolved. Owner reconciliation errors are flagged in a report and resolved before resuming. Each phase emits a row-count reconciliation report before the next phase begins.
Cutover, validation, and automation rebuild handoff
We freeze Supabase writes during the final cutover window, run a delta migration of any records modified during the migration window, then set the migration date and flag records with src_uuid__c. We deliver the automation and stage configuration inventory to the customer's Odoo administrator for rebuild in Odoo Studio. We support a one-week hypercare window for reconciliation issues raised during initial production use. We do not rebuild Odoo Studio automations, email templates, or reporting dashboards as part of the migration scope; those are documented for the admin team and scoped as a separate engagement if needed.
Platform deep dives
Atomic CRM
Source
Strengths
Weaknesses
Odoo CRM
Destination
Strengths
Weaknesses
Complexity grading
Standard CRM migration. 1 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 Atomic CRM and Odoo CRM.
Object compatibility
1 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
Atomic CRM: Per Supabase rate limits applicable to your project tier.
Data volume sensitivity
Atomic CRM exposes a bulk API — large-volume migrations stream efficiently.
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 Atomic CRM to Odoo CRM migration scoping. Not seeing yours? Book a call.
Walk through your Atomic 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 Atomic 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.