mirror of
https://github.com/bringout/oca-ocb-hr.git
synced 2026-04-26 15:32:01 +02:00
19.0 vanilla
This commit is contained in:
parent
a1137a1456
commit
e1d89e11e3
2789 changed files with 1093187 additions and 605897 deletions
|
|
@ -1,7 +1,8 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import hr_employee
|
||||
from . import hr_employee_public
|
||||
from . import ir_websocket
|
||||
from . import res_company
|
||||
from . import res_users_log
|
||||
from . import res_config_settings
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import logging
|
||||
|
|
@ -11,59 +10,44 @@ from odoo.fields import Datetime
|
|||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Employee(models.AbstractModel):
|
||||
_inherit = 'hr.employee.base'
|
||||
class HrEmployee(models.Model):
|
||||
_inherit = 'hr.employee'
|
||||
|
||||
email_sent = fields.Boolean(default=False)
|
||||
ip_connected = fields.Boolean(default=False)
|
||||
manually_set_present = fields.Boolean(default=False)
|
||||
manually_set_presence = fields.Boolean(default=False)
|
||||
|
||||
# Stored field used in the presence kanban reporting view
|
||||
# to allow group by state.
|
||||
hr_presence_state_display = fields.Selection([
|
||||
('to_define', 'To Define'),
|
||||
('out_of_working_hour', 'Off-Hours'),
|
||||
('present', 'Present'),
|
||||
('absent', 'Absent'),
|
||||
])
|
||||
|
||||
def _compute_presence_state(self):
|
||||
super()._compute_presence_state()
|
||||
employees = self.filtered(lambda e: e.hr_presence_state != 'present' and not e.is_absent)
|
||||
company = self.env.company
|
||||
employee_to_check_working = employees.filtered(lambda e:
|
||||
not e.is_absent and
|
||||
(e.email_sent or e.ip_connected or e.manually_set_present))
|
||||
working_now_list = employee_to_check_working._get_employee_working_now()
|
||||
for employee in employees:
|
||||
if not employee.is_absent and company.hr_presence_last_compute_date and employee.id in working_now_list 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'
|
||||
], default='out_of_working_hour')
|
||||
|
||||
@api.model
|
||||
def _check_presence(self):
|
||||
company = self.env.company
|
||||
if not company.hr_presence_last_compute_date or \
|
||||
company.hr_presence_last_compute_date.day != Datetime.now().day:
|
||||
self.env['hr.employee'].search([
|
||||
('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)])
|
||||
|
||||
employees.write({
|
||||
'email_sent': False,
|
||||
'ip_connected': False,
|
||||
'manually_set_present': False,
|
||||
'manually_set_presence': False,
|
||||
})
|
||||
|
||||
all_employees = employees
|
||||
|
||||
|
||||
# Check on IP
|
||||
if literal_eval(self.env['ir.config_parameter'].sudo().get_param('hr_presence.hr_presence_control_ip', 'False')):
|
||||
if company.hr_presence_control_ip:
|
||||
ip_list = company.hr_presence_control_ip_list
|
||||
ip_list = ip_list.split(',') if ip_list else []
|
||||
ip_employees = self.env['hr.employee']
|
||||
for employee in employees:
|
||||
employee_ips = self.env['res.users.log'].search([
|
||||
employee_ips = self.env['res.users.log'].sudo().search([
|
||||
('create_uid', '=', employee.user_id.id),
|
||||
('ip', '!=', False),
|
||||
('create_date', '>=', Datetime.to_string(Datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)))]
|
||||
|
|
@ -74,7 +58,7 @@ class Employee(models.AbstractModel):
|
|||
employees = employees - ip_employees
|
||||
|
||||
# Check on sent emails
|
||||
if literal_eval(self.env['ir.config_parameter'].sudo().get_param('hr_presence.hr_presence_control_email', 'False')):
|
||||
if company.hr_presence_control_email:
|
||||
email_employees = self.env['hr.employee']
|
||||
threshold = company.hr_presence_control_email_amount
|
||||
for employee in employees:
|
||||
|
|
@ -92,32 +76,27 @@ class Employee(models.AbstractModel):
|
|||
for employee in all_employees:
|
||||
employee.hr_presence_state_display = employee.hr_presence_state
|
||||
|
||||
@api.model
|
||||
def _action_open_presence_view(self):
|
||||
# Compute the presence/absence for the employees on the same
|
||||
# company than the HR/manager. Then opens the kanban view
|
||||
# of the employees with an undefined presence/absence
|
||||
|
||||
_logger.info("Employees presence checked by: %s" % self.env.user.name)
|
||||
|
||||
self._check_presence()
|
||||
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "hr.employee",
|
||||
"views": [[self.env.ref('hr_presence.hr_employee_view_kanban').id, "kanban"], [False, "tree"], [False, "form"]],
|
||||
'view_mode': 'kanban,tree,form',
|
||||
"domain": [],
|
||||
"name": _("Employee's Presence to Define"),
|
||||
"search_view_id": [self.env.ref('hr_presence.hr_employee_view_presence_search').id, 'search'],
|
||||
"context": {'search_default_group_hr_presence_state': 1,
|
||||
'searchpanel_default_hr_presence_state_display': 'to_define'},
|
||||
}
|
||||
def get_presence_server_action_data(self):
|
||||
server_action_xmlids = [
|
||||
'action_hr_employee_presence_present',
|
||||
'action_hr_employee_presence_absent',
|
||||
'action_hr_employee_presence_log',
|
||||
'action_hr_employee_presence_sms',
|
||||
'action_hr_employee_presence_time_off',
|
||||
]
|
||||
actions = self.env['ir.actions.server'].sudo()
|
||||
for xmlid in server_action_xmlids:
|
||||
actions += actions.env.ref(f"hr_presence.{xmlid}")
|
||||
return actions.read(['id', 'value'])
|
||||
|
||||
def _action_set_manual_presence(self, state):
|
||||
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({'manually_set_present': state})
|
||||
self.write({
|
||||
'manually_set_present': state,
|
||||
'manually_set_presence': True,
|
||||
"hr_presence_state_display": 'present' if state else 'absent',
|
||||
})
|
||||
|
||||
def action_set_present(self):
|
||||
self._action_set_manual_presence(True)
|
||||
|
|
@ -131,13 +110,25 @@ class Employee(models.AbstractModel):
|
|||
return super().write(vals)
|
||||
|
||||
def action_open_leave_request(self):
|
||||
self.ensure_one()
|
||||
if len(self) == 1:
|
||||
model = 'hr.leave'
|
||||
context = {'default_employee_id': self.id}
|
||||
else:
|
||||
model = 'hr.leave.generate.multi.wizard'
|
||||
context = {
|
||||
'default_employee_ids': self.ids,
|
||||
'default_date_from': fields.Date.today(),
|
||||
'default_date_to': fields.Date.today(),
|
||||
'default_name': _('Unplanned Absence'),
|
||||
}
|
||||
|
||||
return {
|
||||
"type": "ir.actions.act_window",
|
||||
"res_model": "hr.leave",
|
||||
"views": [[False, "form"]],
|
||||
"view_mode": 'form',
|
||||
"context": {'default_employee_id': self.id},
|
||||
'type': 'ir.actions.act_window',
|
||||
'res_model': model,
|
||||
'views': [[False, 'form']],
|
||||
'view_mode': 'form',
|
||||
'context': context,
|
||||
'target': 'new',
|
||||
}
|
||||
|
||||
# --------------------------------------------------
|
||||
|
|
@ -145,20 +136,17 @@ class Employee(models.AbstractModel):
|
|||
# --------------------------------------------------
|
||||
|
||||
def action_send_sms(self):
|
||||
self.ensure_one()
|
||||
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."))
|
||||
if not self.mobile_phone:
|
||||
raise UserError(_("There is no professional mobile for this employee."))
|
||||
|
||||
context = dict(self.env.context)
|
||||
context.update(default_res_model='hr.employee', default_res_id=self.id, default_composition_mode='comment', default_number_field_name='mobile_phone')
|
||||
context.update(default_res_model='hr.employee', default_res_ids=self.ids, default_composition_mode='mass', default_number_field_name='mobile_phone', default_mass_keep_log=True)
|
||||
|
||||
template = self.env.ref('hr_presence.sms_template_presence', False)
|
||||
if not template:
|
||||
context['default_body'] = _("""Exception made if there was a mistake of ours, it seems that you are not at your office and there is not request of time off from you.
|
||||
Please, take appropriate measures in order to carry out this work absence.
|
||||
Do not hesitate to contact your manager or the human resource department.""")
|
||||
context['default_body'] = _("""We hope this message finds you well. It has come to our attention that you are currently not present at work, and there is no record of a time off request from you. If this absence is due to an oversight on our part, we sincerely apologize for any confusion.
|
||||
Please take the necessary steps to address this unplanned absence. Should you have any questions or need assistance, do not hesitate to reach out to your manager or the HR department at your earliest convenience.
|
||||
Thank you for your prompt attention to this matter.""")
|
||||
else:
|
||||
context['default_template_id'] = template.id
|
||||
|
||||
|
|
@ -167,34 +155,38 @@ Do not hesitate to contact your manager or the human resource department.""")
|
|||
"res_model": "sms.composer",
|
||||
"view_mode": 'form',
|
||||
"context": context,
|
||||
"name": _("Send SMS Text Message"),
|
||||
"name": self.env._("Send SMS"),
|
||||
"target": "new",
|
||||
}
|
||||
|
||||
def action_send_mail(self):
|
||||
self.ensure_one()
|
||||
def action_send_log(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."))
|
||||
if not self.work_email:
|
||||
raise UserError(_("There is no professional email address for this employee."))
|
||||
template = self.env.ref('hr_presence.mail_template_presence', False)
|
||||
compose_form = self.env.ref('mail.email_compose_message_wizard_form', False)
|
||||
ctx = dict(
|
||||
default_model="hr.employee",
|
||||
default_res_id=self.id,
|
||||
default_use_template=bool(template),
|
||||
default_template_id=template.id,
|
||||
default_composition_mode='comment',
|
||||
default_is_log=True,
|
||||
default_email_layout_xmlid='mail.mail_notification_light',
|
||||
)
|
||||
return {
|
||||
'name': _('Compose Email'),
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_mode': 'form',
|
||||
'res_model': 'mail.compose.message',
|
||||
'views': [(compose_form.id, 'form')],
|
||||
'view_id': compose_form.id,
|
||||
'target': 'new',
|
||||
'context': ctx,
|
||||
}
|
||||
|
||||
for employee in self:
|
||||
employee.message_post(body=_(
|
||||
"%(name)s has been noted as %(state)s today",
|
||||
name=employee.name,
|
||||
state=employee.hr_presence_state_display))
|
||||
|
||||
@api.depends("user_id.im_status", "hr_presence_state_display")
|
||||
def _compute_presence_state(self):
|
||||
super()._compute_presence_state()
|
||||
company = self.env.company
|
||||
working_now_list = self._get_employee_working_now()
|
||||
for employee in self:
|
||||
if employee.manually_set_presence:
|
||||
employee.hr_presence_state = employee.hr_presence_state_display
|
||||
continue
|
||||
|
||||
if not employee.company_id.hr_presence_control_email and not employee.company_id.hr_presence_control_ip:
|
||||
continue
|
||||
if company.hr_presence_last_compute_date and employee.id in working_now_list and \
|
||||
company.hr_presence_last_compute_date.day == fields.Datetime.now().day and \
|
||||
(employee.email_sent or employee.ip_connected or employee.manually_set_present):
|
||||
employee.hr_presence_state = 'present'
|
||||
elif employee.id in working_now_list and employee.is_absent and \
|
||||
not (employee.email_sent or employee.ip_connected or employee.manually_set_present):
|
||||
employee.hr_presence_state = 'absent'
|
||||
else:
|
||||
employee.hr_presence_state = 'out_of_working_hour'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,17 @@
|
|||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class HrEmployeePublic(models.Model):
|
||||
_inherit = 'hr.employee.public'
|
||||
|
||||
email_sent = fields.Boolean(default=False)
|
||||
ip_connected = fields.Boolean(default=False)
|
||||
manually_set_present = fields.Boolean(default=False)
|
||||
manually_set_presence = fields.Boolean(default=False)
|
||||
hr_presence_state_display = fields.Selection([
|
||||
('out_of_working_hour', 'Off-Hours'),
|
||||
('present', 'Present'),
|
||||
('absent', 'Absent'),
|
||||
], default='out_of_working_hour')
|
||||
|
|
@ -1,28 +1,31 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import models, registry
|
||||
from odoo import fields, models
|
||||
from odoo.api import Environment
|
||||
from odoo.fields import Datetime
|
||||
from odoo.http import request
|
||||
from odoo.modules.registry import Registry
|
||||
from odoo.addons.bus.websocket import wsrequest
|
||||
|
||||
class IrWebsocket(models.AbstractModel):
|
||||
_inherit = 'ir.websocket'
|
||||
|
||||
def _update_bus_presence(self, inactivity_period, im_status_ids_by_model):
|
||||
super()._update_bus_presence(inactivity_period, im_status_ids_by_model)
|
||||
class IrWebsocket(models.AbstractModel):
|
||||
_inherit = "ir.websocket"
|
||||
|
||||
def _update_mail_presence(self, inactivity_period):
|
||||
super()._update_mail_presence(inactivity_period)
|
||||
# This method can either be called due to an http or a
|
||||
# websocket request. The request itself is necessary to
|
||||
# retrieve the current guest. Let's retrieve the proper
|
||||
# request.
|
||||
req = request or wsrequest
|
||||
if req.env.user._is_internal():
|
||||
if self.env.user._is_internal():
|
||||
ip_address = req.httprequest.remote_addr
|
||||
users_log = req.env['res.users.log'].search_count([
|
||||
('create_uid', '=', req.env.user.id),
|
||||
('ip', '=', ip_address),
|
||||
('create_date', '>=', Datetime.to_string(Datetime.now().replace(hour=0, minute=0, second=0, microsecond=0)))])
|
||||
if not users_log:
|
||||
with registry(req.env.cr.dbname).cursor() as cr:
|
||||
env = Environment(cr, req.env.user.id, {})
|
||||
env['res.users.log'].create({'ip': ip_address})
|
||||
domain = [
|
||||
("create_uid", "=", self.env.user.id),
|
||||
("ip", "=", ip_address),
|
||||
("create_date", ">=", fields.Date.today()),
|
||||
]
|
||||
if not self.env["res.users.log"].sudo().search_count(domain, limit=1):
|
||||
with Registry(self.env.cr.dbname).cursor() as cr:
|
||||
env = Environment(cr, self.env.user.id, {})
|
||||
env["res.users.log"].sudo().create({"ip": ip_address})
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
from odoo import api, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
_inherit = 'res.config.settings'
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
configs = super().create(vals_list)
|
||||
if any(config.hr_presence_control_ip or config.hr_presence_control_email for config in configs):
|
||||
self.env['hr.employee']._check_presence()
|
||||
return configs
|
||||
|
|
@ -6,5 +6,4 @@ from odoo import api, fields, models
|
|||
class ResUsersLog(models.Model):
|
||||
_inherit = 'res.users.log'
|
||||
|
||||
create_uid = fields.Integer(index=True)
|
||||
ip = fields.Char(string="IP Address")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue