ERP with a CRM module
Not a CRM - fails on core objects, status, linked buyer, opportunity reporting; calendar sync unresolved
Core objects
Not at all
Odoo has no separate person and organization models. res.partner serves both, distinguished only by the is_company boolean (`is_company = fields.Boolean(... help="Check if the contact is a company, otherwise it is a person")`) and a derived company_type selection. crm.lead is a separate opportunity object and mail.activity.* separate activity objects, but the person/company split required for this dimension does not exist as separate record types - it is one contact object with a person/company flag.
Source: https://raw.githubusercontent.com/odoo/odoo/18.0/odoo/addons/base/models/res_partner.py, https://raw.githubusercontent.com/odoo/odoo/18.0/addons/crm/models/crm_lead.py
Verification: Verified
Pipeline stages
In full
crm.stage carries `sequence = fields.Integer('Sequence', default=1, help="Used to order stages. Lower is better.")` and crm.lead carries `stage_id` (Many2one to crm.stage, store=True). Stage order lives on the stage record itself and each opportunity carries its current stage.
Source: https://raw.githubusercontent.com/odoo/odoo/18.0/addons/crm/models/crm_stage.py, https://raw.githubusercontent.com/odoo/odoo/18.0/addons/crm/models/crm_lead.py
Verification: Verified
Status
Not at all
Closed status is not a value independent of stage: the write() override reads `if stage.is_won: vals.update({'probability': 100, ...})` when stage_id changes, i.e. reaching a vendor-flagged terminal stage is what marks the opportunity won. Lost is set via a separate `active` flag (archiving), but won is read from which stage the record occupies.
Source: https://raw.githubusercontent.com/odoo/odoo/18.0/addons/crm/models/crm_lead.py, https://raw.githubusercontent.com/odoo/odoo/18.0/addons/crm/models/crm_stage.py
Verification: Verified
Resolution
In part
Won and lost are not a dedicated outcome attribute; both are read off other signals. Won comes from stage metadata (`crm.stage.is_won` boolean triggering probability=100 on stage change); lost comes from a separate `active` boolean (archiving). Reading the outcome only from the stage's own metadata is the case the spec treats as Partial.
Source: https://raw.githubusercontent.com/odoo/odoo/18.0/addons/crm/models/crm_lead.py, https://raw.githubusercontent.com/odoo/odoo/18.0/addons/crm/models/crm_stage.py
Verification: Verified
Value
In full
crm.lead ships `expected_revenue = fields.Monetary('Expected Revenue', currency_field='company_currency', tracking=True, default=0.0)` directly on the opportunity model.
Source: https://raw.githubusercontent.com/odoo/odoo/18.0/addons/crm/models/crm_lead.py
Verification: Verified
Scope of work
In part
crm.lead itself only carries `description = fields.Html('Notes')`, a free-text box. Product line items exist only on a linked sale.order created via the CRM-documented "New Quotation" button, a separate record from the opportunity, and require the Sales app installed.
Source: https://raw.githubusercontent.com/odoo/odoo/18.0/addons/crm/models/crm_lead.py, https://www.odoo.com/documentation/18.0/applications/sales/crm/acquire_leads/send_quotes.html
Verification: Verified
Expected close date
In full
`date_deadline = fields.Date('Expected Closing', help='Estimate of the date on which the opportunity will be won.')` is a native field on crm.lead.
Source: https://raw.githubusercontent.com/odoo/odoo/18.0/addons/crm/models/crm_lead.py
Verification: Verified
Actual close date
In full
`date_closed = fields.Datetime('Closed Date', readonly=True, copy=False)` is a native field, auto-set by the write() override (`if vals.get('probability',0) >= 100 or not vals.get('active', True): vals['date_closed'] = fields.Datetime.now()`).
Source: https://raw.githubusercontent.com/odoo/odoo/18.0/addons/crm/models/crm_lead.py
Verification: Verified
Opportunity owner
In full
`user_id = fields.Many2one('res.users', string='Salesperson', default=lambda self: self.env.user, ...)` is a shipped user reference on the opportunity, distinct from the ORM's own create_uid audit field.
Source: https://raw.githubusercontent.com/odoo/odoo/18.0/addons/crm/models/crm_lead.py
Verification: Verified
Linked buyer
Not at all
crm.lead exposes one relationship field to a buyer, `partner_id` ('Customer'), which can point to either an individual or a company res.partner record. Vendor docs confirm the Customer field can hold "the company or contact" - there is no second, separate link specifically to a person distinct from an org link.
Source: https://raw.githubusercontent.com/odoo/odoo/18.0/addons/crm/models/crm_lead.py, https://www.odoo.com/documentation/18.0/applications/sales/crm/acquire_leads/send_quotes.html
Verification: Verified
Record preservation
In full
Lost (archived) opportunities are not deleted and remain queryable: the Pipeline Analysis page ships explicit `Lost` and `Archived` filters, and vendor docs note lost records must be "specifically marked as lost" to be counted correctly in that reporting - confirming archived records still feed reports.
Source: https://www.odoo.com/documentation/18.0/_sources/applications/sales/crm/performance/win_loss.rst.txt, https://www.odoo.com/documentation/18.0/_sources/applications/sales/crm/acquire_leads/send_quotes.rst.txt
Verification: Verified
Structured lost reasons
In full
`lost_reason_id = fields.Many2one('crm.lost.reason', string='Lost Reason', index=True, ondelete='restrict', tracking=True)` is a shipped structured field, configured under CRM > Configuration > Lost Reasons, and the Pipeline Analysis report ships a native "Lost Reason" Group By, i.e. it is countable.
Source: https://raw.githubusercontent.com/odoo/odoo/18.0/addons/crm/models/crm_lead.py, https://www.odoo.com/documentation/18.0/_sources/applications/sales/crm/performance/win_loss.rst.txt
Verification: Verified
Email sync
In part
Vendor docs describe the Outlook and Gmail plugins as "creating leads and centralizing prospects' emails into the CRM app" - a one-direction path from the mailbox into Odoo records, alongside outgoing send-from-CRM (e.g. the quotation "Send by Email" flow). No two-way full mailbox sync is documented.
Source: https://www.odoo.com/documentation/18.0/_sources/applications/general/integrations/mail_plugins.rst.txt, https://www.odoo.com/documentation/18.0/_sources/applications/sales/crm/acquire_leads/send_quotes.rst.txt
Verification: Verified
Calendar sync
No verdict
Odoo's Calendar app documents two-way sync with Outlook ("The synchronization is a two-way process, meaning that events are reconciled in both accounts") and Google, but this is documented under the general Productivity/Calendar app, not found stated as a CRM-specific feature in CRM's own documentation tree.
Source: https://www.odoo.com/documentation/18.0/_sources/applications/productivity/calendar/outlook.rst.txt
Verification: Pending vendor verification
Schedulable activity types
In full
Vendor docs state default activity types are "Email, Call, Meeting, or To-Do" and that users can create new ones: "To create a new activity type, click New from the Activity Types page."
Source: https://www.odoo.com/documentation/18.0/_sources/applications/essentials/activities.rst.txt
Verification: Verified
Opportunity reporting
Not at all
Pipeline Analysis ships Count, Days to Assign/Close/Convert, Expected/Prorated Revenue and MRR measures. A "Win/Loss Ratio" is documented only as a formula (Won opportunities / Lost opportunities) the user builds via a custom filter and Stage group-by - not a shipped rate field - and no stage-to-stage conversion rate measure is documented.
Source: https://www.odoo.com/documentation/18.0/_sources/applications/sales/crm/performance/win_loss.rst.txt
Verification: Verified