changes
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from odoo import models, fields, api
|
||||
|
||||
|
||||
@@ -21,16 +22,167 @@ class HrEmployee(models.Model):
|
||||
x_fclk_break_minutes = fields.Float(
|
||||
string='Custom Break (min)',
|
||||
default=0.0,
|
||||
help="Override default break duration for this employee. 0 = use company default.",
|
||||
help="Override default break duration for this employee. 0 = use shift or company default.",
|
||||
)
|
||||
|
||||
# Shift scheduling
|
||||
x_fclk_shift_id = fields.Many2one(
|
||||
'fusion.clock.shift',
|
||||
string='Work Shift',
|
||||
help="Assigned shift schedule. Leave empty to use global defaults.",
|
||||
)
|
||||
|
||||
# Pending reason enforcement
|
||||
x_fclk_pending_reason = fields.Boolean(
|
||||
string='Pending Reason Required',
|
||||
default=False,
|
||||
help="If set, employee must explain a missed clock-out before clocking in again.",
|
||||
)
|
||||
|
||||
# Kiosk PIN
|
||||
x_fclk_kiosk_pin = fields.Char(
|
||||
string='Kiosk PIN',
|
||||
help="PIN code for kiosk clock-in/out identification.",
|
||||
groups="fusion_clock.group_fusion_clock_manager",
|
||||
)
|
||||
|
||||
# On-time streak
|
||||
x_fclk_ontime_streak = fields.Integer(
|
||||
string='On-Time Streak',
|
||||
default=0,
|
||||
help="Consecutive workdays clocked in on time.",
|
||||
)
|
||||
|
||||
# Absence tracking (computed)
|
||||
x_fclk_absences_this_month = fields.Integer(
|
||||
string='Absences This Month',
|
||||
compute='_compute_absence_counts',
|
||||
)
|
||||
x_fclk_absences_this_year = fields.Integer(
|
||||
string='Absences This Year',
|
||||
compute='_compute_absence_counts',
|
||||
)
|
||||
|
||||
# Overtime tracking (computed)
|
||||
x_fclk_overtime_this_week = fields.Float(
|
||||
string='Overtime This Week (h)',
|
||||
compute='_compute_overtime',
|
||||
)
|
||||
x_fclk_overtime_this_month = fields.Float(
|
||||
string='Overtime This Month (h)',
|
||||
compute='_compute_overtime',
|
||||
)
|
||||
|
||||
# Activity log relation
|
||||
x_fclk_activity_log_ids = fields.One2many(
|
||||
'fusion.clock.activity.log',
|
||||
'employee_id',
|
||||
string='Activity Logs',
|
||||
)
|
||||
|
||||
# Leave request relation
|
||||
x_fclk_leave_request_ids = fields.One2many(
|
||||
'fusion.clock.leave.request',
|
||||
'employee_id',
|
||||
string='Leave Requests',
|
||||
)
|
||||
|
||||
# Correction request relation
|
||||
x_fclk_correction_ids = fields.One2many(
|
||||
'fusion.clock.correction',
|
||||
'employee_id',
|
||||
string='Correction Requests',
|
||||
)
|
||||
|
||||
# Reminder tracking
|
||||
x_fclk_last_reminder_date = fields.Date(
|
||||
string='Last Reminder Date',
|
||||
help="Tracks the last date a reminder was sent to avoid duplicates.",
|
||||
)
|
||||
|
||||
def _get_fclk_break_minutes(self):
|
||||
"""Return effective break minutes for this employee."""
|
||||
"""Return effective break minutes for this employee.
|
||||
Priority: employee override > shift > global setting.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.x_fclk_break_minutes > 0:
|
||||
return self.x_fclk_break_minutes
|
||||
if self.x_fclk_shift_id and self.x_fclk_shift_id.break_minutes > 0:
|
||||
return self.x_fclk_shift_id.break_minutes
|
||||
return float(
|
||||
self.env['ir.config_parameter'].sudo().get_param(
|
||||
'fusion_clock.default_break_minutes', '30'
|
||||
)
|
||||
)
|
||||
|
||||
def _get_fclk_scheduled_times(self, date):
|
||||
"""Return (scheduled_in_dt, scheduled_out_dt) for a given date.
|
||||
Uses employee shift if assigned, otherwise global settings.
|
||||
"""
|
||||
self.ensure_one()
|
||||
if self.x_fclk_shift_id:
|
||||
in_hour = self.x_fclk_shift_id.start_time
|
||||
out_hour = self.x_fclk_shift_id.end_time
|
||||
else:
|
||||
ICP = self.env['ir.config_parameter'].sudo()
|
||||
in_hour = float(ICP.get_param('fusion_clock.default_clock_in_time', '9.0'))
|
||||
out_hour = float(ICP.get_param('fusion_clock.default_clock_out_time', '17.0'))
|
||||
|
||||
in_h = int(in_hour)
|
||||
in_m = int((in_hour - in_h) * 60)
|
||||
out_h = int(out_hour)
|
||||
out_m = int((out_hour - out_h) * 60)
|
||||
|
||||
scheduled_in = datetime.combine(date, datetime.min.time().replace(hour=in_h, minute=in_m))
|
||||
scheduled_out = datetime.combine(date, datetime.min.time().replace(hour=out_h, minute=out_m))
|
||||
return scheduled_in, scheduled_out
|
||||
|
||||
def _get_fclk_scheduled_hours(self):
|
||||
"""Return the expected work hours for this employee's shift."""
|
||||
self.ensure_one()
|
||||
if self.x_fclk_shift_id:
|
||||
return self.x_fclk_shift_id.scheduled_hours
|
||||
ICP = self.env['ir.config_parameter'].sudo()
|
||||
in_hour = float(ICP.get_param('fusion_clock.default_clock_in_time', '9.0'))
|
||||
out_hour = float(ICP.get_param('fusion_clock.default_clock_out_time', '17.0'))
|
||||
break_hrs = self._get_fclk_break_minutes() / 60.0
|
||||
return max((out_hour - in_hour) - break_hrs, 0.0)
|
||||
|
||||
def _compute_absence_counts(self):
|
||||
ActivityLog = self.env['fusion.clock.activity.log'].sudo()
|
||||
today = fields.Date.today()
|
||||
month_start = today.replace(day=1)
|
||||
year_start = today.replace(month=1, day=1)
|
||||
|
||||
for emp in self:
|
||||
emp.x_fclk_absences_this_month = ActivityLog.search_count([
|
||||
('employee_id', '=', emp.id),
|
||||
('log_type', '=', 'absent'),
|
||||
('log_date', '>=', datetime.combine(month_start, datetime.min.time())),
|
||||
])
|
||||
emp.x_fclk_absences_this_year = ActivityLog.search_count([
|
||||
('employee_id', '=', emp.id),
|
||||
('log_type', '=', 'absent'),
|
||||
('log_date', '>=', datetime.combine(year_start, datetime.min.time())),
|
||||
])
|
||||
|
||||
def _compute_overtime(self):
|
||||
Attendance = self.env['hr.attendance'].sudo()
|
||||
today = fields.Date.today()
|
||||
week_start = today - timedelta(days=today.weekday())
|
||||
month_start = today.replace(day=1)
|
||||
|
||||
for emp in self:
|
||||
week_atts = Attendance.search([
|
||||
('employee_id', '=', emp.id),
|
||||
('check_in', '>=', datetime.combine(week_start, datetime.min.time())),
|
||||
('check_out', '!=', False),
|
||||
])
|
||||
emp.x_fclk_overtime_this_week = sum(a.x_fclk_overtime_hours or 0 for a in week_atts)
|
||||
|
||||
month_atts = Attendance.search([
|
||||
('employee_id', '=', emp.id),
|
||||
('check_in', '>=', datetime.combine(month_start, datetime.min.time())),
|
||||
('check_out', '!=', False),
|
||||
])
|
||||
emp.x_fclk_overtime_this_month = sum(a.x_fclk_overtime_hours or 0 for a in month_atts)
|
||||
|
||||
Reference in New Issue
Block a user