[IMP] crm: merge the followers of the leads if they are active

Purpose
=======
When merging multiple leads, we want to move the followers to the
destination lead if they posted a message in the last 30 days.

A message will be added in the merge note to know which followers have
been added.

Task-2447721
PR odoo/odoo#75742
This commit is contained in:
std-odoo
2021-10-19 15:58:58 +00:00
parent be996fdc00
commit ef04bec628
3 changed files with 160 additions and 2 deletions
+17 -1
View File
@@ -122,7 +122,7 @@
<div t-if="lead.state_id" t-field="lead.state_id"/>
<div t-if="lead.country_id" t-field="lead.country_id"/>
</div>
<div class="mt-3 mb-3" name="marketing"
<div class="mt-3" name="marketing"
t-if="lead.campaign_id or lead.medium_id or lead.source_id">
<div class="font-weight-bold">
Marketing:
@@ -137,6 +137,22 @@
Source: <span t-field="lead.source_id"/>
</div>
</div>
<t t-set="lead_followers" t-value="merged_followers and merged_followers.get(lead.id)"/>
<div class="mt-3 mb-3" name="merged_followers" t-if="lead_followers">
<div>
The contacts below have been added as followers of this lead
because they have been contacted less than 30 days ago on
<span class="font-weight-bold" t-esc="lead.name"/>.
</div>
<ul>
<li t-foreach="lead_followers" t-as="follower">
<t t-esc="follower.partner_id.name"/>
<t t-if="follower.partner_id.email">
(<t t-esc="follower.partner_id.email"/>)
</t>
</li>
</ul>
</div>
</blockquote>
</t>
</div>
+48 -1
View File
@@ -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
@@ -1296,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
},
@@ -1434,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<crm.lead> of opportunities to transfer
:return: {old_lead_id: Record<mail.followers>} 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
# ----------------------------------------------------------------------
+95
View File
@@ -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
@@ -391,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')