diff --git a/addons/crm_livechat/__manifest__.py b/addons/crm_livechat/__manifest__.py
index d43908c47ea..91ee112bc14 100644
--- a/addons/crm_livechat/__manifest__.py
+++ b/addons/crm_livechat/__manifest__.py
@@ -7,6 +7,9 @@
'summary': 'Create lead from livechat conversation',
'data': [
'data/utm_data.xml',
+ 'data/crm_livechat_chatbot_data.xml',
+ 'views/chatbot_script_views.xml',
+ 'views/chatbot_script_step_views.xml',
],
'depends': [
'crm',
diff --git a/addons/crm_livechat/data/crm_livechat_chatbot_data.xml b/addons/crm_livechat/data/crm_livechat_chatbot_data.xml
new file mode 100644
index 00000000000..a47411dd0ff
--- /dev/null
+++ b/addons/crm_livechat/data/crm_livechat_chatbot_data.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+ Lead Generation Bot
+
+
+
+
+ Hi there, what brings you to our website today? 👋
+ 1
+ free_input_multi
+
+
+
+
+ 2
+ forward_operator
+
+
+
+
+ Hu-ho, it looks like none of our operators are available 🙁
+ 3
+ text
+
+
+
+
+ Would you mind leaving your email address so that we can reach you back?
+ 4
+ question_email
+
+
+
+
+ Thank you, you should hear back from us very soon!
+ 5
+ create_lead
+
+
+
+
diff --git a/addons/crm_livechat/models/__init__.py b/addons/crm_livechat/models/__init__.py
index 58536ecc3e7..c1a45883018 100644
--- a/addons/crm_livechat/models/__init__.py
+++ b/addons/crm_livechat/models/__init__.py
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from . import chatbot_script
+from . import chatbot_script_step
from . import mail_channel
diff --git a/addons/crm_livechat/models/chatbot_script.py b/addons/crm_livechat/models/chatbot_script.py
new file mode 100644
index 00000000000..8fff92a94dd
--- /dev/null
+++ b/addons/crm_livechat/models/chatbot_script.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models, fields
+
+
+class ChatbotScript(models.Model):
+ _inherit = 'chatbot.script'
+
+ lead_count = fields.Integer(
+ string='Generated Lead Count', compute='_compute_lead_count')
+
+ def _compute_lead_count(self):
+ mapped_leads = {}
+ if self.ids:
+ leads_data = self.env['crm.lead'].with_context(active_test=False).sudo()._read_group(
+ [('source_id', 'in', self.mapped('source_id').ids)], ['source_id'], ['source_id'])
+ mapped_leads = {lead['source_id'][0]: lead['source_id_count'] for lead in leads_data}
+
+ for script in self:
+ script.lead_count = mapped_leads.get(script.source_id.id, 0)
+
+ def action_view_leads(self):
+ self.ensure_one()
+ action = self.env['ir.actions.act_window']._for_xml_id('crm.crm_lead_all_leads')
+ action['domain'] = [('source_id', '=', self.source_id.id)]
+ action['context'] = {'create': False}
+ return action
diff --git a/addons/crm_livechat/models/chatbot_script_step.py b/addons/crm_livechat/models/chatbot_script_step.py
new file mode 100644
index 00000000000..0c9e21cecc6
--- /dev/null
+++ b/addons/crm_livechat/models/chatbot_script_step.py
@@ -0,0 +1,65 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import _, models, fields
+
+
+class ChatbotScriptStep(models.Model):
+ _inherit = 'chatbot.script.step'
+
+ step_type = fields.Selection(
+ selection_add=[('create_lead', 'Create Lead')], ondelete={'create_lead': 'cascade'})
+ crm_team_id = fields.Many2one(
+ 'crm.team', string='Sales Team', ondelete='set null',
+ help="Used in combination with 'create_lead' step type in order to automatically "
+ "assign the created lead/opportunity to the defined team")
+
+ def _chatbot_crm_prepare_lead_values(self, mail_channel, description):
+ return {
+ 'description': description + mail_channel._get_channel_history(),
+ 'name': _("%s's New Lead", self.chatbot_script_id.title),
+ 'source_id': self.chatbot_script_id.source_id.id,
+ 'team_id': self.crm_team_id.id,
+ 'type': 'lead' if self.crm_team_id.use_leads else 'opportunity',
+ 'user_id': False,
+ }
+
+ def _process_step(self, mail_channel):
+ self.ensure_one()
+
+ posted_message = super()._process_step(mail_channel)
+
+ if self.step_type == 'create_lead':
+ self._process_step_create_lead(mail_channel)
+
+ return posted_message
+
+ def _process_step_create_lead(self, mail_channel):
+ """ When reaching a 'create_lead' step, we extract the relevant information: visitor's
+ email, phone and conversation history to create a crm.lead.
+
+ We use the email and phone to update the environment partner's information (if not a public
+ user) if they differ from the current values.
+
+ The whole conversation history will be saved into the lead's description for reference.
+ This also allows having a question of type 'free_input_multi' to let the visitor explain
+ his interest / needs before creating the lead. """
+
+ customer_values = self._chatbot_prepare_customer_values(
+ mail_channel, create_partner=False, update_partner=True)
+ if self.env.user._is_public():
+ create_values = {
+ 'email_from': customer_values['email'],
+ 'phone': customer_values['phone'],
+ }
+ else:
+ partner = self.env.user.partner_id
+ create_values = {
+ 'partner_id': partner.id,
+ 'company_id': partner.company_id.id,
+ }
+
+ create_values.update(self._chatbot_crm_prepare_lead_values(
+ mail_channel, customer_values['description']))
+
+ self.env['crm.lead'].create(create_values)
diff --git a/addons/crm_livechat/models/mail_channel.py b/addons/crm_livechat/models/mail_channel.py
index 4c5e6ef806f..51d3e905d9a 100644
--- a/addons/crm_livechat/models/mail_channel.py
+++ b/addons/crm_livechat/models/mail_channel.py
@@ -26,10 +26,6 @@ class MailChannel(models.Model):
:param partner: internal user partner (operator) that created the lead;
:param key: operator input in chat ('/lead Lead about Product')
"""
- description = ''.join(
- '%s: %s
' % (message.author_id.name or self.anonymous_name, html2plaintext(message.body))
- for message in self.message_ids.sorted('id')
- ) # converting message body back to plaintext for correct data formating in description
# if public user is part of the chat: consider lead to be linked to an
# anonymous user whatever the participants. Otherwise keep only share
# partners (no user or portal user) to link to the lead.
@@ -47,7 +43,7 @@ class MailChannel(models.Model):
'partner_id': customers[0].id if customers else False,
'user_id': False,
'team_id': False,
- 'description': description,
+ 'description': self._get_channel_history(),
'referred': partner.name,
'source_id': utm_source and utm_source.id,
})
diff --git a/addons/crm_livechat/tests/__init__.py b/addons/crm_livechat/tests/__init__.py
index 3721104aa97..9219bc543f7 100644
--- a/addons/crm_livechat/tests/__init__.py
+++ b/addons/crm_livechat/tests/__init__.py
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from . import chatbot_common
+from . import test_chatbot_lead
from . import test_crm_lead
diff --git a/addons/crm_livechat/tests/chatbot_common.py b/addons/crm_livechat/tests/chatbot_common.py
new file mode 100644
index 00000000000..9fc0ced2cda
--- /dev/null
+++ b/addons/crm_livechat/tests/chatbot_common.py
@@ -0,0 +1,66 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.im_livechat.tests.chatbot_common import ChatbotCase
+from odoo.addons.mail.tests.common import mail_new_test_user
+
+
+class CrmChatbotCase(ChatbotCase):
+
+ @classmethod
+ def setUpClass(cls):
+ super(CrmChatbotCase, cls).setUpClass()
+
+ cls.company_id = cls.env['res.company'].create({
+ 'name': 'Test Company',
+ 'country_id': cls.env.ref('base.be').id,
+ })
+
+ cls.user_public = mail_new_test_user(
+ cls.env, login='user_public', groups='base.group_public', name='Public User')
+ cls.user_portal = mail_new_test_user(
+ cls.env, login='user_portal', groups='base.group_portal', name='Portal User',
+ company_id=cls.company_id.id, email='portal@example.com')
+ # update company_id on partner since the user's company is not propagated
+ cls.user_portal.partner_id.write({'company_id': cls.company_id.id})
+
+ cls.sale_team = cls.env['crm.team'].create({
+ 'name': 'Test Sale Team 1',
+ 'company_id': cls.company_id.id,
+ })
+
+ cls.sale_team_with_lead = cls.env['crm.team'].create({
+ 'name': 'Test Sale Team 2',
+ 'use_leads': True,
+ 'company_id': cls.company_id.id,
+ })
+
+ cls.step_dispatch_create_lead = cls.env['chatbot.script.answer'].sudo().create({
+ 'name': 'Create a lead',
+ 'script_step_id': cls.step_dispatch.id,
+ })
+
+ [
+ cls.step_create_lead_email,
+ cls.step_create_lead_phone,
+ cls.step_create_lead,
+ ] = cls.env['chatbot.script.step'].sudo().create([{
+ 'step_type': 'question_email',
+ 'message': 'Could you provide us your email please.',
+ 'sequence': 20,
+ 'triggering_answer_ids': [(4, cls.step_dispatch_create_lead.id)],
+ 'chatbot_script_id': cls.chatbot_script.id,
+ }, {
+ 'step_type': 'question_phone',
+ 'message': 'Could you also provide your phone please.',
+ 'sequence': 21,
+ 'triggering_answer_ids': [(4, cls.step_dispatch_create_lead.id)],
+ 'chatbot_script_id': cls.chatbot_script.id,
+ }, {
+ 'step_type': 'create_lead',
+ 'message': 'Thank you! A lead has been created.',
+ 'sequence': 22,
+ 'triggering_answer_ids': [(4, cls.step_dispatch_create_lead.id)],
+ 'crm_team_id': cls.sale_team.id,
+ 'chatbot_script_id': cls.chatbot_script.id,
+ }])
diff --git a/addons/crm_livechat/tests/test_chatbot_lead.py b/addons/crm_livechat/tests/test_chatbot_lead.py
new file mode 100644
index 00000000000..35e7a1b3c2a
--- /dev/null
+++ b/addons/crm_livechat/tests/test_chatbot_lead.py
@@ -0,0 +1,51 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo.addons.crm_livechat.tests import chatbot_common
+from odoo.tests.common import users
+
+
+class CrmChatbotCase(chatbot_common.CrmChatbotCase):
+
+ @users('user_public')
+ def test_chatbot_lead_public_user(self):
+ self._chatbot_create_lead(self.user_public)
+
+ created_lead = self.env['crm.lead'].sudo().search([], limit=1, order='id desc')
+ self.assertEqual(created_lead.name, "Testing Bot's New Lead")
+ self.assertEqual(created_lead.email_from, 'test2@example.com')
+ self.assertEqual(created_lead.phone, '123456')
+
+ self.assertEqual(created_lead.team_id, self.sale_team)
+ self.assertEqual(created_lead.type, 'opportunity')
+
+ @users('user_portal')
+ def test_chatbot_lead_portal_user(self):
+ self.step_create_lead.write({'crm_team_id': self.sale_team_with_lead})
+ self._chatbot_create_lead(self.user_portal)
+
+ created_lead = self.env['crm.lead'].sudo().search([], limit=1, order='id desc')
+ self.assertEqual(created_lead.name, "Testing Bot's New Lead")
+ self.assertNotEqual(created_lead.email_from, 'test2@example.com', "User's email should'nt have been overridden")
+ self.assertEqual(created_lead.phone, '123456', "User's phone should have been updated")
+
+ self.assertEqual(created_lead.team_id, self.sale_team_with_lead)
+ self.assertEqual(created_lead.type, 'lead')
+
+ def _chatbot_create_lead(self, user):
+ channel_info = self.livechat_channel._open_livechat_mail_channel(
+ anonymous_name='Test Visitor', chatbot_script=self.chatbot_script, user_id=user.id)
+ mail_channel = self.env['mail.channel'].sudo().browse(channel_info['id'])
+
+ self._post_answer_and_trigger_next_step(
+ mail_channel,
+ self.step_dispatch_create_lead.name,
+ chatbot_script_answer=self.step_dispatch_create_lead
+ )
+ self.assertEqual(mail_channel.chatbot_current_step_id, self.step_create_lead_email)
+ self._post_answer_and_trigger_next_step(mail_channel, 'test2@example.com')
+
+ self.assertEqual(mail_channel.chatbot_current_step_id, self.step_create_lead_phone)
+ self._post_answer_and_trigger_next_step(mail_channel, '123456')
+
+ self.assertEqual(mail_channel.chatbot_current_step_id, self.step_create_lead)
diff --git a/addons/crm_livechat/views/chatbot_script_step_views.xml b/addons/crm_livechat/views/chatbot_script_step_views.xml
new file mode 100644
index 00000000000..adfebca1eee
--- /dev/null
+++ b/addons/crm_livechat/views/chatbot_script_step_views.xml
@@ -0,0 +1,15 @@
+
+
+
+
+ chatbot.script.step.view.form.inherit.crm.livechat
+ chatbot.script.step
+
+
+
+
+
+
+
+
+
diff --git a/addons/crm_livechat/views/chatbot_script_views.xml b/addons/crm_livechat/views/chatbot_script_views.xml
new file mode 100644
index 00000000000..5f556ac9232
--- /dev/null
+++ b/addons/crm_livechat/views/chatbot_script_views.xml
@@ -0,0 +1,18 @@
+
+
+
+
+ chatbot.script.view.form.inherit.crm.livechat
+ chatbot.script
+
+
+
+
+
+
+
+
+
diff --git a/addons/website_crm_livechat/models/__init__.py b/addons/website_crm_livechat/models/__init__.py
index e81172ec3dd..61d3b3b91e7 100644
--- a/addons/website_crm_livechat/models/__init__.py
+++ b/addons/website_crm_livechat/models/__init__.py
@@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
+from . import chatbot_script_step
from . import crm_lead
from . import mail_channel
diff --git a/addons/website_crm_livechat/models/chatbot_script_step.py b/addons/website_crm_livechat/models/chatbot_script_step.py
new file mode 100644
index 00000000000..27a23f7d49b
--- /dev/null
+++ b/addons/website_crm_livechat/models/chatbot_script_step.py
@@ -0,0 +1,16 @@
+
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import _, models
+
+
+class ChatbotScriptStep(models.Model):
+ _inherit = 'chatbot.script.step'
+
+ def _chatbot_crm_prepare_lead_values(self, mail_channel, description):
+ values = super()._chatbot_crm_prepare_lead_values(mail_channel, description)
+ if mail_channel.livechat_visitor_id:
+ values['name'] = _("%s's New Lead", mail_channel.livechat_visitor_id.display_name)
+ values['visitor_ids'] = [(4, mail_channel.livechat_visitor_id.id)]
+ return values