[FIX] hr_org_chart: update to new views and guidelines
This commit is contained in:
@@ -1,76 +1,93 @@
|
|||||||
odoo.define('web.OrgChart', function (require) {
|
odoo.define('web.OrgChart', function (require) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var ajax = require('web.ajax');
|
var AbstractField = require('web.AbstractField');
|
||||||
|
var concurrency = require('web.concurrency');
|
||||||
var core = require('web.core');
|
var core = require('web.core');
|
||||||
var form_common = require('web.form_common');
|
var field_registry = require('web.field_registry');
|
||||||
|
|
||||||
var QWeb = core.qweb;
|
var QWeb = core.qweb;
|
||||||
var _t = core._t;
|
var _t = core._t;
|
||||||
|
|
||||||
var FieldOrgChart = form_common.AbstractField.extend({
|
var FieldOrgChart = AbstractField.extend({
|
||||||
|
|
||||||
events: {
|
events: {
|
||||||
"click .o_employee_redirect": "on_employee_redirect",
|
"click .o_employee_redirect": "_onEmployeeRedirect",
|
||||||
"click .o_employee_sub_redirect": "on_employee_sub_redirect",
|
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* @constructor
|
||||||
|
* @override
|
||||||
|
*/
|
||||||
init: function () {
|
init: function () {
|
||||||
this._super.apply(this, arguments);
|
this._super.apply(this, arguments);
|
||||||
|
this.dm = new concurrency.DropMisordered();
|
||||||
},
|
},
|
||||||
|
|
||||||
start: function () {
|
//--------------------------------------------------------------------------
|
||||||
this.reinit();
|
// Private
|
||||||
return this._super();
|
//--------------------------------------------------------------------------
|
||||||
},
|
|
||||||
|
|
||||||
reinit: function () {
|
/**
|
||||||
this.emp_data = {
|
* Get the chart data through a rpc call.
|
||||||
managers: [],
|
*
|
||||||
children: [],
|
* @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) {
|
* @override
|
||||||
this.reinit();
|
* @private
|
||||||
this._super(_value);
|
*/
|
||||||
},
|
_render: function () {
|
||||||
|
if (!this.recordData.id) {
|
||||||
render_value: function () {
|
return this.$el.html(QWeb.render("hr_org_chart", {
|
||||||
if (! this.view.datarecord.id) {
|
managers: [],
|
||||||
return this.$el.html(QWeb.render("hr_org_chart", {widget: this}));
|
children: [],
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
this.get_org_chart_data(this.view.datarecord.id).then(function () {
|
return this._getOrgData(this.recordData.id).then(function () {
|
||||||
self.$el.html(QWeb.render("hr_org_chart", {widget: self}));
|
self.$el.html(QWeb.render("hr_org_chart", self.orgData));
|
||||||
}).then(function () {
|
self.$('[data-toggle="popover"]').each(function () {
|
||||||
self.$el.find('[data-toggle="popover"]').each(function () {
|
|
||||||
$(this).popover({
|
$(this).popover({
|
||||||
html: true,
|
html: true,
|
||||||
title: function() {
|
title: function () {
|
||||||
var $title = $(QWeb.render('hr_orgchart_emp_popover_title', {employee: {
|
var $title = $(QWeb.render('hr_orgchart_emp_popover_title', {
|
||||||
name: $(this).data('emp-name'),
|
employee: {
|
||||||
id: $(this).data('emp-id'),
|
name: $(this).data('emp-name'),
|
||||||
}}));
|
id: $(this).data('emp-id'),
|
||||||
$title.on('click', '.o_employee_redirect', function(event) {
|
},
|
||||||
self.on_employee_redirect(event);
|
}));
|
||||||
});
|
$title.on('click',
|
||||||
|
'.o_employee_redirect', _.bind(self._onEmployeeRedirect, self));
|
||||||
return $title;
|
return $title;
|
||||||
},
|
},
|
||||||
container: 'body',
|
container: 'body',
|
||||||
placement: 'left',
|
placement: 'left',
|
||||||
trigger: 'focus',
|
trigger: 'focus',
|
||||||
content: function() {
|
content: function () {
|
||||||
var $content = $(QWeb.render('hr_orgchart_emp_popover_content', {employee: {
|
var $content = $(QWeb.render('hr_orgchart_emp_popover_content', {
|
||||||
id: $(this).data('emp-id'),
|
employee: {
|
||||||
name: $(this).data('emp-name'),
|
id: $(this).data('emp-id'),
|
||||||
direct_sub_count: parseInt($(this).data('emp-dir-subs')),
|
name: $(this).data('emp-name'),
|
||||||
indirect_sub_count: parseInt($(this).data('emp-ind-subs')),
|
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.on('click',
|
||||||
|
'.o_employee_sub_redirect', _.bind(self._onEmployeeSubRedirect, self));
|
||||||
return $content;
|
return $content;
|
||||||
},
|
},
|
||||||
template: $(QWeb.render('hr_orgchart_emp_popover', {})),
|
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;
|
// Handlers
|
||||||
return ajax.jsonRpc('/hr/get_org_chart', 'call', {
|
//--------------------------------------------------------------------------
|
||||||
employee_id: employee_id,
|
|
||||||
}).then(function (data) {
|
|
||||||
self.emp_data = data;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
on_employee_redirect: function (event) {
|
/**
|
||||||
|
* Redirect to the employee form view.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {MouseEvent} event
|
||||||
|
* @returns {Deferred} action loaded
|
||||||
|
*/
|
||||||
|
_onEmployeeRedirect: function (event) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
var employee_id = parseInt($(event.currentTarget).data('employee-id'));
|
var employee_id = parseInt($(event.currentTarget).data('employee-id'));
|
||||||
return this.do_action({
|
return this.do_action({
|
||||||
@@ -101,8 +120,14 @@ var FieldOrgChart = form_common.AbstractField.extend({
|
|||||||
res_id: employee_id,
|
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();
|
event.preventDefault();
|
||||||
var employee_id = parseInt($(event.currentTarget).data('employee-id'));
|
var employee_id = parseInt($(event.currentTarget).data('employee-id'));
|
||||||
var employee_name = $(event.currentTarget).data('employee-name');
|
var employee_name = $(event.currentTarget).data('employee-name');
|
||||||
@@ -112,29 +137,30 @@ var FieldOrgChart = form_common.AbstractField.extend({
|
|||||||
if (type === 'total') {
|
if (type === 'total') {
|
||||||
domain = ['&', ['parent_id', 'child_of', employee_id], ['id', '!=', employee_id]];
|
domain = ['&', ['parent_id', 'child_of', employee_id], ['id', '!=', employee_id]];
|
||||||
name = _.str.sprintf(_t("Subordinates of %s"), employee_name);
|
name = _.str.sprintf(_t("Subordinates of %s"), employee_name);
|
||||||
}
|
} else if (type === 'indirect') {
|
||||||
else if (type === 'indirect') {
|
domain = ['&', '&',
|
||||||
domain = ['&', '&', ['parent_id', 'child_of', employee_id], ['parent_id', '!=', employee_id], ['id', '!=', employee_id]];
|
['parent_id', 'child_of', employee_id],
|
||||||
|
['parent_id', '!=', employee_id],
|
||||||
|
['id', '!=', employee_id]
|
||||||
|
];
|
||||||
name = _.str.sprintf(_t("Indirect Subordinates of %s"), employee_name);
|
name = _.str.sprintf(_t("Indirect Subordinates of %s"), employee_name);
|
||||||
}
|
}
|
||||||
if (employee_id) {
|
if (employee_id) {
|
||||||
return this.do_action({
|
return this.do_action({
|
||||||
name: name,
|
name: name,
|
||||||
type: 'ir.actions.act_window',
|
type: 'ir.actions.act_window',
|
||||||
view_type: 'tree',
|
view_mode: 'kanban,list,form',
|
||||||
view_mode: 'kanban,tree,form',
|
views: [[false, 'kanban'], [false, 'list'], [false, 'form']],
|
||||||
views: [[false, 'kanban'], [false, 'tree'], [false, 'form']],
|
|
||||||
target: 'current',
|
target: 'current',
|
||||||
res_model: 'hr.employee',
|
res_model: 'hr.employee',
|
||||||
domain: domain,
|
domain: domain,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
core.form_widget_registry.add('hr_org_chart', FieldOrgChart);
|
field_registry.add('hr_org_chart', FieldOrgChart);
|
||||||
|
|
||||||
|
|
||||||
return FieldOrgChart;
|
return FieldOrgChart;
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -56,20 +56,20 @@
|
|||||||
-->
|
-->
|
||||||
<t t-set="emp_count" t-value="0"/>
|
<t t-set="emp_count" t-value="0"/>
|
||||||
|
|
||||||
<div t-if='widget.emp_data.managers.length > 0' class="o_org_chart_group_up">
|
<div t-if='managers.length > 0' class="o_org_chart_group_up">
|
||||||
<t t-if='widget.emp_data.managers_more'>
|
<t t-if='managers_more'>
|
||||||
<div class="o_org_chart_entry o_org_chart_more media">
|
<div class="o_org_chart_entry o_org_chart_more media">
|
||||||
<div class="media-left">
|
<div class="media-left">
|
||||||
<a class="well media-object text-center o_employee_redirect"
|
<a class="well media-object text-center o_employee_redirect"
|
||||||
t-att-href="widget.emp_data.managers[0].link"
|
t-att-href="managers[0].link"
|
||||||
t-att-data-employee-id="widget.emp_data.managers[0].id">
|
t-att-data-employee-id="managers[0].id">
|
||||||
<i t-attf-class="fa fa-angle-double-up"/>
|
<i t-attf-class="fa fa-angle-double-up"/>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
<t t-foreach="widget.emp_data.managers" t-as="employee">
|
<t t-foreach="managers" t-as="employee">
|
||||||
<t t-set="emp_count" t-value="emp_count + 1"/>
|
<t t-set="emp_count" t-value="emp_count + 1"/>
|
||||||
<t t-call="hr_org_chart_employee">
|
<t t-call="hr_org_chart_employee">
|
||||||
<t t-set="employee_type" t-value="'manager'"/>
|
<t t-set="employee_type" t-value="'manager'"/>
|
||||||
@@ -77,12 +77,12 @@
|
|||||||
</t>
|
</t>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<t t-if="widget.emp_data.children.length || widget.emp_data.managers.length" t-call="hr_org_chart_employee">
|
<t t-if="children.length || managers.length" t-call="hr_org_chart_employee">
|
||||||
<t t-set="employee_type" t-value="'self'"/>
|
<t t-set="employee_type" t-value="'self'"/>
|
||||||
<t t-set="employee" t-value="widget.emp_data.self"/>
|
<t t-set="employee" t-value="self"/>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
<t t-if="!widget.emp_data.children.length && !widget.emp_data.managers.length">
|
<t t-if="!children.length && !managers.length">
|
||||||
<div class="alert alert-info">
|
<div class="alert alert-info">
|
||||||
<p><b>No hierarchy position.</b></p>
|
<p><b>No hierarchy position.</b></p>
|
||||||
<p>This employee has no manager or subordinate.</p>
|
<p>This employee has no manager or subordinate.</p>
|
||||||
@@ -90,8 +90,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
<div t-if="widget.emp_data.children.length" class="o_org_chart_group_down">
|
<div t-if="children.length" class="o_org_chart_group_down">
|
||||||
<t t-foreach="widget.emp_data.children" t-as="employee">
|
<t t-foreach="children" t-as="employee">
|
||||||
<t t-set="emp_count" t-value="emp_count + 1"/>
|
<t t-set="emp_count" t-value="emp_count + 1"/>
|
||||||
<t t-if="emp_count < 8">
|
<t t-if="emp_count < 8">
|
||||||
<t t-call="hr_org_chart_employee">
|
<t t-call="hr_org_chart_employee">
|
||||||
@@ -100,11 +100,11 @@
|
|||||||
</t>
|
</t>
|
||||||
</t>
|
</t>
|
||||||
|
|
||||||
<t t-if="(widget.emp_data.children.length + widget.emp_data.managers.length) > 7">
|
<t t-if="(children.length + managers.length) > 7">
|
||||||
<div class="o_org_chart_entry o_org_chart_more media">
|
<div class="o_org_chart_entry o_org_chart_more media">
|
||||||
<div class="media-left">
|
<div class="media-left">
|
||||||
<a href="#"
|
<a href="#"
|
||||||
t-att-data-employee-id="widget.emp_data.self.id"
|
t-att-data-employee-id="self.id"
|
||||||
class="well o_org_chart_show_more media-object text-center o_employee_sub_redirect">…</a>
|
class="well o_org_chart_show_more media-object text-center o_employee_sub_redirect">…</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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:
|
||||||
|
'<form>' +
|
||||||
|
'<field name="child_ids" widget="hr_org_chart"/>' +
|
||||||
|
'</form>',
|
||||||
|
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:
|
||||||
|
'<form>' +
|
||||||
|
'<sheet>' +
|
||||||
|
'<div id="o_employee_container"><div id="o_employee_main">' +
|
||||||
|
'<div id="o_employee_right">' +
|
||||||
|
'<field name="child_ids" widget="hr_org_chart"/>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div></div>' +
|
||||||
|
'</sheet>' +
|
||||||
|
'</form>',
|
||||||
|
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:
|
||||||
|
'<form>' +
|
||||||
|
'<sheet>' +
|
||||||
|
'<div id="o_employee_container"><div id="o_employee_main">' +
|
||||||
|
'<div id="o_employee_right">' +
|
||||||
|
'<field name="child_ids" widget="hr_org_chart"/>' +
|
||||||
|
'</div>' +
|
||||||
|
'</div></div>' +
|
||||||
|
'</sheet>' +
|
||||||
|
'</form>',
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
@@ -6,4 +6,10 @@
|
|||||||
<script type="text/javascript" src="/hr_org_chart/static/src/js/hr_org_chart.js"></script>
|
<script type="text/javascript" src="/hr_org_chart/static/src/js/hr_org_chart.js"></script>
|
||||||
</xpath>
|
</xpath>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
<template id="qunit_suite" inherit_id="web.qunit_suite">
|
||||||
|
<xpath expr="//script[last()]" position="after">
|
||||||
|
<script type="text/javascript" src="/hr_org_chart/static/tests/hr_org_chart_tests.js"/>
|
||||||
|
</xpath>
|
||||||
|
</template>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
Reference in New Issue
Block a user