diff --git a/addons/crm/__manifest__.py b/addons/crm/__manifest__.py
index c5d31ed7477..559e317f66c 100644
--- a/addons/crm/__manifest__.py
+++ b/addons/crm/__manifest__.py
@@ -47,6 +47,7 @@
'views/crm_lost_reason_views.xml',
'views/crm_stage_views.xml',
'views/crm_lead_views.xml',
+ 'views/crm_team_member_views.xml',
'views/digest_views.xml',
'views/mail_activity_views.xml',
'views/res_config_settings_views.xml',
@@ -59,6 +60,7 @@
'demo': [
'data/crm_team_demo.xml',
'data/mail_template_demo.xml',
+ 'data/crm_team_member_demo.xml',
'data/mail_activity_demo.xml',
'data/crm_lead_demo.xml',
],
diff --git a/addons/crm/data/crm_team_member_demo.xml b/addons/crm/data/crm_team_member_demo.xml
new file mode 100644
index 00000000000..00472c3a1c7
--- /dev/null
+++ b/addons/crm/data/crm_team_member_demo.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ 1
+
+
+ 2
+ ['&', ['lang_id', '=', 'English'], ['country_id', '=', 'Belgium']]
+
+
+
diff --git a/addons/crm/models/__init__.py b/addons/crm/models/__init__.py
index 7b1a7fdcd7a..2282f96b1d3 100644
--- a/addons/crm/models/__init__.py
+++ b/addons/crm/models/__init__.py
@@ -7,6 +7,7 @@ from . import crm_lead
from . import crm_lost_reason
from . import crm_stage
from . import crm_team
+from . import crm_team_member
from . import ir_config_parameter
from . import res_config_settings
from . import res_partner
diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py
index 1c8cf04fe38..07738d4f1ed 100644
--- a/addons/crm/models/crm_lead.py
+++ b/addons/crm/models/crm_lead.py
@@ -3,16 +3,16 @@
import logging
import threading
+from collections import OrderedDict, defaultdict
from datetime import date, datetime, timedelta
from psycopg2 import sql
from odoo import api, fields, models, tools, SUPERUSER_ID
from odoo.addons.phone_validation.tools import phone_validation
+from odoo.exceptions import UserError, AccessError
from odoo.osv import expression
from odoo.tools.translate import _
-from odoo.tools import email_re, email_split
-from odoo.exceptions import UserError, AccessError
-from collections import OrderedDict, defaultdict
+from odoo.tools import email_re, email_split, safe_eval
from . import crm_stage
@@ -71,6 +71,12 @@ PARTNER_ADDRESS_FIELDS_TO_SYNC = [
PLS_COMPUTE_BATCH_STEP = 50000 # odoo.models.PREFETCH_MAX = 1000 but larger cluster can speed up global computation
PLS_UPDATE_BATCH_STEP = 5000
+# Evaluation context for all domain-based lead auto assignment
+LEAD_ASSIGN_EVAL_CONTEXT = {
+ 'datetime': safe_eval.datetime,
+ 'context_today': datetime.now,
+}
+
class Lead(models.Model):
_name = "crm.lead"
@@ -249,7 +255,7 @@ class Lead(models.Model):
if not lead.user_id:
continue
user = lead.user_id
- if lead.team_id and user in lead.team_id.member_ids | lead.team_id.user_id:
+ if lead.team_id and user in (lead.team_id.member_ids | lead.team_id.user_id):
continue
team_domain = [('use_leads', '=', True)] if lead.type == 'lead' else [('use_opportunities', '=', True)]
team = self.env['crm.team']._get_default_team_id(user_id=user.id, domain=team_domain)
diff --git a/addons/crm/models/crm_team_member.py b/addons/crm/models/crm_team_member.py
new file mode 100644
index 00000000000..b8d5573fec5
--- /dev/null
+++ b/addons/crm/models/crm_team_member.py
@@ -0,0 +1,39 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import datetime
+
+from odoo import api, exceptions, fields, models, _
+from odoo.addons.crm.models.crm_lead import LEAD_ASSIGN_EVAL_CONTEXT
+from odoo.tools import safe_eval
+
+
+class Team(models.Model):
+ _inherit = 'crm.team.member'
+
+ # assignment
+ team_user_domain = fields.Char('Domain', tracking=True)
+ maximum_user_leads = fields.Integer('Leads Per Month')
+ lead_month_count = fields.Integer(
+ 'Assigned Leads', compute='_compute_lead_month_count',
+ help='Lead assigned to this member those last 30 days')
+
+ def _compute_lead_month_count(self):
+ for member in self:
+ if member.id:
+ limit_date = fields.Datetime.now() - datetime.timedelta(days=30)
+ domain = [('user_id', '=', member.user_id.id),
+ ('team_id', '=', member.crm_team_id.id),
+ ('date_open', '>=', limit_date)]
+ member.lead_month_count = self.env['crm.lead'].search_count(domain)
+ else:
+ member.lead_month_count = 0
+
+ @api.constrains('team_user_domain')
+ def _constrains_team_user_domain(self):
+ for member in self:
+ try:
+ domain = safe_eval.safe_eval(member.team_user_domain or '[]', LEAD_ASSIGN_EVAL_CONTEXT)
+ self.env['crm.lead'].search(domain, limit=1)
+ except Exception:
+ raise exceptions.UserError(_('Team membership assign domain is incorrectly formatted'))
diff --git a/addons/crm/views/crm_menu_views.xml b/addons/crm/views/crm_menu_views.xml
index 20fb5503662..eda5f6583b5 100644
--- a/addons/crm/views/crm_menu_views.xml
+++ b/addons/crm/views/crm_menu_views.xml
@@ -96,6 +96,13 @@
parent="crm_menu_config"
action="sales_team.crm_team_action_config"
sequence="5"/>
+