CRM migration
Field-level mapping, validation, and rollback between Practice by Numbers and Odoo CRM. We move data and schema; workflows are rebuilt natively in Odoo CRM.
Practice by Numbers
Source
Odoo CRM
Destination
Compatibility
9 of 10
objects map 1:1 between Practice by Numbers and Odoo CRM.
Complexity
BStandard
Timeline
3–5 days
Overview
Practice by Numbers stores dental-practice data as patients, providers, appointments, treatments, and insurance records with a reporting layer built for dental KPIs. Odoo CRM uses crm.lead for leads and opportunities, res.partner for contacts and companies, and ir.model.data for custom fields — a fundamentally different relational model. We extract from Practice by Numbers via its reporting export and direct API where available, transform dental-record types into Odoo-compatible partner and lead records, and land them in the correct crm.lead stages and res.partner addresses. Treatment plans and insurance data that have no direct Odoo equivalent migrate as custom fields on res.partner and crm.lead, flagged for manual review. Our migration does not move automated reminders, patient-communication sequences, or goal-management rules — those live in Practice by Numbers and must be rebuilt as Odoo自动化规则 (automation rules) post-migration. A sample migration with field-level diff runs first; a 24–48-hour delta pickup window captures any records modified during cutover.
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 Practice by Numbers 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.
Practice by Numbers
Patient
Odoo CRM
res.partner
1:1Practice by Numbers patient records map directly to Odoo res.partner. The patient's full name splits into firstname and lastname on res.partner. Email, phone, and address fields migrate one-to-one. The original Practice by Numbers patient ID is stored as x_source_patient_id for delta-run de-duplication.
Practice by Numbers
Provider
Odoo CRM
res.users
1:1Practice by Numbers providers (doctors, hygienists) are staff members, not contacts. We resolve each provider against Odoo res.users by email match. Providers without Odoo user accounts are flagged and can be invited before migration or assigned to a fallback user. Provider specialty (GP vs. specialist) maps to a custom x_provider_specialty field.
Practice by Numbers
Appointment
Odoo CRM
crm.activity
1:1Each Practice by Numbers appointment becomes an Odoo crm.activity record linked to the crm.lead that owns the patient. The activity type (checkup, cleaning, procedure) maps to an Odoo activity_type_id; the appointment date and duration become the crm.activity date_deadline and duration fields. Cancelled appointments migrate with a 'cancelled' state flag.
Practice by Numbers
Treatment Plan
Odoo CRM
sale.order
1:1Proposed treatments in Practice by Numbers translate to Odoo sale.order lines. Each treatment item becomes a sale.order.line with the procedure description as name, fee as price_unit, and the responsible provider as user_id. Confirmed treatment plans with patient acceptance migrate as sale.order records in the 'sale' state; unconfirmed plans remain as draft quotations.
Practice by Numbers
Insurance Record
Odoo CRM
res.partner (custom fields)
1:1Insurance carrier name, group number, policy number, subscriber ID, and coverage percentage have no native Odoo res.partner fields. These migrate as x_insurance_carrier, x_group_number, x_policy_number, x_subscriber_id, and x_coverage_pct custom fields on res.partner. If the practice uses secondary insurance, a second set of fields is created or a related x_insurance_partner_id many2one is added.
Practice by Numbers
Recall / Reactivation
Odoo CRM
crm.lead + mail.activity
many:1Practice by Numbers recall dates (6-month hygiene recall, annual checkup) merge into Odoo crm.lead records: one lead per recall type, with the recall date stored as x_recall_date and a scheduled mail.activity set for 7 days before that date. This gives Odoo's native reminder system rather than a separate recall table.
Practice by Numbers
Payment / Ledger
Odoo CRM
account.move
1:1Practice by Numbers payment history is billing data that lives in its own ledger. Odoo account.move records are accounting entries tied to the Odoo accounting module, which may not be installed. We preserve the payment ledger as a custom x_payment_history JSON field on res.partner for reference — the accounting module handles future payments post-migration.
Practice by Numbers
Goal / KPI Target
Odoo CRM
Custom x_goal target fields on crm.lead
1:1Practice by Numbers goal management records per-provider production, acceptance rate, and new‑patient targets. Each target maps to a custom float field on res.users: x_monthly_production_target, x_acceptance_rate_target, and x_new_patient_target. These fields enable Odoo’s native dashboard, pivot, and graph views to show team and individual performance against the same metrics. Default values preserve the original goal figures for each provider.
Practice by Numbers
Communication Log
Odoo CRM
mail.message
1:1Patient communication history (appointment reminders, confirmations, recalls) in Practice by Numbers migrates as mail.message records linked to res.partner. The original message content, send date, and channel (SMS, email) are preserved. Odoo's mail_thread on res.partner surfaces these messages in the chatter sidebar.
Practice by Numbers
Custom Dental Properties
Odoo CRM
ir.model.fields (x_* custom fields)
1:1Any custom properties defined in Practice by Numbers (e.g., referring dentist, preferred appointment time, treatment preferences) migrate as Odoo x_ prefixed custom fields on res.partner or crm.lead. Field type is inferred from the source value type: text strings become char, numeric values become float or integer, date values become date.
| Practice by Numbers | Odoo CRM | Compatibility | |
|---|---|---|---|
| Patient | res.partner1:1 | Fully supported | |
| Provider | res.users1:1 | Fully supported | |
| Appointment | crm.activity1:1 | Fully supported | |
| Treatment Plan | sale.order1:1 | Fully supported | |
| Insurance Record | res.partner (custom fields)1:1 | Fully supported | |
| Recall / Reactivation | crm.lead + mail.activitymany:1 | Fully supported | |
| Payment / Ledger | account.move1:1 | Fully supported | |
| Goal / KPI Target | Custom x_goal target fields on crm.lead1:1 | Fully supported | |
| Communication Log | mail.message1:1 | Fully supported | |
| Custom Dental Properties | ir.model.fields (x_* custom fields)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.
Practice by Numbers gotchas
No publicly documented API for automated migration
Dental EHR data is inherently messy during extraction
Goal management metrics require explicit field mapping
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 Practice by Numbers data via report exports and API
FlitStack AI connects to Practice by Numbers using scoped read access (report exports for bulk patient and appointment data, API where available for real-time fields). We extract all patients, providers, appointments, treatment plans, insurance records, and communication logs. A data audit report identifies duplicate emails, missing required fields, and unmapped custom properties before any transformation logic runs. The source system remains fully operational during this read-only phase.
Set up Odoo schema: custom fields, activity types, and user accounts
Before data lands, FlitStack AI generates an Odoo setup plan listing every x_ custom field to create (x_insurance_carrier, x_recall_date, x_provider_specialty, etc.), every crm.activity.type record to create per appointment type, and every res.users record needed for provider-to-user resolution. Your Odoo admin creates the fields and invites providers as users. This step runs in parallel with the source data extract and prevents validation errors during the import phase.
Resolve owners and run field-level mapping validation
Every provider_id in Practice by Numbers is matched against Odoo res.users by email. Unmatched providers appear in a resolution report with recommended fallback assignments. Simultaneously, FlitStack AI validates the field mapping plan — checking that date formats, pick-list values, and numeric ranges conform to Odoo's field type constraints. Any mapping that would cause an Odoo validation error is flagged with a recommended fix before migration commits any data.
Run sample migration with field-level diff on 100–500 records
A representative slice of 100–500 records — spanning patients, appointments, treatment plans, and at least one recall record — migrates to Odoo first. FlitStack AI generates a field-level diff comparing each source field value against the destination field value, with any transformation noted. Your team reviews the diff in a shared report and confirms the mapping is correct before FlitStack AI runs the full migration. This step is the last chance to adjust mapping logic without committing the full dataset.
Execute full migration with delta-pickup and audit log
The full migration runs in Odoo using batched writes via the XML-RPC API, respecting Odoo's transaction boundaries. A 24–48-hour delta-pickup window opens immediately after the full run, capturing any new patient records or appointment updates made in Practice by Numbers during the cutover. FlitStack AI generates a complete audit log of every record written, every field populated, and every record that failed validation with the reason. One-click rollback reverts all migrated records if reconciliation finds unexpected gaps.
Platform deep dives
Practice by Numbers
Source
Strengths
Weaknesses
Odoo CRM
Destination
Strengths
Weaknesses
Complexity grading
Standard CRM migration. All 8 core objects map 1:1 between Practice by Numbers and Odoo CRM.
Overall complexity
Standard migration
Derived from compatibility, mapping clarity, API constraints, and data volume across Practice by Numbers and Odoo CRM.
Object compatibility
All 8 core objects map 1:1 between Practice by Numbers 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
Practice by Numbers: Not publicly documented.
Data volume sensitivity
Practice by Numbers 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 Practice by Numbers to Odoo CRM migration scoping. Not seeing yours? Book a call.
Walk through your Practice by Numbers 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 Practice by Numbers
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.