summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/hr/models/hr.py8
-rw-r--r--addons/hr_org_chart/controllers/hr_org_chart.py60
-rw-r--r--addons/hr_org_chart/models/hr_employee.py35
-rw-r--r--addons/hr_org_chart/static/src/js/hr_org_chart.js72
-rw-r--r--addons/hr_org_chart/static/src/xml/hr_org_chart.xml9
5 files changed, 126 insertions, 58 deletions
diff --git a/addons/hr/models/hr.py b/addons/hr/models/hr.py
index e6e7d2e47d5..949b6472b1e 100644
--- a/addons/hr/models/hr.py
+++ b/addons/hr/models/hr.py
@@ -189,7 +189,7 @@ class Employee(models.Model):
job_id = fields.Many2one('hr.job', 'Job Position')
department_id = fields.Many2one('hr.department', 'Department')
parent_id = fields.Many2one('hr.employee', 'Manager')
- child_ids = fields.One2many('hr.employee', 'parent_id', string='Subordinates')
+ child_ids = fields.One2many('hr.employee', 'parent_id', string='Direct subordinates')
coach_id = fields.Many2one('hr.employee', 'Coach')
category_ids = fields.Many2many(
'hr.employee.category', 'employee_category_rel',
@@ -199,12 +199,6 @@ class Employee(models.Model):
notes = fields.Text('Notes')
color = fields.Integer('Color Index', default=0)
- @api.constrains('parent_id')
- def _check_parent_id(self):
- for employee in self:
- if not employee._check_recursion():
- raise ValidationError(_('You cannot create a recursive hierarchy.'))
-
@api.onchange('job_id')
def _onchange_job_id(self):
if self.job_id:
diff --git a/addons/hr_org_chart/controllers/hr_org_chart.py b/addons/hr_org_chart/controllers/hr_org_chart.py
index 5729997636f..20f1c30047a 100644
--- a/addons/hr_org_chart/controllers/hr_org_chart.py
+++ b/addons/hr_org_chart/controllers/hr_org_chart.py
@@ -7,7 +7,23 @@ from odoo.http import request
class HrOrgChartController(http.Controller):
- _managers_level = 2 # FP request
+ _managers_level = 5 # FP request
+
+ def _check_employee(self, employee_id):
+ if not employee_id: # to check
+ return None
+ employee_id = int(employee_id)
+
+ Employee = request.env['hr.employee']
+ # check and raise
+ if not Employee.check_access_rights('read', raise_exception=False):
+ return None
+ try:
+ Employee.browse(employee_id).check_access_rule('read')
+ except AccessError:
+ return None
+ else:
+ return Employee.browse(employee_id)
def _prepare_employee_data(self, employee):
job = employee.sudo().job_id
@@ -23,32 +39,46 @@ class HrOrgChartController(http.Controller):
@http.route('/hr/get_org_chart', type='json', auth='user')
def get_org_chart(self, employee_id):
- if not employee_id: # to check
- return {}
- employee_id = int(employee_id)
- Employee = request.env['hr.employee']
- # check and raise
- if not Employee.check_access_rights('read', raise_exception=False):
- return {}
- try:
- Employee.browse(employee_id).check_access_rule('read')
- except AccessError:
+ employee = self._check_employee(employee_id)
+ if not employee: # to check
return {}
- else:
- employee = Employee.browse(employee_id)
# compute employee data for org chart
ancestors, current = request.env['hr.employee'], employee
- while current.parent_id:
+ while current.parent_id and len(ancestors) < self._managers_level+1:
ancestors += current.parent_id
current = current.parent_id
values = dict(
self=self._prepare_employee_data(employee),
- managers=[self._prepare_employee_data(ancestor) for idx, ancestor in enumerate(ancestors) if idx < self._managers_level],
+ managers=[
+ self._prepare_employee_data(ancestor)
+ for idx, ancestor in enumerate(ancestors)
+ if idx < self._managers_level
+ ],
managers_more=len(ancestors) > self._managers_level,
children=[self._prepare_employee_data(child) for child in employee.child_ids],
)
values['managers'].reverse()
return values
+
+ @http.route('/hr/get_subordinates', type='json', auth='user')
+ def get_subordinates(self, employee_id, subordinates_type=None):
+ """
+ Get employee subordinates.
+ Possible values for 'subordinates_type':
+ - 'indirect'
+ - 'direct'
+ """
+
+ employee = self._check_employee(employee_id)
+ if not employee: # to check
+ return {}
+
+ if subordinates_type == 'direct':
+ return employee.child_ids.ids
+ elif subordinates_type == 'indirect':
+ return (employee.subordinate_ids - employee.child_ids).ids
+ else:
+ return employee.subordinate_ids.ids
diff --git a/addons/hr_org_chart/models/hr_employee.py b/addons/hr_org_chart/models/hr_employee.py
index ff56b1b1bb3..15fc50645d2 100644
--- a/addons/hr_org_chart/models/hr_employee.py
+++ b/addons/hr_org_chart/models/hr_employee.py
@@ -3,16 +3,41 @@
from odoo import api, fields, models
-
class Employee(models.Model):
_name = "hr.employee"
_inherit = "hr.employee"
child_all_count = fields.Integer(
'Indirect Surbordinates Count',
- compute='_compute_child_all_count', store=False)
+ compute='_compute_subordinates', store=False)
+
+ subordinate_ids = fields.One2many('hr.employee', string='Subordinates', compute='_compute_subordinates', help="Direct and indirect subordinates", groups='base.group_user')
+
+
+ def _get_subordinates(self, parents=None):
+ """
+ Helper function to compute subordinates_ids.
+ Get all subordinates (direct and indirect) of an employee.
+ An employee can be a manager of his own manager (recursive hierarchy; e.g. the CEO is manager of everyone but is also
+ member of the RD department, managed by the CTO itself managed by the CEO).
+ In that case, the manager in not counted as a subordinate if it's in the 'parents' set.
+ """
+
+ if not parents:
+ parents = self.env['hr.employee']
+
+ indirect_subordinates = self.env['hr.employee']
+ parents |= self
+ direct_subordinates = self.child_ids - parents
+ for child in direct_subordinates:
+ child_subordinate = child._get_subordinates(parents=parents)
+ child.subordinate_ids = child_subordinate
+ indirect_subordinates |= child_subordinate
+ return indirect_subordinates | direct_subordinates
+
- @api.depends('child_ids.child_all_count')
- def _compute_child_all_count(self):
+ @api.depends('child_ids', 'child_ids.child_all_count')
+ def _compute_subordinates(self):
for employee in self:
- employee.child_all_count = len(employee.child_ids) + sum(child.child_all_count for child in employee.child_ids)
+ employee.subordinate_ids = employee._get_subordinates()
+ employee.child_all_count = len(employee.subordinate_ids)
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 b889375cac4..70bf295d4f5 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
@@ -14,20 +14,21 @@ var FieldOrgChart = AbstractField.extend({
events: {
"click .o_employee_redirect": "_onEmployeeRedirect",
"click .o_employee_sub_redirect": "_onEmployeeSubRedirect",
+ "click .o_employee_more_managers": "_onEmployeeMoreManager"
},
/**
* @constructor
* @override
*/
- init: function () {
+ init: function (parent, options) {
this._super.apply(this, arguments);
this.dm = new concurrency.DropMisordered();
+ this.employee;
},
//--------------------------------------------------------------------------
// Private
//--------------------------------------------------------------------------
-
/**
* Get the chart data through a rpc call.
*
@@ -40,13 +41,29 @@ var FieldOrgChart = AbstractField.extend({
return this.dm.add(this._rpc({
route: '/hr/get_org_chart',
params: {
- employee_id: employee_id,
+ employee_id: employee_id
},
})).then(function (data) {
self.orgData = data;
});
},
/**
+ * Get subordonates of an employee through a rpc call.
+ *
+ * @private
+ * @param {integer} employee_id
+ * @returns {Deferred}
+ */
+ _getSubordinatesData: function (employee_id, type) {
+ return this.dm.add(this._rpc({
+ route: '/hr/get_subordinates',
+ params: {
+ employee_id: employee_id,
+ subordinates_type: type
+ },
+ }))
+ },
+ /**
* @override
* @private
*/
@@ -57,9 +74,14 @@ var FieldOrgChart = AbstractField.extend({
children: [],
}));
}
+ else if (!this.employee) {
+ this.employee = this.recordData.id
+ }
var self = this;
- return this._getOrgData(this.recordData.id).then(function () {
+ return this._getOrgData(this.employee).then(function () {
+
+ self.orgData['view_employee_id'] = self.recordData.id;
self.$el.html(QWeb.render("hr_org_chart", self.orgData));
self.$('[data-toggle="popover"]').each(function () {
$(this).popover({
@@ -101,6 +123,11 @@ var FieldOrgChart = AbstractField.extend({
// Handlers
//--------------------------------------------------------------------------
+ _onEmployeeMoreManager: function(event) {
+ event.preventDefault();
+ this.employee = parseInt($(event.currentTarget).data('employee-id'));
+ this._render()
+ },
/**
* Redirect to the employee form view.
*
@@ -133,29 +160,22 @@ var FieldOrgChart = AbstractField.extend({
var employee_id = parseInt($(event.currentTarget).data('employee-id'));
var employee_name = $(event.currentTarget).data('employee-name');
var type = $(event.currentTarget).data('type') || 'direct';
- var domain = [['parent_id', '=', employee_id]];
- var name = _.str.sprintf(_t("Direct Subordinates of %s"), employee_name);
- 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]
- ];
- name = _.str.sprintf(_t("Indirect Subordinates of %s"), employee_name);
- }
+ var self = this
if (employee_id) {
- return this.do_action({
- name: name,
- type: 'ir.actions.act_window',
- view_mode: 'kanban,list,form',
- views: [[false, 'kanban'], [false, 'list'], [false, 'form']],
- target: 'current',
- res_model: 'hr.employee',
- domain: domain,
- });
+ this._getSubordinatesData(employee_id, type).then(function(data) {
+ var domain = [['id', 'in', data]];
+
+ return self.do_action({
+ name: employee_name,
+ type: 'ir.actions.act_window',
+ view_mode: 'kanban,list,form',
+ views: [[false, 'kanban'], [false, 'list'], [false, 'form']],
+ target: 'current',
+ res_model: 'hr.employee',
+ domain: domain,
+ });
+ })
+
}
},
});
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 e4e7f9fa231..fa5db5f32a8 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
@@ -3,7 +3,7 @@
<t t-name="hr_org_chart_employee">
<div t-attf-class="o_org_chart_entry o_org_chart_entry_#{employee_type} media">
- <t t-set="is_self" t-value="employee_type == 'self'"/>
+ <t t-set="is_self" t-value="employee.id == view_employee_id"/>
<div class="o_media_left">
<!-- NOTE: Since by the default on not squared images odoo add white borders,
@@ -60,8 +60,7 @@
<t t-if='managers_more'>
<div class="o_org_chart_entry o_org_chart_more media">
<div class="o_media_left">
- <a class="text-center o_employee_redirect"
- t-att-href="managers[0].link"
+ <a class="text-center o_employee_more_managers"
t-att-data-employee-id="managers[0].id">
<i t-attf-class="fa fa-angle-double-up" role="img" aria-label="More managers" title="More managers"/>
</a>
@@ -93,14 +92,14 @@
<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-if="emp_count &lt; 20">
<t t-call="hr_org_chart_employee">
<t t-set="employee_type" t-value="'sub'"/>
</t>
</t>
</t>
- <t t-if="(children.length + managers.length) &gt; 7">
+ <t t-if="(children.length + managers.length) &gt; 19">
<div class="o_org_chart_entry o_org_chart_more media">
<div class="o_media_left">
<a href="#"