CRM migration
Field-level mapping, validation, and rollback between PHP CRM and Odoo CRM. We move data and schema; workflows are rebuilt natively in Odoo CRM.
PHP CRM
Source
Odoo CRM
Destination
Compatibility
8 of 12
objects map 1:1 between PHP CRM and Odoo CRM.
Complexity
BStandard
Timeline
3-5 weeks
Overview
Moving from PHP CRM to Odoo CRM is a structural migration from a small-business niche platform to a modular ERP suite used by over five million organizations. PHP CRM stores all data in a flat or loosely relational schema with user-defined custom fields, no published API documentation, and application-layer workflows that are not accessible for export. Odoo CRM uses a strict ORM model with crm.lead for pre-qualification and crm.lead for qualified opportunities, both inheriting from res.partner. We enumerate every custom field in the source PHP CRM deployment during discovery, then map them to Odoo custom fields on the correct model before import. Pipeline stages migrate as Odoo stage records attached to crm.lead pipeline definitions. Activities (calls, emails, meetings) map to Odoo's mail.message and crm.activity models. Workflows, email templates, and automations in PHP CRM are application-layer code and do not migrate; we deliver a written inventory of every configured rule requiring rebuild in Odoo Studio or server actions 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 PHP 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.
PHP CRM
Contact
Odoo CRM
res.partner
1:1PHP CRM Contact records map to Odoo res.partner with partner_type set to 'contact'. The source email field maps to Odoo's email field; phone maps to phone. We resolve the parent Company relationship by matching the source company_name against the already-migrated res.partner records and populate parent_id accordingly. If the source contact has no company, the record is imported as an individual partner without a parent_id. Address fields (street, city, state, country, zip) map via Odoo's partner address fields.
PHP CRM
Company
Odoo CRM
res.partner
1:1PHP CRM Company records map to Odoo res.partner with partner_type set to 'company'. The company_name field maps to Odoo's name field on the partner record. We import Companies first as parent entities before any Contact import so that the parent_id lookup is satisfied at Contact insert time. Company-level custom fields from PHP CRM map to custom res.partner fields in Odoo. If the source Company has no associated address, we set address fields to empty rather than null to satisfy Odoo's address format expectations.
PHP CRM
Deal
Odoo CRM
crm.lead
1:1PHP CRM Deal records map to Odoo crm.lead (the opportunity model). The source dealstage maps to an Odoo crm.stage record on the crm.lead.pipeline. We resolve partner_id from the source deal's linked Company or Contact at migration time. The deal's expected_close_date maps to Odoo's date_deadline field; amount maps to Odoo's planned_revenue field. Type on crm.lead is set to 'opportunity' during import. Owner assignment maps to Odoo user_id via email matching against res.users.
PHP CRM
Pipeline
Odoo CRM
crm.lead.model
lossyPHP CRM Pipeline definitions map to Odoo crm.lead.model records. Each source pipeline becomes a separate crm.lead.model in Odoo with a name matching the source pipeline name. We configure the model's stage_ids to reference the migrated crm.stage records. The pipeline's sequence order is preserved via Odoo's sequence field on the model. If the source deployment has a single default pipeline, it maps to the standard Odoo CRM pipeline without configuration changes.
PHP CRM
Pipeline Stage
Odoo CRM
crm.stage
lossyPHP CRM Pipeline Stage records map to Odoo crm.stage records attached to the corresponding crm.lead.model. Stage name, sequence order, and probability percentage migrate from the source stage values. Odoo requires stage records to belong to a specific pipeline model; we resolve the parent pipeline before inserting each stage. If a source stage has no Odoo equivalent (e.g., custom named stages with no close/won equivalent), we flag them for manual Odoo stage creation before migration resumes.
PHP CRM
Task
Odoo CRM
project.task
1:1PHP CRM Task records map to Odoo project.task linked to the default crm_project if the task is CRM-related. The source due date maps to date_deadline; assignee maps to user_id via email matching. Task status (open/closed) maps to Odoo's kanban_state field. If the source task has a linked Deal or Contact, we resolve the destination crm.lead or res.partner ID and set the task's project_id and partner_id accordingly. Task description maps to the Odoo description field as plain text.
PHP CRM
Note
Odoo CRM
mail.message
1:1PHP CRM Note records attach free-text content to any entity. We migrate Notes as Odoo mail.message records with message_type set to 'comment'. The WhoId (Contact or Company) and WhatId (Deal) are resolved to destination res.partner and crm.lead IDs, and the message is linked via mail.message.model and mail.message.res_id. Author is set to the migrating Odoo user who owns the note assignment. Note body migrates as HTML-formatted plain text without image attachments unless the source provides a file path accessible during extraction.
PHP CRM
Activity
Odoo CRM
mail.message or crm.activity
1:1PHP CRM Activity records (calls, emails, meetings) with timestamps and outcome data map to a combination of Odoo mail.message and crm.activity. Call activities map to mail.message with message_type='comment' and a custom call disposition field; the source call duration and outcome map to custom crm.activity fields if defined. Email activities map to mail.message with attachments preserved as ir.attachment records linked via mail.message. Meeting activities map to calendar.event records in Odoo's calendar module if available, or to mail.message if calendar is not in scope. We enumerate the actual activity field schema present in the source PHP CRM instance during discovery since PHP CRM activity schemas vary by deployment.
PHP CRM
Custom Field
Odoo CRM
ir.model.fields
lossyPHP CRM user-defined custom fields on any standard object map to Odoo ir.model.fields records created via Odoo Studio or direct Python model definition. Each custom field requires a separate Odoo field creation step before the associated record import begins. We enumerate all custom fields during mandatory discovery, capture the field type (text, number, date, checkbox, dropdown) from PHP CRM, and map to the equivalent Odoo field type (char, text, float, datetime, boolean, selection). Fields with no Odoo equivalent are flagged for manual field creation before migration resumes.
PHP CRM
User/Owner
Odoo CRM
res.users
1:1PHP CRM Owner records on Deals, Tasks, and Contacts map to Odoo res.users by email matching. We extract every distinct owner referenced across all objects, match against the destination Odoo res.users table, and flag any owner without a matching user for admin provisioning. Owner assignment on crm.lead maps to Odoo's user_id; task assignment maps to project.task user_id. Active/inactive status from the source is preserved in the Odoo user record. The customer's Odoo admin provisions any missing users before record import resumes.
PHP CRM
Tag/Label
Odoo CRM
crm.tag
lossyPHP CRM Tags are per-instance label vocabulary that map to Odoo crm.tag records. We extract the full tag taxonomy from the source, create matching crm.tag records in Odoo, then link them to the appropriate crm.lead records via crm.lead.tag_rel at migration time. If the source tag vocabulary is large (50+ tags), we recommend the customer audit the taxonomy before import to remove deprecated labels. Tags with no clear Odoo equivalent are flagged for customer review.
PHP CRM
Attachment
Odoo CRM
ir.attachment (manual)
1:1PHP CRM stores file attachments on the local filesystem (self-hosted) or cloud storage path, not through a documented API endpoint. We cannot programmatically extract attachments. We export the attachment directory to a local path for the customer's review and advise re-uploading manually via Odoo's document management interface or using Odoo's bulk file import tools post-migration. This gap is documented explicitly in the migration scope.
| PHP CRM | Odoo CRM | Compatibility | |
|---|---|---|---|
| Contact | res.partner1:1 | Fully supported | |
| Company | res.partner1:1 | Fully supported | |
| Deal | crm.lead1:1 | Fully supported | |
| Pipeline | crm.lead.modellossy | Fully supported | |
| Pipeline Stage | crm.stagelossy | Fully supported | |
| Task | project.task1:1 | Fully supported | |
| Note | mail.message1:1 | Fully supported | |
| Activity | mail.message or crm.activity1:1 | Fully supported | |
| Custom Field | ir.model.fieldslossy | Fully supported | |
| User/Owner | res.users1:1 | Fully supported | |
| Tag/Label | crm.taglossy | Fully supported | |
| Attachment | ir.attachment (manual)1: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.
PHP CRM gotchas
No publicly documented API rate limits or endpoints
Attachment and file storage not accessible via API
Custom field taxonomy varies per deployment
Workflows and automations are not portable
Limited review corpus for accurate benchmarking
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 credentials scoping
We audit the source PHP CRM instance via REST API and admin panel access, enumerating every object type, custom field, pipeline, stage, user, tag, and activity schema present. We capture record counts per object, API pagination behavior, and any errors encountered during API calls to determine whether API extraction or CSV export is the primary extraction method. We request PHP CRM admin credentials, Odoo database credentials or API key, and Odoo.sh or self-hosted access for field creation and record import. The discovery output is a written migration scope with object inventory, record counts, and a recommendation on extraction method (API or CSV).
Source data extraction and transformation
We extract data in dependency order: Companies first (parent), then Contacts (with parent_id resolved to Companies), then Deals/Leads (with partner_id resolved), then Tasks and Notes (with parent entity resolved), then Activities (with linked entity IDs from the prior phases). For CSV exports, we handle the relationship by including foreign-key columns (company_id, contact_id) that we resolve against the extracted records. For API extraction, we paginate through the source records using the API's pagination parameters. All extracted data is staged in a transformation workspace with a schema manifest documenting every field, type, and mapping rule.
Odoo schema preparation and custom field creation
We create all required Odoo schema elements before importing records. This includes crm.lead.model pipeline definitions with stage sequences, crm.stage records per pipeline, custom res.partner fields for company-level custom data, custom crm.lead fields for deal-level custom data, and crm.tag records for the source tag taxonomy. Custom field creation is sequenced as a gate: no record import begins until the corresponding custom field exists in the destination Odoo instance. We deploy schema changes via Odoo XML-RPC API or direct database insert into a staging environment first for validation.
Staging migration and reconciliation
We run a full migration into a staging Odoo environment (copy of production database or a separate Odoo instance) using the full extracted data volume. The customer's Odoo administrator reconciles record counts against the source PHP CRM database, spot-checks 25-50 records per object for field accuracy and relationship correctness, and validates that pipeline stages display correctly in the Odoo Kanban view. Any mapping corrections, dropped fields, or relationship resolution failures are fixed in the transformation layer and re-run in staging before production migration begins.
Production migration in dependency order
We run production migration in strict record-dependency order: Companies (res.partner with partner_type=company), then Contacts (res.partner with partner_type=contact and parent_id resolved), then crm.tag records, then crm.lead.model and crm.stage pipeline definitions, then crm.lead records (leads and opportunities with user_id and partner_id resolved), then project.task records, then mail.message records for Notes and Activities. Each phase emits a row-count reconciliation report before the next phase begins. The PHP CRM instance remains writable during migration; we capture any records modified during the migration window as a delta load before cutover.
Cutover, validation, and workflow rebuild handoff
We freeze writes to PHP CRM during the cutover window, execute the final delta migration of any records modified during the migration window, then set Odoo CRM as the system of record. We validate record counts, spot-check 10 records per object in Odoo against the source PHP CRM database, and confirm that pipeline stages and Kanban views display correctly. We deliver the workflow and automation inventory document to the customer's Odoo administrator. We support a one-week hypercare window where we resolve reconciliation issues raised by the customer's team. Post-migration admin support, Odoo Studio workflow rebuild, and user training are outside standard scope and are separate engagements.
Platform deep dives
PHP CRM
Source
Strengths
Weaknesses
Odoo CRM
Destination
Strengths
Weaknesses
Complexity grading
Standard CRM migration. 2 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 PHP CRM and Odoo CRM.
Object compatibility
2 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
PHP CRM: Not publicly documented.
Data volume sensitivity
PHP 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 PHP CRM to Odoo CRM migration scoping. Not seeing yours? Book a call.
Walk through your PHP 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 PHP 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.