From d025251c7e4ee4c26462e61b5d63e5c5d58689f8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?=
Date: Tue, 3 Nov 2015 11:28:55 +0100
Subject: [PATCH] [IMP] crm: stage management update 42
Stages are now shared by default between sales team. As the sales flow should
be quite the same whatever the sales team, stages are now shared and not
sales team dependant.
The possibility of customizing the flow by adding stages specific to a sales
team still exist. Stages have a team_id field that can be set in the stage
form, in configuration. However this is not the default behavior. A stage
is either common to all sales team, either specific to only one sales team.
This is why there is no team_ids field anymore, only team_id.
Moreover leads do not use stage anymore. A lead is either alive or archived.
The type field on stages is then removed, as all stages are now used in
opportunities only.
A small hack has been introduced in crm_stage default_get. Indeed when going
from the pipeline, creating a stage with a sales team in context should not
create a stage for the current sales team only. Therefore unless specified
in the context, default_team_id is discarded in the default get context.
---
addons/crm/__openerp__.py | 1 -
addons/crm/crm_lead.py | 50 +++----------
addons/crm/crm_lead_demo.xml | 30 ++++----
addons/crm/crm_lead_view.xml | 41 +----------
addons/crm/crm_stage.py | 19 +++--
addons/crm/crm_stage_views.xml | 35 ++++++++--
addons/crm/data/crm_stage_data.xml | 15 +---
addons/crm/data/crm_stage_demo.xml | 70 -------------------
addons/crm/report/crm_opportunity_report.py | 2 +-
.../report/crm_opportunity_report_view.xml | 4 +-
addons/crm/sales_team.py | 45 ------------
addons/crm/sales_team_view.xml | 6 --
addons/crm/test/crm_lead_find_stage.yml | 13 ++--
.../crm_partner_assign_data.xml | 2 -
14 files changed, 76 insertions(+), 257 deletions(-)
delete mode 100644 addons/crm/data/crm_stage_demo.xml
diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py
index 04feb481db2..89e649289b2 100644
--- a/addons/crm/__openerp__.py
+++ b/addons/crm/__openerp__.py
@@ -80,7 +80,6 @@ Dashboard for CRM will include:
'crm_mail_template_data.xml'
],
'demo': [
- 'data/crm_stage_demo.xml',
'crm_demo.xml',
'crm_lead_demo.xml',
'data/crm_action_demo.xml',
diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py
index 8f046fc8cdb..ef52be93934 100644
--- a/addons/crm/crm_lead.py
+++ b/addons/crm/crm_lead.py
@@ -72,14 +72,6 @@ class crm_lead(format_address, osv.osv):
team_id = self.pool['crm.team']._get_default_team_id(cr, SUPERUSER_ID, context=context, user_id=uid)
return self.stage_find(cr, uid, [], team_id, [('fold', '=', False)], context=context)
- def _resolve_type_from_context(self, cr, uid, context=None):
- """ Returns the type (lead or opportunity) from the type context
- key. Returns None if it cannot be resolved.
- """
- if context is None:
- context = {}
- return context.get('default_type')
-
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
access_rights_uid = access_rights_uid or uid
stage_obj = self.pool.get('crm.stage')
@@ -91,17 +83,11 @@ class crm_lead(format_address, osv.osv):
# - ('id', 'in', 'ids'): add columns that should be present
# - OR ('fold', '=', False): add default columns that are not folded
# - OR ('team_ids', '=', team_id), ('fold', '=', False) if team_id: add team columns that are not folded
- search_domain = []
team_id = context and context.get('default_team_id') or False
if team_id:
- search_domain += ['|', ('team_ids', '=', team_id)]
- search_domain += [('id', 'in', ids)]
+ search_domain = ['|', ('id', 'in', ids), '|', ('team_id', '=', False), ('team_id', '=', team_id)]
else:
- search_domain += [('id', 'in', ids)]
- # retrieve type from the context (if set: choose 'type' or 'both')
- type = self._resolve_type_from_context(cr, uid, context=context)
- if type:
- search_domain += ['|', ('type', '=', type), ('type', '=', 'both')]
+ search_domain = ['|', ('id', 'in', ids), ('team_id', '=', False)]
# perform search
stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
@@ -206,7 +192,7 @@ class crm_lead(format_address, osv.osv):
'priority': fields.selection(crm_stage.AVAILABLE_PRIORITIES, 'Rating', select=True),
'date_closed': fields.datetime('Closed', readonly=True, copy=False),
'stage_id': fields.many2one('crm.stage', 'Stage', track_visibility='onchange', select=True,
- domain="['&', ('team_ids', '=', team_id), '|', ('type', '=', type), ('type', '=', 'both')]"),
+ domain="['|', ('team_id', '=', False), ('team_id', '=', team_id)]"),
'user_id': fields.many2one('res.users', 'Salesperson', select=True, track_visibility='onchange'),
'referred': fields.char('Referred By'),
'date_open': fields.datetime('Assigned', readonly=True),
@@ -320,32 +306,20 @@ class crm_lead(format_address, osv.osv):
cases = self.browse(cr, uid, cases, context=context)
if context is None:
context = {}
- # check whether we should try to add a condition on type
- avoid_add_type_term = any([term for term in domain if len(term) == 3 if term[0] == 'type'])
# collect all team_ids
team_ids = set()
- types = ['both']
- if not cases and context.get('default_type'):
- ctx_type = context.get('default_type')
- types += [ctx_type]
if team_id:
team_ids.add(team_id)
for lead in cases:
if lead.team_id:
team_ids.add(lead.team_id.id)
- if lead.type not in types:
- types.append(lead.type)
# OR all team_ids
- search_domain = []
if team_ids:
- search_domain += [('|')] * (len(team_ids) - 1)
- for team_id in team_ids:
- search_domain.append(('team_ids', '=', team_id))
- # AND with cases types
- if not avoid_add_type_term:
- search_domain.append(('type', 'in', types))
+ search_domain = ['|', ('team_id', '=', False), ('team_id', 'in', list(team_ids))]
+ else:
+ search_domain = [('team_id', '=', False)]
# AND with the domain in parameter
- search_domain += list(domain)
+ search_domain = ['&'] + list(domain) + search_domain
# perform search, return the first found
stage_ids = self.pool.get('crm.stage').search(cr, uid, search_domain, order=order, limit=1, context=context)
if stage_ids:
@@ -628,7 +602,7 @@ class crm_lead(format_address, osv.osv):
# Check if the stage is in the stages of the sales team. If not, assign the stage with the lowest sequence
if merged_data.get('team_id'):
- team_stage_ids = self.pool.get('crm.stage').search(cr, uid, [('team_ids', 'in', merged_data['team_id']), ('type', 'in', [merged_data.get('type'), 'both'])], order='sequence', context=context)
+ team_stage_ids = self.pool.get('crm.stage').search(cr, uid, [('team_id', '=', merged_data['team_id'])], order='sequence', context=context)
if merged_data.get('stage_id') not in team_stage_ids:
merged_data['stage_id'] = team_stage_ids and team_stage_ids[0] or False
# Write merged data into first opportunity
@@ -640,10 +614,6 @@ class crm_lead(format_address, osv.osv):
return highest.id
def _convert_opportunity_data(self, cr, uid, lead, customer, team_id=False, context=None):
- crm_stage = self.pool.get('crm.stage')
- contact_id = False
- if customer:
- contact_id = self.pool.get('res.partner').address_get(cr, uid, [customer.id])['contact']
if not team_id:
team_id = lead.team_id and lead.team_id.id or False
val = {
@@ -657,8 +627,8 @@ class crm_lead(format_address, osv.osv):
'phone': customer and customer.phone or lead.phone,
'date_conversion': fields.datetime.now(),
}
- if not lead.stage_id or lead.stage_id.type=='lead':
- val['stage_id'] = self.stage_find(cr, uid, [lead], team_id, [('type', 'in', ('opportunity', 'both'))], context=context)
+ if not lead.stage_id:
+ val['stage_id'] = self.stage_find(cr, uid, [lead], team_id, [], context=context)
return val
def convert_opportunity(self, cr, uid, ids, partner_id, user_ids=False, team_id=False, context=None):
diff --git a/addons/crm/crm_lead_demo.xml b/addons/crm/crm_lead_demo.xml
index 2bfcba26553..f0abb56096e 100644
--- a/addons/crm/crm_lead_demo.xml
+++ b/addons/crm/crm_lead_demo.xml
@@ -127,7 +127,7 @@
-
+
@@ -148,7 +148,7 @@
-
+
@@ -170,7 +170,7 @@
2
-
+
@@ -208,7 +208,7 @@ Contact: +1 813 494 5005
- Click to set a new stage in your lead/opportunity pipeline.
-
- Stages will allow salespersons to easily track how a specific
- lead or opportunity is positioned in the sales cycle.
-
-
-
-
-
-
@@ -106,10 +71,6 @@
-
@@ -400,7 +361,7 @@
attrs="{'invisible': [('active', '=', False)]}"/>
diff --git a/addons/crm/crm_stage.py b/addons/crm/crm_stage.py
index c83461a9c0f..c686b62f3b2 100644
--- a/addons/crm/crm_stage.py
+++ b/addons/crm/crm_stage.py
@@ -22,32 +22,31 @@ class crm_stage(osv.Model):
_rec_name = 'name'
_order = "sequence"
- def _default_team_ids(self, cr, uid, context=None):
- return context.get('default_team_id') and [(6, 0, [context['default_team_id']])] or False
-
_columns = {
'name': fields.char('Stage Name', required=True, translate=True),
'sequence': fields.integer('Sequence', help="Used to order stages. Lower is better."),
'probability': fields.float('Probability (%)', required=True, help="This percentage depicts the default/average probability of the Case for this stage to be a success"),
'on_change': fields.boolean('Change Probability Automatically', help="Setting this stage will change the probability automatically on the opportunity."),
'requirements': fields.text('Requirements', help="Enter here the internal requirements for this stage (ex: Offer sent to customer). It will appear as a tooltip over the stage's name."),
- 'team_ids': fields.many2many('crm.team', 'crm_team_stage_rel', 'stage_id', 'team_id', string='Teams',
- help="Link between stages and sales teams. When set, this limitate the current stage to the selected sales teams."),
+ 'team_id': fields.many2one('crm.team', 'Team',
+ ondelete='set null',
+ help='Specific team that uses this stage. Other teams will not ne able to see or use this stage.'),
'legend_priority': fields.text(
'Priority Management Explanation', translate=True,
help='Explanation text to help users using the star and priority mechanism on stages or issues that are in this stage.'),
'fold': fields.boolean('Folded in Pipeline',
help='This stage is folded in the kanban view when '
'there are no records in that stage to display.'),
- 'type': fields.selection([('lead', 'Lead'), ('opportunity', 'Opportunity'), ('both', 'Both')],
- string='Type', required=True,
- help="This field is used to distinguish stages related to Leads from stages related to Opportunities, or to specify stages available for both types."),
}
_defaults = {
'sequence': 1,
'probability': 10.0,
- 'team_ids': _default_team_ids,
'fold': False,
- 'type': 'both',
}
+
+ def default_get(self, cr, uid, fields, context=None):
+ if context and context.get('default_team_id') and not context.get('crm_team_mono', False):
+ context = dict(context)
+ context.pop('default_team_id')
+ return super(crm_stage, self).default_get(cr, uid, fields, context=context)
diff --git a/addons/crm/crm_stage_views.xml b/addons/crm/crm_stage_views.xml
index ad1199d6b27..95d0535a0f1 100644
--- a/addons/crm/crm_stage_views.xml
+++ b/addons/crm/crm_stage_views.xml
@@ -1,5 +1,19 @@
+
+
+ Stage - Search
+ crm.stage
+
+
+
+
+
+
+
+
+
+
crm.stage.treecrm.stage
@@ -8,7 +22,7 @@
-
+
@@ -22,7 +36,7 @@
-
+
@@ -32,16 +46,25 @@
-
-
-
+ Stagescrm.stageform
-
+
+
+
+ Click to set a new stage in your opportunity pipeline.
+
+ Stages allow salespersons to easily track how a specific opportunity
+ is positioned in the sales cycle.
+