summaryrefslogtreecommitdiff
path: root/addons/hr/models/hr_employee_base.py
diff options
context:
space:
mode:
authorstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
committerstephanchrst <stephanchrst@gmail.com>2022-05-10 21:51:50 +0700
commit3751379f1e9a4c215fb6eb898b4ccc67659b9ace (patch)
treea44932296ef4a9b71d5f010906253d8c53727726 /addons/hr/models/hr_employee_base.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/hr/models/hr_employee_base.py')
-rw-r--r--addons/hr/models/hr_employee_base.py179
1 files changed, 179 insertions, 0 deletions
diff --git a/addons/hr/models/hr_employee_base.py b/addons/hr/models/hr_employee_base.py
new file mode 100644
index 00000000..9732f6da
--- /dev/null
+++ b/addons/hr/models/hr_employee_base.py
@@ -0,0 +1,179 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from ast import literal_eval
+
+from odoo import api, fields, models
+from pytz import timezone, UTC, utc
+from datetime import timedelta
+
+from odoo.tools import format_time
+
+
+class HrEmployeeBase(models.AbstractModel):
+ _name = "hr.employee.base"
+ _description = "Basic Employee"
+ _order = 'name'
+
+ name = fields.Char()
+ active = fields.Boolean("Active")
+ color = fields.Integer('Color Index', default=0)
+ department_id = fields.Many2one('hr.department', 'Department', domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
+ job_id = fields.Many2one('hr.job', 'Job Position', domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
+ job_title = fields.Char("Job Title", compute="_compute_job_title", store=True, readonly=False)
+ company_id = fields.Many2one('res.company', 'Company')
+ address_id = fields.Many2one('res.partner', 'Work Address', compute="_compute_address_id", store=True, readonly=False,
+ domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
+ work_phone = fields.Char('Work Phone', compute="_compute_phones", store=True, readonly=False)
+ mobile_phone = fields.Char('Work Mobile')
+ work_email = fields.Char('Work Email')
+ work_location = fields.Char('Work Location')
+ user_id = fields.Many2one('res.users')
+ resource_id = fields.Many2one('resource.resource')
+ resource_calendar_id = fields.Many2one('resource.calendar', domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
+ parent_id = fields.Many2one('hr.employee', 'Manager', compute="_compute_parent_id", store=True, readonly=False,
+ domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]")
+ coach_id = fields.Many2one(
+ 'hr.employee', 'Coach', compute='_compute_coach', store=True, readonly=False,
+ domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
+ help='Select the "Employee" who is the coach of this employee.\n'
+ 'The "Coach" has no specific rights or responsibilities by default.')
+ tz = fields.Selection(
+ string='Timezone', related='resource_id.tz', readonly=False,
+ help="This field is used in order to define in which timezone the resources will work.")
+ 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")
+ last_activity_time = fields.Char(compute="_compute_last_activity")
+ hr_icon_display = fields.Selection([
+ ('presence_present', 'Present'),
+ ('presence_absent_active', 'Present but not active'),
+ ('presence_absent', 'Absent'),
+ ('presence_to_define', 'To define'),
+ ('presence_undetermined', 'Undetermined')], compute='_compute_presence_icon')
+
+ @api.depends('user_id.im_status')
+ 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 = literal_eval(self.env['ir.config_parameter'].sudo().get_param('hr.hr_presence_control_login', 'False'))
+ employee_to_check_working = self.filtered(lambda e: e.user_id.im_status == 'offline')
+ working_now_list = employee_to_check_working._get_employee_working_now()
+ for employee in self:
+ state = 'to_define'
+ if check_login:
+ if employee.user_id.im_status == 'online' or employee.last_activity:
+ state = 'present'
+ elif employee.user_id.im_status == 'offline' and employee.id not in working_now_list:
+ state = 'absent'
+ employee.hr_presence_state = state
+
+ @api.depends('user_id')
+ def _compute_last_activity(self):
+ presences = self.env['bus.presence'].search_read([('user_id', 'in', self.mapped('user_id').ids)], ['user_id', 'last_presence'])
+ # transform the result to a dict with this format {user.id: last_presence}
+ presences = {p['user_id'][0]: p['last_presence'] for p in presences}
+
+ for employee in self:
+ tz = employee.tz
+ last_presence = presences.get(employee.user_id.id, False)
+ if last_presence:
+ last_activity_datetime = last_presence.replace(tzinfo=UTC).astimezone(timezone(tz)).replace(tzinfo=None)
+ employee.last_activity = last_activity_datetime.date()
+ if employee.last_activity == fields.Date.today():
+ employee.last_activity_time = format_time(self.env, last_activity_datetime, time_format='short')
+ else:
+ employee.last_activity_time = False
+ else:
+ employee.last_activity = False
+ employee.last_activity_time = False
+
+ @api.depends('parent_id')
+ def _compute_coach(self):
+ for employee in self:
+ manager = employee.parent_id
+ previous_manager = employee._origin.parent_id
+ if manager and (employee.coach_id == previous_manager or not employee.coach_id):
+ employee.coach_id = manager
+ elif not employee.coach_id:
+ employee.coach_id = False
+
+ @api.depends('job_id')
+ def _compute_job_title(self):
+ for employee in self.filtered('job_id'):
+ employee.job_title = employee.job_id.name
+
+ @api.depends('address_id')
+ def _compute_phones(self):
+ for employee in self:
+ if employee.address_id and employee.address_id.phone:
+ employee.work_phone = employee.address_id.phone
+ else:
+ employee.work_phone = False
+
+ @api.depends('company_id')
+ def _compute_address_id(self):
+ for employee in self:
+ address = employee.company_id.partner_id.address_get(['default'])
+ employee.address_id = address['default'] if address else False
+
+ @api.depends('department_id')
+ def _compute_parent_id(self):
+ for employee in self.filtered('department_id.manager_id'):
+ employee.parent_id = employee.department_id.manager_id
+
+ @api.depends('resource_calendar_id', 'hr_presence_state')
+ def _compute_presence_icon(self):
+ """
+ This method compute the state defining the display icon in the kanban view.
+ It can be overriden to add other possibilities, like time off or attendances recordings.
+ """
+ working_now_list = self.filtered(lambda e: e.hr_presence_state == 'present')._get_employee_working_now()
+ for employee in self:
+ if employee.hr_presence_state == 'present':
+ if employee.id in working_now_list:
+ icon = 'presence_present'
+ else:
+ icon = 'presence_absent_active'
+ elif employee.hr_presence_state == 'absent':
+ # employee is not in the working_now_list and he has a user_id
+ icon = 'presence_absent'
+ else:
+ # without attendance, default employee state is 'to_define' without confirmed presence/absence
+ # we need to check why they are not there
+ if employee.user_id:
+ # Display an orange icon on internal users.
+ icon = 'presence_to_define'
+ else:
+ # We don't want non-user employee to have icon.
+ icon = 'presence_undetermined'
+ employee.hr_icon_display = icon
+
+ @api.model
+ def _get_employee_working_now(self):
+ working_now = []
+ # We loop over all the employee tz and the resource calendar_id to detect working hours in batch.
+ all_employee_tz = set(self.mapped('tz'))
+ for tz in all_employee_tz:
+ employee_ids = self.filtered(lambda e: e.tz == tz)
+ resource_calendar_ids = employee_ids.mapped('resource_calendar_id')
+ for calendar_id in resource_calendar_ids:
+ res_employee_ids = employee_ids.filtered(lambda e: e.resource_calendar_id.id == calendar_id.id)
+ start_dt = fields.Datetime.now()
+ stop_dt = start_dt + timedelta(hours=1)
+ from_datetime = utc.localize(start_dt).astimezone(timezone(tz or 'UTC'))
+ to_datetime = utc.localize(stop_dt).astimezone(timezone(tz or 'UTC'))
+ # Getting work interval of the first is working. Functions called on resource_calendar_id
+ # are waiting for singleton
+ work_interval = res_employee_ids[0].resource_calendar_id._work_intervals(from_datetime, to_datetime)
+ # Employee that is not supposed to work have empty items.
+ if len(work_interval._items) > 0:
+ # The employees should be working now according to their work schedule
+ working_now += res_employee_ids.ids
+ return working_now
+