ERP with a CRM module

Odoo CRM

Not a CRM - fails on core objects, status, linked buyer, opportunity reporting; calendar sync unresolved

Every verdict, with its evidence

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