212 lines
10 KiB
Python
212 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from odoo import api, exceptions, fields, models, _
|
|
|
|
|
|
class CrmTeamMember(models.Model):
|
|
_name = 'crm.team.member'
|
|
_inherit = ['mail.thread']
|
|
_description = 'Sales Team Member'
|
|
_rec_name = 'user_id'
|
|
_order = 'create_date ASC, id'
|
|
_check_company_auto = True
|
|
|
|
crm_team_id = fields.Many2one(
|
|
'crm.team', string='Sales Team', group_expand='_read_group_crm_team_id',
|
|
default=False, # TDE: temporary fix to activate depending computed fields
|
|
check_company=True, index=True, ondelete="cascade", required=True)
|
|
user_id = fields.Many2one(
|
|
'res.users', string='Salesperson', # TDE FIXME check responsible field
|
|
check_company=True, index=True, ondelete='cascade', required=True,
|
|
domain="[('share', '=', False), ('id', 'not in', user_in_teams_ids), ('company_ids', 'in', user_company_ids)]")
|
|
user_in_teams_ids = fields.Many2many(
|
|
'res.users', compute='_compute_user_in_teams_ids',
|
|
help='UX: Give users not to add in the currently chosen team to avoid duplicates')
|
|
user_company_ids = fields.Many2many(
|
|
'res.company', compute='_compute_user_company_ids',
|
|
help='UX: Limit to team company or all if no company')
|
|
active = fields.Boolean(string='Active', default=True)
|
|
is_membership_multi = fields.Boolean(
|
|
'Multiple Memberships Allowed', compute='_compute_is_membership_multi',
|
|
help='If True, users may belong to several sales teams. Otherwise membership is limited to a single sales team.')
|
|
member_warning = fields.Text(compute='_compute_member_warning')
|
|
# salesman information
|
|
image_1920 = fields.Image("Image", related="user_id.image_1920", max_width=1920, max_height=1920)
|
|
image_128 = fields.Image("Image (128)", related="user_id.image_128", max_width=128, max_height=128)
|
|
name = fields.Char(string='Name', related='user_id.display_name', readonly=False)
|
|
email = fields.Char(string='Email', related='user_id.email')
|
|
phone = fields.Char(string='Phone', related='user_id.phone')
|
|
mobile = fields.Char(string='Mobile', related='user_id.mobile')
|
|
company_id = fields.Many2one('res.company', string='Company', related='user_id.company_id')
|
|
|
|
@api.constrains('crm_team_id', 'user_id', 'active')
|
|
def _constrains_membership(self):
|
|
# In mono membership mode: check crm_team_id / user_id is unique for active
|
|
# memberships. Inactive memberships can create duplicate pairs which is whyy
|
|
# we don't use a SQL constraint. Include "self" in search in case we use create
|
|
# multi with duplicated user / team pairs in it. Use an explicit active leaf
|
|
# in domain as we may have an active_test in context that would break computation
|
|
existing = self.env['crm.team.member'].search([
|
|
('crm_team_id', 'in', self.crm_team_id.ids),
|
|
('user_id', 'in', self.user_id.ids),
|
|
('active', '=', True)
|
|
])
|
|
duplicates = self.env['crm.team.member']
|
|
|
|
active_records = dict(
|
|
(membership.user_id.id, membership.crm_team_id.id)
|
|
for membership in self if membership.active
|
|
)
|
|
for membership in self:
|
|
potential = existing.filtered(lambda m: m.user_id == membership.user_id and \
|
|
m.crm_team_id == membership.crm_team_id and m.id != membership.id
|
|
)
|
|
if not potential or len(potential) > 1:
|
|
duplicates += potential
|
|
continue
|
|
if active_records.get(potential.user_id.id):
|
|
duplicates += potential
|
|
else:
|
|
active_records[potential.user_id.id] = potential.crm_team_id.id
|
|
|
|
if duplicates:
|
|
raise exceptions.ValidationError(
|
|
_("You are trying to create duplicate membership(s). We found that %(duplicates)s already exist(s).",
|
|
duplicates=", ".join("%s (%s)" % (m.user_id.name, m.crm_team_id.name) for m in duplicates)
|
|
))
|
|
|
|
@api.depends('crm_team_id', 'is_membership_multi', 'user_id')
|
|
@api.depends_context('default_crm_team_id')
|
|
def _compute_user_in_teams_ids(self):
|
|
""" Give users not to add in the currently chosen team to avoid duplicates.
|
|
In multi membership mode this field is empty as duplicates are allowed. """
|
|
if all(m.is_membership_multi for m in self):
|
|
member_user_ids = self.env['res.users']
|
|
elif self.ids:
|
|
member_user_ids = self.env['crm.team.member'].search([('id', 'not in', self.ids)]).user_id
|
|
else:
|
|
member_user_ids = self.env['crm.team.member'].search([]).user_id
|
|
for member in self:
|
|
if member_user_ids:
|
|
member.user_in_teams_ids = member_user_ids
|
|
elif member.crm_team_id:
|
|
member.user_in_teams_ids = member.crm_team_id.member_ids
|
|
elif self.env.context.get('default_crm_team_id'):
|
|
member.user_in_teams_ids = self.env['crm.team'].browse(self.env.context['default_crm_team_id']).member_ids
|
|
else:
|
|
member.user_in_teams_ids = self.env['res.users']
|
|
|
|
@api.depends('crm_team_id')
|
|
def _compute_user_company_ids(self):
|
|
all_companies = self.env['res.company'].search([])
|
|
for member in self:
|
|
member.user_company_ids = member.crm_team_id.company_id or all_companies
|
|
|
|
@api.depends('crm_team_id')
|
|
def _compute_is_membership_multi(self):
|
|
multi_enabled = self.env['ir.config_parameter'].sudo().get_param('sales_team.membership_multi', False)
|
|
self.is_membership_multi = multi_enabled
|
|
|
|
@api.depends('is_membership_multi', 'active', 'user_id', 'crm_team_id')
|
|
def _compute_member_warning(self):
|
|
""" Display a warning message to warn user they are about to archive
|
|
other memberships. Only valid in mono-membership mode and take into
|
|
account only active memberships as we may keep several archived
|
|
memberships. """
|
|
if all(m.is_membership_multi for m in self):
|
|
self.member_warning = False
|
|
else:
|
|
active = self.filtered('active')
|
|
(self - active).member_warning = False
|
|
if not active:
|
|
return
|
|
existing = self.env['crm.team.member'].search([('user_id', 'in', active.user_id.ids)])
|
|
user_mapping = dict.fromkeys(existing.user_id, self.env['crm.team'])
|
|
for membership in existing:
|
|
user_mapping[membership.user_id] |= membership.crm_team_id
|
|
|
|
for member in active:
|
|
teams = user_mapping.get(member.user_id, self.env['crm.team'])
|
|
remaining = teams - (member.crm_team_id | member._origin.crm_team_id)
|
|
if remaining:
|
|
member.member_warning = _("Adding %(user_name)s in this team would remove him/her from its current teams %(team_names)s.",
|
|
user_name=member.user_id.name,
|
|
team_names=", ".join(remaining.mapped('name'))
|
|
)
|
|
else:
|
|
member.member_warning = False
|
|
|
|
# ------------------------------------------------------------
|
|
# CRUD
|
|
# ------------------------------------------------------------
|
|
|
|
@api.model_create_multi
|
|
def create(self, values_list):
|
|
""" Specific behavior implemented on create
|
|
|
|
* mono membership mode: other user memberships are automatically
|
|
archived (a warning already told it in form view);
|
|
* creating a membership already existing as archived: do nothing as
|
|
people can manage them from specific menu "Members";
|
|
|
|
Also remove autofollow on create. No need to follow team members
|
|
when creating them as chatter is mainly used for information purpose
|
|
(tracked fields).
|
|
"""
|
|
is_membership_multi = self.env['ir.config_parameter'].sudo().get_param('sales_team.membership_multi', False)
|
|
if not is_membership_multi:
|
|
self._synchronize_memberships(values_list)
|
|
return super(CrmTeamMember, self.with_context(
|
|
mail_create_nosubscribe=True
|
|
)).create(values_list)
|
|
|
|
def write(self, values):
|
|
""" Specific behavior about active. If you change user_id / team_id user
|
|
get warnings in form view and a raise in constraint check. We support
|
|
archive / activation of memberships that toggles other memberships. But
|
|
we do not support manual creation or update of user_id / team_id. This
|
|
either works, either crashes). Indeed supporting it would lead to complex
|
|
code with low added value. Users should create or remove members, and
|
|
maybe archive / activate them. Updating manually memberships by
|
|
modifying user_id or team_id is advanced and does not benefit from our
|
|
support. """
|
|
is_membership_multi = self.env['ir.config_parameter'].sudo().get_param('sales_team.membership_multi', False)
|
|
if not is_membership_multi and values.get('active'):
|
|
self._synchronize_memberships([
|
|
dict(user_id=membership.user_id.id, crm_team_id=membership.crm_team_id.id)
|
|
for membership in self
|
|
])
|
|
return super(CrmTeamMember, self).write(values)
|
|
|
|
@api.model
|
|
def _read_group_crm_team_id(self, teams, domain, order):
|
|
"""Read group customization in order to display all the teams in
|
|
Kanban view, even if they are empty.
|
|
"""
|
|
return self.env['crm.team'].search([], order=order)
|
|
|
|
def _synchronize_memberships(self, user_team_ids):
|
|
""" Synchronize memberships: archive other memberships.
|
|
|
|
:param user_team_ids: list of pairs (user_id, crm_team_id)
|
|
"""
|
|
existing = self.search([
|
|
('active', '=', True), # explicit search on active only, whatever context
|
|
('user_id', 'in', [values['user_id'] for values in user_team_ids])
|
|
])
|
|
user_memberships = dict.fromkeys(existing.user_id.ids, self.env['crm.team.member'])
|
|
for membership in existing:
|
|
user_memberships[membership.user_id.id] += membership
|
|
|
|
existing_to_archive = self.env['crm.team.member']
|
|
for values in user_team_ids:
|
|
existing_to_archive += user_memberships.get(values['user_id'], self.env['crm.team.member']).filtered(
|
|
lambda m: m.crm_team_id.id != values['crm_team_id']
|
|
)
|
|
|
|
if existing_to_archive:
|
|
existing_to_archive.action_archive()
|
|
|
|
return existing_to_archive
|