odoo/addons/hr_timesheet_attendance/models/hr_attendance.py

121 lines
5.3 KiB
Python

# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import time
from datetime import datetime
from pytz import timezone
import pytz
from odoo import api, fields, models, _
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT
from odoo.exceptions import UserError
class HrAttendance(models.Model):
_inherit = "hr.attendance"
sheet_id_computed = fields.Many2one('hr_timesheet_sheet.sheet', string='Sheet', compute='_compute_sheet', index=True, ondelete='cascade',
search='_search_sheet')
sheet_id = fields.Many2one('hr_timesheet_sheet.sheet', compute='_compute_sheet', string='Sheet', store=True)
@api.depends('employee_id', 'check_in', 'check_out', 'sheet_id_computed.date_to', 'sheet_id_computed.date_from', 'sheet_id_computed.employee_id')
def _compute_sheet(self):
"""Links the attendance to the corresponding sheet
"""
for attendance in self:
corresponding_sheet = self.env['hr_timesheet_sheet.sheet'].search(
[('date_to', '>=', attendance.check_in), ('date_from', '<=', attendance.check_in),
('employee_id', '=', attendance.employee_id.id),
('state', 'in', ['draft', 'new'])], limit=1)
if corresponding_sheet:
attendance.sheet_id_computed = corresponding_sheet[0]
attendance.sheet_id = corresponding_sheet[0]
def _search_sheet(self, operator, value):
assert operator == 'in'
ids = []
for ts in self.env['hr_timesheet_sheet.sheet'].browse(value):
self._cr.execute("""
SELECT a.id
FROM hr_attendance a
WHERE %(date_to)s >= a.check_in
AND %(date_from)s <= a.check_in
AND %(employee_id)s = a.employee_id
GROUP BY a.id""", {'date_from': ts.date_from,
'date_to': ts.date_to,
'employee_id': ts.employee_id.id, })
ids.extend([row[0] for row in self._cr.fetchall()])
return [('id', 'in', ids)]
def _get_attendance_employee_tz(self, employee_id, date):
""" Simulate timesheet in employee timezone
Return the attendance date in string format in the employee
tz converted from utc timezone as we consider date of employee
timesheet is in employee timezone
"""
tz = False
if employee_id:
employee = self.env['hr.employee'].browse(employee_id)
tz = employee.user_id.partner_id.tz
if not date:
date = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
att_tz = timezone(tz or 'utc')
attendance_dt = datetime.strptime(date, DEFAULT_SERVER_DATETIME_FORMAT)
att_tz_dt = pytz.utc.localize(attendance_dt)
att_tz_dt = att_tz_dt.astimezone(att_tz)
# We take only the date omiting the hours as we compare with timesheet
# date_from which is a date format thus using hours would lead to
# be out of scope of timesheet
att_tz_date_str = datetime.strftime(att_tz_dt, DEFAULT_SERVER_DATE_FORMAT)
return att_tz_date_str
def _get_current_sheet(self, employee_id, date=False):
if not date:
date = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)
att_tz_date_str = self._get_attendance_employee_tz(employee_id, date=date)
sheet = self.env['hr_timesheet_sheet.sheet'].search(
[('date_from', '<=', att_tz_date_str),
('date_to', '>=', att_tz_date_str),
('employee_id', '=', employee_id)], limit=1)
return sheet or False
@api.model
def create(self, vals):
if self.env.context.get('sheet_id'):
sheet = self.env['hr_timesheet_sheet.sheet'].browse(self.env.context.get('sheet_id'))
else:
sheet = self._get_current_sheet(vals.get('employee_id'), vals.get('check_in'))
if sheet:
att_tz_date_str = self._get_attendance_employee_tz(vals.get('employee_id'), date=vals.get('check_in'))
if sheet.state not in ('draft', 'new'):
raise UserError(_('You can not enter an attendance in a submitted timesheet. Ask your manager to reset it before adding attendance.'))
elif sheet.date_from > att_tz_date_str or sheet.date_to < att_tz_date_str:
raise UserError(_('You can not enter an attendance date outside the current timesheet dates.'))
return super(HrAttendance, self).create(vals)
@api.multi
def unlink(self):
self._check()
return super(HrAttendance, self).unlink()
@api.multi
def write(self, vals):
self._check()
res = super(HrAttendance, self).write(vals)
if 'sheet_id' in self.env.context:
for attendance in self:
if self.env.context['sheet_id'] != attendance.sheet_id.id:
raise UserError(_('You cannot enter an attendance date outside the current timesheet dates.'))
return res
def _check(self):
for att in self:
if att.sheet_id and att.sheet_id.state not in ('draft', 'new'):
raise UserError(_('You cannot modify an entry in a confirmed timesheet'))
return True