diff --git a/addons/crm/controllers/main.py b/addons/crm/controllers/main.py
index 4f1a6960645..f9d12a6c236 100644
--- a/addons/crm/controllers/main.py
+++ b/addons/crm/controllers/main.py
@@ -4,6 +4,7 @@ import logging
from odoo.addons.mail.controllers.main import MailController
from odoo import http
+from odoo.http import request
_logger = logging.getLogger(__name__)
@@ -15,7 +16,7 @@ class CrmController(http.Controller):
comparison, record, redirect = MailController._check_token_and_record_or_redirect('crm.lead', int(res_id), token)
if comparison and record:
try:
- record.action_set_won()
+ record.action_set_won_rainbowman()
except Exception:
_logger.exception("Could not mark crm.lead as won")
return MailController._redirect_to_messaging()
diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py
index 48008cf577c..4827eddd040 100644
--- a/addons/crm/models/crm_lead.py
+++ b/addons/crm/models/crm_lead.py
@@ -739,6 +739,26 @@ class Lead(models.Model):
self.ensure_one()
self.action_set_won()
+ message = self._get_rainbowman_message()
+ if message:
+ return {
+ 'effect': {
+ 'fadeout': 'slow',
+ 'message': message,
+ 'img_url': '/web/image/%s/%s/image_1024' % (self.team_id.user_id._name, self.team_id.user_id.id) if self.team_id.user_id.image_1024 else '/web/static/src/img/smile.svg',
+ 'type': 'rainbow_man',
+ }
+ }
+ return True
+
+ def get_rainbowman_message(self):
+ self.ensure_one()
+ if self.stage_id.is_won:
+ return self._get_rainbowman_message()
+ return False
+
+ def _get_rainbowman_message(self):
+ message = False
if self.user_id and self.team_id and self.expected_revenue:
query = """
SELECT
@@ -763,7 +783,6 @@ class Lead(models.Model):
'team_id': self.team_id.id})
query_result = self.env.cr.dictfetchone()
- message = False
if query_result['total_won'] == 1:
message = _('Go, go, go! Congrats for your first deal.')
elif query_result['max_team_30'] == self.expected_revenue:
@@ -774,17 +793,7 @@ class Lead(models.Model):
message = _('You just beat your personal record for the past 30 days.')
elif query_result['max_user_7'] == self.expected_revenue:
message = _('You just beat your personal record for the past 7 days.')
-
- if message:
- return {
- 'effect': {
- 'fadeout': 'slow',
- 'message': message,
- 'img_url': '/web/image/%s/%s/image_1024' % (self.team_id.user_id._name, self.team_id.user_id.id) if self.team_id.user_id.image_1024 else '/web/static/src/img/smile.svg',
- 'type': 'rainbow_man',
- }
- }
- return True
+ return message
def action_schedule_meeting(self):
""" Open meeting's calendar view to schedule meeting on current opportunity.
diff --git a/addons/crm/static/src/js/crm_form.js b/addons/crm/static/src/js/crm_form.js
new file mode 100644
index 00000000000..b8f026e618f
--- /dev/null
+++ b/addons/crm/static/src/js/crm_form.js
@@ -0,0 +1,53 @@
+odoo.define("crm.crm_form", function (require) {
+ "use strict";
+
+ /**
+ * This From Controller makes sure we display a rainbowman message
+ * when the stage is won, even when we click on the statusbar.
+ * When the stage of a lead is changed and data are saved, we check
+ * if the lead is won and if a message should be displayed to the user
+ * with a rainbowman like when the user click on the button "Mark Won".
+ */
+
+ var FormController = require('web.FormController');
+ var FormView = require('web.FormView');
+ var viewRegistry = require('web.view_registry');
+
+ var CrmFormController = FormController.extend({
+ /**
+ * After data are saved we display a possible rainbowman
+ * message when the stage is won.
+ * @override
+ */
+ _applyChanges: async function (dataPointID, changes, event) {
+ var result = await this._super(...arguments);
+ if ('stage_id' in changes) {
+ const message = await this._rpc({
+ model: 'crm.lead',
+ method : 'get_rainbowman_message',
+ args: [[parseInt(event.target.res_id)]],
+ });
+ if (message) {
+ this.trigger_up('show_effect', {
+ message: message,
+ type: 'rainbow_man',
+ });
+ }
+ }
+ return result;
+ },
+ });
+
+ var CrmFormView = FormView.extend({
+ config: _.extend({}, FormView.prototype.config, {
+ Controller: CrmFormController,
+ }),
+ });
+
+ viewRegistry.add('crm_form', CrmFormView);
+
+ return {
+ CrmFormController: CrmFormController,
+ CrmFormView: CrmFormView,
+ };
+});
diff --git a/addons/crm/static/src/js/crm_kanban.js b/addons/crm/static/src/js/crm_kanban.js
new file mode 100644
index 00000000000..dbc50064c7a
--- /dev/null
+++ b/addons/crm/static/src/js/crm_kanban.js
@@ -0,0 +1,52 @@
+odoo.define('crm.crm_kanban', function (require) {
+ "use strict";
+
+ /**
+ * This Kanban Model make sure we display a rainbowman
+ * message when a lead is won after we moved it in the
+ * correct column and when it's grouped by stage_id (default).
+ */
+
+ var KanbanModel = require('web.KanbanModel');
+ var KanbanView = require('web.KanbanView');
+ var viewRegistry = require('web.view_registry');
+
+ var CrmKanbanModel = KanbanModel.extend({
+ /**
+ * Check if the kanban view is grouped by "stage_id" before checking if the lead is won
+ * and displaying a possible rainbowman message.
+ * @override
+ */
+ moveRecord: async function (recordID, groupID, parentID) {
+ var result = await this._super(...arguments);
+ if (this.localData[parentID].groupedBy[0] === this.defaultGroupedBy[0]) {
+ const message = await this._rpc({
+ model: 'crm.lead',
+ method : 'get_rainbowman_message',
+ args: [[parseInt(this.localData[recordID].res_id)]],
+ });
+ if (message) {
+ this.trigger_up('show_effect', {
+ message: message,
+ type: 'rainbow_man',
+ });
+ }
+ }
+ return result;
+ },
+ });
+
+ var CrmKanbanView = KanbanView.extend({
+ config: _.extend({}, KanbanView.prototype.config, {
+ Model: CrmKanbanModel,
+ }),
+ });
+
+ viewRegistry.add('crm_kanban', CrmKanbanView);
+
+ return {
+ CrmKanbanModel: CrmKanbanModel,
+ CrmKanbanView: CrmKanbanView,
+ };
+
+});
diff --git a/addons/crm/static/tests/crm_rainbowman_tests.js b/addons/crm/static/tests/crm_rainbowman_tests.js
new file mode 100644
index 00000000000..ff7f91967eb
--- /dev/null
+++ b/addons/crm/static/tests/crm_rainbowman_tests.js
@@ -0,0 +1,360 @@
+odoo.define('crm.form_rainbowman_tests', function (require) {
+ "use strict";
+
+ var CrmFormView = require('crm.crm_form').CrmFormView;
+ var CrmKanbanView = require('crm.crm_kanban').CrmKanbanView;
+ var testUtils = require('web.test_utils');
+ var createView = testUtils.createView;
+
+ QUnit.module('Crm Rainbowman Triggers', {
+ beforeEach: function () {
+ const format = "YYYY-MM-DD HH:mm:ss";
+ this.data = {
+ 'res.users': {
+ fields: {
+ display_name: { string: 'Name', type: 'char' },
+ },
+ records: [
+ { id: 1, name: 'Mario' },
+ { id: 2, name: 'Luigi' },
+ { id: 3, name: 'Link' },
+ { id: 4, name: 'Zelda' },
+ ],
+ },
+ 'crm.team': {
+ fields: {
+ display_name: { string: 'Name', type: 'char' },
+ member_ids: { string: 'Members', type: 'many2many', relation: 'res.users' },
+ },
+ records: [
+ { id: 1, name: 'Mushroom Kingdom', member_ids: [1, 2] },
+ { id: 2, name: 'Hyrule', member_ids: [3, 4] },
+ ],
+ },
+ 'crm.stage': {
+ fields: {
+ display_name: { string: 'Name', type: 'char' },
+ is_won: { string: 'Is won', type: 'boolean' },
+ },
+ records: [
+ { id: 1, name: 'Start' },
+ { id: 2, name: 'Middle' },
+ { id: 3, name: 'Won', is_won: true},
+ ],
+ },
+ 'crm.lead': {
+ fields: {
+ display_name: { string: 'Name', type: 'char' },
+ planned_revenue: { string: 'Revenue', type: 'float' },
+ stage_id: { string: 'Stage', type: 'many2one', relation: 'crm.stage' },
+ team_id: { string: 'Sales Team', type: 'many2one', relation: 'crm.team' },
+ user_id: { string: 'Salesperson', type: 'many2one', relation: 'res.users' },
+ date_closed: { string: 'Date closed', type: 'datetime' },
+ },
+ records : [
+ { id: 1, name: 'Lead 1', planned_revenue: 5.0, stage_id: 1, team_id: 1, user_id: 1 },
+ { id: 2, name: 'Lead 2', planned_revenue: 5.0, stage_id: 2, team_id: 2, user_id: 4 },
+ { id: 3, name: 'Lead 3', planned_revenue: 3.0, stage_id: 3, team_id: 1, user_id: 1, date_closed: moment().subtract(5, 'days').format(format) },
+ { id: 4, name: 'Lead 4', planned_revenue: 4.0, stage_id: 3, team_id: 2, user_id: 4, date_closed: moment().subtract(23, 'days').format(format) },
+ { id: 5, name: 'Lead 5', planned_revenue: 7.0, stage_id: 3, team_id: 1, user_id: 1, date_closed: moment().subtract(20, 'days').format(format) },
+ { id: 6, name: 'Lead 6', planned_revenue: 4.0, stage_id: 2, team_id: 1, user_id: 2 },
+ { id: 7, name: 'Lead 7', planned_revenue: 1.8, stage_id: 3, team_id: 2, user_id: 3, date_closed: moment().subtract(23, 'days').format(format) },
+ { id: 8, name: 'Lead 8', planned_revenue: 1.9, stage_id: 1, team_id: 2, user_id: 3 },
+ { id: 9, name: 'Lead 9', planned_revenue: 1.5, stage_id: 3, team_id: 2, user_id: 3, date_closed: moment().subtract(5, 'days').format(format) },
+ { id: 10, name: 'Lead 10', planned_revenue: 1.7, stage_id: 2, team_id: 2, user_id: 3 },
+ { id: 11, name: 'Lead 11', planned_revenue: 2.0, stage_id: 3, team_id: 2, user_id: 4, date_closed: moment().subtract(5, 'days').format(format) },
+ ],
+ },
+ };
+ this.testFormView = {
+ arch: `
+
`,
+ data: this.data,
+ model: 'crm.lead',
+ View: CrmFormView,
+ };
+ this.testKanbanView = {
+ arch: `
+
+
+
+
+
+
+ `,
+ data: this.data,
+ model: 'crm.lead',
+ View: CrmKanbanView,
+ groupBy: ['stage_id'],
+ };
+ },
+ }, function () {
+ QUnit.test("first lead won, click on statusbar", async function (assert) {
+ assert.expect(2);
+
+ this.testFormView.res_id = 6;
+ this.testFormView.mockRPC = async function (route, args) {
+ const result = await this._super(...arguments);
+ if (args.model === 'crm.lead' && args.method === 'get_rainbowman_message') {
+ assert.step(result || "no rainbowman");
+ }
+ return result;
+ };
+ const form = await createView(this.testFormView);
+
+ await testUtils.dom.click(form.$(".o_statusbar_status button[data-value='3']"));
+ assert.verifySteps(['Go, go, go! Congrats for your first deal.']);
+
+ form.destroy();
+ });
+
+ QUnit.test("team record 30 days, click on statusbar", async function (assert) {
+ assert.expect(2);
+
+ this.testFormView.res_id = 2;
+ this.testFormView.mockRPC = async function (route, args) {
+ const result = await this._super(...arguments);
+ if (args.model === 'crm.lead' && args.method === 'get_rainbowman_message') {
+ assert.step(result || "no rainbowman");
+ }
+ return result;
+ };
+ const form = await createView(this.testFormView);
+
+ await testUtils.dom.click(form.$(".o_statusbar_status button[data-value='3']"));
+ assert.verifySteps(['Boom! Team record for the past 30 days.']);
+
+ form.destroy();
+ });
+
+ QUnit.test("team record 7 days, click on statusbar", async function (assert) {
+ assert.expect(2);
+
+ this.testFormView.res_id = 1;
+ this.testFormView.mockRPC = async function (route, args) {
+ const result = await this._super(...arguments);
+ if (args.model === 'crm.lead' && args.method === 'get_rainbowman_message') {
+ assert.step(result || "no rainbowman");
+ }
+ return result;
+ };
+ const form = await createView(this.testFormView);
+
+ await testUtils.dom.click(form.$(".o_statusbar_status button[data-value='3']"));
+ assert.verifySteps(['Yeah! Deal of the last 7 days for the team.']);
+
+ form.destroy();
+ });
+
+ QUnit.test("user record 30 days, click on statusbar", async function (assert) {
+ assert.expect(2);
+
+ this.testFormView.res_id = 8;
+ this.testFormView.mockRPC = async function (route, args) {
+ const result = await this._super(...arguments);
+ if (args.model === 'crm.lead' && args.method === 'get_rainbowman_message') {
+ assert.step(result || "no rainbowman");
+ }
+ return result;
+ };
+ const form = await createView(this.testFormView);
+
+ await testUtils.dom.click(form.$(".o_statusbar_status button[data-value='3']"));
+ assert.verifySteps(['You just beat your personal record for the past 30 days.']);
+
+ form.destroy();
+ });
+
+ QUnit.test("user record 7 days, click on statusbar", async function (assert) {
+ assert.expect(2);
+
+ this.testFormView.res_id = 10;
+ this.testFormView.mockRPC = async function (route, args) {
+ const result = await this._super(...arguments);
+ if (args.model === 'crm.lead' && args.method === 'get_rainbowman_message') {
+ assert.step(result || "no rainbowman");
+ }
+ return result;
+ };
+ const form = await createView(this.testFormView);
+
+ await testUtils.dom.click(form.$(".o_statusbar_status button[data-value='3']"));
+ assert.verifySteps(['You just beat your personal record for the past 7 days.']);
+
+ form.destroy();
+ });
+
+ QUnit.test("click on stage (not won) on statusbar", async function (assert) {
+ assert.expect(2);
+
+ this.testFormView.res_id = 1;
+ this.testFormView.mockRPC = async function (route, args) {
+ const result = await this._super(...arguments);
+ if (args.model === 'crm.lead' && args.method === 'get_rainbowman_message') {
+ assert.step(result || "no rainbowman");
+ }
+ return result;
+ };
+ const form = await createView(this.testFormView);
+
+ await testUtils.dom.click(form.$(".o_statusbar_status button[data-value='2']"));
+ assert.verifySteps(['no rainbowman']);
+
+ form.destroy();
+ });
+
+ QUnit.test("first lead won, drag & drop kanban", async function (assert) {
+ assert.expect(2);
+
+ this.testKanbanView.mockRPC = async function (route, args) {
+ const result = await this._super(...arguments);
+ if (args.model === 'crm.lead' && args.method === 'get_rainbowman_message') {
+ assert.step(result || "no rainbowman");
+ }
+ return result;
+ };
+ const kanban = await createView(this.testKanbanView);
+
+ kanban.model.defaultGroupedBy = ['stage_id'];
+ await kanban.reload();
+
+ await testUtils.dom.dragAndDrop(kanban.$('.o_kanban_record:contains("Lead 6")'), kanban.$('.o_kanban_group:eq(2)'));
+ assert.verifySteps(['Go, go, go! Congrats for your first deal.']);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("team record 30 days, drag & drop kanban", async function (assert) {
+ assert.expect(2);
+
+ this.testKanbanView.mockRPC = async function (route, args) {
+ const result = await this._super(...arguments);
+ if (args.model === 'crm.lead' && args.method === 'get_rainbowman_message') {
+ assert.step(result || "no rainbowman");
+ }
+ return result;
+ };
+ const kanban = await createView(this.testKanbanView);
+
+ kanban.model.defaultGroupedBy = ['stage_id'];
+ await kanban.reload();
+
+ await testUtils.dom.dragAndDrop(kanban.$('.o_kanban_record:contains("Lead 2")'), kanban.$('.o_kanban_group:eq(2)'));
+ assert.verifySteps(['Boom! Team record for the past 30 days.']);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("team record 7 days, drag & drop kanban", async function (assert) {
+ assert.expect(2);
+
+ this.testKanbanView.mockRPC = async function (route, args) {
+ const result = await this._super(...arguments);
+ if (args.model === 'crm.lead' && args.method === 'get_rainbowman_message') {
+ assert.step(result || "no rainbowman");
+ }
+ return result;
+ };
+ const kanban = await createView(this.testKanbanView);
+
+ kanban.model.defaultGroupedBy = ['stage_id'];
+ await kanban.reload();
+
+ await testUtils.dom.dragAndDrop(kanban.$('.o_kanban_group:eq(0) .o_kanban_record:contains("Lead 1")'), kanban.$('.o_kanban_group:eq(2)'));
+ assert.verifySteps(['Yeah! Deal of the last 7 days for the team.']);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("user record 30 days, drag & drop kanban", async function (assert) {
+ assert.expect(2);
+
+ this.testKanbanView.mockRPC = async function (route, args) {
+ const result = await this._super(...arguments);
+ if (args.model === 'crm.lead' && args.method === 'get_rainbowman_message') {
+ assert.step(result || "no rainbowman");
+ }
+ return result;
+ };
+ const kanban = await createView(this.testKanbanView);
+
+ kanban.model.defaultGroupedBy = ['stage_id'];
+ await kanban.reload();
+
+ await testUtils.dom.dragAndDrop(kanban.$('.o_kanban_record:contains("Lead 8")'), kanban.$('.o_kanban_group:eq(2)'));
+ assert.verifySteps(['You just beat your personal record for the past 30 days.']);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("user record 7 days, drag & drop kanban", async function (assert) {
+ assert.expect(2);
+
+ this.testKanbanView.mockRPC = async function (route, args) {
+ const result = await this._super(...arguments);
+ if (args.model === 'crm.lead' && args.method === 'get_rainbowman_message') {
+ assert.step(result || "no rainbowman");
+ }
+ return result;
+ };
+ const kanban = await createView(this.testKanbanView);
+
+ kanban.model.defaultGroupedBy = ['stage_id'];
+ await kanban.reload();
+
+ await testUtils.dom.dragAndDrop(kanban.$('.o_kanban_record:contains("Lead 10")'), kanban.$('.o_kanban_group:eq(2)'));
+ assert.verifySteps(['You just beat your personal record for the past 7 days.']);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("drag & drop record kanban in stage not won", async function (assert) {
+ assert.expect(2);
+
+ this.testKanbanView.mockRPC = async function (route, args) {
+ const result = await this._super(...arguments);
+ if (args.model === 'crm.lead' && args.method === 'get_rainbowman_message') {
+ assert.step(result || "no rainbowman");
+ }
+ return result;
+ };
+ const kanban = await createView(this.testKanbanView);
+
+ kanban.model.defaultGroupedBy = ['stage_id'];
+ await kanban.reload();
+
+ await testUtils.dom.dragAndDrop(kanban.$('.o_kanban_record:contains("Lead 8")'), kanban.$('.o_kanban_group:eq(1)'));
+ assert.verifySteps(["no rainbowman"]);
+
+ kanban.destroy();
+ });
+
+ QUnit.test("drag & drop record in kanban not grouped by stage_id", async function (assert) {
+ assert.expect(1);
+
+ this.testKanbanView.mockRPC = async function (route, args) {
+ const result = await this._super(...arguments);
+ if (args.model === 'crm.lead' && args.method === 'get_rainbowman_message') {
+ assert.step(result || "no rainbowman");
+ }
+ return result;
+ };
+ this.testKanbanView.groupBy = ['user_id'];
+ const kanban = await createView(this.testKanbanView);
+
+ kanban.model.defaultGroupedBy = ['stage_id'];
+ await kanban.reload();
+
+ await testUtils.dom.dragAndDrop(kanban.$('.o_kanban_group:eq(0) .o_kanban_record:first'), kanban.$('.o_kanban_group:eq(1)'));
+ assert.verifySteps([]); // Should never pass by the rpc
+
+ kanban.destroy();
+ });
+ });
+});
diff --git a/addons/crm/static/tests/mock_server.js b/addons/crm/static/tests/mock_server.js
new file mode 100644
index 00000000000..0c6c0fbb64e
--- /dev/null
+++ b/addons/crm/static/tests/mock_server.js
@@ -0,0 +1,56 @@
+odoo.define('crm.MockServer', function (require) {
+ 'use strict';
+
+ var MockServer = require('web.MockServer');
+
+ MockServer.include({
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ * @private
+ */
+ async _performRpc(route, args) {
+ if (args.model === 'crm.lead' && args.method === 'get_rainbowman_message') {
+ let message = false;
+ const records = this.data['crm.lead'].records;
+ const record = records.find(r => r.id === args.args[0][0]);
+ const won_stage = this.data['crm.stage'].records.find(s => s.is_won);
+ if (record.stage_id === won_stage.id && record.user_id && record.team_id && record.planned_revenue > 0) {
+ const now = moment();
+ let query_result = {};
+ // Total won
+ query_result['total_won'] = records.filter(r => r.stage_id === won_stage.id && r.user_id === record.user_id).length;
+ // Max team 30 days
+ const recordsTeam30 = records.filter(r => r.stage_id === won_stage.id && r.team_id === record.team_id && (!r.date_closed || moment.duration(now.diff(moment(r.date_closed))).days() <= 30));
+ query_result['max_team_30'] = Math.max(...recordsTeam30.map(r => r.planned_revenue));
+ // Max team 7 days
+ const recordsTeam7 = records.filter(r => r.stage_id === won_stage.id && r.team_id === record.team_id && (!r.date_closed || moment.duration(now.diff(moment(r.date_closed))).days() <= 7));
+ query_result['max_team_7'] = Math.max(...recordsTeam7.map(r => r.planned_revenue));
+ // Max User 30 days
+ const recordsUser30 = records.filter(r => r.stage_id === won_stage.id && r.user_id === record.user_id && (!r.date_closed || moment.duration(now.diff(moment(r.date_closed))).days() <= 30));
+ query_result['max_user_30'] = Math.max(...recordsUser30.map(r => r.planned_revenue));
+ // Max User 7 days
+ const recordsUser7 = records.filter(r => r.stage_id === won_stage.id && r.user_id === record.user_id && (!r.date_closed || moment.duration(now.diff(moment(r.date_closed))).days() <= 7));
+ query_result['max_user_7'] = Math.max(...recordsUser7.map(r => r.planned_revenue));
+
+ if (query_result.total_won === 1) {
+ message = "Go, go, go! Congrats for your first deal.";
+ } else if (query_result.max_team_30 === record.planned_revenue) {
+ message = "Boom! Team record for the past 30 days.";
+ } else if (query_result.max_team_7 === record.planned_revenue) {
+ message = "Yeah! Deal of the last 7 days for the team.";
+ } else if (query_result.max_user_30 === record.planned_revenue) {
+ message = "You just beat your personal record for the past 30 days.";
+ } else if (query_result.max_user_7 === record.planned_revenue) {
+ message = "You just beat your personal record for the past 7 days.";
+ }
+ }
+ return message;
+ }
+ return this._super(...arguments);
+ },
+ });
+});
diff --git a/addons/crm/static/tests/tours/crm_rainbowman.js b/addons/crm/static/tests/tours/crm_rainbowman.js
new file mode 100644
index 00000000000..67c488b816e
--- /dev/null
+++ b/addons/crm/static/tests/tours/crm_rainbowman.js
@@ -0,0 +1,66 @@
+odoo.define('crm.tour_crm_rainbowman', function (require) {
+ "use strict";
+
+ var tour = require('web_tour.tour');
+
+ tour.register('crm_rainbowman', {
+ test: true,
+ url: "/web",
+ }, [
+ tour.stepUtils.showAppsMenuItem(),
+ {
+ trigger: ".o_app[data-menu-xmlid='crm.crm_menu_root']",
+ content: "open crm app",
+ }, {
+ trigger: ".o-kanban-button-new",
+ content: "click create",
+ }, {
+ trigger: "input[name=name]",
+ content: "complete name",
+ run: "text Test Lead 1",
+ }, {
+ trigger: "div[name=planned_revenue] > input",
+ content: "complete planned revenue",
+ run: "text 999999997",
+ }, {
+ trigger: "button.o_kanban_add",
+ content: "create lead",
+ }, {
+ trigger: ".o_kanban_record .o_kanban_record_title:contains('Test Lead 1')",
+ content: "move to won stage",
+ run: "drag_and_drop .o_opportunity_kanban .o_kanban_group:eq(3) "
+ }, {
+ trigger: ".o-kanban-button-new",
+ extra_trigger: ".o_reward_rainbow",
+ content: "click create",
+ }, {
+ trigger: "input[name=name]",
+ content: "complete name",
+ run: "text Test Lead 2",
+ }, {
+ trigger: "div[name=planned_revenue] > input",
+ content: "complete planned revenue",
+ run: "text 999999998",
+ }, {
+ trigger: "button.o_kanban_add",
+ content: "create lead",
+ }, {
+ trigger: ".o_kanban_record .o_kanban_record_title:contains('Test Lead 2')",
+ content: "click on lead",
+ }, {
+ trigger: ".o_statusbar_status button[data-value='4']",
+ content: "move lead to won stage",
+ }, {
+ trigger: ".o_statusbar_status button[data-value='1']",
+ extra_trigger: ".o_reward_rainbow",
+ content: "move lead to previous stage & rainbowman appears",
+ }, {
+ trigger: "button[name=action_set_won_rainbowman]",
+ content: "click button mark won",
+ }, {
+ trigger: ".o_menu_brand",
+ extra_trigger: ".o_reward_rainbow",
+ content: "last rainbowman appears",
+ }
+ ]);
+});
diff --git a/addons/crm/views/assets.xml b/addons/crm/views/assets.xml
index e3edab1eee4..1ffed45b2a8 100644
--- a/addons/crm/views/assets.xml
+++ b/addons/crm/views/assets.xml
@@ -2,8 +2,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/crm/views/crm_lead_views.xml b/addons/crm/views/crm_lead_views.xml
index cdc95c11ada..2c3357d1d47 100644
--- a/addons/crm/views/crm_lead_views.xml
+++ b/addons/crm/views/crm_lead_views.xml
@@ -4,7 +4,7 @@
crm.lead.form
crm.lead
-
-
+
diff --git a/addons/crm_iap_lead/static/src/js/leads_tree_generate_leads.js b/addons/crm_iap_lead/static/src/js/leads_tree_generate_leads.js
index e60d8deb9a9..7db2d99e1ab 100644
--- a/addons/crm_iap_lead/static/src/js/leads_tree_generate_leads.js
+++ b/addons/crm_iap_lead/static/src/js/leads_tree_generate_leads.js
@@ -4,7 +4,7 @@ odoo.define('crm.leads.tree', function (require) {
var ListView = require('web.ListView');
var KanbanController = require('web.KanbanController');
- var KanbanView = require('web.KanbanView');
+ var KanbanView = require('crm.crm_kanban').CrmKanbanView;
var viewRegistry = require('web.view_registry');