changes
This commit is contained in:
165
fusion_clock/models/clock_correction.py
Normal file
165
fusion_clock/models/clock_correction.py
Normal file
@@ -0,0 +1,165 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
|
||||
import logging
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FusionClockCorrection(models.Model):
|
||||
_name = 'fusion.clock.correction'
|
||||
_description = 'Timesheet Correction Request'
|
||||
_order = 'create_date desc, id desc'
|
||||
_rec_name = 'display_name'
|
||||
_inherit = ['mail.thread']
|
||||
|
||||
employee_id = fields.Many2one(
|
||||
'hr.employee',
|
||||
string='Employee',
|
||||
required=True,
|
||||
index=True,
|
||||
ondelete='cascade',
|
||||
)
|
||||
attendance_id = fields.Many2one(
|
||||
'hr.attendance',
|
||||
string='Attendance Record',
|
||||
required=True,
|
||||
ondelete='cascade',
|
||||
)
|
||||
original_check_in = fields.Datetime(
|
||||
string='Original Clock-In',
|
||||
related='attendance_id.check_in',
|
||||
)
|
||||
original_check_out = fields.Datetime(
|
||||
string='Original Clock-Out',
|
||||
related='attendance_id.check_out',
|
||||
)
|
||||
requested_check_in = fields.Datetime(
|
||||
string='Corrected Clock-In',
|
||||
)
|
||||
requested_check_out = fields.Datetime(
|
||||
string='Corrected Clock-Out',
|
||||
)
|
||||
reason = fields.Text(
|
||||
string='Reason for Correction',
|
||||
required=True,
|
||||
)
|
||||
state = fields.Selection(
|
||||
[
|
||||
('pending', 'Pending'),
|
||||
('approved', 'Approved'),
|
||||
('rejected', 'Rejected'),
|
||||
],
|
||||
string='Status',
|
||||
default='pending',
|
||||
tracking=True,
|
||||
index=True,
|
||||
)
|
||||
reviewed_by = fields.Many2one(
|
||||
'res.users',
|
||||
string='Reviewed By',
|
||||
)
|
||||
reviewed_date = fields.Datetime(string='Reviewed Date')
|
||||
company_id = fields.Many2one(
|
||||
'res.company',
|
||||
string='Company',
|
||||
related='employee_id.company_id',
|
||||
store=True,
|
||||
)
|
||||
display_name = fields.Char(
|
||||
compute='_compute_display_name',
|
||||
store=True,
|
||||
)
|
||||
|
||||
@api.depends('employee_id', 'attendance_id', 'state')
|
||||
def _compute_display_name(self):
|
||||
for rec in self:
|
||||
emp = rec.employee_id.name or ''
|
||||
date_str = rec.attendance_id.check_in.strftime('%Y-%m-%d') if rec.attendance_id and rec.attendance_id.check_in else ''
|
||||
rec.display_name = f"{emp} - Correction ({date_str}) [{rec.state}]"
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
records = super().create(vals_list)
|
||||
for rec in records:
|
||||
rec._notify_office_user()
|
||||
rec._create_activity_log('pending')
|
||||
return records
|
||||
|
||||
def action_approve(self):
|
||||
"""Approve the correction and update the attendance record."""
|
||||
self.ensure_one()
|
||||
if self.state != 'pending':
|
||||
raise UserError(_("Only pending corrections can be approved."))
|
||||
|
||||
vals = {}
|
||||
if self.requested_check_in:
|
||||
vals['check_in'] = self.requested_check_in
|
||||
if self.requested_check_out:
|
||||
vals['check_out'] = self.requested_check_out
|
||||
|
||||
if vals:
|
||||
self.attendance_id.sudo().write(vals)
|
||||
|
||||
self.write({
|
||||
'state': 'approved',
|
||||
'reviewed_by': self.env.uid,
|
||||
'reviewed_date': fields.Datetime.now(),
|
||||
})
|
||||
self._create_activity_log('approved')
|
||||
|
||||
def action_reject(self):
|
||||
"""Reject the correction request."""
|
||||
self.ensure_one()
|
||||
if self.state != 'pending':
|
||||
raise UserError(_("Only pending corrections can be rejected."))
|
||||
|
||||
self.write({
|
||||
'state': 'rejected',
|
||||
'reviewed_by': self.env.uid,
|
||||
'reviewed_date': fields.Datetime.now(),
|
||||
})
|
||||
self._create_activity_log('rejected')
|
||||
|
||||
def _notify_office_user(self):
|
||||
"""Schedule a mail.activity for the office user."""
|
||||
ICP = self.env['ir.config_parameter'].sudo()
|
||||
office_user_id = int(ICP.get_param('fusion_clock.office_user_id', '0'))
|
||||
if not office_user_id:
|
||||
return
|
||||
office_user = self.env['res.users'].sudo().browse(office_user_id)
|
||||
if not office_user.exists():
|
||||
return
|
||||
try:
|
||||
date_str = self.attendance_id.check_in.strftime('%Y-%m-%d') if self.attendance_id.check_in else 'unknown'
|
||||
self.env['mail.activity'].sudo().create({
|
||||
'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id,
|
||||
'summary': f"Timesheet Correction: {self.employee_id.name} ({date_str})",
|
||||
'note': f"Reason: {self.reason}",
|
||||
'user_id': office_user.id,
|
||||
'res_model_id': self.env['ir.model']._get_id('fusion.clock.correction'),
|
||||
'res_id': self.id,
|
||||
'date_deadline': fields.Date.today(),
|
||||
})
|
||||
except Exception as e:
|
||||
_logger.error("Fusion Clock: Failed to create correction activity: %s", e)
|
||||
|
||||
def _create_activity_log(self, action):
|
||||
"""Log the correction event."""
|
||||
try:
|
||||
desc = f"Correction {action} for attendance on "
|
||||
if self.attendance_id.check_in:
|
||||
desc += self.attendance_id.check_in.strftime('%Y-%m-%d')
|
||||
desc += f": {self.reason}"
|
||||
self.env['fusion.clock.activity.log'].sudo().create({
|
||||
'employee_id': self.employee_id.id,
|
||||
'log_type': 'correction_request',
|
||||
'description': desc,
|
||||
'attendance_id': self.attendance_id.id,
|
||||
'source': 'system',
|
||||
})
|
||||
except Exception as e:
|
||||
_logger.error("Fusion Clock: Failed to create correction log: %s", e)
|
||||
Reference in New Issue
Block a user