[IMP] crm : various code cleaning

- try to make better use of recordsets (and recordset APIs)
- remove pointless decorators
- remove redundant ensure_one
- make 'stage_find' a private method returning a recordset
- etc ...
This commit is contained in:
Xavier Morel
2016-07-04 17:28:06 +02:00
committed by Jérome Maes
parent 4f899f17d4
commit 3e2e1d6b90
12 changed files with 156 additions and 191 deletions
+61 -69
View File
@@ -11,7 +11,8 @@ from odoo.tools import email_re, email_split
from odoo.exceptions import UserError, AccessError
from odoo.addons.base.res.res_partner import FormatAddress
from odoo.addons.crm.models import crm_stage
from . import crm_stage
_logger = logging.getLogger(__name__)
@@ -65,7 +66,7 @@ class Lead(FormatAddress, models.Model):
def _default_stage_id(self):
team = self.env['crm.team'].sudo()._get_default_team_id(user_id=self.env.uid)
return self.stage_find(team_id=team.id, domain=[('fold', '=', False)])
return self._stage_find(team_id=team.id, domain=[('fold', '=', False)]).id
name = fields.Char('Opportunity', required=True, index=True)
partner_id = fields.Many2one('res.partner', string='Customer', track_visibility='onchange', index=True,
@@ -91,7 +92,7 @@ class Lead(FormatAddress, models.Model):
type = fields.Selection([('lead', 'Lead'), ('opportunity', 'Opportunity')], index=True, required=True,
default=lambda self: 'lead' if self.env['res.users'].has_group('crm.group_use_lead') else 'opportunity',
help="Type is used to separate Leads and Opportunities")
priority = fields.Selection(crm_stage.AVAILABLE_PRIORITIES, string='Rating', index=True, default=lambda *a: crm_stage.AVAILABLE_PRIORITIES[0][0])
priority = fields.Selection(crm_stage.AVAILABLE_PRIORITIES, string='Rating', index=True, default=crm_stage.AVAILABLE_PRIORITIES[0][0])
date_closed = fields.Datetime('Closed Date', readonly=True, copy=False)
stage_id = fields.Many2one('crm.stage', string='Stage', track_visibility='onchange', index=True,
@@ -170,7 +171,7 @@ class Lead(FormatAddress, models.Model):
stages = Stage.browse(stage_ids)
result = stages.name_get()
# restore order of the search
order_mapping = dict((item[0], stage_ids.index(item[0])) for item in result) # match stage_id with its index
order_mapping = {item[0]: stage_ids.index(item[0]) for item in result} # match stage_id with its index
result.sort(key=lambda item: order_mapping.get(item[0]))
fold = {}
@@ -191,7 +192,7 @@ class Lead(FormatAddress, models.Model):
lead_date = fields.Date.from_string(lead.date_action)
if lead_date >= today:
kanban_state = 'green'
elif lead_date < today:
else:
kanban_state = 'grey'
lead.kanban_state = kanban_state
@@ -214,7 +215,7 @@ class Lead(FormatAddress, models.Model):
@api.multi
def _compute_meeting_count(self):
meeting_data = self.env['calendar.event'].read_group([('opportunity_id', 'in', self.ids)], ['opportunity_id'], ['opportunity_id'])
mapped_data = dict([(m['opportunity_id'][0], m['opportunity_id_count']) for m in meeting_data])
mapped_data = {m['opportunity_id'][0]: m['opportunity_id_count'] for m in meeting_data}
for lead in self:
lead.meeting_count = mapped_data.get(lead.id, 0)
@@ -230,12 +231,11 @@ class Lead(FormatAddress, models.Model):
@api.onchange('stage_id')
def _onchange_stage_id(self):
values = self._onchange_stage_id_values(self.stage_id.id if self.stage_id else False)
values = self._onchange_stage_id_values(self.stage_id.id)
self.update(values)
def _onchange_partner_id_values(self, partner_id):
""" returns the new values when partner_id has changed """
values = {}
if partner_id:
partner = self.env['res.partner'].browse(partner_id)
@@ -243,7 +243,7 @@ class Lead(FormatAddress, models.Model):
if not partner_name and partner.is_company:
partner_name = partner.name
values = {
return {
'partner_name': partner_name,
'contact_name': partner.name if not partner.is_company else False,
'title': partner.title.id,
@@ -259,7 +259,7 @@ class Lead(FormatAddress, models.Model):
'zip': partner.zip,
'function': partner.function,
}
return values
return {}
@api.onchange('partner_id')
def _onchange_partner_id(self):
@@ -305,11 +305,14 @@ class Lead(FormatAddress, models.Model):
@api.model
def create(self, vals):
# set up context used to find the lead's sales team which is needed
# to correctly set the default stage_id
context = dict(self._context or {})
if vals.get('type') and not self._context.get('default_type'):
context['default_type'] = vals.get('type')
if vals.get('team_id') and not self._context.get('default_team_id'):
context['default_team_id'] = vals.get('team_id')
if vals.get('user_id') and 'date_open' not in vals:
vals['date_open'] = fields.Datetime.now()
# context: no_log, because subtype already handle this
@@ -376,8 +379,8 @@ class Lead(FormatAddress, models.Model):
def action_set_won(self):
""" Won semantic: probability = 100 (active untouched) """
for lead in self:
stage_id = lead.stage_find(domain=[('probability', '=', 100.0), ('on_change', '=', True)])
lead.write({'stage_id': stage_id, 'probability': 100})
stage_id = lead._stage_find(domain=[('probability', '=', 100.0), ('on_change', '=', True)])
lead.write({'stage_id': stage_id.id, 'probability': 100})
return True
@api.multi
@@ -386,7 +389,7 @@ class Lead(FormatAddress, models.Model):
:return dict: dictionary value for created Meeting view
"""
self.ensure_one()
action = self.env['ir.actions.act_window'].for_xml_id('calendar', 'action_calendar_event')
action = self.env.ref('calendar.action_calendar_event').read()[0]
partner_ids = self.env.user.partner_id.ids
if self.partner_id:
partner_ids.append(self.partner_id.id)
@@ -408,12 +411,11 @@ class Lead(FormatAddress, models.Model):
# Business Methods
# ----------------------------------------
@api.multi
def stage_find(self, team_id=False, domain=None, order='sequence'):
def _stage_find(self, team_id=False, domain=None, order='sequence'):
""" Determine the stage of the current lead with its teams, the given domain and the given team_id
:param team_id
:param domain : base search domain for stage
:returns crm.stage record identifier
:returns crm.stage recordset
"""
# collect all team_ids by adding given one, and the ones related to the current leads
team_ids = set()
@@ -431,10 +433,7 @@ class Lead(FormatAddress, models.Model):
if domain:
search_domain += list(domain)
# perform search, return the first found
stage = self.env['crm.stage'].search(search_domain, order=order, limit=1)
if stage:
return stage.id
return False
return self.env['crm.stage'].search(search_domain, order=order, limit=1)
@api.multi
def _merge_get_result_type(self):
@@ -448,9 +447,8 @@ class Lead(FormatAddress, models.Model):
:param list opps: list of browse records containing the leads/opps to process
:return string type: the type of the final element
"""
for record in self:
if record.type == 'opportunity':
return 'opportunity'
if any(record.type == 'opportunity' for record in self):
return 'opportunity'
return 'lead'
@api.multi
@@ -616,20 +614,16 @@ class Lead(FormatAddress, models.Model):
# Sorting the leads/opps according to the confidence level of its stage, which relates to the probability of winning it
# The confidence level increases with the stage sequence, except when the stage probability is 0.0 (Lost cases)
# An Opportunity always has higher confidence level than a lead, unless its stage probability is 0.0
sequenced_opps = []
for opportunity in self:
def opps_key(opportunity):
sequence = -1
if opportunity.stage_id and opportunity.stage_id.on_change:
if opportunity.stage_id.on_change:
sequence = opportunity.stage_id.sequence
sequenced_opps.append(((int(sequence != -1 and opportunity.type == 'opportunity'), sequence, -opportunity.id), opportunity))
sequenced_opps.sort(reverse=True)
sorted_opportunities = [item[1] for item in sequenced_opps] # get sorted list of crm.lead record
return (sequence != -1 and opportunity.type == 'opportunity'), sequence, -opportunity.id
opportunities = self.sorted(key=opps_key, reverse=True)
# get SORTED recordset of head and tail, and complete list
opportunities = self.browse([opportunity.id for opportunity in sorted_opportunities])
opportunities_head = sorted_opportunities[0]
opportunities_tail = self.browse([opportunity.id for opportunity in sorted_opportunities[1:]])
opportunities_head = opportunities[0]
opportunities_tail = opportunities[1:]
# merge all the sorted opportunity. This means the value of
# the first (head opp) will be a priority.
@@ -704,10 +698,10 @@ class Lead(FormatAddress, models.Model):
'date_conversion': fields.Datetime.now(),
}
if not self.stage_id:
stage_id = self.stage_find(team_id=team_id)
value['stage_id'] = stage_id
if stage_id:
value['probability'] = self.env['crm.stage'].browse(stage_id).probability
stage = self._stage_find(team_id=team_id)
value['stage_id'] = stage.id
if stage:
value['probability'] = stage.probability
return value
@api.multi
@@ -800,9 +794,9 @@ class Lead(FormatAddress, models.Model):
if action == 'create':
partner = lead._create_lead_partner()
partner_id = partner.id
partner.write({'team_id': lead.team_id.id if lead.team_id else False})
partner.team_id = lead.team_id
if partner_id:
lead.write({'partner_id': partner_id})
lead.partner_id = partner_id
partner_ids[lead.id] = partner_id
return partner_ids
@@ -847,8 +841,8 @@ class Lead(FormatAddress, models.Model):
'res_id': self.id,
'view_id': False,
'views': [
(form_view.id if form_view else False, 'form'),
(tree_view.id if tree_view else False, 'tree'),
(form_view.id, 'form'),
(tree_view.id, 'tree'),
(False, 'kanban'),
(False, 'calendar'),
(False, 'graph')
@@ -872,8 +866,8 @@ class Lead(FormatAddress, models.Model):
'res_id': self.id,
'view_id': False,
'views': [
(form_view.id if form_view else False, 'form'),
(tree_view.id if form_view else False, 'tree'),
(form_view.id, 'form'),
(tree_view.id, 'tree'),
(False, 'calendar'),
(False, 'graph')
],
@@ -882,11 +876,6 @@ class Lead(FormatAddress, models.Model):
@api.model
def get_empty_list_help(self, help):
additionnal_context = {
'empty_list_help_model': 'crm.team',
'empty_list_help_id': self._context.get('default_team_id', False),
'empty_list_help_document_name': _("opportunities")
}
if help:
alias_record = self.env.ref("crm.mail_alias_lead_info", raise_if_not_found=False)
if alias_record and alias_record.alias_domain and alias_record.alias_name:
@@ -896,7 +885,11 @@ class Lead(FormatAddress, models.Model):
create new opportunity. Update your business card, phone book, social media,...
Send an email right now and see it here.""") % (email_link,)
return '<p class="oe_view_nocontent_create">%s</p>%s<p>%s</p>' % (_('Click to add a new opportunity'), help, dynamic_help)
return super(Lead, self.with_context(**additionnal_context)).get_empty_list_help(help)
return super(Lead, self.with_context(
empty_list_help_model='crm.team',
empty_list_help_id=self._context.get('default_team_id', False),
empty_list_help_document_name=_("opportunities"),
)).get_empty_list_help(help)
@api.multi
def log_meeting(self, meeting_subject, meeting_date, duration):
@@ -943,36 +936,36 @@ class Lead(FormatAddress, models.Model):
'nb_opportunities': 0,
}
opportunities = self.search_read([('type', '=', 'opportunity'), ('user_id', '=', self._uid)], ['date_deadline', 'next_activity_id', 'date_action', 'date_closed', 'planned_revenue'])
opportunities = self.search([('type', '=', 'opportunity'), ('user_id', '=', self._uid)])
for opp in opportunities:
# Expected closing
if opp['date_deadline']:
date_deadline = fields.Date.from_string(opp['date_deadline'])
if opp.date_deadline:
date_deadline = fields.Date.from_string(opp.date_deadline)
if date_deadline == date.today():
result['closing']['today'] += 1
if date_deadline >= date.today() and date_deadline <= date.today() + timedelta(days=7):
if date.today() <= date_deadline <= date.today() + timedelta(days=7):
result['closing']['next_7_days'] += 1
if date_deadline < date.today():
result['closing']['overdue'] += 1
# Next activities
if opp['next_activity_id'] and opp['date_action']:
date_action = fields.Date.from_string(opp['date_action'])
if opp.next_activity_id and opp.date_action:
date_action = fields.Date.from_string(opp.date_action)
if date_action == date.today():
result['activity']['today'] += 1
if date_action >= date.today() and date_action <= date.today() + timedelta(days=7):
if date.today() <= date_action <= date.today() + timedelta(days=7):
result['activity']['next_7_days'] += 1
if date_action < date.today():
result['activity']['overdue'] += 1
# Won in Opportunities
if opp['date_closed']:
date_closed = fields.Date.from_string(opp['date_closed'])
if date_closed <= date.today() and date_closed >= date.today().replace(day=1):
if opp['planned_revenue']:
result['won']['this_month'] += opp['planned_revenue']
elif date_closed < date.today().replace(day=1) and date_closed >= date.today().replace(day=1) - relativedelta(months=+1):
if opp['planned_revenue']:
result['won']['last_month'] += opp['planned_revenue']
if opp.date_closed:
date_closed = fields.Date.from_string(opp.date_closed)
if date.today().replace(day=1) <= date_closed <= date.today():
if opp.planned_revenue:
result['won']['this_month'] += opp.planned_revenue
elif date.today() + relativedelta(months=-1, day=1) <= date_closed < date.today().replace(day=1):
if opp.planned_revenue:
result['won']['last_month'] += opp.planned_revenue
result['nb_opportunities'] = len(opportunities)
@@ -995,9 +988,9 @@ class Lead(FormatAddress, models.Model):
for activity in activites_done:
if activity['date']:
date_act = fields.Date.from_string(activity['date'])
if date_act <= date.today() and date_act >= date.today().replace(day=1):
if date.today().replace(day=1) <= date_act <= date.today():
result['done']['this_month'] += 1
elif date_act < date.today().replace(day=1) and date_act >= date.today().replace(day=1) - relativedelta(months=+1):
elif date.today() + relativedelta(months=-1, day=1) <= date_act < date.today().replace(day=1):
result['done']['last_month'] += 1
# Meetings
@@ -1014,7 +1007,7 @@ class Lead(FormatAddress, models.Model):
start = datetime.strptime(meeting['start'], tools.DEFAULT_SERVER_DATETIME_FORMAT).date()
if start == date.today():
result['meeting']['today'] += 1
if start >= date.today() and start <= date.today() + timedelta(days=7):
if date.today() <= start <= date.today() + timedelta(days=7):
result['meeting']['next_7_days'] += 1
result['done']['target'] = self.env.user.target_sales_done
@@ -1093,11 +1086,10 @@ class Lead(FormatAddress, models.Model):
def message_get_reply_to(self, res_ids, default=None):
leads = self.sudo().browse(res_ids)
aliases = self.env['crm.team'].message_get_reply_to(leads.mapped('team_id').ids, default=default)
return dict((lead.id, aliases.get(lead.team_id and lead.team_id.id or 0, False)) for lead in leads)
return {lead.id: aliases.get(lead.team_id.id or 0, False) for lead in leads}
@api.multi
def get_formview_id(self):
self.ensure_one()
if self.type == 'opportunity':
view_id = self.env.ref('crm.crm_case_form_view_oppor').id
else:
+5 -10
View File
@@ -3,7 +3,6 @@
from odoo import api, fields, models
from odoo.tools.safe_eval import safe_eval as eval
from odoo.tools.translate import _
class Team(models.Model):
@@ -21,7 +20,7 @@ class Team(models.Model):
return 'crm.lead'
def get_alias_values(self):
has_group_use_lead = self.env['res.users'].has_group('crm.group_use_lead')
has_group_use_lead = self.env.user.has_group('crm.group_use_lead')
values = super(Team, self).get_alias_values()
values['alias_defaults'] = defaults = eval(self.alias_defaults or "{}")
defaults['type'] = 'lead' if has_group_use_lead and self.use_leads else 'opportunity'
@@ -64,22 +63,18 @@ class Team(models.Model):
action_context = eval(action['context'], {'uid': self.env.uid})
if user_team_id:
action_context.update({
'default_team_id': user_team_id,
})
action_context['default_team_id'] = user_team_id
tree_view_id = self.env.ref('crm.crm_case_tree_view_oppor').id
form_view_id = self.env.ref('crm.crm_case_form_view_oppor').id
kanb_view_id = self.env.ref('crm.crm_case_kanban_view_leads').id
action.update({
'views': [
action['views'] = [
[kanb_view_id, 'kanban'],
[tree_view_id, 'tree'],
[form_view_id, 'form'],
[False, 'graph'],
[False, 'calendar'],
[False, 'pivot']
],
'context': action_context,
})
]
action['context'] = action_context
return action
+4 -3
View File
@@ -54,8 +54,9 @@ class CRMSettings(models.TransientModel):
def set_default_alias_prefix(self):
for record in self:
alias = self._find_default_lead_alias_id()
if not alias:
alias = self.env['mail.alias'].with_context(alias_model_name='crm.lead', alias_parent_model_name='crm.team').create({'alias_name': record.alias_prefix})
else:
if alias:
alias.write({'alias_name': record.alias_prefix})
else:
self.env['mail.alias'].with_context(alias_model_name='crm.lead', alias_parent_model_name='crm.team').create({'alias_name': record.alias_prefix})
return True
+1 -1
View File
@@ -37,7 +37,7 @@ class Partner(models.Model):
def schedule_meeting(self):
partner_ids = self.ids
partner_ids.append(self.env.user.partner_id.id)
action = self.env['ir.actions.act_window'].for_xml_id('calendar', 'action_calendar_event')
action = self.env.ref('calendar.action_calendar_event').read()[0]
action['context'] = {
'search_default_partner_ids': self._context['partner_name'],
'default_partner_ids': partner_ids,
-1
View File
@@ -31,7 +31,6 @@ class ActivityReport(models.Model):
probability = fields.Float('Probability', group_operator='avg', readonly=True)
def init(self):
tools.drop_view_if_exists(self._cr, 'crm_activity_report')
self._cr.execute("""
CREATE OR REPLACE VIEW crm_activity_report AS (
select
+1 -3
View File
@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.addons.crm.models import crm_stage
from odoo import fields, models, tools
from ..models import crm_stage
class OpportunityReport(models.Model):
""" CRM Opportunity Analysis """
@@ -30,7 +30,6 @@ class OpportunityReport(models.Model):
nbr_activities = fields.Integer('# of Activities', readonly=True)
city = fields.Char('City')
country_id = fields.Many2one('res.country', string='Country', readonly=True)
company_id = fields.Many2one('res.company', string='Company', readonly=True)
probability = fields.Float(string='Probability', digits=(16, 2), readonly=True, group_operator="avg")
total_revenue = fields.Float(string='Total Revenue', digits=(16, 2), readonly=True)
expected_revenue = fields.Float(string='Probable Turnover', digits=(16, 2), readonly=True)
@@ -50,7 +49,6 @@ class OpportunityReport(models.Model):
medium_id = fields.Many2one('utm.medium', string='Medium', readonly=True)
def init(self):
tools.drop_view_if_exists(self._cr, 'crm_opportunity_report')
self._cr.execute("""
CREATE OR REPLACE VIEW crm_opportunity_report AS (
SELECT
+1 -1
View File
@@ -40,7 +40,7 @@ class TestCRMLead(TestCrmCases):
# Now I change the stage of opportunity to won.
lead.action_set_won()
# I check stage of opp should won, after change stage.
stage_id = lead.stage_find(domain=[('probability', '=', 100.0)])
stage_id = lead._stage_find(domain=[('probability', '=', 100.0)])
self.assertEqual(stage_id, lead.stage_id, "Stage of opportunity is incorrect!")
def test_crm_lead_message(self):
+22 -32
View File
@@ -285,15 +285,13 @@ class MergePartnerAutomatic(models.TransientModel):
except ValidationError:
_logger.info('Skip recursive partner hierarchies for parent_id %s of partner: %s', parent_id, dst_partner.id)
@api.model
@mute_logger('openerp.osv.expression', 'openerp.models')
def _merge(self, partner_ids, dst_partner=None):
""" private implementation of merge partner
:param partner_ids : ids of partner to merge
:param dst_partner : record of destination res.partner
"""
Partner = self.env['res.partner']
partner_ids = Partner.browse(partner_ids).exists().ids
partner_ids = Partner.browse(partner_ids).exists()
if len(partner_ids) < 2:
return
@@ -301,10 +299,10 @@ class MergePartnerAutomatic(models.TransientModel):
raise UserError(_("For safety reasons, you cannot merge more than 3 contacts together. You can re-open the wizard several times if needed."))
# check if the list of partners to merge contains child/parent relation
child_ids = set()
child_ids = self.env['res.partner']
for partner_id in partner_ids:
child_ids = child_ids.union(set(Partner.search([('id', 'child_of', [partner_id])]).ids) - set([partner_id]))
if set(partner_ids).intersection(child_ids):
child_ids |= Partner.search([('id', 'child_of', [partner_id.id])]) - partner_id
if partner_ids & child_ids:
raise UserError(_("You cannot merge a contact with one of his parent."))
# check only admin can merge partners with different emails
@@ -312,23 +310,22 @@ class MergePartnerAutomatic(models.TransientModel):
raise UserError(_("All contacts must have the same email. Only the Administrator can merge contacts with different emails."))
# remove dst_partner from partners to merge
if dst_partner and dst_partner.id in partner_ids:
src_partners = Partner.browse([pid for pid in partner_ids if pid != dst_partner.id])
if dst_partner and dst_partner in partner_ids:
src_partners = partner_ids - dst_partner
else:
ordered_partners = self._get_ordered_partner(partner_ids)
ordered_partners = self._get_ordered_partner(partner_ids.ids)
dst_partner = ordered_partners[-1]
src_partners = ordered_partners[:-1]
_logger.info("dst_partner: %s", dst_partner.id)
# FIXME : is it still required to make and exception for account.move.line since accounting v9.0 ?
# FIXME: is it still required to make and exception for account.move.line since accounting v9.0 ?
if SUPERUSER_ID != self.env.uid and 'account.move.line' in self.env and self.env['account.move.line'].sudo().search([('partner_id', 'in', [partner.id for partner in src_partners])]):
raise UserError(_("Only the destination contact may be linked to existing Journal Items. Please ask the Administrator if you need to merge several contacts linked to existing Journal Items."))
# call sub methods to do the merge
call_it = lambda function: function(src_partners, dst_partner)
call_it(self._update_foreign_keys)
call_it(self._update_reference_fields)
call_it(self._update_values)
self._update_foreign_keys(src_partners, dst_partner)
self._update_reference_fields(src_partners, dst_partner)
self._update_values(src_partners, dst_partner)
_logger.info('(uid = %s) merged the partners %r with %s', self._uid, src_partners.ids, dst_partner.id)
dst_partner.message_post(body='%s %s' % (_("Merged with the following partners:"), ", ".join('%s <%s> (ID %s)' % (p.name, p.email or 'n/a', p.id) for p in src_partners)))
@@ -380,9 +377,7 @@ class MergePartnerAutomatic(models.TransientModel):
])
if maximum_group:
text.extend([
"LIMIT %s" % maximum_group,
])
text.append("LIMIT %s" % maximum_group,)
return ' '.join(text)
@@ -410,25 +405,24 @@ class MergePartnerAutomatic(models.TransientModel):
:param aggr_ids : stringified list of partner ids separated with a comma (sql array_agg)
:param models : dict mapping a model name with its foreign key with res_partner table
"""
for model, field in models.iteritems():
proxy = self.env[model]
if proxy.search_count([(field, 'in', aggr_ids)]):
return True
return False
return any(
self.env[model].search_count([(field, 'in', aggr_ids)], limit=1)
for model, field in models.iteritems()
)
@api.model
def _get_ordered_partner(self, partner_ids):
""" Helper : returns a `res.partner` recordset ordered by create_date/active fields
:param partner_ids : list of partner ids to sort
"""
partners = self.env['res.partner'].browse(partner_ids)
ordered_partners = sorted(sorted(partners, key=operator.attrgetter('create_date'), reverse=True), key=operator.attrgetter('active'), reverse=True)
return self.env['res.partner'].browse([partner.id for partner in ordered_partners])
return self.env['res.partner'].browse(partner_ids).sorted(
key=lambda p: (p.active, p.create_date),
reverse=True,
)
@api.multi
def _compute_models(self):
""" Compute the different models needed by the system if you want to exclude some partners. """
self.ensure_one()
model_mapping = {}
if self.exclude_contact:
model_mapping['res.users'] = 'partner_id'
@@ -448,7 +442,6 @@ class MergePartnerAutomatic(models.TransientModel):
@api.multi
def action_skip(self):
""" Skip this wizard line. Don't compute any thing, and simply redirect to the new step."""
self.ensure_one()
if self.current_line_id:
self.current_line_id.unlink()
return self._action_next_screen()
@@ -459,8 +452,7 @@ class MergePartnerAutomatic(models.TransientModel):
next wizard line. Each line is a subset of partner that can be merged together.
If no line left, the end screen will be displayed (but an action is still returned).
"""
self.ensure_one()
self.refresh() # TODO JEM : it is deprecated, so still usefull ?
self.invalidate_cache() # FIXME: is this still necessary?
values = {}
if self.line_ids:
# in this case, we try to find the next record.
@@ -541,7 +533,7 @@ class MergePartnerAutomatic(models.TransientModel):
"""
self.ensure_one()
self.action_start_manual_process() # here we don't redirect to the next screen, since it is automatic process
self.refresh() # TODO JEM : deprecated, what to do ?
self.invalidate_cache() # FIXME: is this still necessary?
for line in self.line_ids:
partner_ids = literal_eval(line.aggr_ids)
@@ -643,8 +635,6 @@ class MergePartnerAutomatic(models.TransientModel):
""" Merge Contact button. Merge the selected partners, and redirect to
the end screen (since there is no other wizard line to process.
"""
self.ensure_one()
if not self.partner_ids:
self.write({'state': 'finished'})
return {
+2 -5
View File
@@ -11,8 +11,5 @@ class CrmLeadLost(models.TransientModel):
@api.multi
def action_lost_reason_apply(self):
res = False
for wizard in self:
self.lead_id.lost_reason = self.lost_reason_id
res = self.lead_id.action_set_lost()
return res
self.lead_id.lost_reason = self.lost_reason_id
return self.lead_id.action_set_lost()
+35 -44
View File
@@ -20,29 +20,28 @@ class Lead2OpportunityPartner(models.TransientModel):
"""
result = super(Lead2OpportunityPartner, self).default_get(fields)
if self._context.get('active_id'):
tomerge = [int(self._context['active_id'])]
tomerge = {int(self._context['active_id'])}
partner_id = result.get('partner_id')
lead = self.env['crm.lead'].browse(self._context['active_id'])
email = lead.partner_id.email if lead.partner_id else lead.email_from
tomerge.extend(self._get_duplicated_leads(partner_id, email, include_lost=True).ids)
tomerge = list(set(tomerge))
tomerge.update(self._get_duplicated_leads(partner_id, email, include_lost=True).ids)
if 'action' in fields and not result.get('action'):
result.update({'action': 'exist' if partner_id else 'create'})
result['action'] = 'exist' if partner_id else 'create'
if 'partner_id' in fields:
result.update({'partner_id': partner_id})
result['partner_id'] = partner_id
if 'name' in fields:
result.update({'name': 'merge' if len(tomerge) >= 2 else 'convert'})
result['name'] = 'merge' if len(tomerge) >= 2 else 'convert'
if 'opportunity_ids' in fields and len(tomerge) >= 2:
result.update({'opportunity_ids': tomerge})
result['opportunity_ids'] = list(tomerge)
if lead.user_id:
result.update({'user_id': lead.user_id.id})
result['user_id'] = lead.user_id.id
if lead.team_id:
result.update({'team_id': lead.team_id.id})
result['team_id'] = lead.team_id.id
if not partner_id and not lead.contact_name:
result.update({'action': 'nothing'})
result['action'] = 'nothing'
return result
name = fields.Selection([
@@ -93,22 +92,20 @@ class Lead2OpportunityPartner(models.TransientModel):
self.ensure_one()
res = False
lead_ids = vals.get('lead_ids', [])
team_id = vals.get('team_id', False)
partner_id = vals.get('partner_id')
for lead in self.env['crm.lead'].browse(lead_ids):
partner_id = self._create_partner(lead.id, self.action, partner_id or lead.partner_id.id)
leads = self.env['crm.lead'].browse(vals.get('lead_ids'))
for lead in leads:
partner_id = self._create_partner(lead.id, self.action, vals.get('partner_id') or lead.partner_id.id)
res = lead.convert_opportunity(partner_id, [], False)
user_ids = vals.get('user_ids', False)
user_ids = vals.get('user_ids')
leads_to_allocate = leads
if self._context.get('no_force_assignation'):
leads_to_allocate = self.env['crm.lead'].browse(lead_ids).filtered(lambda lead: not lead.user_id).ids
else:
leads_to_allocate = lead_ids
leads_to_allocate = leads_to_allocate.filtered(lambda lead: not lead.user_id)
if user_ids:
self.env['crm.lead'].browse(leads_to_allocate).allocate_salesman(user_ids, team_id=team_id)
leads_to_allocate.allocate_salesman(user_ids, team_id=(vals.get('team_id')))
return res
@api.multi
@@ -117,8 +114,6 @@ class Lead2OpportunityPartner(models.TransientModel):
the freshly created opportunity view.
"""
self.ensure_one()
Lead = self.env['crm.lead']
opp_ids = self.opportunity_ids.ids
values = {
'team_id': self.team_id.id,
}
@@ -127,26 +122,23 @@ class Lead2OpportunityPartner(models.TransientModel):
values['partner_id'] = self.partner_id.id
if self.name == 'merge':
lead = self.env['crm.lead'].browse(opp_ids).merge_opportunity()
lead_ids = [lead.id]
lead_data = lead.read(['type', 'user_id'])[0]
if lead_data['type'] == "lead":
values.update({'lead_ids': lead_ids, 'user_ids': [self.user_id.id]})
self.with_context(active_ids=lead_ids)._convert_opportunity(values)
elif not self._context.get('no_force_assignation') or not lead_data['user_id']:
values.update({'user_id': self.user_id.id})
lead.write(values)
leads = self.opportunity_ids.merge_opportunity()
if leads.type == "lead":
values.update({'lead_ids': leads.ids, 'user_ids': [self.user_id.id]})
self.with_context(active_ids=leads.ids)._convert_opportunity(values)
elif not self._context.get('no_force_assignation') or not leads.user_id:
values['user_id'] = self.user_id.id
leads.write(values)
else:
lead_ids = self._context.get('active_ids', [])
values.update({'lead_ids': lead_ids, 'user_ids': [self.user_id.id]})
leads = self.env['crm.lead'].browse(self._context.get('active_ids', []))
values.update({'lead_ids': leads.ids, 'user_ids': [self.user_id.id]})
self._convert_opportunity(values)
for lead in self.env['crm.lead'].browse(lead_ids):
for lead in leads:
if lead.partner_id and lead.partner_id.user_id != lead.user_id:
self.env['res.partner'].browse(lead.partner_id.id).write({'user_id': lead.user_id.id})
return self.env['crm.lead'].browse(list(lead_ids)[0]).redirect_opportunity_view()
return leads[0].redirect_opportunity_view()
@api.model
def _create_partner(self, lead_id, action, partner_id):
""" Create partner based on action.
:return dict: dictionary organized as followed: {lead_id: partner_assigned_id}
@@ -229,10 +221,9 @@ class Lead2OpportunityMassConvert(models.TransientModel):
@api.multi
def mass_convert(self):
self.ensure_one()
active_ids = []
if self.name == 'convert' and self.deduplicate:
merged_lead_ids = []
remaining_lead_ids = []
merged_lead_ids = set()
remaining_lead_ids = set()
lead_selected = self._context.get('active_ids', [])
for lead_id in lead_selected:
if lead_id not in merged_lead_ids:
@@ -240,11 +231,11 @@ class Lead2OpportunityMassConvert(models.TransientModel):
duplicated_leads = self._get_duplicated_leads(lead.partner_id.id, lead.partner_id.email if lead.partner_id else lead.email_from)
if len(duplicated_leads) > 1:
lead = duplicated_leads.merge_opportunity()
merged_lead_ids.extend(duplicated_leads.ids)
remaining_lead_ids.append(lead.id)
active_ids = set(self._context.get('active_ids', []))
active_ids = active_ids.difference(merged_lead_ids)
active_ids = active_ids.union(remaining_lead_ids)
merged_lead_ids.update(duplicated_leads.ids)
remaining_lead_ids.add(lead.id)
active_ids = set(self._context.get('active_ids', {}))
active_ids = (active_ids - merged_lead_ids) | remaining_lead_ids
self = self.with_context(active_ids=list(active_ids)) # only update active_ids when there are set
no_force_assignation = self._context.get('no_force_assignation', not self.force_assignation)
return self.with_context(no_force_assignation=no_force_assignation).action_apply()
+2 -2
View File
@@ -22,13 +22,13 @@ class MergeOpportunity(models.TransientModel):
""" Use active_ids from the context to fetch the leads/opps to merge.
In order to get merged, these leads/opps can't be in 'Dead' or 'Closed'
"""
record_ids = self._context.get('active_ids', False)
record_ids = self._context.get('active_ids')
result = super(MergeOpportunity, self).default_get(fields)
if record_ids:
if 'opportunity_ids' in fields:
opp_ids = self.env['crm.lead'].browse(record_ids).filtered(lambda opp: opp.probability < 100).ids
result.update({'opportunity_ids': opp_ids})
result['opportunity_ids'] = opp_ids
return result
+22 -20
View File
@@ -2,7 +2,6 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo.tools.translate import _
class PartnerBinding(models.TransientModel):
@@ -24,7 +23,7 @@ class PartnerBinding(models.TransientModel):
partner_id = self._find_matching_partner()
if 'action' in fields and not res.get('action'):
res['action'] = partner_id and 'exist' or 'create'
res['action'] = 'exist' if partner_id else 'create'
if 'partner_id' in fields:
res['partner_id'] = partner_id
return res
@@ -43,23 +42,26 @@ class PartnerBinding(models.TransientModel):
:return int partner_id if any, False otherwise
"""
# active model has to be a lead
lead = None
if self._context.get('active_model') == 'crm.lead' and self._context.get('active_id'):
lead = self.env['crm.lead'].browse(self._context.get('active_id'))
if self._context.get('active_model') != 'crm.lead' or not self._context.get('active_id'):
return False
lead = self.env['crm.lead'].browse(self._context.get('active_id'))
# find the best matching partner for the active model
partner_id = False
if lead:
Partner = self.env['res.partner']
if lead.partner_id: # a partner is set already
partner_id = lead.partner_id.id
elif lead.email_from: # search through the existing partners based on the lead's email
partner = Partner.search([('email', '=', lead.email_from)], limit=1)
partner_id = partner.id if partner else False
elif lead.partner_name: # search through the existing partners based on the lead's partner or contact name
partner = Partner.search([('name', 'ilike', '%' + lead.partner_name + '%')], limit=1)
partner_id = partner.id if partner else False
elif lead.contact_name:
partner = Partner.search([('name', 'ilike', '%' + lead.contact_name+'%')], limit=1)
partner_id = partner.id if partner else False
return partner_id
Partner = self.env['res.partner']
if lead.partner_id: # a partner is set already
return lead.partner_id.id
if lead.email_from: # search through the existing partners based on the lead's email
partner = Partner.search([('email', '=', lead.email_from)], limit=1)
return partner.id
if lead.partner_name: # search through the existing partners based on the lead's partner or contact name
partner = Partner.search([('name', 'ilike', '%' + lead.partner_name + '%')], limit=1)
return partner.id
if lead.contact_name:
partner = Partner.search([('name', 'ilike', '%' + lead.contact_name+'%')], limit=1)
return partner.id
return False