From bf3942b553fe45e9638e0ea2daacc652bf8b8476 Mon Sep 17 00:00:00 2001 From: Horacio Tellez Date: Mon, 13 Jun 2022 14:13:31 +0000 Subject: [PATCH] [IMP] sale: progressbar js logic migration to owl framework This PR is part of the whole migration of the sale javascript code and related code to the owl framework. Why: better developmet framework for javascript code in odoo. This PR in particular targets the progressbar of sales team appearing on the CRM app. Task - 2918635 closes odoo/odoo#95565 Signed-off-by: Victor Feyens (vfe) --- addons/sale/__manifest__.py | 5 +- addons/sale/static/src/js/sale.js | 34 ---- .../static/src/js/sale_progressbar_field.js | 45 +++++ .../xml/sales_team_progress_bar_template.xml | 16 ++ .../tests/sales_team_dashboard_tests.js | 177 ++++++++---------- .../progress_bar/kanban_progress_bar_field.js | 2 +- 6 files changed, 141 insertions(+), 138 deletions(-) delete mode 100644 addons/sale/static/src/js/sale.js create mode 100644 addons/sale/static/src/js/sale_progressbar_field.js create mode 100644 addons/sale/static/src/xml/sales_team_progress_bar_template.xml diff --git a/addons/sale/__manifest__.py b/addons/sale/__manifest__.py index bd902063075..8026aeacf9b 100644 --- a/addons/sale/__manifest__.py +++ b/addons/sale/__manifest__.py @@ -60,7 +60,7 @@ This module contains all the common features of Sales Management and eCommerce. 'web.assets_backend': [ 'sale/static/src/scss/sale_onboarding.scss', 'sale/static/src/scss/product_configurator.scss', - 'sale/static/src/js/sale.js', + 'sale/static/src/js/sale_progressbar_field.js', 'sale/static/src/js/tours/sale.js', 'sale/static/src/js/sale_order_line_mixin.js', 'sale/static/src/js/product_configurator_widget.js', @@ -83,6 +83,9 @@ This module contains all the common features of Sales Management and eCommerce. 'web.report_assets_common': [ 'sale/static/src/scss/sale_report.scss', ], + 'web.assets_qweb': [ + 'sale/static/src/xml/**/*', + ], }, 'post_init_hook': '_synchronize_cron', 'license': 'LGPL-3', diff --git a/addons/sale/static/src/js/sale.js b/addons/sale/static/src/js/sale.js deleted file mode 100644 index e46e368f93b..00000000000 --- a/addons/sale/static/src/js/sale.js +++ /dev/null @@ -1,34 +0,0 @@ -odoo.define('sale.sales_team_dashboard', function (require) { -"use strict"; - -const { FieldProgressBar } = require('web.basic_fields'); -const fieldRegistry = require('web.field_registry'); - -var core = require('web.core'); -var _t = core._t; - -const SalesTeamProgressBar = FieldProgressBar.extend({ - _render() { - this._super.apply(this, arguments); - const isUnset = !this.recordData[this.nodeOptions.max_value]; - if (isUnset) { - const msg = document.createElement('a'); - msg.innerText = _t("Click to define an invoicing target"); - msg.setAttribute('href', '#') - msg.addEventListener("click", (ev) => { - ev.preventDefault(); - msg.parentElement.removeChild(msg); - for (let child of this.el.children) { - child.classList.remove('d-none') - } - }); - for (let child of this.el.children) { - child.classList.add('d-none'); - } - this.el.insertBefore(msg, this.el.firstChild) - } - }, -}); - -fieldRegistry.add("sales_team_progressbar", SalesTeamProgressBar); -}); diff --git a/addons/sale/static/src/js/sale_progressbar_field.js b/addons/sale/static/src/js/sale_progressbar_field.js new file mode 100644 index 00000000000..49491c3e4c6 --- /dev/null +++ b/addons/sale/static/src/js/sale_progressbar_field.js @@ -0,0 +1,45 @@ +/** @odoo-module **/ + +import { registry } from "@web/core/registry"; +import { useService } from "@web/core/utils/hooks"; +import { KanbanProgressBarField } from "@web/views/fields/progress_bar/kanban_progress_bar_field"; + +const { useEffect } = owl; + +/** + * A custom Component for the view of sales teams on the kanban view in the CRM app. + * + * The wanted behavior is to show a progress bar when an invoicing target is defined or show + * a link redirecting to the record's form view otherwise. + */ +export class SaleProgressBarField extends KanbanProgressBarField { + /** + * Anything used by the component is defined on the setup method. + */ + setup() { + super.setup(); + + this.actionService = useService("action"); + this.orm = useService("orm"); + + useEffect(() => { + this.state.isInvoicingTargetDefined = this.props.record.data[this.props.maxValueField]; + }); + } + + /** + * Display the form view of the record on click. + */ + async defineInvoicingTarget() { + const { resId, resModel } = this.props.record; + const action = await this.orm.call(resModel, "get_formview_action", [[resId]]); + this.actionService.doAction(action, { props: { mode: "edit" } }); + } +} + +/** + * Define the template name used on the component. + */ +SaleProgressBarField.template = "sale.SaleProgressBarField"; + +registry.category("fields").add("sales_team_progressbar", SaleProgressBarField); diff --git a/addons/sale/static/src/xml/sales_team_progress_bar_template.xml b/addons/sale/static/src/xml/sales_team_progress_bar_template.xml new file mode 100644 index 00000000000..5d049e9fee9 --- /dev/null +++ b/addons/sale/static/src/xml/sales_team_progress_bar_template.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + Click to define an invoicing target + + + + + diff --git a/addons/sale/static/tests/sales_team_dashboard_tests.js b/addons/sale/static/tests/sales_team_dashboard_tests.js index dadea52f230..ca97efd57c8 100644 --- a/addons/sale/static/tests/sales_team_dashboard_tests.js +++ b/addons/sale/static/tests/sales_team_dashboard_tests.js @@ -1,117 +1,90 @@ -odoo.define('sale.dashboard_tests', function (require) { -"use strict"; +/** @odoo-module **/ -var KanbanView = require('web.KanbanView'); -var testUtils = require('web.test_utils'); +import { click, getFixture } from "@web/../tests/helpers/utils"; +import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; +import { registry } from "@web/core/registry"; -var createView = testUtils.createView; +const serviceRegistry = registry.category("services"); -QUnit.module('Sales Team Dashboard', { - beforeEach: function () { - this.data = { - 'crm.team': { - fields: { - foo: {string: "Foo", type: 'char'}, - invoiced: {string: "Invoiced", type: 'integer'}, - invoiced_target: {string: "Invoiced_target", type: 'integer'}, +let serverData; +let target; + +QUnit.module("Sales Team Dashboard", { + beforeEach() { + target = getFixture(); + serverData = { + models: { + "crm.team": { + fields: { + foo: { string: "Foo", type: "char" }, + invoiced: { string: "Invoiced", type: "integer" }, + invoiced_target: { string: "Invoiced_target", type: "integer" }, + }, + records: [{ id: 1, foo: "yop", invoiced: 0, invoiced_target: 0 }], }, - records: [ - {id: 1, foo: "yop"}, - ], }, }; - } + + setupViewRegistries(); + }, }); -QUnit.test('edit target with several o_kanban_primary_bottom divs [REQUIRE FOCUS]', async function (assert) { - assert.expect(7); +QUnit.test("edit target with several o_kanban_primary_bottom divs", async (assert) => { + assert.expect(4); - var kanban = await createView({ - View: KanbanView, - model: 'crm.team', - data: this.data, - arch: '' + - '' + - '' + - '' + - '
' + - ` ` + - '
' + - '
' + - '
' + - '' + - '' + - '', - mockRPC: function (route, args) { - if (args.method === 'write') { - assert.strictEqual(args.args[1].invoiced_target, 123, - "new value is correctly saved"); + const fakeActionService = { + start: () => ({ + async doAction(action) { + assert.deepEqual( + action, + { + res_model: "crm.team", + target: "current", + type: "ir.actions.act_window", + method: "get_formview_action", + }, + "should trigger do_action with the correct args" + ); + return true; + }, + }), + }; + serviceRegistry.add("action", fakeActionService, { force: true }); + + await makeView({ + serverData, + type: "kanban", + resModel: "crm.team", + arch: /* xml */` + + + +
+ +
+
+
+ + `, + resId: 1, + async mockRPC(route, { method, model }) { + if (route === "/web/dataset/call_kw/crm.team/get_formview_action") { + return { + method, + res_model: model, + target: "current", + type: "ir.actions.act_window", + }; } - if (args.method === 'read') { // Read happens after the write - assert.deepEqual(args.args[1], ['invoiced_target', 'invoiced', 'display_name'], - 'the read (after write) should ask for invoiced_target'); - } - return this._super.apply(this, arguments); }, }); - assert.containsOnce(kanban, '.o_legacy_kanban_view .o_progressbar:contains(Click to define an invoicing target)') - assert.containsN(kanban, '.o_kanban_primary_bottom', 2, - "should have two divs with classname 'o_kanban_primary_bottom'"); - - assert.containsNone(kanban, '.o_progressbar input') - await testUtils.dom.click(kanban.$('.o_progressbar a')); - assert.containsOnce(kanban, '.o_progressbar input') - - kanban.$('.o_progressbar input').focus(); - kanban.$('.o_progressbar input').val('123'); - kanban.$('.o_progressbar input').trigger('blur'); - await testUtils.nextTick(); - assert.strictEqual(kanban.$('.o_kanban_record').text().trim(), "0 / 123"); - - kanban.destroy(); -}); - -QUnit.test('edit target supports push Enter', async function (assert) { - assert.expect(3); - - var kanban = await createView({ - View: KanbanView, - model: 'crm.team', - data: this.data, - arch: '' + - '' + - '' + - '' + - '
' + - ` ` + - '
' + - '
' + - '
' + - '' + - '' + - '', - mockRPC: function (route, args) { - if (args.method === 'write') { - assert.strictEqual(args.args[1].invoiced_target, 123, - "new value is correctly saved"); - } - if (args.method === 'read') { // Read happens after the write - assert.deepEqual(args.args[1], ['invoiced_target', 'invoiced', 'display_name'], - 'the read (after write) should ask for invoiced_target'); - } - return this._super.apply(this, arguments); - }, - }); - - await testUtils.dom.click(kanban.$('.o_progressbar a')); - kanban.$('.o_progressbar input').focus(); - kanban.$('.o_progressbar input').val('123'); - kanban.$('.o_progressbar input').trigger($.Event('keyup', {which: $.ui.keyCode.ENTER, keyCode: $.ui.keyCode.ENTER})); - await testUtils.nextTick(); - assert.strictEqual(kanban.$('.o_kanban_record').text().trim(), "0 / 123"); - - kanban.destroy(); -}); + assert.containsOnce( + target, + ".o_field_sales_team_progressbar:contains(Click to define an invoicing target)" + ); + assert.containsN(target, ".o_kanban_primary_bottom", 2); + assert.containsNone(target, ".o_progressbar input"); + await click(target, ".sale_progressbar_form_link"); // should trigger a do_action }); diff --git a/addons/web/static/src/views/fields/progress_bar/kanban_progress_bar_field.js b/addons/web/static/src/views/fields/progress_bar/kanban_progress_bar_field.js index fc3a812e7f9..97c39bf4aa1 100644 --- a/addons/web/static/src/views/fields/progress_bar/kanban_progress_bar_field.js +++ b/addons/web/static/src/views/fields/progress_bar/kanban_progress_bar_field.js @@ -3,7 +3,7 @@ import { registry } from "@web/core/registry"; import { ProgressBarField } from "./progress_bar_field"; -class KanbanProgressBarField extends ProgressBarField { +export class KanbanProgressBarField extends ProgressBarField { onClick() { if (this.props.isEditable && !this.props.record.isReadonly(this.props.name)) { this.state.isEditing = true;