From 3e287843f030e652d3218307fa1bc5c3658d3ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 8 Dec 2020 08:51:21 +0000 Subject: [PATCH 1/3] [MOV] sales_team: reorganize tests PURPOSE Prepare sales team membership and lead assignment improvements by reorganizing and cleaning some code bits. SPECIFICATIONS Reorganize sales team tests to ease addition and modifications of tests when adding multi-membership and lead assignment features. LINKS Task ID-2428882 Prepares Task ID-2086889 COM PR odoo/odoo#64196 ENT PT odoo/enterprise#15610 --- addons/sales_team/tests/__init__.py | 2 +- ...ales_team_membership.py => test_sales_team.py} | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 4 deletions(-) rename addons/sales_team/tests/{test_sales_team_membership.py => test_sales_team.py} (82%) diff --git a/addons/sales_team/tests/__init__.py b/addons/sales_team/tests/__init__.py index 828034121fe..d3d022e9d99 100644 --- a/addons/sales_team/tests/__init__.py +++ b/addons/sales_team/tests/__init__.py @@ -2,4 +2,4 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import common -from . import test_sales_team_membership +from . import test_sales_team diff --git a/addons/sales_team/tests/test_sales_team_membership.py b/addons/sales_team/tests/test_sales_team.py similarity index 82% rename from addons/sales_team/tests/test_sales_team_membership.py rename to addons/sales_team/tests/test_sales_team.py index 8e08178915f..c8b7f965f8e 100644 --- a/addons/sales_team/tests/test_sales_team_membership.py +++ b/addons/sales_team/tests/test_sales_team.py @@ -1,17 +1,26 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo.addons.sales_team.tests.common import TestSalesMC +from odoo.addons.sales_team.tests.common import TestSalesCommon -class TestDefaultTeam(TestSalesMC): +class TestDefaultTeam(TestSalesCommon): """Tests to check if correct default team is found.""" @classmethod def setUpClass(cls): """Set up data for default team tests.""" super(TestDefaultTeam, cls).setUpClass() - + cls.company_2 = cls.env['res.company'].create({ + 'name': 'New Test Company', + 'email': 'company.2@test.example.com', + 'country_id': cls.env.ref('base.fr'), + }) + cls.team_c2 = cls.env['crm.team'].create({ + 'name': 'C2 Team1', + 'sequence': 1, + 'company_id': cls.company_2.id, + }) cls.team_sequence = cls.env['crm.team'].create({ 'name': 'Team LowSequence', 'sequence': 0, From 4819df905e5cb329e79aa9070ce12b1f32a3001b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 7 Jan 2021 10:29:43 +0000 Subject: [PATCH 2/3] [MOV] crm: reorganize convert / merge tool methods PURPOSE Prepare sales team membership and lead assignment improvements by reorganizing and cleaning some code bits. SPECIFICATIONS Separate business methods from tool methods. It eases their discovering and their re-use through various other methods and models. Moreover it helps reading business oriented methods without being disturbed by tools. LINKS Task ID-2428882 Prepares Task ID-2086889 COM PR odoo/odoo#64196 ENT PT odoo/enterprise#15610 --- addons/crm/models/crm_lead.py | 320 ++++++++++++++++++---------------- 1 file changed, 165 insertions(+), 155 deletions(-) diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index a15e42a4eda..a13d2102e4f 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -856,6 +856,43 @@ class Lead(models.Model): }) return True + # ------------------------------------------------------------ + # VIEWS + # ------------------------------------------------------------ + + def redirect_lead_opportunity_view(self): + self.ensure_one() + return { + 'name': _('Lead or Opportunity'), + 'view_mode': 'form', + 'res_model': 'crm.lead', + 'domain': [('type', '=', self.type)], + 'res_id': self.id, + 'view_id': False, + 'type': 'ir.actions.act_window', + 'context': {'default_type': self.type} + } + + @api.model + def get_empty_list_help(self, help): + help_title, sub_title = "", "" + if self._context.get('default_type') == 'lead': + help_title = _('Create a new lead') + else: + help_title = _('Create an opportunity to start playing with your pipeline.') + alias_record = self.env['mail.alias'].search([ + ('alias_name', '!=', False), + ('alias_name', '!=', ''), + ('alias_model_id.model', '=', 'crm.lead'), + ('alias_parent_model_id.model', '=', 'crm.team'), + ('alias_force_thread_id', '=', False) + ], limit=1) + if alias_record and alias_record.alias_domain and alias_record.alias_name: + email = '%s@%s' % (alias_record.alias_name, alias_record.alias_domain) + email_link = "%s" % (email, email) + sub_title = _('Use the top left Create button, or send an email to %s to test the email gateway.') % (email_link) + return '

%s

%s

' % (help_title, sub_title) + # ------------------------------------------------------------ # BUSINESS # ------------------------------------------------------------ @@ -872,7 +909,7 @@ class Lead(models.Model): return self.message_post(body=message) # ------------------------------------------------------------ - # MERGE LEADS / OPPS + # MERGE AND CONVERT LEADS / OPPORTUNITIES # ------------------------------------------------------------ def _merge_get_result_type(self): @@ -1083,16 +1120,6 @@ class Lead(models.Model): return opportunities_head - def _sort_by_confidence_level(self, reverse=False): - """ 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 - An Opportunity always has higher confidence level than a lead - """ - def opps_key(opportunity): - return opportunity.type == 'opportunity', opportunity.stage_id.sequence, -opportunity._origin.id - - return self.sorted(key=opps_key, reverse=reverse) - def _convert_opportunity_data(self, customer, team_id=False): """ Extract the data from a lead to create the opportunity :param customer : res.partner record @@ -1126,118 +1153,6 @@ class Lead(models.Model): return True - def _get_lead_duplicates(self, partner=None, email=None, include_lost=False): - """ Search for leads that seem duplicated based on partner / email. - - :param partner : optional customer when searching duplicated - :param email: email (possibly formatted) to search - :param boolean include_lost: if True, search includes archived opportunities - (still only active leads are considered). If False, search for active - and not won leads and opportunities; - """ - if not email and not partner: - return self.env['crm.lead'] - - domain = [] - for normalized_email in [tools.email_normalize(email) for email in tools.email_split(email)]: - domain.append(('email_normalized', '=', normalized_email)) - if partner: - domain.append(('partner_id', '=', partner.id)) - - if not domain: - return self.env['crm.lead'] - - domain = ['|'] * (len(domain) - 1) + domain - if include_lost: - domain += ['|', ('type', '=', 'opportunity'), ('active', '=', True)] - else: - domain += ['&', ('active', '=', True), '|', ('probability', '=', False), ('probability', '<', 100)] - - return self.with_context(active_test=False).search(domain) - - def _create_customer(self): - """ Create a partner from lead data and link it to the lead. - - :return: newly-created partner browse record - """ - Partner = self.env['res.partner'] - contact_name = self.contact_name - if not contact_name: - contact_name = Partner._parse_partner_name(self.email_from)[0] if self.email_from else False - - if self.partner_name: - partner_company = Partner.create(self._prepare_customer_values(self.partner_name, is_company=True)) - elif self.partner_id: - partner_company = self.partner_id - else: - partner_company = None - - if contact_name: - return Partner.create(self._prepare_customer_values(contact_name, is_company=False, parent_id=partner_company.id if partner_company else False)) - - if partner_company: - return partner_company - return Partner.create(self._prepare_customer_values(self.name, is_company=False)) - - def _prepare_customer_values(self, partner_name, is_company=False, parent_id=False): - """ Extract data from lead to create a partner. - - :param name : furtur name of the partner - :param is_company : True if the partner is a company - :param parent_id : id of the parent partner (False if no parent) - - :return: dictionary of values to give at res_partner.create() - """ - email_split = tools.email_split(self.email_from) - res = { - 'name': partner_name, - 'user_id': self.env.context.get('default_user_id') or self.user_id.id, - 'comment': self.description, - 'team_id': self.team_id.id, - 'parent_id': parent_id, - 'phone': self.phone, - 'mobile': self.mobile, - 'email': email_split[0] if email_split else False, - 'title': self.title.id, - 'function': self.function, - 'street': self.street, - 'street2': self.street2, - 'zip': self.zip, - 'city': self.city, - 'country_id': self.country_id.id, - 'state_id': self.state_id.id, - 'website': self.website, - 'is_company': is_company, - 'type': 'contact' - } - if self.lang_id: - res['lang'] = self.lang_id.code - return res - - def _find_matching_partner(self, email_only=False): - """ Try to find a matching partner with available information on the - lead, using notably customer's name, email, ... - - :param email_only: Only find a matching based on the email. To use - for automatic process where ilike based on name can be too dangerous - :return: partner browse record - """ - self.ensure_one() - partner = self.partner_id - - if not partner and self.email_from: - partner = self.env['res.partner'].search([('email', '=', self.email_from)], limit=1) - - if not partner and not email_only: - # search through the existing partners based on the lead's partner or contact name - # to be aligned with _create_customer, search on lead's name as last possibility - for customer_potential_name in [self[field_name] for field_name in ['partner_name', 'contact_name', 'name'] if self[field_name]]: - partner = self.env['res.partner'].search([('name', 'ilike', '%' + customer_potential_name + '%')], limit=1) - if partner: - break - - return partner - def handle_partner_assignment(self, force_partner_id=False, create_missing=True): """ Update customer (partner_id) of leads. Purpose is to set the same partner on most leads; either through a newly created partner either @@ -1279,41 +1194,136 @@ class Lead(models.Model): self.env['crm.lead'].browse(subset_ids).write(update_vals) # ------------------------------------------------------------ - # TOOLS - # ------------------------------------------------------------ + # MERGE / CONVERT TOOLS + # --------------------------------------------------------- - def redirect_lead_opportunity_view(self): - self.ensure_one() - return { - 'name': _('Lead or Opportunity'), - 'view_mode': 'form', - 'res_model': 'crm.lead', - 'domain': [('type', '=', self.type)], - 'res_id': self.id, - 'view_id': False, - 'type': 'ir.actions.act_window', - 'context': {'default_type': self.type} - } + # CLASSIFICATION TOOLS + # -------------------------------------------------- - @api.model - def get_empty_list_help(self, help): - help_title, sub_title = "", "" - if self._context.get('default_type') == 'lead': - help_title = _('Create a new lead') + def _get_lead_duplicates(self, partner=None, email=None, include_lost=False): + """ Search for leads that seem duplicated based on partner / email. + + :param partner : optional customer when searching duplicated + :param email: email (possibly formatted) to search + :param boolean include_lost: if True, search includes archived opportunities + (still only active leads are considered). If False, search for active + and not won leads and opportunities; + """ + if not email and not partner: + return self.env['crm.lead'] + + domain = [] + for normalized_email in [tools.email_normalize(email) for email in tools.email_split(email)]: + domain.append(('email_normalized', '=', normalized_email)) + if partner: + domain.append(('partner_id', '=', partner.id)) + + if not domain: + return self.env['crm.lead'] + + domain = ['|'] * (len(domain) - 1) + domain + if include_lost: + domain += ['|', ('type', '=', 'opportunity'), ('active', '=', True)] else: - help_title = _('Create an opportunity to start playing with your pipeline.') - alias_record = self.env['mail.alias'].search([ - ('alias_name', '!=', False), - ('alias_name', '!=', ''), - ('alias_model_id.model', '=', 'crm.lead'), - ('alias_parent_model_id.model', '=', 'crm.team'), - ('alias_force_thread_id', '=', False) - ], limit=1) - if alias_record and alias_record.alias_domain and alias_record.alias_name: - email = '%s@%s' % (alias_record.alias_name, alias_record.alias_domain) - email_link = "%s" % (email, email) - sub_title = _('Use the top left Create button, or send an email to %s to test the email gateway.') % (email_link) - return '

%s

%s

' % (help_title, sub_title) + domain += ['&', ('active', '=', True), '|', ('probability', '=', False), ('probability', '<', 100)] + + return self.with_context(active_test=False).search(domain) + + def _sort_by_confidence_level(self, reverse=False): + """ 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 + An Opportunity always has higher confidence level than a lead + """ + def opps_key(opportunity): + return opportunity.type == 'opportunity', opportunity.stage_id.sequence, -opportunity._origin.id + + return self.sorted(key=opps_key, reverse=reverse) + + # CUSTOMER TOOLS + # -------------------------------------------------- + + def _find_matching_partner(self, email_only=False): + """ Try to find a matching partner with available information on the + lead, using notably customer's name, email, ... + + :param email_only: Only find a matching based on the email. To use + for automatic process where ilike based on name can be too dangerous + :return: partner browse record + """ + self.ensure_one() + partner = self.partner_id + + if not partner and self.email_from: + partner = self.env['res.partner'].search([('email', '=', self.email_from)], limit=1) + + if not partner and not email_only: + # search through the existing partners based on the lead's partner or contact name + # to be aligned with _create_customer, search on lead's name as last possibility + for customer_potential_name in [self[field_name] for field_name in ['partner_name', 'contact_name', 'name'] if self[field_name]]: + partner = self.env['res.partner'].search([('name', 'ilike', '%' + customer_potential_name + '%')], limit=1) + if partner: + break + + return partner + + def _create_customer(self): + """ Create a partner from lead data and link it to the lead. + + :return: newly-created partner browse record + """ + Partner = self.env['res.partner'] + contact_name = self.contact_name + if not contact_name: + contact_name = Partner._parse_partner_name(self.email_from)[0] if self.email_from else False + + if self.partner_name: + partner_company = Partner.create(self._prepare_customer_values(self.partner_name, is_company=True)) + elif self.partner_id: + partner_company = self.partner_id + else: + partner_company = None + + if contact_name: + return Partner.create(self._prepare_customer_values(contact_name, is_company=False, parent_id=partner_company.id if partner_company else False)) + + if partner_company: + return partner_company + return Partner.create(self._prepare_customer_values(self.name, is_company=False)) + + def _prepare_customer_values(self, partner_name, is_company=False, parent_id=False): + """ Extract data from lead to create a partner. + + :param name : furtur name of the partner + :param is_company : True if the partner is a company + :param parent_id : id of the parent partner (False if no parent) + + :return: dictionary of values to give at res_partner.create() + """ + email_parts = tools.email_split(self.email_from) + res = { + 'name': partner_name, + 'user_id': self.env.context.get('default_user_id') or self.user_id.id, + 'comment': self.description, + 'team_id': self.team_id.id, + 'parent_id': parent_id, + 'phone': self.phone, + 'mobile': self.mobile, + 'email': email_parts[0] if email_parts else False, + 'title': self.title.id, + 'function': self.function, + 'street': self.street, + 'street2': self.street2, + 'zip': self.zip, + 'city': self.city, + 'country_id': self.country_id.id, + 'state_id': self.state_id.id, + 'website': self.website, + 'is_company': is_company, + 'type': 'contact' + } + if self.lang_id: + res['lang'] = self.lang_id.code + return res # ------------------------------------------------------------ # MAILING From fff19019de34f30286086d0bcb7f2b80664f3ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 6 Jan 2021 11:06:12 +0000 Subject: [PATCH 3/3] [IMP] crm: make some internal methods private PURPOSE Prepare sales team membership and lead assignment improvements by reorganizing and cleaning some code bits. SPECIFICATIONS Tools methods should be private by default to indicate those are not part of official convert or merge API for leads. In this commit we make convert and merge tool methods private, keeping only main API methods public. LINKS Task ID-2428882 Prepares Task ID-2086889 COM PR odoo/odoo#64196 ENT PT odoo/enterprise#15610 --- addons/crm/models/crm_lead.py | 18 +++++++++--------- addons/crm/tests/test_crm_lead.py | 2 +- addons/crm/tests/test_crm_lead_convert_mass.py | 4 ++-- addons/crm/wizard/crm_lead_to_opportunity.py | 6 +++--- .../wizard/crm_opportunity_to_quotation.py | 2 +- addons/website_crm/models/website_visitor.py | 2 +- .../models/crm_lead.py | 2 +- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index a13d2102e4f..ac09ea61462 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -999,7 +999,7 @@ class Lead(models.Model): which fields has been merged and their new value. `self` is the resulting merge crm.lead record. - :param opportunities: see ``merge_dependences`` + :param opportunities: see ``_merge_dependences`` """ # TODO JEM: mail template should be used instead of fix body, subject text self.ensure_one() @@ -1016,7 +1016,7 @@ class Lead(models.Model): """ Move mail.message from the given opportunities to the current one. `self` is the crm.lead record destination for message of `opportunities`. - :param opportunities: see ``merge_dependences`` + :param opportunities: see ``_merge_dependences`` """ self.ensure_one() for opportunity in opportunities: @@ -1035,7 +1035,7 @@ class Lead(models.Model): """ Move attachments of given opportunities to the current one `self`, and rename the attachments having same name than native ones. - :param opportunities: see ``merge_dependences`` + :param opportunities: see ``_merge_dependences`` """ self.ensure_one() @@ -1057,7 +1057,7 @@ class Lead(models.Model): attachment.write(values) return True - def merge_dependences(self, opportunities): + def _merge_dependences(self, opportunities): """ Merge dependences (messages, attachments, ...). These dependences will be transfered to `self`, the most important lead. @@ -1102,7 +1102,7 @@ class Lead(models.Model): merged_data['team_id'] = team_id # merge other data (mail.message, attachments, ...) from tail into head - opportunities_head.merge_dependences(opportunities_tail) + opportunities_head._merge_dependences(opportunities_tail) # 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'): @@ -1149,11 +1149,11 @@ class Lead(models.Model): lead.write(vals) if user_ids or team_id: - self.handle_salesmen_assignment(user_ids, team_id) + self._handle_salesmen_assignment(user_ids=user_ids, team_id=team_id) return True - def handle_partner_assignment(self, force_partner_id=False, create_missing=True): + def _handle_partner_assignment(self, force_partner_id=False, create_missing=True): """ Update customer (partner_id) of leads. Purpose is to set the same partner on most leads; either through a newly created partner either through a given partner_id. @@ -1169,7 +1169,7 @@ class Lead(models.Model): partner = lead._create_customer() lead.partner_id = partner.id - def handle_salesmen_assignment(self, user_ids=None, team_id=False): + def _handle_salesmen_assignment(self, user_ids=False, team_id=False): """ Assign salesmen and salesteam to a batch of leads. If there are more leads than salesmen, these salesmen will be assigned in round-robin. E.g. 4 salesmen (S1, S2, S3, S4) for 6 leads (L1, L2, ... L6) will assigned as @@ -1179,7 +1179,7 @@ class Lead(models.Model): :param int team_id: salesteam to assign """ update_vals = {'team_id': team_id} if team_id else {} - if not user_ids: + if not user_ids and team_id: self.write(update_vals) else: lead_ids = self.ids diff --git a/addons/crm/tests/test_crm_lead.py b/addons/crm/tests/test_crm_lead.py index 92f7ff638e1..3ab0e9fe526 100644 --- a/addons/crm/tests/test_crm_lead.py +++ b/addons/crm/tests/test_crm_lead.py @@ -291,6 +291,6 @@ class TestCRMLead(TestCrmCommon): subtype_xmlid='mail.mt_comment') self.assertEqual(message.author_id, self.user_sales_manager.partner_id) - new_lead.handle_partner_assignment(create_missing=True) + new_lead._handle_partner_assignment(create_missing=True) self.assertEqual(new_lead.partner_id.email, 'unknown.sender@test.example.com') self.assertEqual(new_lead.partner_id.team_id, self.sales_team_1) diff --git a/addons/crm/tests/test_crm_lead_convert_mass.py b/addons/crm/tests/test_crm_lead_convert_mass.py index 44d6281cef2..a58a60d269d 100644 --- a/addons/crm/tests/test_crm_lead_convert_mass.py +++ b/addons/crm/tests/test_crm_lead_convert_mass.py @@ -25,7 +25,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon): test_leads = self.env['crm.lead'].browse(test_leads.ids) with self.assertQueryCount(user_sales_manager=254): # crm only: 251 - test_leads.handle_salesmen_assignment(user_ids=user_ids, team_id=False) + test_leads._handle_salesmen_assignment(user_ids=user_ids, team_id=False) self.assertEqual(test_leads.team_id, self.sales_team_convert | self.sales_team_1) self.assertEqual(test_leads[0::3].user_id, self.user_sales_manager) @@ -43,7 +43,7 @@ class TestLeadConvertMass(crm_common.TestLeadConvertMassCommon): test_leads = self.env['crm.lead'].browse(test_leads.ids) with self.assertQueryCount(user_sales_manager=220): # crm only: 215 - test_leads.handle_salesmen_assignment(user_ids=user_ids, team_id=team_id) + test_leads._handle_salesmen_assignment(user_ids=user_ids, team_id=team_id) self.assertEqual(test_leads.team_id, self.sales_team_convert) self.assertEqual(test_leads[0::3].user_id, self.user_sales_manager) diff --git a/addons/crm/wizard/crm_lead_to_opportunity.py b/addons/crm/wizard/crm_lead_to_opportunity.py index 7f138f2a281..fbc8923cbc2 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity.py +++ b/addons/crm/wizard/crm_lead_to_opportunity.py @@ -152,18 +152,18 @@ class Lead2OpportunityPartner(models.TransientModel): self._convert_handle_partner( lead, self.action, self.partner_id.id or lead.partner_id.id) - lead.convert_opportunity(lead.partner_id.id, [], False) + lead.convert_opportunity(lead.partner_id.id, user_ids=False, team_id=False) leads_to_allocate = leads if not self.force_assignment: leads_to_allocate = leads_to_allocate.filtered(lambda lead: not lead.user_id) if user_ids: - leads_to_allocate.handle_salesmen_assignment(user_ids, team_id=team_id) + leads_to_allocate._handle_salesmen_assignment(user_ids, team_id=team_id) def _convert_handle_partner(self, lead, action, partner_id): # used to propagate user_id (salesman) on created partners during conversion - lead.with_context(default_user_id=self.user_id.id).handle_partner_assignment( + lead.with_context(default_user_id=self.user_id.id)._handle_partner_assignment( force_partner_id=partner_id, create_missing=(action == 'create') ) diff --git a/addons/sale_crm/wizard/crm_opportunity_to_quotation.py b/addons/sale_crm/wizard/crm_opportunity_to_quotation.py index aef9a259ff8..24a18383581 100644 --- a/addons/sale_crm/wizard/crm_opportunity_to_quotation.py +++ b/addons/sale_crm/wizard/crm_opportunity_to_quotation.py @@ -46,5 +46,5 @@ class Opportunity2Quotation(models.TransientModel): """ self.ensure_one() if self.action != 'nothing': - self.lead_id.handle_partner_assignment(force_partner_id=self.partner_id.id, create_missing=(self.action == 'create')) + self.lead_id._handle_partner_assignment(force_partner_id=self.partner_id.id, create_missing=(self.action == 'create')) return self.lead_id.action_new_quotation() diff --git a/addons/website_crm/models/website_visitor.py b/addons/website_crm/models/website_visitor.py index 6c996df424c..5f2bf253451 100644 --- a/addons/website_crm/models/website_visitor.py +++ b/addons/website_crm/models/website_visitor.py @@ -38,7 +38,7 @@ class WebsiteVisitor(models.Model): partners = sorted_leads.mapped('partner_id') if not partners: main_lead = self.lead_ids[0] - main_lead.handle_partner_assignment(create_missing=True) + main_lead._handle_partner_assignment(create_missing=True) self.partner_id = main_lead.partner_id.id return True return check diff --git a/addons/website_crm_partner_assign/models/crm_lead.py b/addons/website_crm_partner_assign/models/crm_lead.py index 42213a6c2ca..b5f49d02dde 100644 --- a/addons/website_crm_partner_assign/models/crm_lead.py +++ b/addons/website_crm_partner_assign/models/crm_lead.py @@ -68,7 +68,7 @@ class CrmLead(models.Model): lead.assign_geo_localize(lead.partner_latitude, lead.partner_longitude) partner = self.env['res.partner'].browse(partner_id) if partner.user_id: - lead.handle_salesmen_assignment(partner.user_id.ids, team_id=partner.team_id.id) + lead._handle_salesmen_assignment(user_ids=partner.user_id.ids, team_id=partner.team_id.id) lead.write({'partner_assigned_id': partner_id}) return res