fix(fusion_clock): settings audit — remove 2 dead knobs, make IP-fallback + all Boolean toggles work
Audit of all 41 settings found 3 that were shown but read nowhere, and 17 Boolean toggles that couldn't be turned OFF. - Remove grace_period_minutes (orphaned by the schedule-driven cron rewrite) and weekly_overtime_threshold (never implemented): field + view + seed. - enable_ip_fallback now actually gates _verify_location's IP-whitelist check (default ON to preserve current behaviour). - All 17 fusion_clock Boolean settings now persist explicitly as 'True'/'False' via a _FCLK_BOOL_PARAMS loop in get_values/set_values (config_parameter Booleans can't store False, so OFF never stuck). Add round-trip tests. Bump 3.15.2 -> 3.16.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -23,7 +23,6 @@ class ResConfigSettings(models.TransientModel):
|
||||
)
|
||||
fclk_auto_deduct_break = fields.Boolean(
|
||||
string='Auto-Deduct Break',
|
||||
config_parameter='fusion_clock.auto_deduct_break',
|
||||
default=True,
|
||||
help="Automatically deduct break from worked hours on clock-out.",
|
||||
)
|
||||
@@ -43,15 +42,10 @@ class ResConfigSettings(models.TransientModel):
|
||||
# ── Attendance Rules ───────────────────────────────────────────────
|
||||
fclk_enable_auto_clockout = fields.Boolean(
|
||||
string='Enable Auto Clock-Out',
|
||||
config_parameter='fusion_clock.enable_auto_clockout',
|
||||
default=True,
|
||||
help="Automatically clock out employees who forget. Triggers after shift end time plus grace period, or after max shift hours.",
|
||||
)
|
||||
fclk_grace_period_minutes = fields.Float(
|
||||
string='Grace Period (min)',
|
||||
config_parameter='fusion_clock.grace_period_minutes',
|
||||
default=15.0,
|
||||
help="Minutes allowed after scheduled end before auto clock-out.",
|
||||
help="Automatically clock out employees who forget — closes an attendance "
|
||||
"left open past the Max Shift Length safety cap (overtime up to the cap "
|
||||
"is never cut off).",
|
||||
)
|
||||
fclk_max_shift_hours = fields.Float(
|
||||
string='Max Shift Length (hours)',
|
||||
@@ -64,7 +58,6 @@ class ResConfigSettings(models.TransientModel):
|
||||
)
|
||||
fclk_enable_penalties = fields.Boolean(
|
||||
string='Enable Penalty Tracking',
|
||||
config_parameter='fusion_clock.enable_penalties',
|
||||
default=True,
|
||||
help="Deduct minutes from worked hours when employees clock in late or clock out early.",
|
||||
)
|
||||
@@ -82,9 +75,8 @@ class ResConfigSettings(models.TransientModel):
|
||||
)
|
||||
fclk_enable_overtime = fields.Boolean(
|
||||
string='Enable Overtime Tracking',
|
||||
config_parameter='fusion_clock.enable_overtime',
|
||||
default=True,
|
||||
help="Calculate and track overtime when net hours exceed the daily or weekly threshold.",
|
||||
help="Calculate and track overtime when net hours exceed the daily threshold.",
|
||||
)
|
||||
fclk_daily_overtime_threshold = fields.Float(
|
||||
string='Daily OT Threshold (hours)',
|
||||
@@ -92,12 +84,6 @@ class ResConfigSettings(models.TransientModel):
|
||||
default=8.0,
|
||||
help="Net hours beyond this threshold count as daily overtime.",
|
||||
)
|
||||
fclk_weekly_overtime_threshold = fields.Float(
|
||||
string='Weekly OT Threshold (hours)',
|
||||
config_parameter='fusion_clock.weekly_overtime_threshold',
|
||||
default=40.0,
|
||||
help="Net hours beyond this threshold count as weekly overtime.",
|
||||
)
|
||||
|
||||
# ── Notifications ──────────────────────────────────────────────────
|
||||
fclk_office_user_id = fields.Many2one(
|
||||
@@ -119,7 +105,6 @@ class ResConfigSettings(models.TransientModel):
|
||||
)
|
||||
fclk_enable_employee_notifications = fields.Boolean(
|
||||
string='Enable Employee Notifications',
|
||||
config_parameter='fusion_clock.enable_employee_notifications',
|
||||
default=True,
|
||||
help="Send clock-in/out reminders to employees.",
|
||||
)
|
||||
@@ -137,7 +122,6 @@ class ResConfigSettings(models.TransientModel):
|
||||
)
|
||||
fclk_send_weekly_summary = fields.Boolean(
|
||||
string='Send Weekly Summary',
|
||||
config_parameter='fusion_clock.send_weekly_summary',
|
||||
default=True,
|
||||
help="Send weekly attendance summary to each employee on Monday.",
|
||||
)
|
||||
@@ -145,13 +129,12 @@ class ResConfigSettings(models.TransientModel):
|
||||
# ── Location & Verification ────────────────────────────────────────
|
||||
fclk_enable_ip_fallback = fields.Boolean(
|
||||
string='Enable IP Fallback',
|
||||
config_parameter='fusion_clock.enable_ip_fallback',
|
||||
default=False,
|
||||
help="Allow IP-based location verification when GPS is unavailable.",
|
||||
default=True,
|
||||
help="Allow IP-whitelist location verification when GPS is unavailable "
|
||||
"or outside all geofences.",
|
||||
)
|
||||
fclk_enable_photo_verification = fields.Boolean(
|
||||
string='Enable Photo Verification',
|
||||
config_parameter='fusion_clock.enable_photo_verification',
|
||||
default=False,
|
||||
help="Global toggle for selfie verification on clock-in (per-location control).",
|
||||
)
|
||||
@@ -163,25 +146,21 @@ class ResConfigSettings(models.TransientModel):
|
||||
# ── Kiosk & Portal ─────────────────────────────────────────────────
|
||||
fclk_enable_kiosk = fields.Boolean(
|
||||
string='Enable Kiosk Mode',
|
||||
config_parameter='fusion_clock.enable_kiosk',
|
||||
default=False,
|
||||
help="Allow employees to clock in/out from a shared device using their PIN code.",
|
||||
)
|
||||
fclk_kiosk_pin_required = fields.Boolean(
|
||||
string='Require PIN for Kiosk',
|
||||
config_parameter='fusion_clock.kiosk_pin_required',
|
||||
default=True,
|
||||
help="Require employees to enter a PIN when using kiosk mode.",
|
||||
)
|
||||
fclk_enable_correction_requests = fields.Boolean(
|
||||
string='Enable Correction Requests',
|
||||
config_parameter='fusion_clock.enable_correction_requests',
|
||||
default=True,
|
||||
help="Allow employees to request timesheet corrections from the portal.",
|
||||
)
|
||||
fclk_enable_sounds = fields.Boolean(
|
||||
string='Enable Clock Sounds',
|
||||
config_parameter='fusion_clock.enable_sounds',
|
||||
default=True,
|
||||
help="Play audio confirmation sounds when employees clock in or out.",
|
||||
)
|
||||
@@ -211,13 +190,11 @@ class ResConfigSettings(models.TransientModel):
|
||||
)
|
||||
fclk_auto_generate_reports = fields.Boolean(
|
||||
string='Auto-Generate Reports',
|
||||
config_parameter='fusion_clock.auto_generate_reports',
|
||||
default=True,
|
||||
help="Automatically create attendance reports at the end of each pay period.",
|
||||
)
|
||||
fclk_send_employee_reports = fields.Boolean(
|
||||
string='Send Employee Copies',
|
||||
config_parameter='fusion_clock.send_employee_reports',
|
||||
default=True,
|
||||
help="Send each employee a copy of their individual attendance report.",
|
||||
)
|
||||
@@ -243,13 +220,11 @@ class ResConfigSettings(models.TransientModel):
|
||||
# ── NFC Clock Kiosk ────────────────────────────────────────────────
|
||||
fclk_enable_nfc_kiosk = fields.Boolean(
|
||||
string='Enable NFC Clock Kiosk',
|
||||
config_parameter='fusion_clock.enable_nfc_kiosk',
|
||||
default=False,
|
||||
help="Enable the tap-to-clock NFC kiosk page at /fusion_clock/kiosk/nfc.",
|
||||
)
|
||||
fclk_nfc_photo_required = fields.Boolean(
|
||||
string='Require Photo on Tap',
|
||||
config_parameter='fusion_clock.nfc_photo_required',
|
||||
default=True,
|
||||
help="If enabled, the kiosk rejects taps when the front camera is unavailable. "
|
||||
"Recommended for buddy-punch deterrence.",
|
||||
@@ -262,7 +237,6 @@ class ResConfigSettings(models.TransientModel):
|
||||
)
|
||||
fclk_nfc_kiosk_debug = fields.Boolean(
|
||||
string='Debug Mode (overlay + mock-tap)',
|
||||
config_parameter='fusion_clock.nfc_kiosk_debug',
|
||||
default=False,
|
||||
help="Enables two dev/troubleshooting features on the NFC kiosk page: "
|
||||
"(1) a green-text debug overlay at the top of the screen logging every NFC and tap event in real time, "
|
||||
@@ -286,9 +260,35 @@ class ResConfigSettings(models.TransientModel):
|
||||
"Set to 0 to disable the auto-wipe.",
|
||||
)
|
||||
|
||||
# Boolean settings persisted explicitly (NOT via config_parameter): Odoo
|
||||
# deletes a config param when you write a falsy value, so a config_parameter
|
||||
# Boolean can never be turned OFF (the row vanishes and get_param returns the
|
||||
# default). Storing 'True'/'False' strings ourselves makes the toggles work.
|
||||
_FCLK_BOOL_PARAMS = [
|
||||
('fclk_auto_deduct_break', 'fusion_clock.auto_deduct_break', True),
|
||||
('fclk_enable_auto_clockout', 'fusion_clock.enable_auto_clockout', True),
|
||||
('fclk_enable_penalties', 'fusion_clock.enable_penalties', True),
|
||||
('fclk_enable_overtime', 'fusion_clock.enable_overtime', True),
|
||||
('fclk_enable_employee_notifications', 'fusion_clock.enable_employee_notifications', True),
|
||||
('fclk_send_weekly_summary', 'fusion_clock.send_weekly_summary', True),
|
||||
('fclk_enable_ip_fallback', 'fusion_clock.enable_ip_fallback', True),
|
||||
('fclk_enable_photo_verification', 'fusion_clock.enable_photo_verification', False),
|
||||
('fclk_enable_kiosk', 'fusion_clock.enable_kiosk', False),
|
||||
('fclk_kiosk_pin_required', 'fusion_clock.kiosk_pin_required', True),
|
||||
('fclk_enable_correction_requests', 'fusion_clock.enable_correction_requests', True),
|
||||
('fclk_enable_sounds', 'fusion_clock.enable_sounds', True),
|
||||
('fclk_auto_generate_reports', 'fusion_clock.auto_generate_reports', True),
|
||||
('fclk_send_employee_reports', 'fusion_clock.send_employee_reports', True),
|
||||
('fclk_enable_nfc_kiosk', 'fusion_clock.enable_nfc_kiosk', False),
|
||||
('fclk_nfc_photo_required', 'fusion_clock.nfc_photo_required', True),
|
||||
('fclk_nfc_kiosk_debug', 'fusion_clock.nfc_kiosk_debug', False),
|
||||
]
|
||||
|
||||
def set_values(self):
|
||||
super().set_values()
|
||||
ICP = self.env['ir.config_parameter'].sudo()
|
||||
for fname, key, _default in self._FCLK_BOOL_PARAMS:
|
||||
ICP.set_param(key, 'True' if self[fname] else 'False')
|
||||
if self.fclk_office_user_id:
|
||||
ICP.set_param('fusion_clock.office_user_id', str(self.fclk_office_user_id.id))
|
||||
else:
|
||||
@@ -308,6 +308,8 @@ class ResConfigSettings(models.TransientModel):
|
||||
def get_values(self):
|
||||
res = super().get_values()
|
||||
ICP = self.env['ir.config_parameter'].sudo()
|
||||
for fname, key, default in self._FCLK_BOOL_PARAMS:
|
||||
res[fname] = ICP.get_param(key, 'True' if default else 'False') == 'True'
|
||||
office_user_id = int(ICP.get_param('fusion_clock.office_user_id', '0'))
|
||||
if office_user_id:
|
||||
res['fclk_office_user_id'] = office_user_id
|
||||
|
||||
Reference in New Issue
Block a user