[REF] sales_team, various: improve default team computation

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 cleaned.

SPECIFICATIONS

Compute default team id for sales related documents. Note that this method is
not called by default_get as it takes some additional parameters and is meant
to be called by other default methods. Heuristic (when multiple match: take
first sequence ordered)

  1- any of my teams (member OR responsible) matching domain
  2- any of my teams (member OR responsible)
  3- default from context
  4- any team matching my company and domain
  5- any team matching my company

Note: ResPartner.team_id field is explicitly not taken into account. We think
this field causes a lot of noises compared to its added value. Think notably:
team not in responsible teams, team company not matching responsible or lead
company, asked domain not matching, ...

We also improve various places where sale team is computed on sales documents.
Purpose is to try to use same heuristic as much as possible in various
team_id fields.

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 30e3fc1377
commit fdc02d7dcb
6 changed files with 64 additions and 16 deletions
@@ -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=1363): # crm only: 1352
with self.assertQueryCount(user_sales_manager=1364): # crm only: 1353
mass_convert = self.env['crm.lead2opportunity.partner.mass'].with_context({
'active_model': 'crm.lead',
'active_ids': test_leads.ids,
+1 -2
View File
@@ -101,8 +101,7 @@ class Lead2OpportunityPartner(models.TransientModel):
user = convert.user_id
if convert.team_id and user in convert.team_id.member_ids | convert.team_id.user_id:
continue
team_domain = []
team = self.env['crm.team']._get_default_team_id(user_id=user.id, domain=team_domain)
team = self.env['crm.team']._get_default_team_id(user_id=user.id, domain=None)
convert.team_id = team.id
@api.model
@@ -50,8 +50,7 @@ class Lead2OpportunityMassConvert(models.TransientModel):
user = convert.user_id or convert.user_ids and convert.user_ids[0] or self.env.user
if convert.team_id and user in convert.team_id.member_ids | convert.team_id.user_id:
continue
team_domain = []
team = self.env['crm.team']._get_default_team_id(user_id=user.id, domain=team_domain)
team = self.env['crm.team']._get_default_team_id(user_id=user.id, domain=None)
convert.team_id = team.id
@api.depends('lead_tomerge_ids')
@@ -104,10 +104,20 @@ class CRMLeadMiningRequest(models.Model):
for request in self:
request.lead_count = mapped_data.get(request.id, 0)
@api.depends('user_id')
@api.depends('user_id', 'lead_type')
def _compute_team_id(self):
for record in self:
record.team_id = record.user_id.sale_team_id
""" When changing the user, also set a team_id or restrict team id
to the ones user_id is member of. """
for mining in self:
# setting user as void should not trigger a new team computation
if not mining.user_id:
continue
user = mining.user_id
if mining.team_id and user in mining.team_id.member_ids | mining.team_id.user_id:
continue
team_domain = [('use_leads', '=', True)] if mining.lead_type == 'lead' else [('use_opportunities', '=', True)]
team = self.env['crm.team']._get_default_team_id(user_id=user.id, domain=team_domain)
mining.team_id = team.id
@api.onchange('lead_number')
def _onchange_lead_number(self):
+1 -1
View File
@@ -407,7 +407,7 @@ class SaleOrder(models.Model):
if self.user_id:
self.team_id = self.env['crm.team'].with_context(
default_team_id=self.team_id.id
)._get_default_team_id(user_id=self.user_id.id)
)._get_default_team_id(user_id=self.user_id.id, domain=None)
@api.onchange('partner_id')
def onchange_partner_id_warning(self):
+47 -7
View File
@@ -20,17 +20,57 @@ class CrmTeam(models.Model):
_check_company_auto = True
def _get_default_team_id(self, user_id=None, domain=None):
if user_id is None:
user_id = self.env.user.id
""" Compute default team id for sales related documents. Note that this
method is not called by default_get as it takes some additional
parameters and is meant to be called by other default methods.
team = self.env['crm.team'].search([
('company_id', 'in', [False, self.env.company.id]),
'|', ('user_id', '=', user_id), ('member_ids', 'in', [user_id]),
], limit=1)
Heuristic (when multiple match: take first sequence ordered)
1- any of my teams (member OR responsible) matching domain
2- any of my teams (member OR responsible)
3- default from context
4- any team matching my company and domain
5- any team matching my company
Note: ResPartner.team_id field is explicitly not taken into account. We
think this field causes a lot of noises compared to its added value.
Think notably: team not in responsible teams, team company not matching
responsible or lead company, asked domain not matching, ...
:param user_id: salesperson to target, fallback on env.uid;
:domain: optional domain to filter teams (like use_lead = True);
"""
if user_id is None:
user = self.env.user
else:
user = self.env['res.users'].sudo().browse(user_id)
valid_cids = [False] + user.company_ids.ids
# 1- find in user memberships - note that if current user in C1 searches
# for team belonging to a user in C1/C2 -> only results for C1 will be returned
team = self.env['crm.team']
teams = self.env['crm.team'].search([
('company_id', 'in', valid_cids),
'|', ('user_id', '=', user.id), ('member_ids', 'in', [user.id]),
])
if teams and domain:
team = teams.filtered_domain(domain)[:1]
# 2- any of my teams
if not team:
team = teams[:1]
# 3- default: context
if not team and 'default_team_id' in self.env.context:
team = self.env['crm.team'].browse(self.env.context.get('default_team_id'))
# 4- default: first one matching domain, then first one
if not team:
team = self.env['crm.team'].search(domain or [], limit=1)
teams = self.env['crm.team'].search([('company_id', 'in', valid_cids)])
if teams and domain:
team = teams.filtered_domain(domain)[:1]
if not team:
team = teams[:1]
return team
def _get_default_favorite_user_ids(self):