[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:
@@ -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,
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user