[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) {
|
||||
"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;
|
||||
|
||||
});
|
||||
|
||||
@@ -56,20 +56,20 @@
|
||||
-->
|
||||
<t t-set="emp_count" t-value="0"/>
|
||||
|
||||
<div t-if='widget.emp_data.managers.length > 0' class="o_org_chart_group_up">
|
||||
<t t-if='widget.emp_data.managers_more'>
|
||||
<div t-if='managers.length > 0' class="o_org_chart_group_up">
|
||||
<t t-if='managers_more'>
|
||||
<div class="o_org_chart_entry o_org_chart_more media">
|
||||
<div class="media-left">
|
||||
<a class="well media-object text-center o_employee_redirect"
|
||||
t-att-href="widget.emp_data.managers[0].link"
|
||||
t-att-data-employee-id="widget.emp_data.managers[0].id">
|
||||
t-att-href="managers[0].link"
|
||||
t-att-data-employee-id="managers[0].id">
|
||||
<i t-attf-class="fa fa-angle-double-up"/>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</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-call="hr_org_chart_employee">
|
||||
<t t-set="employee_type" t-value="'manager'"/>
|
||||
@@ -77,12 +77,12 @@
|
||||
</t>
|
||||
</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" t-value="widget.emp_data.self"/>
|
||||
<t t-set="employee" t-value="self"/>
|
||||
</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">
|
||||
<p><b>No hierarchy position.</b></p>
|
||||
<p>This employee has no manager or subordinate.</p>
|
||||
@@ -90,8 +90,8 @@
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<div t-if="widget.emp_data.children.length" class="o_org_chart_group_down">
|
||||
<t t-foreach="widget.emp_data.children" t-as="employee">
|
||||
<div t-if="children.length" class="o_org_chart_group_down">
|
||||
<t t-foreach="children" t-as="employee">
|
||||
<t t-set="emp_count" t-value="emp_count + 1"/>
|
||||
<t t-if="emp_count < 8">
|
||||
<t t-call="hr_org_chart_employee">
|
||||
@@ -100,11 +100,11 @@
|
||||
</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="media-left">
|
||||
<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>
|
||||
</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>
|
||||
</xpath>
|
||||
</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>
|
||||
|
||||
Reference in New Issue
Block a user