diff options
| author | Lucas Lefèvre <lul@odoo.com> | 2019-06-24 07:28:44 +0000 |
|---|---|---|
| committer | Yannick Tivisse <yti@odoo.com> | 2019-07-17 09:11:18 +0000 |
| commit | f6fdde96bddfedf8764cd8a73089e44340d476ae (patch) | |
| tree | c7acd4cccf2286e7ba333f5da501e674ec643227 /addons/hr_presence | |
| parent | 008634730adb0e834e759d5f356b8978e083395a (diff) | |
[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) <yti@odoo.com>
Diffstat (limited to 'addons/hr_presence')
| -rw-r--r-- | addons/hr_presence/__manifest__.py | 1 | ||||
| -rw-r--r-- | addons/hr_presence/models/__init__.py | 1 | ||||
| -rw-r--r-- | addons/hr_presence/models/hr_employee.py | 77 | ||||
| -rw-r--r-- | addons/hr_presence/models/res_company.py | 3 | ||||
| -rw-r--r-- | addons/hr_presence/models/res_config_settings.py | 13 | ||||
| -rw-r--r-- | addons/hr_presence/views/hr_employee_views.xml | 62 | ||||
| -rw-r--r-- | addons/hr_presence/views/res_config_settings_views.xml | 44 |
7 files changed, 42 insertions, 159 deletions
diff --git a/addons/hr_presence/__manifest__.py b/addons/hr_presence/__manifest__.py index d0fdba48fff..05b27fca5ca 100644 --- a/addons/hr_presence/__manifest__.py +++ b/addons/hr_presence/__manifest__.py @@ -18,7 +18,6 @@ Allows to contact directly the employee in case of unjustified absence. """, 'depends': ['hr', 'hr_holidays', 'sms'], 'data': [ - 'views/res_config_settings_views.xml', 'data/ir_actions_server.xml', 'views/hr_employee_views.xml', 'data/sms_data.xml', diff --git a/addons/hr_presence/models/__init__.py b/addons/hr_presence/models/__init__.py index 996b19c0a45..76aa02054b2 100644 --- a/addons/hr_presence/models/__init__.py +++ b/addons/hr_presence/models/__init__.py @@ -3,5 +3,4 @@ from . import hr_employee from . import res_company -from . import res_config_settings from . import res_users_log diff --git a/addons/hr_presence/models/hr_employee.py b/addons/hr_presence/models/hr_employee.py index 2f0c55a9571..9860fa0ec1b 100644 --- a/addons/hr_presence/models/hr_employee.py +++ b/addons/hr_presence/models/hr_employee.py @@ -10,21 +10,28 @@ from odoo.fields import Datetime _logger = logging.getLogger(__name__) -class ResCompany(models.Model): - _inherit = 'hr.employee' +class Employee(models.AbstractModel): + _inherit = 'hr.employee.base' - hr_presence_state = fields.Selection([ + email_sent = fields.Boolean(default=False) + ip_connected = fields.Boolean(default=False) + manually_set_present = fields.Boolean(default=False) + + # Stored field used in the presence kanban reporting view + # to allow group by state. + hr_presence_state_display = fields.Selection([ ('present', 'Present'), ('absent', 'Absent'), - ('to_define', 'To Define')], groups="hr.group_hr_user", default='to_define') - last_activity = fields.Date(compute="_compute_last_activity") - - 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'))]) + ('to_define', 'To Define')]) - for presence in presences: - presence.user_id.employee_ids.last_activity = presence.last_presence.date() + def _compute_presence_state(self): + super()._compute_presence_state() + employees = self.filtered(lambda employee: employee.hr_presence_state != 'present' and not employee.is_absent) + company = self.env.company + for employee in employees: + if not employee.is_absent and company.hr_presence_last_compute_date and company.hr_presence_last_compute_date.day == Datetime.now().day and \ + (employee.email_sent or employee.ip_connected or employee.manually_set_present): + employee.hr_presence_state = 'present' @api.model def _check_presence(self): @@ -32,28 +39,16 @@ class ResCompany(models.Model): if not company.hr_presence_last_compute_date or \ company.hr_presence_last_compute_date.day != Datetime.now().day: self.env['hr.employee'].search([ - ('department_id.company_id', '=', company.id) - ]).write({'hr_presence_state': 'to_define'}) - - employees = self.env['hr.employee'].search([ - ('department_id.company_id', '=', company.id), - ('user_id', '!=', False), - ('hr_presence_state', '=', 'to_define')]) - - # Remove employees on holidays - leaves = self.env['hr.leave'].search([ - ('state', '=', 'validate'), - ('date_from', '<=', Datetime.to_string(Datetime.now())), - ('date_to', '>=', Datetime.to_string(Datetime.now()))]) - employees_on_holiday = leaves.mapped('employee_id') - employees_on_holiday.write({'hr_presence_state': 'absent'}) - employees = employees - employees_on_holiday - - # Check on system login - if self.env['ir.config_parameter'].sudo().get_param('hr_presence.hr_presence_control_login'): - online_employees = employees.filtered(lambda employee: employee.user_id.im_status in ['away', 'online']) - online_employees.write({'hr_presence_state': 'present'}) - employees = employees - online_employees + ('company_id', '=', company.id) + ]).write({ + 'email_sent': False, + 'ip_connected': False, + 'manually_set_present': False + }) + + employees = self.env['hr.employee'].search([('company_id', '=', company.id)]) + all_employees = employees + # Check on IP if self.env['ir.config_parameter'].sudo().get_param('hr_presence.hr_presence_control_ip'): @@ -68,7 +63,7 @@ class ResCompany(models.Model): ).mapped('ip') if any([ip in ip_list for ip in employee_ips]): ip_employees |= employee - ip_employees.write({'hr_presence_state': 'present'}) + ip_employees.write({'ip_connected': True}) employees = employees - ip_employees # Check on sent emails @@ -82,10 +77,13 @@ class ResCompany(models.Model): ('date', '<=', Datetime.to_string(Datetime.now()))]) if sent_emails >= threshold: email_employees |= employee - email_employees.write({'hr_presence_state': 'present'}) + email_employees.write({'email_sent': True}) employees = employees - email_employees - company.hr_presence_last_compute_date = Datetime.now() + company.sudo().hr_presence_last_compute_date = Datetime.now() + + for employee in all_employees: + employee.hr_presence_state_display = employee.hr_presence_state @api.model def _action_open_presence_view(self): @@ -110,7 +108,12 @@ class ResCompany(models.Model): def action_set_present(self): if not self.env.user.has_group('hr.group_hr_manager'): raise UserError(_("You don't have the right to do this. Please contact an Administrator.")) - self.write({'hr_presence_state': 'present'}) + self.write({'manually_set_present': True}) + + def write(self, vals): + if vals.get('hr_presence_state_display') == 'present': + vals['manually_set_present'] = True + return super().write(vals) def action_open_leave_request(self): self.ensure_one() diff --git a/addons/hr_presence/models/res_company.py b/addons/hr_presence/models/res_company.py index 7de70604a92..834fd26cf21 100644 --- a/addons/hr_presence/models/res_company.py +++ b/addons/hr_presence/models/res_company.py @@ -7,7 +7,4 @@ from odoo import fields, models class ResCompany(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") hr_presence_last_compute_date = fields.Datetime() diff --git a/addons/hr_presence/models/res_config_settings.py b/addons/hr_presence/models/res_config_settings.py deleted file mode 100644 index e831d7506bb..00000000000 --- a/addons/hr_presence/models/res_config_settings.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- - -from odoo import fields, models - - -class ResConfigSettings(models.TransientModel): - _inherit = 'res.config.settings' - - hr_presence_control_login = fields.Boolean(string="According to the system login (User status on chat)", config_parameter='hr_presence.hr_presence_control_login') - 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') - 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) diff --git a/addons/hr_presence/views/hr_employee_views.xml b/addons/hr_presence/views/hr_employee_views.xml index 6f226b3dc71..61cb26184f8 100644 --- a/addons/hr_presence/views/hr_employee_views.xml +++ b/addons/hr_presence/views/hr_employee_views.xml @@ -7,42 +7,11 @@ <field name="groups_id" eval="[(4, ref('hr.group_hr_manager'))]"/> <field name="arch" type="xml"> <filter name="group_manager" position="after"> - <filter name="group_hr_presence_state" string="Presence" domain="[]" context="{'group_by':'hr_presence_state'}"/> + <filter name="group_hr_presence_state" string="Presence" domain="[]" context="{'group_by':'hr_presence_state_display'}"/> </filter> </field> </record> - <record id="hr_employee_view_form" model="ir.ui.view"> - <field name="name">hr.employee.presence.form.inherit</field> - <field name="model">hr.employee</field> - <field name="inherit_id" ref="hr.view_employee_form"/> - <field name="groups_id" eval="[(4, ref('hr.group_hr_manager'))]"/> - <field name="arch" type="xml"> - <div name="button_box" position="inside"> - <field name="hr_presence_state" invisible="1"/> - <button name="%(ir_actions_server_action_open_presence_view)d" - class="oe_stat_button" - type="action" - attrs="{'invisible': ['|', ('hr_presence_state', '=', 'absent'), ('user_id', '=', False)]}"> - <div role="img" class="fa fa-fw fa-circle text-success o_button_icon" attrs="{'invisible': [('hr_presence_state', '!=', 'present')]}" aria-label="Available" title="Available"/> - <div role="img" class="fa fa-fw fa-circle text-warning o_button_icon" attrs="{'invisible': [('hr_presence_state', '!=', 'to_define')]}" aria-label="Not available" title="Not available"/> - - <div class="o_stat_info" attrs="{'invisible': [('hr_presence_state', '!=', 'to_define')]}"> - <span class="o_stat_value"> - <field name="last_activity"/> - </span> - <span class="o_stat_text"> - Last Activity - </span> - </div> - <div class="o_stat_info" attrs="{'invisible': [('hr_presence_state', '!=', 'present')]}"> - <span class="o_stat_text">Connected</span> - </div> - </button> - </div> - </field> - </record> - <record id="hr_employee_view_kanban" model="ir.ui.view"> <field name="name">hr.employee.view.kanban</field> <field name="model">hr.employee</field> @@ -54,7 +23,7 @@ <div class="o_kanban_record_bottom"> <div class="oe_kanban_bottom_left"> <field name="hr_presence_state" invisible="1"/> - <button name="action_set_present" type="object" class="btn btn-primary btn-sm mt8 fa fa-check" title="Set as present" attrs="{'invisible': [('hr_presence_state', '!=', 'to_define')]}"> </button> + <button name="action_set_present" type="object" class="btn btn-primary btn-sm mt8 fa fa-check" title="Set as present" attrs="{'invisible': [('hr_presence_state', '=', 'present')]}"> </button> <button name="action_open_leave_request" type="object" class="btn btn-primary btn-sm mt8">Time Off</button> <button name="action_send_sms" type="object" class="btn btn-primary btn-sm mt8">SMS</button> <button name="action_send_mail" type="object" class="btn btn-primary btn-sm mt8">Mail</button> @@ -64,31 +33,4 @@ </field> </record> - <record id="hr_employee_view_kanban_inherit" model="ir.ui.view"> - <field name="name">hr.employee</field> - <field name="model">hr.employee</field> - <field name="inherit_id" ref="hr.hr_kanban_view_employees"/> - <field name="priority">1</field> - <field name="arch" type="xml"> - <templates position="before"> - <field name="hr_presence_state"/> - </templates> - <field name="name" position="replace"> - <div> - <div class="float-right" t-if="record.hr_presence_state.raw_value == 'present'"> - <span class="fa fa-circle text-success" role="img" aria-label="Present" title="Present" name="presence_present"></span> - </div> - <div class="float-right" t-if="record.hr_presence_state.raw_value == 'absent'"> - <span class="fa fa-circle text-danger" role="img" aria-label="Absent" title="Absent" name="presence_absent"></span> - </div> - <div class="float-right" t-if="record.hr_presence_state.raw_value == 'to_define'"> - <span class="fa fa-circle text-warning" role="img" aria-label="To define" title="To define" name="presence_to_define"></span> - </div> - <strong> - <field name="name" placeholder="Employee's Name"/> - </strong> - </div> - </field> - </field> - </record> </odoo> 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 @@ -<?xml version="1.0" encoding="utf-8"?> -<odoo> - <record id="res_config_settings_view_form" model="ir.ui.view"> - <field name="name">res.config.settings.view.form.inherit.hr</field> - <field name="model">res.config.settings</field> - <field name="priority" eval="70"/> - <field name="inherit_id" ref="hr.res_config_settings_view_form"/> - <field name="arch" type="xml"> - <xpath expr="//div[@data-key='hr']//div[hasclass('o_settings_container')]" position="inside"> - <div class="col-12 col-lg-6 o_setting_box" title="Control presence of employees"> - <div class="o_setting_left_pane"> - <field name="module_hr_presence"/> - </div> - <div class="o_setting_right_pane"> - <label for="module_hr_presence"/> - <div class="content-group" name="hr_presence_options"> - <div class="row"> - <field name="hr_presence_control_login" class="col-lg-1 ml16"/> - <label for="hr_presence_control_login" class="col-lg-10 o_light_label"/> - </div> - <div class="row"> - <field name="hr_presence_control_email" class="col-lg-1 ml16"/> - <label for="hr_presence_control_email" class="col-lg-10 o_light_label"/> - </div> - <div attrs="{'invisible': [('hr_presence_control_email', '=', False)]}"> - <span>At least </span> - <field name="hr_presence_control_email_amount"/> - <span> sent emails</span> - </div> - <div class="row"> - <field name="hr_presence_control_ip" class="col-lg-1 ml16"/> - <label for="hr_presence_control_ip" class="col-lg-10 o_light_label"/> - </div> - <div attrs="{'invisible': [('hr_presence_control_ip', '=', False)]}"> - <span>IP List (comma separated):</span> - <field name="hr_presence_control_ip_list"/> - </div> - </div> - </div> - </div> - </xpath> - </field> - </record> -</odoo> |
