[REF] crm: add multi company check on lead model

GLOBAL PURPOSE

Ability to have salesmen belonging to several sales team is a core requirement
of CRM. It is therefore moved from website_crm_score to crm along with cleaning
and behavior improvement. Automatic lead assignment is also moved and clean

SPECIFICATIONS

Set _check_company_auto on lead model to enable automatic company check when
updating company_id field.

Add a company check on user_id field to ensure user and lead company match.
Also add a company check on team_id to ensure team and lead company match.

Add a dynamic domain to be used in form view to limit available users to set
on leads to those matching domain.

LINKS

Task ID-2086889 (main task)
Task ID-2357969 (scoring migration task)
Community PR odoo/odoo#48422
Enterprise PR odoo/enterprise#499
Upgrade PR odoo/upgrade#996
This commit is contained in:
Thibault Delavallée
2021-01-21 12:22:09 +00:00
parent fdc02d7dcb
commit 0edc854a84
3 changed files with 62 additions and 12 deletions
+49 -4
View File
@@ -84,15 +84,24 @@ class Lead(models.Model):
'format.address.mixin',
]
_primary_email = 'email_from'
_check_company_auto = True
# Description
name = fields.Char(
'Opportunity', index=True, required=True,
compute='_compute_name', readonly=False, store=True)
user_id = fields.Many2one('res.users', string='Salesperson', index=True, tracking=True, default=lambda self: self.env.user)
user_id = fields.Many2one(
'res.users', string='Salesperson', default=lambda self: self.env.user,
domain="['&', ('share', '=', False), ('company_ids', 'in', user_company_ids)]",
check_company=True, index=True, tracking=True)
user_company_ids = fields.Many2many(
'res.company', compute='_compute_user_company_ids',
help='UX: Limit to lead company or all if no company')
user_email = fields.Char('User Email', related='user_id.email', readonly=True)
user_login = fields.Char('User Login', related='user_id.login', readonly=True)
company_id = fields.Many2one('res.company', string='Company', index=True, default=lambda self: self.env.company.id)
company_id = fields.Many2one(
'res.company', string='Company', index=True,
compute='_compute_company_id', readonly=False, store=True)
referred = fields.Char('Referred By')
description = fields.Text('Notes')
active = fields.Boolean('Active', default=True, tracking=True)
@@ -104,7 +113,8 @@ class Lead(models.Model):
crm_stage.AVAILABLE_PRIORITIES, string='Priority', index=True,
default=crm_stage.AVAILABLE_PRIORITIES[0][0])
team_id = fields.Many2one(
'crm.team', string='Sales Team', index=True, tracking=True,
'crm.team', string='Sales Team', check_company=True, index=True, tracking=True,
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
compute='_compute_team_id', readonly=False, store=True)
stage_id = fields.Many2one(
'crm.stage', string='Stage', index=True, tracking=True,
@@ -149,7 +159,7 @@ class Lead(models.Model):
date_deadline = fields.Date('Expected Closing', help="Estimate of the date on which the opportunity will be won.")
# Customer / contact
partner_id = fields.Many2one(
'res.partner', string='Customer', index=True, tracking=10,
'res.partner', string='Customer', check_company=True, index=True, tracking=10,
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
help="Linked partner (optional). Usually created when converting the lead. You can find a partner by its Name, TIN, Email or Internal Reference.")
partner_is_blacklisted = fields.Boolean('Partner is blacklisted', related='partner_id.is_blacklisted', readonly=True)
@@ -240,6 +250,15 @@ class Lead(models.Model):
def _search_activity_date_deadline_my(self, operator, operand):
return ['&', ('activity_ids.user_id', '=', self._uid), ('activity_ids.date_deadline', operator, operand)]
@api.depends('company_id')
def _compute_user_company_ids(self):
all_companies = self.env['res.company'].search([])
for lead in self:
if not lead.company_id:
lead.user_company_ids = all_companies
else:
lead.user_company_ids = lead.company_id
@api.depends('user_id', 'type')
def _compute_team_id(self):
""" When changing the user, also set a team_id or restrict team id
@@ -255,6 +274,32 @@ class Lead(models.Model):
team = self.env['crm.team']._get_default_team_id(user_id=user.id, domain=team_domain)
lead.team_id = team.id
@api.depends('user_id', 'team_id')
def _compute_company_id(self):
""" Compute company_id coherency. """
for lead in self:
proposal = lead.company_id
# invalidate wrong configuration: company not in responsible companies or in team company if set
if proposal and lead.user_id and proposal not in lead.user_id.company_ids:
proposal = False
if proposal and lead.team_id.company_id and proposal != lead.team_id.company_id:
proposal = False
# propose a new company based on responsible, limited by team
if not proposal:
if not lead.user_id or lead.user_id == self.env.user:
proposal = self.env.company
elif lead.user_id:
proposal = lead.user_id.company_id
if lead.team_id.company_id and proposal != lead.team_id.company_id:
proposal = False
# set a new company
if lead.company_id != proposal:
lead.company_id = proposal
@api.depends('team_id', 'type')
def _compute_stage_id(self):
for lead in self:
@@ -24,7 +24,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon):
with self.assertQueryCount(user_sales_manager=0):
test_leads = self.env['crm.lead'].browse(test_leads.ids)
with self.assertQueryCount(user_sales_manager=254): # crm only: 251
with self.assertQueryCount(user_sales_manager=254): # crm only: 252
test_leads._handle_salesmen_assignment(user_ids=user_ids, team_id=False)
self.assertEqual(test_leads.team_id, self.sales_team_convert | self.sales_team_1)
@@ -42,7 +42,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon):
with self.assertQueryCount(user_sales_manager=0):
test_leads = self.env['crm.lead'].browse(test_leads.ids)
with self.assertQueryCount(user_sales_manager=220): # crm only: 215
with self.assertQueryCount(user_sales_manager=220): # crm only: 218
test_leads._handle_salesmen_assignment(user_ids=user_ids, team_id=team_id)
self.assertEqual(test_leads.team_id, self.sales_team_convert)
@@ -166,7 +166,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon):
test_leads = self._create_leads_batch(count=50, user_ids=[False])
user_ids = self.assign_users.ids
with self.assertQueryCount(user_sales_manager=1364): # crm only: 1353
with self.assertQueryCount(user_sales_manager=1367): # crm only: 1357
mass_convert = self.env['crm.lead2opportunity.partner.mass'].with_context({
'active_model': 'crm.lead',
'active_ids': test_leads.ids,
+10 -5
View File
@@ -213,10 +213,9 @@
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color', 'no_create_edit': True}"/>
</group>
<group>
<field name="user_id" domain="[('share', '=', False)]"
<field name="user_id"
context="{'default_sales_team_id': team_id}" widget="many2one_avatar_user"/>
<field name="team_id" widget="selection"
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]"/>
<field name="team_id" widget="selection"/>
<field name="type" invisible="1"/>
</group>
<group name="lead_priority" attrs="{'invisible': [('type', '=', 'opportunity')]}">
@@ -227,6 +226,7 @@
<field name="lost_reason" attrs="{'invisible': [('active', '=', True)]}"/>
<field name="date_conversion" invisible="1"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="user_company_ids" invisible="1"/>
</group>
</group>
@@ -338,7 +338,9 @@
<field name="state_id" optional="hide"/>
<field name="country_id" optional="show" options="{'no_open': True, 'no_create': True}"/>
<field name="partner_id" invisible="1"/>
<field name="user_id" optional="show" widget="many2one_avatar_user" domain="[('share', '=', False)]"/>
<!-- Explicit domain due to multi edit -> real company domain would be complicated -->
<field name="user_id" optional="show" widget="many2one_avatar_user"
domain="[('share', '=', False)]"/>
<field name="team_id" optional="show"/>
<field name="active" invisible="1"/>
<field name="probability" invisible="1"/>
@@ -443,6 +445,7 @@
<field name="company_currency" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="user_id" invisible="1"/>
<field name="user_company_ids" invisible="1"/>
<field name="team_id" invisible="1"/>
<field name="type" invisible="1"/>
<field name="partner_name" invisible="1"/>
@@ -669,7 +672,9 @@
<field name="city" optional="hide"/>
<field name="state_id" optional="hide"/>
<field name="country_id" optional="hide" options="{'no_open': True, 'no_create': True}"/>
<field name="user_id" widget="many2one_avatar_user" optional="show" domain="[('share', '=', False)]"/>
<!-- Explicit domain due to multi edit -> real company domain would be complicated -->
<field name="user_id" widget="many2one_avatar_user" optional="show"
domain="[('share', '=', False)]"/>
<field name="team_id" optional="hide"/>
<field name="priority" optional="hide"/>
<field name="activity_ids" widget="list_activity"/>