diff --git a/addons/hr_org_chart/static/src/js/hr_org_chart.js b/addons/hr_org_chart/static/src/js/hr_org_chart.js index 60b6f94bc8b..c2c425d2612 100644 --- a/addons/hr_org_chart/static/src/js/hr_org_chart.js +++ b/addons/hr_org_chart/static/src/js/hr_org_chart.js @@ -1,76 +1,93 @@ odoo.define('web.OrgChart', function (require) { "use strict"; -var ajax = require('web.ajax'); +var AbstractField = require('web.AbstractField'); +var concurrency = require('web.concurrency'); var core = require('web.core'); -var form_common = require('web.form_common'); +var field_registry = require('web.field_registry'); var QWeb = core.qweb; var _t = core._t; -var FieldOrgChart = form_common.AbstractField.extend({ +var FieldOrgChart = AbstractField.extend({ events: { - "click .o_employee_redirect": "on_employee_redirect", - "click .o_employee_sub_redirect": "on_employee_sub_redirect", + "click .o_employee_redirect": "_onEmployeeRedirect", }, - + /** + * @constructor + * @override + */ init: function () { this._super.apply(this, arguments); + this.dm = new concurrency.DropMisordered(); }, - start: function () { - this.reinit(); - return this._super(); - }, + //-------------------------------------------------------------------------- + // Private + //-------------------------------------------------------------------------- - reinit: function () { - this.emp_data = { - managers: [], - children: [], - }; + /** + * Get the chart data through a rpc call. + * + * @private + * @param {integer} employee_id + * @returns {Deferred} + */ + _getOrgData: function (employee_id) { + var self = this; + return this.dm.add(this._rpc({ + route: '/hr/get_org_chart', + params: { + employee_id: employee_id, + }, + })).then(function (data) { + self.orgData = data; + }); }, - - set_value: function (_value) { - this.reinit(); - this._super(_value); - }, - - render_value: function () { - if (! this.view.datarecord.id) { - return this.$el.html(QWeb.render("hr_org_chart", {widget: this})); + /** + * @override + * @private + */ + _render: function () { + if (!this.recordData.id) { + return this.$el.html(QWeb.render("hr_org_chart", { + managers: [], + children: [], + })); } var self = this; - this.get_org_chart_data(this.view.datarecord.id).then(function () { - self.$el.html(QWeb.render("hr_org_chart", {widget: self})); - }).then(function () { - self.$el.find('[data-toggle="popover"]').each(function () { + return this._getOrgData(this.recordData.id).then(function () { + self.$el.html(QWeb.render("hr_org_chart", self.orgData)); + self.$('[data-toggle="popover"]').each(function () { $(this).popover({ html: true, - title: function() { - var $title = $(QWeb.render('hr_orgchart_emp_popover_title', {employee: { - name: $(this).data('emp-name'), - id: $(this).data('emp-id'), - }})); - $title.on('click', '.o_employee_redirect', function(event) { - self.on_employee_redirect(event); - }); + title: function () { + var $title = $(QWeb.render('hr_orgchart_emp_popover_title', { + employee: { + name: $(this).data('emp-name'), + id: $(this).data('emp-id'), + }, + })); + $title.on('click', + '.o_employee_redirect', _.bind(self._onEmployeeRedirect, self)); return $title; }, container: 'body', placement: 'left', trigger: 'focus', - content: function() { - var $content = $(QWeb.render('hr_orgchart_emp_popover_content', {employee: { - id: $(this).data('emp-id'), - name: $(this).data('emp-name'), - direct_sub_count: parseInt($(this).data('emp-dir-subs')), - indirect_sub_count: parseInt($(this).data('emp-ind-subs')), - }})); - $content.on('click', '.o_employee_sub_redirect', function(event) { - self.on_employee_sub_redirect(event); - }); + content: function () { + var $content = $(QWeb.render('hr_orgchart_emp_popover_content', { + employee: { + id: $(this).data('emp-id'), + name: $(this).data('emp-name'), + direct_sub_count: parseInt($(this).data('emp-dir-subs')), + indirect_sub_count: parseInt($(this).data('emp-ind-subs')), + }, + })); + $content.on('click', + '.o_employee_sub_redirect', _.bind(self._onEmployeeSubRedirect, self)); return $content; }, template: $(QWeb.render('hr_orgchart_emp_popover', {})), @@ -79,16 +96,18 @@ var FieldOrgChart = form_common.AbstractField.extend({ }); }, - get_org_chart_data: function (employee_id) { - var self = this; - return ajax.jsonRpc('/hr/get_org_chart', 'call', { - employee_id: employee_id, - }).then(function (data) { - self.emp_data = data; - }); - }, + //-------------------------------------------------------------------------- + // Handlers + //-------------------------------------------------------------------------- - on_employee_redirect: function (event) { + /** + * Redirect to the employee form view. + * + * @private + * @param {MouseEvent} event + * @returns {Deferred} action loaded + */ + _onEmployeeRedirect: function (event) { event.preventDefault(); var employee_id = parseInt($(event.currentTarget).data('employee-id')); return this.do_action({ @@ -101,8 +120,14 @@ var FieldOrgChart = form_common.AbstractField.extend({ res_id: employee_id, }); }, - - on_employee_sub_redirect: function (event) { + /** + * Redirect to the sub employee form view. + * + * @private + * @param {MouseEvent} event + * @returns {Deferred} action loaded + */ + _onEmployeeSubRedirect: function (event) { event.preventDefault(); var employee_id = parseInt($(event.currentTarget).data('employee-id')); var employee_name = $(event.currentTarget).data('employee-name'); @@ -112,29 +137,30 @@ var FieldOrgChart = form_common.AbstractField.extend({ if (type === 'total') { domain = ['&', ['parent_id', 'child_of', employee_id], ['id', '!=', employee_id]]; name = _.str.sprintf(_t("Subordinates of %s"), employee_name); - } - else if (type === 'indirect') { - domain = ['&', '&', ['parent_id', 'child_of', employee_id], ['parent_id', '!=', employee_id], ['id', '!=', employee_id]]; + } else if (type === 'indirect') { + domain = ['&', '&', + ['parent_id', 'child_of', employee_id], + ['parent_id', '!=', employee_id], + ['id', '!=', employee_id] + ]; name = _.str.sprintf(_t("Indirect Subordinates of %s"), employee_name); } if (employee_id) { return this.do_action({ name: name, type: 'ir.actions.act_window', - view_type: 'tree', - view_mode: 'kanban,tree,form', - views: [[false, 'kanban'], [false, 'tree'], [false, 'form']], + view_mode: 'kanban,list,form', + views: [[false, 'kanban'], [false, 'list'], [false, 'form']], target: 'current', res_model: 'hr.employee', domain: domain, }); } }, - }); -core.form_widget_registry.add('hr_org_chart', FieldOrgChart); - +field_registry.add('hr_org_chart', FieldOrgChart); return FieldOrgChart; + }); diff --git a/addons/hr_org_chart/static/src/xml/hr_org_chart.xml b/addons/hr_org_chart/static/src/xml/hr_org_chart.xml index 661a1edc9d6..6aa027882f0 100644 --- a/addons/hr_org_chart/static/src/xml/hr_org_chart.xml +++ b/addons/hr_org_chart/static/src/xml/hr_org_chart.xml @@ -56,20 +56,20 @@ --> -
- + - + - + - +

No hierarchy position.

This employee has no manager or subordinate.

@@ -90,8 +90,8 @@
-
- +
+ @@ -100,11 +100,11 @@ - +
…
diff --git a/addons/hr_org_chart/static/tests/hr_org_chart_tests.js b/addons/hr_org_chart/static/tests/hr_org_chart_tests.js new file mode 100644 index 00000000000..a6041e25dcd --- /dev/null +++ b/addons/hr_org_chart/static/tests/hr_org_chart_tests.js @@ -0,0 +1,164 @@ +odoo.define('hr_org_chart.tests', function (require) { +"use strict"; + +var FormView = require('web.FormView'); +var testUtils = require("web.test_utils"); + +var createView = testUtils.createView; + +QUnit.module('hr_org_chart', { + before: function () { + this.data = { + hr_employee: { + fields: { + child_ids: {string: "one2many Subordinates field", type: "one2many", relation: 'hr_employee'}, + }, + records: [{ + id: 1, + child_ids: [], + }] + } + }; + }, +}, function () { + QUnit.test("hr org chart: empty render", function (assert) { + assert.expect(2); + + var form = createView({ + View: FormView, + model: 'hr_employee', + data: this.data, + arch: + '
' + + '' + + '', + res_id: 1, + mockRPC: function (route, args) { + if (route === '/hr/get_org_chart') { + assert.ok('employee_id' in args, "it should have 'employee_id' as argument"); + return $.when({ + children: [], + managers: [], + managers_more: false, + }); + } + return this._super(route, args); + } + }); + assert.strictEqual(form.$('[name="child_ids"]').children().length, 1, "the chart should have 1 child"); + form.destroy(); + }); + QUnit.test("hr org chart: basic render", function (assert) { + assert.expect(3); + + var form = createView({ + View: FormView, + model: 'hr_employee', + data: this.data, + arch: + '
' + + '' + + '
' + + '
' + + '' + + '
' + + '
' + + '
' + + '
', + res_id: 1, + mockRPC: function (route, args) { + if (route === '/hr/get_org_chart') { + assert.ok('employee_id' in args, "it should have 'employee_id' as argument"); + return $.when({ + children: [{ + direct_sub_count: 0, + indirect_sub_count: 0, + job_id: 2, + job_name: 'Sub-Gooroo', + link: 'fake_link', + name: 'Michael Hawkins', + }], + managers: [], + managers_more: false, + self: { + direct_sub_count: 1, + id: 1, + indirect_sub_count: 1, + job_id: 1, + job_name: 'Gooroo', + link: 'fake_link', + name: 'Antoine Langlais', + } + }); + } + return this._super(route, args); + } + }); + assert.strictEqual(form.$('.o_org_chart_entry_sub').length, 1, + "the chart should have 1 subordinate"); + assert.strictEqual(form.$('.o_org_chart_entry_self').length, 1, + "the current employee should only be displayed once in the chart"); + form.destroy(); + }); + QUnit.test("hr org chart: basic manager render", function (assert) { + assert.expect(4); + + var form = createView({ + View: FormView, + model: 'hr_employee', + data: this.data, + arch: + '
' + + '' + + '
' + + '
' + + '' + + '
' + + '
' + + '
' + + '
', + res_id: 1, + mockRPC: function (route, args) { + if (route === '/hr/get_org_chart') { + assert.ok('employee_id' in args, "should have 'employee_id' as argument"); + return $.when({ + children: [{ + direct_sub_count: 0, + indirect_sub_count: 0, + job_id: 2, + job_name: 'Sub-Gooroo', + link: 'fake_link', + name: 'Michael Hawkins', + }], + managers: [{ + direct_sub_count: 1, + id: 1, + indirect_sub_count: 2, + job_id: 1, + job_name: 'Chief Gooroo', + link: 'fake_link', + name: 'Antoine Langlais', + }], + managers_more: false, + self: { + direct_sub_count: 1, + id: 1, + indirect_sub_count: 1, + job_id: 3, + job_name: 'Gooroo', + link: 'fake_link', + name: 'John Smith', + } + }); + } + return this._super(route, args); + } + }); + assert.strictEqual(form.$('.o_org_chart_group_up .o_org_chart_entry_manager').length, 1, "the chart should have 1 manager"); + assert.strictEqual(form.$('.o_org_chart_group_down .o_org_chart_entry_sub').length, 1, "the chart should have 1 subordinate"); + assert.strictEqual(form.$('.o_org_chart_entry_self').length, 1, "the chart should have only once the current employee"); + form.destroy(); + }); +}); + +}); \ No newline at end of file diff --git a/addons/hr_org_chart/views/hr_templates.xml b/addons/hr_org_chart/views/hr_templates.xml index ae1e3665cfc..fbaf9ac1142 100644 --- a/addons/hr_org_chart/views/hr_templates.xml +++ b/addons/hr_org_chart/views/hr_templates.xml @@ -6,4 +6,10 @@ + +