[IMP] crm: allow to log a feedback when marking a lead as lost

PURPOSE

Allow sales reps to add a closing note to their lead while it is being marked
as lost. This is currently already done by a lot of sales reps but manually
with the "Log a Note" button.

SPECIFICATIONS

In ``Lost Reason`` model: add a new html field allowing to log a note on the
lost leads. Below the m2o, add an Extra Comment field where users can add a
"closing note". When the wizard is submitted, log this message as a note on
selected records.

Add tests, allowing to test both the wizard and this new feature.

Task-2671709

Part-of: odoo/odoo#78648
This commit is contained in:
Thibault Delavallée
2021-11-23 09:39:51 +00:00
parent d348c5e880
commit a830ceaaf9
4 changed files with 91 additions and 9 deletions
+67 -4
View File
@@ -4,6 +4,7 @@
from odoo.addons.crm.tests import common as crm_common
from odoo.exceptions import AccessError
from odoo.tests.common import tagged, users
from odoo.tools import mute_logger
@tagged('lead_manage', 'lead_lost')
@@ -18,34 +19,96 @@ class TestLeadConvert(crm_common.TestCrmCommon):
@users('user_sales_salesman')
def test_lead_lost(self):
""" Test setting a lead as lost using the wizard. Also check that an
'html editor' void content used as feedback is not logged on the lead. """
# Initial data
self.assertEqual(len(self.lead_1.message_ids), 1, 'Should contain creation message')
creation_message = self.lead_1.message_ids[0]
self.assertEqual(creation_message.subtype_id, self.env.ref('crm.mt_lead_create'))
# Update responsible as ACLs is "own only" for user_sales_salesman
self.lead_1.with_user(self.user_sales_manager).write({
'user_id': self.user_sales_salesman.id,
'probability': 32,
})
self.flush_tracking()
lead = self.lead_1.with_user(self.env.user)
lead = self.env['crm.lead'].browse(self.lead_1.ids)
self.assertFalse(lead.lost_reason)
self.assertEqual(lead.probability, 32)
self.assertEqual(len(lead.message_ids), 2, 'Should have tracked new responsible')
update_message = lead.message_ids[0]
self.assertEqual(update_message.subtype_id, self.env.ref('mail.mt_note'))
# mark as lost using the wizard
lost_wizard = self.env['crm.lead.lost'].with_context({
'active_ids': lead.ids,
}).create({
'lost_reason_id': self.lost_reason.id
'lost_reason_id': self.lost_reason.id,
'lost_feedback': '<p></p>', # void content
})
lost_wizard.action_lost_reason_apply()
self.flush_tracking()
self.assertEqual(lead.probability, 0)
self.assertEqual(lead.automated_probability, 0)
# check lead update
self.assertFalse(lead.active)
self.assertEqual(lead.automated_probability, 0)
self.assertEqual(lead.lost_reason, self.lost_reason) # TDE FIXME: should be called lost_reason_id non didjou
self.assertEqual(lead.probability, 0)
# check messages
self.assertEqual(len(lead.message_ids), 3, 'Should have logged a tracking message for lost lead with reason')
update_message = lead.message_ids[0]
self.assertEqual(update_message.subtype_id, self.env.ref('crm.mt_lead_lost'))
self.assertEqual(len(update_message.tracking_value_ids), 2, 'Tracking: active, lost reason')
self.assertTracking(
update_message,
[('active', 'boolean', True, False),
('lost_reason', 'many2one', False, self.lost_reason)
]
)
@users('user_sales_leads')
def test_lead_lost_batch_wfeedback(self):
""" Test setting leads as lost in batch using the wizard, including a log
message. """
leads = self._create_leads_batch(lead_type='lead', count=10, probabilities=[10, 20, 30])
self.assertEqual(len(leads), 10)
self.flush_tracking()
lost_wizard = self.env['crm.lead.lost'].with_context({
'active_ids': leads.ids,
}).create({
'lost_reason_id': self.lost_reason.id,
'lost_feedback': '<p>I cannot find it. It was in my closet and pouf, disappeared.</p>',
})
lost_wizard.action_lost_reason_apply()
self.flush_tracking()
for lead in leads:
# check content
self.assertFalse(lead.active)
self.assertEqual(lead.automated_probability, 0)
self.assertEqual(lead.probability, 0)
self.assertEqual(lead.lost_reason, self.lost_reason)
# check messages
self.assertEqual(len(lead.message_ids), 3, 'Should have 3 messages: creation, lost, and log')
lost_message = lead.message_ids.filtered(lambda msg: msg.subtype_id == self.env.ref('crm.mt_lead_lost'))
self.assertTrue(lost_message)
self.assertTracking(
lost_message,
[('active', 'boolean', True, False),
('lost_reason', 'many2one', False, self.lost_reason)
]
)
note_message = lead.message_ids.filtered(lambda msg: msg.subtype_id == self.env.ref('mail.mt_note'))
self.assertTrue(note_message)
self.assertEqual(note_message.body, '<p>I cannot find it. It was in my closet and pouf, disappeared.</p>')
@users('user_sales_salesman')
@mute_logger('odoo.addons.base.models')
def test_lead_lost_crm_rights(self):
""" Test ACLs of lost reasons management and usage """
lead = self.lead_1.with_user(self.env.user)
# nice try little salesman but only managers can create lost reason to avoid bloating the DB
+17 -2
View File
@@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
from odoo import fields, models, _
from odoo.tools.mail import is_html_empty
class CrmLeadLost(models.TransientModel):
@@ -8,7 +10,20 @@ class CrmLeadLost(models.TransientModel):
_description = 'Get Lost Reason'
lost_reason_id = fields.Many2one('crm.lost.reason', 'Lost Reason')
lost_feedback = fields.Html(
'Closing Note', sanitize=True,
help="Closing note logged in leads discussion history."
)
def action_lost_reason_apply(self):
self.ensure_one()
leads = self.env['crm.lead'].browse(self.env.context.get('active_ids'))
return leads.action_set_lost(lost_reason=self.lost_reason_id.id)
res = leads.action_set_lost(lost_reason=self.lost_reason_id.id)
if not is_html_empty(self.lost_feedback):
leads._message_log_batch(
bodies=dict(
(lead.id, self.lost_feedback)
for lead in leads),
subject=False,
)
return res
@@ -8,6 +8,7 @@
<group class="oe_title">
<field name="lost_reason_id" options="{'no_create_edit': True}" />
</group>
<field name="lost_feedback" class="oe_title" placeholder="What went wrong ?"/>
<footer>
<button name="action_lost_reason_apply" string="Submit" type="object" class="btn-primary" data-hotkey="q"/>
<button string="Cancel" class="btn-secondary" special="cancel" data-hotkey="z"/>
+6 -3
View File
@@ -860,15 +860,18 @@ class MailCase(MockEmail):
for field_name, value_type, old_value, new_value in data:
tracking = tracking_values.filtered(lambda track: track.field.name == field_name)
self.assertEqual(len(tracking), 1)
if value_type in ('char', 'integer'):
if value_type == 'char':
self.assertEqual(tracking.old_value_char, old_value)
self.assertEqual(tracking.new_value_char, new_value)
elif value_type in ('many2one'):
elif value_type in ('boolean', 'integer'):
self.assertEqual(tracking.old_value_integer, old_value)
self.assertEqual(tracking.new_value_integer, new_value)
elif value_type == 'many2one':
self.assertEqual(tracking.old_value_integer, old_value and old_value.id or False)
self.assertEqual(tracking.new_value_integer, new_value and new_value.id or False)
self.assertEqual(tracking.old_value_char, old_value and old_value.display_name or '')
self.assertEqual(tracking.new_value_char, new_value and new_value.display_name or '')
elif value_type in ('monetary'):
elif value_type == 'monetary':
self.assertEqual(tracking.old_value_monetary, old_value)
self.assertEqual(tracking.new_value_monetary, new_value)
else: