summaryrefslogtreecommitdiff
path: root/addons/hr_holidays/static/src
diff options
context:
space:
mode:
authorLucas Lefèvre <lul@odoo.com>2019-05-02 15:35:09 +0200
committerYannick Tivisse <yti@odoo.com>2019-05-17 11:22:10 +0200
commit2b9aec24155e2ecc6164ad44a0b3d2dde14ea7f4 (patch)
tree2d1d0c41884a16b37ee9f4ca48769ca1d21b4352 /addons/hr_holidays/static/src
parent09e515eecc5610c8af0c3989b5b023ad435ca437 (diff)
[IMP] hr_holidays: Add leave stats widget
When approving a leave, to make an informed decision, it might be needed to check leaves already taken by the employee or leaves of its collaborators. This commit adds a widget displaying some stats in a leave form view. Stats displayed are: - the number of days taken by the employee this year, grouped by leave type - Leaves taken by employees in the same department in the month of the leave. This widget is only displayed in the context of the action leading to leaves to approve.
Diffstat (limited to 'addons/hr_holidays/static/src')
-rw-r--r--addons/hr_holidays/static/src/js/leave_stats_widget.js152
-rw-r--r--addons/hr_holidays/static/src/xml/leave_stats_templates.xml64
2 files changed, 216 insertions, 0 deletions
diff --git a/addons/hr_holidays/static/src/js/leave_stats_widget.js b/addons/hr_holidays/static/src/js/leave_stats_widget.js
new file mode 100644
index 00000000000..d224fe6ddca
--- /dev/null
+++ b/addons/hr_holidays/static/src/js/leave_stats_widget.js
@@ -0,0 +1,152 @@
+odoo.define('hr_holidays.LeaveStatsWidget', function (require) {
+ "use strict";
+
+ var time = require('web.time');
+ var Widget = require('web.Widget');
+ var widget_registry = require('web.widget_registry');
+
+ var LeaveStatsWidget = Widget.extend({
+ template: 'hr_holidays.leave_stats',
+
+ /**
+ * @override
+ * @param {Widget|null} parent
+ * @param {Object} params
+ */
+ init: function (parent, params) {
+ this._setState(params);
+ this._super(parent);
+ },
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override to fetch data before rendering.
+ */
+ willStart: function () {
+ return Promise.all([this._super(), this._fetchLeaveTypesData(), this._fetchDepartmentLeaves()]);
+ },
+
+ /**
+ * Fetch new data if needed (according to updated fields) and re-render the widget.
+ * Called by the basic renderer when the view changes.
+ * @param {Object} state
+ * @returns {Promise}
+ */
+ updateState: function (state) {
+ var self = this;
+ var to_await = [];
+ var updatedFields = this._setState(state);
+
+ if (_.intersection(updatedFields, ['employee', 'date']).length) {
+ to_await.push(this._fetchLeaveTypesData());
+ }
+ if (_.intersection(updatedFields, ['department', 'date']).length) {
+ to_await.push(this._fetchDepartmentLeaves());
+ }
+ return Promise.all(to_await).then(function () {
+ self.renderElement();
+ });
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Update the state
+ * @param {Object} state
+ * @returns {String[]} list of updated fields
+ */
+ _setState(state) {
+ var updatedFields = [];
+ if (state.data.employee_id.res_id !== (this.employee && this.employee.res_id)) {
+ updatedFields.push('employee');
+ this.employee = state.data.employee_id;
+ }
+ if (state.data.department_id.res_id !== (this.department && this.department.res_id)) {
+ updatedFields.push('department');
+ this.department = state.data.department_id;
+ }
+ if (state.data.date_from !== this.date) {
+ updatedFields.push('date');
+ this.date = state.data.date_from;
+ }
+ return updatedFields;
+ },
+
+ /**
+ * Fetch leaves taken by members of ``this.department`` in the
+ * month of ``this.date``.
+ * Three fields are fetched for each leave, namely: employee_id, date_from
+ * and date_to.
+ * The resulting data is assigned to ``this.departmentLeaves``
+ * @private
+ * @returns {Promise}
+ */
+ _fetchDepartmentLeaves: function () {
+ if (!this.date || !this.department) {
+ this.departmentLeaves = null;
+ return Promise.resolve();
+ }
+ var self = this;
+ var month_date_from = this.date.clone().startOf('month');
+ var month_date_to = this.date.clone().endOf('month');
+ return this._rpc({
+ model: 'hr.leave',
+ method: 'search_read',
+ args: [
+ [['department_id', '=', this.department.res_id],
+ ['state', '=', 'validate'],
+ ['holiday_type', '=', 'employee'],
+ ['date_from', '<=', month_date_to],
+ ['date_to', '>=', month_date_from]],
+ ['employee_id', 'date_from', 'date_to'],
+ ],
+ }).then(function (data) {
+ var dateFormat = time.getLangDateFormat();
+ self.departmentLeaves = data.map(function (leave) {
+ // Format datetimes to date (in the user's format)
+ return _.extend(leave, {
+ date_from: moment(leave.date_from).format(dateFormat),
+ date_to: moment(leave.date_to).format(dateFormat),
+ });
+ });
+ });
+ },
+
+ /**
+ * Fetch the number of leaves, grouped by leave type, taken by ``this.employee``
+ * in the year of ``this.date``.
+ * The resulting data is assigned to ``this.leavesPerType``
+ * @private
+ * @returns {Promise}
+ */
+ _fetchLeaveTypesData: function () {
+ if (!this.date || !this.employee) {
+ this.leavesPerType = null;
+ return Promise.resolve();
+ }
+ var self = this;
+ var year_date_from = this.date.clone().startOf('year');
+ var year_date_to = this.date.clone().endOf('year');
+ return this._rpc({
+ model: 'hr.leave',
+ method: 'read_group',
+ kwargs: {
+ domain: [['employee_id', '=', this.employee.res_id], ['state', '=', 'validate'], ['date_from', '<=', year_date_to], ['date_to', '>=', year_date_from]],
+ fields: ['holiday_status_id', 'number_of_days:sum'],
+ groupby: ['holiday_status_id'],
+ },
+ }).then(function (data) {
+ self.leavesPerType = data;
+ });
+ }
+ });
+
+ widget_registry.add('hr_leave_stats', LeaveStatsWidget);
+
+ return LeaveStatsWidget;
+});
diff --git a/addons/hr_holidays/static/src/xml/leave_stats_templates.xml b/addons/hr_holidays/static/src/xml/leave_stats_templates.xml
new file mode 100644
index 00000000000..594ff0e6b61
--- /dev/null
+++ b/addons/hr_holidays/static/src/xml/leave_stats_templates.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<templates id="template" xml:space="preserve">
+ <t t-name="hr_holidays.leave_per_type">
+ <table class="o_group o_inner_group table-striped">
+ <thead>
+ <tr>
+ <td colspan="2">
+ <div class="o_horizontal_separator">Time Off taken by <t t-esc="widget.employee.data.display_name"/> in <t t-esc="widget.date.format('YYYY')"/></div>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <t t-if="widget.leavesPerType.length === 0">
+ <tr>
+ <td>None</td>
+ </tr>
+ </t>
+ <t t-foreach="widget.leavesPerType" t-as="leave_type">
+ <tr>
+ <td><t t-esc="leave_type.holiday_status_id[1]"/></td>
+ <td class="w-50"><t t-esc="leave_type.number_of_days"/></td>
+ </tr>
+ </t>
+ </tbody>
+ </table>
+ </t>
+
+ <t t-name="hr_holidays.department_leave">
+ <table class="o_group o_inner_group table-striped">
+ <thead>
+ <tr>
+ <td colspan="2">
+ <div class="o_horizontal_separator">In <t t-esc="widget.date.format('MMMM')"/> for <t t-esc="widget.department.data.display_name"/> Department
+ </div>
+ </td>
+ </tr>
+ </thead>
+ <tbody>
+ <t t-if="widget.departmentLeaves.length === 0">
+ <tr>
+ <td>None</td>
+ </tr>
+ </t>
+ <t t-foreach="widget.departmentLeaves" t-as="leave">
+ <tr t-attf-class="{{leave.employee_id[0] === widget.employee.res_id ? 'font-weight-bold' : ''}}">
+ <td><t t-esc="leave.employee_id[1]"/></td>
+ <td class="w-50"><t t-esc="leave.date_from"/> - <t t-esc="leave.date_to"/></td>
+ </tr>
+ </t>
+ </tbody>
+ </table>
+ </t>
+
+ <div t-name="hr_holidays.leave_stats" class="o_leave_stats">
+ <t t-if="widget.leavesPerType">
+ <t t-call="hr_holidays.leave_per_type"/>
+ </t>
+ <t t-if="widget.departmentLeaves">
+ <t t-call="hr_holidays.department_leave"/>
+ </t>
+ </div>
+
+</templates>