1
0
forked from Mapan/odoo17e
odoo17e-kedaikipas58/addons/timesheet_grid/models/hr_employee.py
2024-12-10 09:04:09 +07:00

199 lines
9.6 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import datetime, time, timedelta
from pytz import timezone, UTC
from odoo import api, fields, models, _
from odoo.tools import float_round
from odoo.addons.resource.models.utils import sum_intervals, HOURS_PER_DAY
from odoo.exceptions import UserError
class Employee(models.Model):
_inherit = 'hr.employee'
def _get_employees_working_hours(self, employees, start_datetime, end_datetime):
# find working hours for the given period of employees with working calendar
# Note: convert date str into datetime object. Time will be 00:00:00 and 23:59:59
# respectively for date_start and date_stop, because we want the date_stop to be included.
start_datetime = datetime.combine(fields.Date.from_string(start_datetime), time.min)
end_datetime = datetime.combine(fields.Date.from_string(end_datetime), time.max)
start_datetime = start_datetime.replace(tzinfo=UTC)
end_datetime = end_datetime.replace(tzinfo=UTC)
employees_work_days_data, _dummy = employees.sudo().resource_id._get_valid_work_intervals(start_datetime, end_datetime)
return employees_work_days_data
def _get_timesheet_manager_id_domain(self):
group = self.env.ref('hr_timesheet.group_hr_timesheet_approver', raise_if_not_found=False)
return [('groups_id', 'in', [group.id])] if group else []
timesheet_manager_id = fields.Many2one(
'res.users', string='Timesheet',
compute='_compute_timesheet_manager', store=True, readonly=False,
domain=_get_timesheet_manager_id_domain,
help='Select the user responsible for approving "Timesheet" of this employee.\n'
'If empty, the approval is done by a Timesheets > Administrator or a Timesheets > User: all timesheets (as determined in the users settings).')
last_validated_timesheet_date = fields.Date(groups="hr_timesheet.group_timesheet_manager")
@api.depends('parent_id')
def _compute_timesheet_manager(self):
for employee in self:
previous_manager = employee._origin.parent_id.user_id
manager = employee.parent_id.user_id
if manager and manager.has_group('hr_timesheet.group_hr_timesheet_approver') and (employee.timesheet_manager_id == previous_manager or not employee.timesheet_manager_id):
employee.timesheet_manager_id = manager
elif not employee.timesheet_manager_id:
employee.timesheet_manager_id = False
def get_timesheet_and_working_hours(self, date_start, date_stop):
""" Get the difference between the supposed working hour (based on resource calendar) and
the timesheeted hours, for the given period `date_start` - `date_stop` (inclusives).
:param date_start: start date of the period to check (date string)
:param date_stop: end date of the period to check (date string)
:returns dict: a dict mapping the employee_id with his timesheeted and working hours for the
given period.
"""
employees = self.filtered('resource_calendar_id')
result = {i: dict(timesheet_hours=0.0, working_hours=0.0, date_start=date_start, date_stop=date_stop) for i in self.ids}
if not employees:
return result
# find timesheeted hours of employees with working hours
self.env.cr.execute("""
SELECT A.employee_id as employee_id, sum(A.unit_amount) as amount_sum
FROM account_analytic_line A
WHERE A.employee_id IN %s AND date >= %s AND date <= %s
GROUP BY A.employee_id
""", (tuple(employees.ids), date_start, date_stop))
for data_row in self.env.cr.dictfetchall():
result[data_row['employee_id']]['timesheet_hours'] = float_round(data_row['amount_sum'], 2)
employees_work_days_data = self._get_employees_working_hours(employees, date_start, date_stop)
for employee in employees:
working_hours = sum_intervals(employees_work_days_data[employee.resource_id.id])
result[employee.id]['working_hours'] = float_round(working_hours, 2)
return result
@api.model
def get_daily_working_hours(self, date_start, date_stop):
result = {}
# Change the type of the date from string to Date
date_start_date = fields.Date.from_string(date_start)
date_stop_date = min(fields.Date.from_string(date_stop), fields.Date.today())
# Compute the difference between the starting and ending date
delta = date_stop_date - date_start_date
# Get the current user
current_employee = self.env.user.employee_id
if not current_employee:
return result
# Collect the number of hours that an employee should work according to their schedule
calendar = current_employee.resource_calendar_id or current_employee.company_id.resource_calendar_id
datetime_min = timezone(self.env.user.tz or calendar.tz or 'UTC').localize(datetime.combine(date_start_date, time.min)).astimezone(UTC)
datetime_max = timezone(self.env.user.tz or calendar.tz or 'UTC').localize(datetime.combine(date_stop_date, time.max)).astimezone(UTC)
employee_work_days_data = calendar._work_intervals_batch(
datetime_min, datetime_max,
resources=current_employee.resource_id, compute_leaves=False
)
working_hours = employee_work_days_data[current_employee.resource_id.id]
for day_count in range(delta.days + 1):
date = date_start_date + timedelta(days=day_count)
if current_employee.resource_calendar_id:
value = sum(
(stop - start).total_seconds() / 3600
for start, stop, meta in working_hours
if start.date() == date
)
else:
value = current_employee.company_id.resource_calendar_id.hours_per_day or HOURS_PER_DAY
result[fields.Date.to_string(date)] = value
return result
def _get_timesheets_and_working_hours_query(self):
return """
SELECT aal.employee_id as employee_id, COALESCE(SUM(aal.unit_amount), 0) as worked_hours
FROM account_analytic_line aal
WHERE aal.employee_id IN %s AND date >= %s AND date <= %s
GROUP BY aal.employee_id
"""
def get_timesheet_and_working_hours_for_employees(self, date_start, date_stop):
"""
Method called by the timesheet avatar widget on the frontend in gridview to get information
about the hours employees have worked and should work.
:param date_start: date start of the interval to search
:param state_stop: date stop of the interval to search
:return: Dictionary of dictionary
for each employee id =>
number of units to work,
what unit type are we using
the number of worked units by the employees
"""
result = {}
uom = str(self.env.company.timesheet_encode_uom_id.name).lower()
hours_per_day_per_employee = {}
employees_work_days_data = {}
if self:
employees_work_days_data = self._get_employees_working_hours(self, date_start, date_stop)
for employee in self:
units_to_work = sum_intervals(employees_work_days_data[employee.resource_id.id])
# Adjustments if we work with a different unit of measure
if uom == 'days':
calendar = employee.resource_calendar_id or employee.company_id.resource_calendar_id
hours_per_day_per_employee[employee.id] = calendar.hours_per_day
units_to_work = units_to_work / hours_per_day_per_employee[employee.id]
rounding = len(str(self.env.company.timesheet_encode_uom_id.rounding).split('.')[1])
units_to_work = round(units_to_work, rounding)
result[employee.id] = {'units_to_work': units_to_work, 'uom': uom, 'worked_hours': 0.0}
query = self._get_timesheets_and_working_hours_query()
self.env.cr.execute(query, (tuple(self.ids), date_start, date_stop))
for data_row in self.env.cr.dictfetchall():
worked_hours = data_row['worked_hours']
if uom == 'days':
worked_hours /= hours_per_day_per_employee[data_row['employee_id']]
rounding = len(str(self.env.company.timesheet_encode_uom_id.rounding).split('.')[1])
worked_hours = round(worked_hours, rounding)
result[data_row['employee_id']]['worked_hours'] = worked_hours
return result
def _get_user_m2o_to_empty_on_archived_employees(self):
return super()._get_user_m2o_to_empty_on_archived_employees() + ['timesheet_manager_id']
def action_timesheet_from_employee(self):
action = super().action_timesheet_from_employee()
action['context']['group_expand'] = True
return action
def get_last_validated_timesheet_date(self):
if self.user_has_groups('hr_timesheet.group_timesheet_manager'):
return {}
if not self.user_has_groups('hr_timesheet.group_hr_timesheet_user'):
raise UserError(_('You are not allowed to see timesheets.'))
return {
employee.id: employee.last_validated_timesheet_date
for employee in self.sudo()
}
class HrEmployeePublic(models.Model):
_inherit = 'hr.employee.public'
timesheet_manager_id = fields.Many2one('res.users', string='Timesheet',
help="User responsible of timesheet validation. Should be Timesheet Manager.")