From f6fdde96bddfedf8764cd8a73089e44340d476ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lucas=20Lef=C3=A8vre?= Date: Mon, 24 Jun 2019 07:28:44 +0000 Subject: [PATCH] [IMP] hr_*: Improve employee presence state Purpose ======= There are currently multiple ways to determine if an employee is presence or absent: - login state (user status on chat) - checkin/checkout from the Attendance app - leave - hr_presence (email, ip) Those states are displayed in several different places and can be inconsitent. e.g. Logged in -> green in the chat Not checked in -> red on the employee kanban view. The multiple ways to determine employee presence described above should be aggregated together to have a better consistency. Specification ============= There are 3 presence states (previously defined in module `hr_presence`), namely `present`, `absent`, `to_define`. They are now defined as soon as `hr` module is installed. The state computation can have several behaviors according to which apps are installed: 1. `hr` is installed Check employee presence based on login by default (only for employees with a user). Kanban state (private and public): should be green when logged in, red when logged out and orange when the user is away. Private employee form: Display a stat button "Connected" on the form view when the user is logged in or "Last Activity xx/xx/xxx" otherwise. 2. `hr_attendance` is installed: when the user is logged out, the state is determined from the checked in/out state. But when the user is logged in, consider the user as present (even if he checked out). Kanban state: green for checked in, red for checked out Private form view: attendance stat button. 3. `hr_holidays` is installed: Has the highest priority if the employee is on leave. Kanban state: red if employee is on leave. Private form view: stat button "Absent Until xx/xx/xxxx" if employee is on leave. 4. `hr_presence` is installed: Two additionnal presence checking option: emails sent and IP address connected. Once a user sent an email or the IP address was connected, he is considered present for the entire day. There should be at most one stat button on the employee form view, with the relevent information. Task id: 2024482 closes odoo/odoo#34933 Signed-off-by: Yannick Tivisse (yti) --- addons/hr/data/hr_data.xml | 5 ++ addons/hr/models/__init__.py | 1 + addons/hr/models/hr_employee_base.py | 28 +++++++ addons/hr/models/res_company.py | 11 +++ addons/hr/models/res_config_settings.py | 9 ++- addons/hr/models/res_users.py | 4 + addons/hr/views/hr_employee_public_views.xml | 10 +++ addons/hr/views/hr_employee_views.xml | 41 ++++++++++- addons/hr/views/res_config_settings_views.xml | 44 +++++++++++ addons/hr/views/res_users.xml | 22 ++++++ addons/hr_attendance/models/hr_employee.py | 14 ++++ .../hr_attendance/views/hr_employee_view.xml | 51 ++++++------- addons/hr_attendance_presence/__init__.py | 1 - addons/hr_attendance_presence/__manifest__.py | 19 ----- .../hr_attendance_presence/models/__init__.py | 2 - .../models/hr_employee.py | 32 -------- .../models/res_config_settings.py | 9 --- .../views/hr_employee.xml | 21 ------ .../views/res_config_settings_views.xml | 17 ----- addons/hr_holidays/models/hr_employee.py | 5 ++ addons/hr_holidays/models/hr_leave.py | 1 - addons/hr_holidays/views/hr_views.xml | 17 +++-- addons/hr_holidays_attendance/__init__.py | 0 addons/hr_holidays_attendance/__manifest__.py | 15 ++++ .../views/hr_employee_views.xml | 64 ++++++++++++++++ addons/hr_presence/__manifest__.py | 1 - addons/hr_presence/models/__init__.py | 1 - addons/hr_presence/models/hr_employee.py | 73 ++++++++++--------- addons/hr_presence/models/res_company.py | 3 - .../hr_presence/models/res_config_settings.py | 13 ---- .../hr_presence/views/hr_employee_views.xml | 62 +--------------- .../views/res_config_settings_views.xml | 44 ----------- 32 files changed, 339 insertions(+), 301 deletions(-) create mode 100644 addons/hr/models/res_company.py delete mode 100644 addons/hr_attendance_presence/__init__.py delete mode 100644 addons/hr_attendance_presence/__manifest__.py delete mode 100644 addons/hr_attendance_presence/models/__init__.py delete mode 100644 addons/hr_attendance_presence/models/hr_employee.py delete mode 100644 addons/hr_attendance_presence/models/res_config_settings.py delete mode 100644 addons/hr_attendance_presence/views/hr_employee.xml delete mode 100644 addons/hr_attendance_presence/views/res_config_settings_views.xml create mode 100644 addons/hr_holidays_attendance/__init__.py create mode 100644 addons/hr_holidays_attendance/__manifest__.py create mode 100644 addons/hr_holidays_attendance/views/hr_employee_views.xml delete mode 100644 addons/hr_presence/models/res_config_settings.py delete mode 100644 addons/hr_presence/views/res_config_settings_views.xml diff --git a/addons/hr/data/hr_data.xml b/addons/hr/data/hr_data.xml index ab7165fee25..8c70cef3e68 100644 --- a/addons/hr/data/hr_data.xml +++ b/addons/hr/data/hr_data.xml @@ -99,5 +99,10 @@ + + hr.hr_presence_control_login + True + + diff --git a/addons/hr/models/__init__.py b/addons/hr/models/__init__.py index efe3c1d702c..43857d3d83e 100644 --- a/addons/hr/models/__init__.py +++ b/addons/hr/models/__init__.py @@ -13,3 +13,4 @@ from . import mail_channel from . import res_config_settings from . import res_partner from . import res_users +from . import res_company diff --git a/addons/hr/models/hr_employee_base.py b/addons/hr/models/hr_employee_base.py index fec003ccfc6..191a5cb926d 100644 --- a/addons/hr/models/hr_employee_base.py +++ b/addons/hr/models/hr_employee_base.py @@ -23,3 +23,31 @@ class HrEmployeeBase(models.AbstractModel): user_id = fields.Many2one('res.users') resource_id = fields.Many2one('resource.resource') resource_calendar_id = fields.Many2one('resource.calendar') + hr_presence_state = fields.Selection([ + ('present', 'Present'), + ('absent', 'Absent'), + ('to_define', 'To Define')], compute='_compute_presence_state', default='to_define') + last_activity = fields.Date(compute="_compute_last_activity") + + def _compute_presence_state(self): + """ + This method is overritten in several other modules which add additional + presence criterions. e.g. hr_attendance, hr_holidays + """ + # Check on login + check_login = self.env['ir.config_parameter'].sudo().get_param('hr.hr_presence_control_login') + for employee in self: + state = 'to_define' + if check_login: + if employee.user_id.im_status == 'online': + state = 'present' + elif employee.user_id.im_status == 'offline': + state = 'absent' + employee.hr_presence_state = state + + def _compute_last_activity(self): + employees = self.filtered(lambda e: e.user_id) + presences = self.env['bus.presence'].search([('user_id', 'in', employees.mapped('user_id.id'))]) + + for presence in presences: + presence.user_id.employee_ids.last_activity = presence.last_presence.date() diff --git a/addons/hr/models/res_company.py b/addons/hr/models/res_company.py new file mode 100644 index 00000000000..cd1937d639e --- /dev/null +++ b/addons/hr/models/res_company.py @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models + + +class Company(models.Model): + _inherit = 'res.company' + + hr_presence_control_email_amount = fields.Integer(string="# emails to send") + hr_presence_control_ip_list = fields.Char(string="Valid IP addresses") diff --git a/addons/hr/models/res_config_settings.py b/addons/hr/models/res_config_settings.py index 14494a6318e..a75564bc122 100644 --- a/addons/hr/models/res_config_settings.py +++ b/addons/hr/models/res_config_settings.py @@ -10,9 +10,12 @@ class ResConfigSettings(models.TransientModel): 'resource.calendar', 'Company Working Hours', related='company_id.resource_calendar_id', readonly=False) module_hr_org_chart = fields.Boolean(string="Show Organizational Chart") - module_hr_presence = fields.Boolean(string="Control presence of employees") + module_hr_presence = fields.Boolean(string="Advanced control presence of employees") module_hr_skills = fields.Boolean(string="Employee Skills and Resumé") hr_presence_control_login = fields.Boolean(string="According to the system login (User status on chat)", config_parameter='hr.hr_presence_control_login') - hr_presence_control_email = fields.Boolean(string="According to the amount of sent emails", config_parameter='hr.hr_presence_control_email') - hr_presence_control_ip = fields.Boolean(string="According to the IP address", config_parameter='hr.hr_presence_control_ip') + hr_presence_control_email = fields.Boolean(string="According to the amount of sent emails", config_parameter='hr_presence.hr_presence_control_email') + hr_presence_control_ip = fields.Boolean(string="According to the IP address", config_parameter='hr_presence.hr_presence_control_ip') + module_hr_attendance = fields.Boolean(string="According to the Attendance module.") + hr_presence_control_email_amount = fields.Integer(related="company_id.hr_presence_control_email_amount", readonly=False) + hr_presence_control_ip_list = fields.Char(related="company_id.hr_presence_control_ip_list", readonly=False) hr_employee_self_edit = fields.Boolean(string="Employee Edition", config_parameter='hr.hr_employee_self_edit') diff --git a/addons/hr/models/res_users.py b/addons/hr/models/res_users.py index 0e663a8d62b..c64f678070a 100644 --- a/addons/hr/models/res_users.py +++ b/addons/hr/models/res_users.py @@ -52,6 +52,8 @@ class User(models.Model): study_field = fields.Char(related='employee_id.study_field', readonly=False, related_sudo=False) study_school = fields.Char(related='employee_id.study_school', readonly=False, related_sudo=False) employee_count = fields.Integer(compute='_compute_employee_count') + hr_presence_state = fields.Selection(related='employee_id.hr_presence_state') + last_activity = fields.Date(related='employee_id.last_activity') @api.depends('employee_ids') def _compute_employee_count(self): @@ -69,6 +71,8 @@ class User(models.Model): 'employee_id', 'employee_ids', 'employee_parent_id', + 'hr_presence_state', + 'last_activity', ] hr_writable_fields = [ diff --git a/addons/hr/views/hr_employee_public_views.xml b/addons/hr/views/hr_employee_public_views.xml index fa8582f3015..95b277b30f0 100644 --- a/addons/hr/views/hr_employee_public_views.xml +++ b/addons/hr/views/hr_employee_public_views.xml @@ -95,6 +95,7 @@ +
@@ -107,6 +108,15 @@
+
+ +
+
+ +
+
+ +
diff --git a/addons/hr/views/hr_employee_views.xml b/addons/hr/views/hr_employee_views.xml index d7e52389246..56a96215194 100644 --- a/addons/hr/views/hr_employee_views.xml +++ b/addons/hr/views/hr_employee_views.xml @@ -40,14 +40,37 @@
+
- -
- + +
+ +
+
+
+ +
+ Control presence of employees +
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+
+

Work Organization

diff --git a/addons/hr/views/res_users.xml b/addons/hr/views/res_users.xml index d417b93341b..1eb980c20f3 100644 --- a/addons/hr/views/res_users.xml +++ b/addons/hr/views/res_users.xml @@ -36,9 +36,31 @@ +
+
diff --git a/addons/hr_attendance/models/hr_employee.py b/addons/hr_attendance/models/hr_employee.py index 62b7def6d2c..fa5d788c210 100644 --- a/addons/hr_attendance/models/hr_employee.py +++ b/addons/hr_attendance/models/hr_employee.py @@ -17,6 +17,20 @@ class HrEmployeeBase(models.AbstractModel): hours_last_month = fields.Float(compute='_compute_hours_last_month') hours_today = fields.Float(compute='_compute_hours_today') + def _compute_presence_state(self): + """ + Override to include checkin/checkout in the presence state + Attendance has the second highest priority after login + """ + super()._compute_presence_state() + employees = self.filtered(lambda employee: employee.hr_presence_state != 'present') + for employee in employees: + if employee.attendance_state == 'checked_out' and employee.hr_presence_state == 'to_define': + employee.hr_presence_state = 'absent' + for employee in employees: + if employee.attendance_state == 'checked_in': + employee.hr_presence_state = 'present' + def _compute_hours_last_month(self): for employee in self: now = datetime.now() diff --git a/addons/hr_attendance/views/hr_employee_view.xml b/addons/hr_attendance/views/hr_employee_view.xml index f03fddc1730..ad1d073dd60 100644 --- a/addons/hr_attendance/views/hr_employee_view.xml +++ b/addons/hr_attendance/views/hr_employee_view.xml @@ -4,14 +4,24 @@ hr.employee hr.employee - 10 + 20 -
+ + + {'invisible': ['|', '|', ('user_id', '=', False), ('hr_presence_state', '=', 'absent'), ('attendance_state', '=', 'checked_in')]} + + + -
+
@@ -43,11 +53,17 @@ res.users + + + {'invisible': ['|', '|', ('hr_presence_state', '=', 'absent'), ('attendance_state', '=', 'checked_in')]} + + -
-
- - hr.employee.view.kanban hr.employee @@ -54,7 +23,7 @@
- + @@ -64,31 +33,4 @@ - - hr.employee - hr.employee - - 1 - - - - - -
-
- -
-
- -
-
- -
- - - -
-
-
-
diff --git a/addons/hr_presence/views/res_config_settings_views.xml b/addons/hr_presence/views/res_config_settings_views.xml deleted file mode 100644 index 9f447753925..00000000000 --- a/addons/hr_presence/views/res_config_settings_views.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - res.config.settings.view.form.inherit.hr - res.config.settings - - - - -
-
- -
-
-
-
-
-
-
-