diff --git a/addons/crm/data/crm_lead_merge_template.xml b/addons/crm/data/crm_lead_merge_template.xml index c081ada9ae9..a46087183db 100644 --- a/addons/crm/data/crm_lead_merge_template.xml +++ b/addons/crm/data/crm_lead_merge_template.xml @@ -40,15 +40,34 @@
Priority:
+
+ Lost Reason: +
Created on:
Last Action:
+
+ Expected Closing: +
Notes:
+
+ Language: +
+
+ Referred By: +
+
+ Tags: +
+
+
+
Salesperson:
@@ -85,10 +104,14 @@
Website:
+
+ Job Position: +
+ t-if="lead.street or lead.street2 or lead.zip or lead.city or lead.state_id or lead.country_id" + name="address">
Address:
@@ -99,7 +122,7 @@
-
Marketing: @@ -114,6 +137,22 @@ Source:
+ +
+
+ The contacts below have been added as followers of this lead + because they have been contacted less than 30 days ago on + . +
+
    +
  • + + + () + +
  • +
+
diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index f0c34f52554..0e5ce514c78 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -15,7 +15,7 @@ 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 date_utils, email_re, email_split, is_html_empty +from odoo.tools import date_utils, email_re, email_split, is_html_empty, groupby from . import crm_stage @@ -33,31 +33,31 @@ CRM_LEAD_FIELDS_TO_MERGE = [ # description 'name', 'user_id', + 'color', 'company_id', + 'lang_id', 'team_id', + 'referred', # pipeline 'stage_id', # revenues 'expected_revenue', + 'recurring_plan', + 'recurring_revenue', # dates 'create_date', 'date_action_last', + 'date_deadline', # partner / contact 'partner_id', 'title', 'partner_name', 'contact_name', 'email_from', + 'function', 'mobile', 'phone', 'website', - # address - 'street', - 'street2', - 'zip', - 'city', - 'state_id', - 'country_id', ] # Subset of partner fields: sync any of those @@ -146,14 +146,12 @@ class Lead(models.Model): # Revenues expected_revenue = fields.Monetary('Expected Revenue', currency_field='company_currency', tracking=True) prorated_revenue = fields.Monetary('Prorated Revenue', currency_field='company_currency', store=True, compute="_compute_prorated_revenue") - recurring_revenue = fields.Monetary('Recurring Revenues', currency_field='company_currency', groups="crm.group_use_recurring_revenues", tracking=True) - recurring_plan = fields.Many2one('crm.recurring.plan', string="Recurring Plan", groups="crm.group_use_recurring_revenues") + recurring_revenue = fields.Monetary('Recurring Revenues', currency_field='company_currency', tracking=True) + recurring_plan = fields.Many2one('crm.recurring.plan', string="Recurring Plan") recurring_revenue_monthly = fields.Monetary('Expected MRR', currency_field='company_currency', store=True, - compute="_compute_recurring_revenue_monthly", - groups="crm.group_use_recurring_revenues") + compute="_compute_recurring_revenue_monthly") recurring_revenue_monthly_prorated = fields.Monetary('Prorated MRR', currency_field='company_currency', store=True, - compute="_compute_recurring_revenue_monthly_prorated", - groups="crm.group_use_recurring_revenues") + compute="_compute_recurring_revenue_monthly_prorated") company_currency = fields.Many2one("res.currency", string='Currency', related='company_id.currency_id', readonly=True) # Dates date_closed = fields.Datetime('Closed Date', readonly=True, copy=False) @@ -1226,6 +1224,7 @@ class Lead(models.Model): if fnames is None: fnames = self._merge_get_fields() fcallables = self._merge_get_fields_specific() + address_values = self._merge_get_fields_address() # helpers def _get_first_not_null(attr, opportunities): @@ -1246,6 +1245,8 @@ class Lead(models.Model): fcallable = fcallables.get(field_name) if fcallable and callable(fcallable): data[field_name] = fcallable(field_name, self) + elif field_name in address_values: + data[field_name] = address_values[field_name] elif not fcallable and field.type in ('many2many', 'one2many'): continue else: @@ -1295,10 +1296,13 @@ class Lead(models.Model): if team_id: merged_data['team_id'] = team_id + merged_followers = opportunities_head._merge_followers(opportunities_tail) + # log merge message opportunities_head.message_post_with_view( "crm.crm_lead_merge_summary", values={ + "merged_followers": merged_followers, "opportunities": opportunities_tail, "is_html_empty": is_html_empty }, @@ -1323,15 +1327,36 @@ class Lead(models.Model): return opportunities_head + def _merge_get_fields_address(self): + """The address fields are propagated as a whole. + + The address is taken from the lead with the most non-empty address field + (sorted by highest rank if multiple lead have the same amount of non-empty + fields). + """ + source_lead = max(self, key=lambda lead: len(list( + lead[field] for field in PARTNER_ADDRESS_FIELDS_TO_SYNC + if lead[field] + ))) + return {fname: source_lead[fname] for fname in PARTNER_ADDRESS_FIELDS_TO_SYNC} + def _merge_get_fields_specific(self): return { 'description': lambda fname, leads: '

'.join(desc for desc in leads.mapped('description') if not is_html_empty(desc)), 'type': lambda fname, leads: 'opportunity' if any(lead.type == 'opportunity' for lead in leads) else 'lead', 'priority': lambda fname, leads: max(leads.mapped('priority')) if leads else False, + 'tag_ids': lambda fname, leads: leads.mapped('tag_ids'), + 'lost_reason': lambda fname, leads: + False if leads and leads[0].probability + else next((lead.lost_reason for lead in leads if lead.lost_reason), False), } def _merge_get_fields(self): - return list(CRM_LEAD_FIELDS_TO_MERGE) + list(self._merge_get_fields_specific().keys()) + return ( + CRM_LEAD_FIELDS_TO_MERGE + + list(self._merge_get_fields_specific().keys()) + + PARTNER_ADDRESS_FIELDS_TO_SYNC + ) def _merge_dependences(self, opportunities): """ Merge dependences (messages, attachments,activities, calendar events, @@ -1412,6 +1437,50 @@ class Lead(models.Model): 'opportunity_id': self.id, }) + def _merge_followers(self, opportunities): + """Add the followers into the destination lead if they post a message in the last 30 days. + + :param opportunities : Record of opportunities to transfer + :return: {old_lead_id: Record} Followers which have been added in + the destination lead grouped by source lead ID. + """ + self.ensure_one() + + self.env['mail.message'].flush() + self.env['mail.followers'].flush() + + # Get the active followers (followers whose partner post a message on the + # leads in the last 30 days) which should be moved on the destination lead + self.env.cr.execute( + ''' + SELECT MAX(mf.id) AS id + FROM mail_followers AS mf + JOIN mail_message AS mm + ON mm.author_id = mf.partner_id + AND mm.res_id = mf.res_id + AND mm.model = 'crm.lead' + AND mm.date > NOW() - INTERVAL '30 DAY' + /* Check if the partner is already + following the destination lead */ + LEFT JOIN mail_followers AS destf + ON destf.res_model = 'crm.lead' + AND destf.res_id = %(lead_id)s + AND destf.partner_id = mf.partner_id + /* Select only once each partner + to not create duplicated followers */ + WHERE mf.res_model = 'crm.lead' + AND mf.res_id IN %(lead_ids)s + AND destf IS NULL + GROUP BY mf.partner_id + ''', + {'lead_ids': tuple(opportunities.ids), 'lead_id': self.id}, + ) + followers_to_update = [r[0] for r in self.env.cr.fetchall()] + followers_to_update = self.env['mail.followers'].browse(followers_to_update).sudo() + followers_by_old_lead = dict(groupby(followers_to_update, lambda f: f.res_id)) + followers_to_update.write({'res_id': self.id}) + return followers_by_old_lead + # CONVERT # ---------------------------------------------------------------------- diff --git a/addons/crm/static/src/scss/crm.scss b/addons/crm/static/src/scss/crm.scss index c33a456d679..e7bdc20d092 100644 --- a/addons/crm/static/src/scss/crm.scss +++ b/addons/crm/static/src/scss/crm.scss @@ -36,4 +36,34 @@ blockquote { font-style: normal; } -} \ No newline at end of file + + // Tag colors + @for $size from 1 through length($o-colors) { + .o_tag_color_#{$size - 1} { + border: transparent; + line-height: normal; + $background-color: white; + // no color selected + @if $size == 1 { + & { + color: black; + background-color: $background-color; + box-shadow: inset 0 0 0 1px nth($o-colors, $size); + } + } @else { + $background-color: nth($o-colors, $size); + & { + color: white; + background-color: $background-color; + } + } + @at-root a#{&} { + &:hover { + color: color-yiq($background-color); + background-color: darken($background-color, 10%); + } + } + } + } +} + diff --git a/addons/crm/tests/common.py b/addons/crm/tests/common.py index 9527089281d..c1a1088d0df 100644 --- a/addons/crm/tests/common.py +++ b/addons/crm/tests/common.py @@ -5,7 +5,7 @@ from ast import literal_eval from contextlib import contextmanager from unittest.mock import patch -from odoo.addons.crm.models.crm_lead import CRM_LEAD_FIELDS_TO_MERGE +from odoo.addons.crm.models.crm_lead import PARTNER_ADDRESS_FIELDS_TO_SYNC from odoo.addons.mail.tests.common import MailCase, mail_new_test_user from odoo.addons.phone_validation.tools import phone_validation from odoo.addons.sales_team.tests.common import TestSalesCommon @@ -423,7 +423,10 @@ class TestCrmCommon(TestSalesCommon, MailCase): finally: # support specific values caller may want to check in addition to generic tests for fname, expected in expected.items(): - self.assertEqual(opportunity[fname], expected) + if expected is False: + self.assertFalse(opportunity[fname], "%s must be False" % fname) + else: + self.assertEqual(opportunity[fname], expected, "%s must be equal to %s" % (fname, expected)) # classic fields: first not void wins or specific computation for fname in fields_all: @@ -438,6 +441,9 @@ class TestCrmCommon(TestSalesCommon, MailCase): self.assertEqual(opp_value, _get_priority()) elif fname in ('order_ids', 'visitor_ids'): self.assertEqual(opp_value, _aggregate(fname)) + elif fname in PARTNER_ADDRESS_FIELDS_TO_SYNC: + # Specific computation, has its own test + continue else: self.assertEqual( opp_value if opp_value or not isinstance(opp_value, models.BaseModel) else False, @@ -607,6 +613,9 @@ class TestLeadConvertMassCommon(TestLeadConvertCommon): 'partner_id': cls.contact_1.id, }) cls.lead_w_partner.write({'stage_id': False}) + + cls.tags = cls.env['crm.tag'].create([{'name': 'Tag %i' % i} for i in range(4)]) + cls.lead_1.tag_ids = cls.tags[:3] cls.lead_w_partner_company = cls.env['crm.lead'].create({ 'name': 'New1', 'type': 'lead', @@ -616,6 +625,7 @@ class TestLeadConvertMassCommon(TestLeadConvertCommon): 'partner_id': cls.contact_company_1.id, 'contact_name': 'Hermes Conrad', 'email_from': 'hermes.conrad@test.example.com', + 'tag_ids': (cls.tags[:2] | cls.tags[3]), }) cls.lead_w_contact = cls.env['crm.lead'].create({ 'name': 'LeadContact', diff --git a/addons/crm/tests/test_crm_lead_merge.py b/addons/crm/tests/test_crm_lead_merge.py index 8f580585058..bbf73b70155 100644 --- a/addons/crm/tests/test_crm_lead_merge.py +++ b/addons/crm/tests/test_crm_lead_merge.py @@ -2,6 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import base64 +from datetime import timedelta from odoo.addons.crm.tests.common import TestLeadConvertMassCommon from odoo.fields import Datetime @@ -84,6 +85,73 @@ class TestLeadMerge(TestLeadMergeCommon): self.assertEqual(self.lead_w_email_lost.user_id, self.env['res.users']) self.assertEqual(self.lead_w_email_lost.team_id, self.sales_team_1) + @users('user_sales_manager') + @mute_logger('odoo.models.unlink') + def test_lead_merge_address_not_propagated(self): + """All addresses have the same number of non-empty address fields, take the first one (lead_w_contact) + because it's the lead that has the best confidence level after being sorted with '_sort_by_confidence_level'""" + initial_address = { + 'street': 'Test street', + 'street2': 'Test street2', + 'city': 'Test City', + 'zip': '5000', + 'state_id': False, + 'country_id': self.env.ref('base.be'), + } + self.lead_w_contact.write(initial_address) + + (self.leads - self.lead_w_contact).write({ + 'street': 'Other street', + 'street2': 'Other street2', + 'city': 'Other City', + 'zip': '6666', + 'state_id': self.env.ref('base.state_us_1'), + 'country_id': False, + }) + + leads = self.env['crm.lead'].browse(self.leads.ids)._sort_by_confidence_level(reverse=True) + with self.assertLeadMerged(self.lead_w_contact, leads, **initial_address): + leads._merge_opportunity(auto_unlink=False, max_length=None) + + @users('user_sales_manager') + @mute_logger('odoo.models.unlink') + def test_lead_merge_address_propagated(self): + """Test that the address with the most non-empty fields is propagated. + + Should take the address of "lead_w_partner" (maximum number of non-empty address + fields and with an highest rank than "lead_w_email_lost") + """ + self.leads.write({ + 'street': 'Original street', + 'street2': False, + 'city': False, + 'zip': False, + 'state_id': False, + 'country_id': False, + }) + new_address = { + 'street': 'New street', + 'street2': False, + 'city': 'New City', + 'zip': False, + 'state_id': False, + 'country_id': False, + } + self.lead_w_partner.write(new_address) + self.lead_w_email_lost.write({ + 'street': 'Other street', + 'street2': False, + 'city': 'Other City', + 'zip': False, + 'state_id': False, + 'country_id': False, + }) + + leads = self.env['crm.lead'].browse(self.leads.ids)._sort_by_confidence_level(reverse=True) + + with self.assertLeadMerged(self.lead_w_contact, leads, **new_address): + leads._merge_opportunity(auto_unlink=False, max_length=None) + @users('user_sales_manager') @mute_logger('odoo.models.unlink') def test_lead_merge_internals(self): @@ -234,10 +302,40 @@ class TestLeadMerge(TestLeadMergeCommon): # ensure initial data (self.lead_w_partner_company | self.lead_1).write({'type': 'opportunity', 'probability': 50}) leads = self.env['crm.lead'].browse(self.leads.ids)._sort_by_confidence_level(reverse=True) + + # lead_w_partner is lost, check that the "lost_reason" is not propagated + # because "lead_1" is not lost + lost_reason = self.env['crm.lost.reason'].create({'name': 'Test Reason'}) + self.lead_w_partner.write({ + 'lost_reason': lost_reason, + 'probability': 0, + }) + + all_tags = self.leads.mapped('tag_ids') + with self.assertLeadMerged(self.lead_1, leads, name='Nibbler Spacecraft Request', partner_id=self.contact_company_1, - priority='2'): + priority='2', + lost_reason=False, + tag_ids=all_tags): + leads._merge_opportunity(auto_unlink=False, max_length=None) + + @users('user_sales_manager') + @mute_logger('odoo.models.unlink') + def test_merge_method_propagate_lost_reason(self): + """Check that the lost reason is propagated to the final lead if it's lost.""" + self.leads.write({ + 'probability': 0, + 'automated_probability': 50, # Do not automatically update the probability + }) + + lost_reason = self.env['crm.lost.reason'].create({'name': 'Test Reason'}) + self.lead_w_partner.lost_reason = lost_reason + + leads = self.env['crm.lead'].browse(self.leads.ids)._sort_by_confidence_level(reverse=True) + + with self.assertLeadMerged(leads[0], leads, lost_reason=lost_reason): leads._merge_opportunity(auto_unlink=False, max_length=None) @users('user_sales_manager') @@ -294,3 +392,97 @@ class TestLeadMerge(TestLeadMergeCommon): # 2many accessors updated self.assertEqual(master_lead.activity_ids, activity) self.assertEqual(master_lead.calendar_event_ids, calendar_event) + + @users('user_sales_manager') + @mute_logger('odoo.models.unlink') + def test_merge_method_followers(self): + """ Test that the followers of the leads are added in the destination lead. + + They should be added if: + - The related partner was active on the lead (posted a message in the last 30 days) + - The related partner is not already following the destination lead + + Leads Followers Info + --------------------------------------------------------------------------------- + lead_w_contact contact_1 OK (destination lead) + lead_w_email contact_1 KO (already following the destination lead) + contact_2 OK (active on lead_w_email) + contact_company KO (most recent message on lead_w_email is 35 days ago, message + on lead_w_partner is not counted as he doesn't follow it) + lead_w_partner contact_2 KO (already added with lead_w_email) + lead_w_partner_company + """ + self.leads.message_follower_ids.unlink() + self.leads.message_ids.unlink() + + self.lead_w_contact.message_subscribe([self.contact_1.id]) + self.lead_w_email.message_subscribe([self.contact_1.id, self.contact_2.id, self.contact_company.id]) + self.lead_w_partner.message_subscribe([self.contact_2.id]) + + self.env['mail.message'].create([{ + 'author_id': self.contact_1.id, + 'model': 'crm.lead', + 'res_id': self.lead_w_contact.id, + 'date': Datetime.now() - timedelta(days=1), + 'subtype_id': self.ref('mail.mt_comment'), + 'reply_to': False, + 'body': 'Test follower', + }, { + 'author_id': self.contact_1.id, + 'model': 'crm.lead', + 'res_id': self.lead_w_email.id, + 'date': Datetime.now() - timedelta(days=20), + 'subtype_id': self.ref('mail.mt_comment'), + 'reply_to': False, + 'body': 'Test follower', + }, { + 'author_id': self.contact_2.id, + 'model': 'crm.lead', + 'res_id': self.lead_w_email.id, + 'date': Datetime.now() - timedelta(days=15), + 'subtype_id': self.ref('mail.mt_comment'), + 'reply_to': False, + 'body': 'Test follower', + }, { + 'author_id': self.contact_2.id, + 'model': 'crm.lead', + 'res_id': self.lead_w_partner.id, + 'date': Datetime.now() - timedelta(days=29), + 'subtype_id': self.ref('mail.mt_comment'), + 'reply_to': False, + 'body': 'Test follower', + }, { + 'author_id': self.contact_company.id, + 'model': 'crm.lead', + 'res_id': self.lead_w_email.id, + 'date': Datetime.now() - timedelta(days=35), + 'subtype_id': self.ref('mail.mt_comment'), + 'reply_to': False, + 'body': 'Test follower', + }, { + 'author_id': self.contact_company.id, + 'model': 'crm.lead', + 'res_id': self.lead_w_partner.id, + 'date': Datetime.now(), + 'subtype_id': self.ref('mail.mt_comment'), + 'reply_to': False, + 'body': 'Test follower', + }]) + initial_followers = self.lead_w_contact.message_follower_ids + + leads = self.env['crm.lead'].browse(self.leads.ids)._sort_by_confidence_level(reverse=True) + master_lead = leads._merge_opportunity(max_length=None) + + self.assertEqual(master_lead, self.lead_w_contact) + + # Check followers of the destination lead + new_partner_followers = (master_lead.message_follower_ids - initial_followers).partner_id + self.assertIn(self.contact_2, new_partner_followers, + 'The partner must follow the destination lead') + # "contact_company" posted a message 35 days ago on lead_2, so it's considered as inactive + # "contact_company" posted a message now on lead_3, but he doesn't follow lead_3 + # so this message is just ignored + self.assertNotIn(self.contact_company, new_partner_followers, + 'The partner was not active on the lead') + self.assertIn(self.contact_1, master_lead.message_follower_ids.partner_id, + 'Should not have removed follower of the destination lead') diff --git a/addons/crm/views/crm_lead_views.xml b/addons/crm/views/crm_lead_views.xml index ca0237a9125..3ae7b9d5a47 100644 --- a/addons/crm/views/crm_lead_views.xml +++ b/addons/crm/views/crm_lead_views.xml @@ -462,7 +462,7 @@
-
+
@@ -559,11 +559,11 @@
- + + + - - + +
@@ -634,11 +634,11 @@
- + + + - - + +
@@ -772,9 +772,11 @@ - - - + + + @@ -896,7 +898,7 @@ - + diff --git a/addons/crm_iap_enrich/models/crm_lead.py b/addons/crm_iap_enrich/models/crm_lead.py index 869d2704bd4..215e7090577 100644 --- a/addons/crm_iap_enrich/models/crm_lead.py +++ b/addons/crm_iap_enrich/models/crm_lead.py @@ -159,3 +159,9 @@ class Lead(models.Model): values=template_values, subtype_id=self.env.ref('mail.mt_note').id ) + + def _merge_get_fields_specific(self): + return { + ** super(Lead, self)._merge_get_fields_specific(), + 'iap_enrich_done': lambda fname, leads: any(lead.iap_enrich_done for lead in leads), + } diff --git a/addons/crm_iap_enrich/tests/__init__.py b/addons/crm_iap_enrich/tests/__init__.py index 33ab21b0da9..76341f51790 100644 --- a/addons/crm_iap_enrich/tests/__init__.py +++ b/addons/crm_iap_enrich/tests/__init__.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from . import test_crm_lead_merge from . import test_lead_enrich diff --git a/addons/crm_iap_enrich/tests/test_crm_lead_merge.py b/addons/crm_iap_enrich/tests/test_crm_lead_merge.py new file mode 100644 index 00000000000..054a51b38e7 --- /dev/null +++ b/addons/crm_iap_enrich/tests/test_crm_lead_merge.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + + +from odoo.addons.crm.tests.test_crm_lead_merge import TestLeadMergeCommon +from odoo.tests.common import tagged, users +from odoo.tools import mute_logger + + +@tagged('lead_manage') +class TestLeadMerge(TestLeadMergeCommon): + + @users('user_sales_manager') + @mute_logger('odoo.models.unlink') + def test_merge_method_iap_enrich_done(self): + """Test that the "iap_enrich_done" is set to True if at least one lead have this value True""" + self.leads.iap_enrich_done = False + self.lead_w_contact.write({ + 'reveal_id': 'test_reveal_id', + 'iap_enrich_done': True, + }) + + leads = self.env['crm.lead'].browse(self.leads.ids)._sort_by_confidence_level(reverse=True) + + with self.assertLeadMerged(leads[0], leads, iap_enrich_done=True, reveal_id='test_reveal_id'): + leads._merge_opportunity(auto_unlink=False, max_length=None) diff --git a/addons/crm_iap_mine/models/crm_lead.py b/addons/crm_iap_mine/models/crm_lead.py index eaed1d92bd7..35107394157 100644 --- a/addons/crm_iap_mine/models/crm_lead.py +++ b/addons/crm_iap_mine/models/crm_lead.py @@ -8,3 +8,6 @@ class Lead(models.Model): _inherit = 'crm.lead' lead_mining_request_id = fields.Many2one('crm.iap.lead.mining.request', string='Lead Mining Request', index=True) + + def _merge_get_fields(self): + return super(Lead, self)._merge_get_fields() + ['lead_mining_request_id'] diff --git a/addons/event_crm/__manifest__.py b/addons/event_crm/__manifest__.py index a9a5aeb3dbe..dba6b325056 100644 --- a/addons/event_crm/__manifest__.py +++ b/addons/event_crm/__manifest__.py @@ -11,6 +11,7 @@ 'data': [ 'security/event_crm_security.xml', 'security/ir.model.access.csv', + 'data/crm_lead_merge_template.xml', 'views/crm_lead_views.xml', 'views/event_registration_views.xml', 'views/event_lead_rule_views.xml', diff --git a/addons/event_crm/data/crm_lead_merge_template.xml b/addons/event_crm/data/crm_lead_merge_template.xml new file mode 100644 index 00000000000..3dfecd8d128 --- /dev/null +++ b/addons/event_crm/data/crm_lead_merge_template.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/addons/event_crm/models/crm_lead.py b/addons/event_crm/models/crm_lead.py index de709404e95..07a67e90f4d 100644 --- a/addons/event_crm/models/crm_lead.py +++ b/addons/event_crm/models/crm_lead.py @@ -30,3 +30,6 @@ class Lead(models.Model): self.sudo().write({ 'registration_ids': [(4, registration.id) for registration in opportunities.sudo().registration_ids] }) + + def _merge_get_fields(self): + return super(Lead, self)._merge_get_fields() + ['event_lead_rule_id', 'event_id'] diff --git a/addons/iap_crm/models/crm_lead.py b/addons/iap_crm/models/crm_lead.py index c45d9da847d..43d3da90902 100644 --- a/addons/iap_crm/models/crm_lead.py +++ b/addons/iap_crm/models/crm_lead.py @@ -8,3 +8,6 @@ class Lead(models.Model): _inherit = 'crm.lead' reveal_id = fields.Char(string='Reveal ID', help="Technical ID of reveal request done by IAP.") + + def _merge_get_fields(self): + return super(Lead, self)._merge_get_fields() + ['reveal_id'] diff --git a/addons/sale_crm/__manifest__.py b/addons/sale_crm/__manifest__.py index c8c5d551283..3fc3bbc7d7e 100644 --- a/addons/sale_crm/__manifest__.py +++ b/addons/sale_crm/__manifest__.py @@ -19,6 +19,7 @@ modules. 'depends': ['sale_management', 'crm'], 'data': [ 'security/ir.model.access.csv', + 'data/crm_lead_merge_template.xml', 'views/partner_views.xml', 'views/sale_order_views.xml', 'views/crm_lead_views.xml', diff --git a/addons/sale_crm/data/crm_lead_merge_template.xml b/addons/sale_crm/data/crm_lead_merge_template.xml new file mode 100644 index 00000000000..dc21ebb7b4c --- /dev/null +++ b/addons/sale_crm/data/crm_lead_merge_template.xml @@ -0,0 +1,18 @@ + + + + + + diff --git a/addons/website_crm_iap_reveal/models/crm_lead.py b/addons/website_crm_iap_reveal/models/crm_lead.py index 97cf26914e3..90b8ca89d69 100644 --- a/addons/website_crm_iap_reveal/models/crm_lead.py +++ b/addons/website_crm_iap_reveal/models/crm_lead.py @@ -10,3 +10,6 @@ class Lead(models.Model): reveal_ip = fields.Char(string='IP Address') reveal_iap_credits = fields.Integer(string='IAP Credits') reveal_rule_id = fields.Many2one('crm.reveal.rule', string='Lead Generation Rule', index=True) + + def _merge_get_fields(self): + return super(Lead, self)._merge_get_fields() + ['reveal_ip', 'reveal_iap_credits', 'reveal_rule_id'] diff --git a/addons/website_crm_partner_assign/data/crm_lead_merge_template.xml b/addons/website_crm_partner_assign/data/crm_lead_merge_template.xml index 5381daccace..f624710faa9 100644 --- a/addons/website_crm_partner_assign/data/crm_lead_merge_template.xml +++ b/addons/website_crm_partner_assign/data/crm_lead_merge_template.xml @@ -11,6 +11,17 @@ Partner Assignment Date:
+ + + lead.street or lead.street2 or lead.zip or lead.city or lead.state_id or lead.country_id + or lead.partner_latitude or lead.partner_longitude + + + +
+ Geolocation: latitude, longitude +
+