Files
Odoo-Modules/fusion_clock/models/clock_activity_log.py
gsinghpal 50c209b8d3 feat(fusion_clock): NFC kiosk attendance fields + activity-log selections
- Add 'nfc_kiosk' to x_fclk_clock_source selection on hr.attendance
- Add x_fclk_check_in_photo and x_fclk_check_out_photo Binary fields (attachment=True)
- Add 'card_enrollment' and 'unknown_card_tap' to activity log log_type selection
- Add 'nfc_kiosk' to activity log source selection
- Add TestNfcAttendanceFields test class (3 tests); all 6 fusion_clock tests pass

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 00:24:35 -04:00

137 lines
4.5 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
from odoo import models, fields, api
class FusionClockActivityLog(models.Model):
_name = 'fusion.clock.activity.log'
_description = 'Clock Activity Log'
_order = 'log_date desc, id desc'
_rec_name = 'display_name'
employee_id = fields.Many2one(
'hr.employee',
string='Employee',
required=True,
index=True,
ondelete='cascade',
)
log_type = fields.Selection(
[
('clock_in', 'Clock In'),
('clock_out', 'Clock Out'),
('late_clock_in', 'Late Clock-In'),
('early_clock_out', 'Early Clock-Out'),
('outside_geofence', 'Outside Geofence'),
('auto_clock_out', 'Auto Clock-Out'),
('missed_clock_out', 'Missed Clock-Out'),
('absent', 'Absent'),
('leave_request', 'Leave Request'),
('reason_provided', 'Reason Provided'),
('overtime', 'Overtime'),
('correction_request', 'Correction Request'),
('ip_fallback', 'IP Fallback Used'),
('streak_milestone', 'Streak Milestone'),
('card_enrollment', 'Card Enrollment'),
('unknown_card_tap', 'Unknown Card Tap'),
],
string='Log Type',
required=True,
index=True,
)
log_date = fields.Datetime(
string='Date/Time',
required=True,
default=fields.Datetime.now,
index=True,
)
description = fields.Text(string='Description')
attendance_id = fields.Many2one(
'hr.attendance',
string='Attendance',
ondelete='set null',
index=True,
)
location_id = fields.Many2one(
'fusion.clock.location',
string='Location',
ondelete='set null',
)
latitude = fields.Float(string='Latitude', digits=(10, 7))
longitude = fields.Float(string='Longitude', digits=(10, 7))
distance = fields.Float(
string='Distance (m)',
digits=(10, 2),
help="Distance from location center in meters.",
)
source = fields.Selection(
[
('portal', 'Portal'),
('portal_fab', 'Portal FAB'),
('systray', 'Systray'),
('backend_fab', 'Backend FAB'),
('kiosk', 'Kiosk'),
('system', 'System (Cron)'),
('nfc_kiosk', 'NFC Kiosk'),
],
string='Source',
)
company_id = fields.Many2one(
'res.company',
string='Company',
related='employee_id.company_id',
store=True,
)
attempt_map_url = fields.Char(
string='Map Preview',
compute='_compute_attempt_map_url',
)
display_name = fields.Char(
compute='_compute_display_name',
store=True,
)
LOG_TYPE_LABELS = {
'clock_in': 'Clock In',
'clock_out': 'Clock Out',
'late_clock_in': 'Late Clock-In',
'early_clock_out': 'Early Clock-Out',
'outside_geofence': 'Outside Geofence',
'auto_clock_out': 'Auto Clock-Out',
'missed_clock_out': 'Missed Clock-Out',
'absent': 'Absent',
'leave_request': 'Leave Request',
'reason_provided': 'Reason Provided',
'overtime': 'Overtime',
'correction_request': 'Correction Request',
'ip_fallback': 'IP Fallback Used',
'streak_milestone': 'Streak Milestone',
}
@api.depends('latitude', 'longitude')
def _compute_attempt_map_url(self):
api_key = self.env['ir.config_parameter'].sudo().get_param(
'fusion_clock.google_maps_api_key', ''
)
for rec in self:
if rec.latitude and rec.longitude and api_key:
lat, lng = rec.latitude, rec.longitude
rec.attempt_map_url = (
f"https://maps.googleapis.com/maps/api/staticmap?"
f"center={lat},{lng}&zoom=16&size=600x250&maptype=roadmap"
f"&markers=color:red%7C{lat},{lng}"
f"&key={api_key}"
)
else:
rec.attempt_map_url = False
@api.depends('employee_id', 'log_type', 'log_date')
def _compute_display_name(self):
for rec in self:
emp = rec.employee_id.name or ''
ltype = self.LOG_TYPE_LABELS.get(rec.log_type, rec.log_type or '')
date_str = rec.log_date.strftime('%Y-%m-%d %H:%M') if rec.log_date else ''
rec.display_name = f"{emp} - {ltype} ({date_str})"