summaryrefslogtreecommitdiff
path: root/addons/hr_attendance/models/hr_attendance.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_attendance/models/hr_attendance.py
parent0a15094050bfde69a06d6eff798e9a8ddf2b8c21 (diff)
initial commit 2
Diffstat (limited to 'addons/hr_attendance/models/hr_attendance.py')
-rw-r--r--addons/hr_attendance/models/hr_attendance.py104
1 files changed, 104 insertions, 0 deletions
diff --git a/addons/hr_attendance/models/hr_attendance.py b/addons/hr_attendance/models/hr_attendance.py
new file mode 100644
index 00000000..5e91d90b
--- /dev/null
+++ b/addons/hr_attendance/models/hr_attendance.py
@@ -0,0 +1,104 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models, fields, api, exceptions, _
+from odoo.tools import format_datetime
+
+
+class HrAttendance(models.Model):
+ _name = "hr.attendance"
+ _description = "Attendance"
+ _order = "check_in desc"
+
+ def _default_employee(self):
+ return self.env.user.employee_id
+
+ employee_id = fields.Many2one('hr.employee', string="Employee", default=_default_employee, required=True, ondelete='cascade', index=True)
+ department_id = fields.Many2one('hr.department', string="Department", related="employee_id.department_id",
+ readonly=True)
+ check_in = fields.Datetime(string="Check In", default=fields.Datetime.now, required=True)
+ check_out = fields.Datetime(string="Check Out")
+ worked_hours = fields.Float(string='Worked Hours', compute='_compute_worked_hours', store=True, readonly=True)
+
+ def name_get(self):
+ result = []
+ for attendance in self:
+ if not attendance.check_out:
+ result.append((attendance.id, _("%(empl_name)s from %(check_in)s") % {
+ 'empl_name': attendance.employee_id.name,
+ 'check_in': format_datetime(self.env, attendance.check_in, dt_format=False),
+ }))
+ else:
+ result.append((attendance.id, _("%(empl_name)s from %(check_in)s to %(check_out)s") % {
+ 'empl_name': attendance.employee_id.name,
+ 'check_in': format_datetime(self.env, attendance.check_in, dt_format=False),
+ 'check_out': format_datetime(self.env, attendance.check_out, dt_format=False),
+ }))
+ return result
+
+ @api.depends('check_in', 'check_out')
+ def _compute_worked_hours(self):
+ for attendance in self:
+ if attendance.check_out and attendance.check_in:
+ delta = attendance.check_out - attendance.check_in
+ attendance.worked_hours = delta.total_seconds() / 3600.0
+ else:
+ attendance.worked_hours = False
+
+ @api.constrains('check_in', 'check_out')
+ def _check_validity_check_in_check_out(self):
+ """ verifies if check_in is earlier than check_out. """
+ for attendance in self:
+ if attendance.check_in and attendance.check_out:
+ if attendance.check_out < attendance.check_in:
+ raise exceptions.ValidationError(_('"Check Out" time cannot be earlier than "Check In" time.'))
+
+ @api.constrains('check_in', 'check_out', 'employee_id')
+ def _check_validity(self):
+ """ Verifies the validity of the attendance record compared to the others from the same employee.
+ For the same employee we must have :
+ * maximum 1 "open" attendance record (without check_out)
+ * no overlapping time slices with previous employee records
+ """
+ for attendance in self:
+ # we take the latest attendance before our check_in time and check it doesn't overlap with ours
+ last_attendance_before_check_in = self.env['hr.attendance'].search([
+ ('employee_id', '=', attendance.employee_id.id),
+ ('check_in', '<=', attendance.check_in),
+ ('id', '!=', attendance.id),
+ ], order='check_in desc', limit=1)
+ if last_attendance_before_check_in and last_attendance_before_check_in.check_out and last_attendance_before_check_in.check_out > attendance.check_in:
+ raise exceptions.ValidationError(_("Cannot create new attendance record for %(empl_name)s, the employee was already checked in on %(datetime)s") % {
+ 'empl_name': attendance.employee_id.name,
+ 'datetime': format_datetime(self.env, attendance.check_in, dt_format=False),
+ })
+
+ if not attendance.check_out:
+ # if our attendance is "open" (no check_out), we verify there is no other "open" attendance
+ no_check_out_attendances = self.env['hr.attendance'].search([
+ ('employee_id', '=', attendance.employee_id.id),
+ ('check_out', '=', False),
+ ('id', '!=', attendance.id),
+ ], order='check_in desc', limit=1)
+ if no_check_out_attendances:
+ raise exceptions.ValidationError(_("Cannot create new attendance record for %(empl_name)s, the employee hasn't checked out since %(datetime)s") % {
+ 'empl_name': attendance.employee_id.name,
+ 'datetime': format_datetime(self.env, no_check_out_attendances.check_in, dt_format=False),
+ })
+ else:
+ # we verify that the latest attendance with check_in time before our check_out time
+ # is the same as the one before our check_in time computed before, otherwise it overlaps
+ last_attendance_before_check_out = self.env['hr.attendance'].search([
+ ('employee_id', '=', attendance.employee_id.id),
+ ('check_in', '<', attendance.check_out),
+ ('id', '!=', attendance.id),
+ ], order='check_in desc', limit=1)
+ if last_attendance_before_check_out and last_attendance_before_check_in != last_attendance_before_check_out:
+ raise exceptions.ValidationError(_("Cannot create new attendance record for %(empl_name)s, the employee was already checked in on %(datetime)s") % {
+ 'empl_name': attendance.employee_id.name,
+ 'datetime': format_datetime(self.env, last_attendance_before_check_out.check_in, dt_format=False),
+ })
+
+ @api.returns('self', lambda value: value.id)
+ def copy(self):
+ raise exceptions.UserError(_('You cannot duplicate an attendance.'))