[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:
@@ -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
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user