[FIX] hr_org_chart: update to new views and guidelines

This commit is contained in:
fwi-odoo
2017-04-11 19:44:38 +02:00
committed by Aaron Bohy
parent ef6b330d8f
commit e3d1abc75f
4 changed files with 274 additions and 78 deletions
@@ -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 &gt; 0' class="o_org_chart_group_up">
<t t-if='widget.emp_data.managers_more'>
<div t-if='managers.length &gt; 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 &amp;&amp; !widget.emp_data.managers.length">
<t t-if="!children.length &amp;&amp; !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 &lt; 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) &gt; 7">
<t t-if="(children.length + managers.length) &gt; 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">&#8230;</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>