Files
Odoo-Modules/fusion_clock/views/hr_employee_views.xml
gsinghpal 78fa8f07ee fix(fusion_clock): stop stale missed-clock-in nag; add Owner role + attendance exemption
The "explain your missed clock-out" dialog (driven by hr.employee.
x_fclk_pending_reason) was set by the absence + auto-clock-out crons but only
cleared by the systray reason dialog -- never by the kiosk/NFC clock paths that
staff actually use. During the kiosk rollout the absence cron flagged the whole
company (hundreds of "absent" logs); those stale flags then nagged everyone
forever, even while currently clocked in.

Fixes:
- Clear x_fclk_pending_reason on every successful clock-in (portal, systray,
  PIN kiosk, NFC kiosk). Back on the clock => no nag.
- get_status / dashboard never report pending while checked-in or exempt; the
  systray also guards the dialog client-side.
- Absence detection no longer sets x_fclk_pending_reason (an absence has no
  "departure time" to explain). It still logs 'absent' + notifies the office.
- One-time migration (19.0.4.2.0) clears existing stale flags.

Owner / attendance exemption:
- New "Owner" role (top of the Fusion Clock access dropdown, implies Manager)
  plus a per-employee "Exempt from Attendance" checkbox.
- hr.employee._fclk_is_attendance_exempt(); the absence, auto-clock-out,
  reminder and weekly-summary crons all skip exempt employees, and the dialog
  is suppressed for them.

Tests: tests/test_pending_reason_exempt.py (13 cases). Full fusion_clock suite
green except pre-existing env-sensitive failures.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 17:54:00 -04:00

163 lines
9.1 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Employee Form: Fusion Clock Tab -->
<record id="view_employee_form_fusion_clock" model="ir.ui.view">
<field name="name">hr.employee.form.fusion.clock</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr.view_employee_form"/>
<field name="arch" type="xml">
<xpath expr="//notebook" position="inside">
<page string="Fusion Clock" name="fusion_clock_tab"
groups="fusion_clock.group_fusion_clock_manager,fusion_clock.group_fusion_clock_team_lead">
<!-- Configuration & Status -->
<group>
<group string="Configuration">
<field name="x_fclk_enable_clock"/>
<field name="x_fclk_exempt_from_attendance"/>
<field name="x_fclk_shift_id"/>
<field name="x_fclk_default_location_id"/>
<field name="x_fclk_break_minutes"/>
<field name="x_fclk_kiosk_pin" password="True"
groups="fusion_clock.group_fusion_clock_manager"/>
<field name="x_fclk_nfc_card_uid"
placeholder="Tap card on USB reader, or paste UID"
groups="fusion_clock.group_fusion_clock_manager"/>
</group>
<group string="Status">
<field name="x_fclk_ontime_streak"/>
<field name="x_fclk_absences_this_month"/>
<field name="x_fclk_absences_this_year"/>
<field name="x_fclk_overtime_this_week" widget="float_time"/>
<field name="x_fclk_overtime_this_month" widget="float_time"/>
<field name="x_fclk_pending_reason"/>
</group>
</group>
<!-- Activity Log Sub-Tabs -->
<notebook>
<page string="Clock Events" name="fclk_sub_clock_events">
<field name="x_fclk_activity_log_ids" nolabel="1" colspan="2"
domain="[('log_type', 'in', ['clock_in', 'clock_out'])]">
<list create="false" delete="false" limit="20" default_order="log_date desc">
<field name="log_date"/>
<field name="log_type"/>
<field name="description"/>
<field name="location_id"/>
<field name="source"/>
</list>
</field>
</page>
<page string="Penalties" name="fclk_sub_penalties">
<field name="x_fclk_activity_log_ids" nolabel="1" colspan="2"
domain="[('log_type', 'in', ['late_clock_in', 'early_clock_out'])]">
<list create="false" delete="false" limit="20" default_order="log_date desc">
<field name="log_date"/>
<field name="log_type"/>
<field name="description"/>
</list>
</field>
</page>
<page string="Geofence" name="fclk_sub_geofence">
<field name="x_fclk_activity_log_ids" nolabel="1" colspan="2"
domain="[('log_type', 'in', ['outside_geofence', 'ip_fallback'])]">
<list create="false" delete="false" limit="20" default_order="log_date desc">
<field name="log_date"/>
<field name="log_type"/>
<field name="description"/>
<field name="latitude"/>
<field name="longitude"/>
<field name="distance"/>
</list>
</field>
</page>
<page string="System" name="fclk_sub_system">
<field name="x_fclk_activity_log_ids" nolabel="1" colspan="2"
domain="[('log_type', 'in', ['auto_clock_out', 'missed_clock_out'])]">
<list create="false" delete="false" limit="20" default_order="log_date desc">
<field name="log_date"/>
<field name="log_type"/>
<field name="description"/>
<field name="attendance_id"/>
</list>
</field>
</page>
<page string="Absences" name="fclk_sub_absences">
<field name="x_fclk_activity_log_ids" nolabel="1" colspan="2"
domain="[('log_type', '=', 'absent')]">
<list create="false" delete="false" limit="20" default_order="log_date desc">
<field name="log_date"/>
<field name="description"/>
</list>
</field>
</page>
<page string="Leave Requests" name="fclk_sub_leaves">
<field name="x_fclk_leave_request_ids" nolabel="1" colspan="2">
<list create="false" delete="false" limit="20" default_order="leave_date desc">
<field name="leave_date"/>
<field name="reason"/>
<field name="state"/>
<field name="created_from"/>
</list>
</field>
</page>
<page string="Reasons" name="fclk_sub_reasons">
<field name="x_fclk_activity_log_ids" nolabel="1" colspan="2"
domain="[('log_type', '=', 'reason_provided')]">
<list create="false" delete="false" limit="20" default_order="log_date desc">
<field name="log_date"/>
<field name="description"/>
</list>
</field>
</page>
<page string="Overtime" name="fclk_sub_overtime">
<field name="x_fclk_activity_log_ids" nolabel="1" colspan="2"
domain="[('log_type', '=', 'overtime')]">
<list create="false" delete="false" limit="20" default_order="log_date desc">
<field name="log_date"/>
<field name="description"/>
<field name="attendance_id"/>
</list>
</field>
</page>
<page string="Corrections" name="fclk_sub_corrections">
<field name="x_fclk_correction_ids" nolabel="1" colspan="2">
<list create="false" delete="false" limit="20">
<field name="attendance_id"/>
<field name="requested_check_in"/>
<field name="requested_check_out"/>
<field name="reason"/>
<field name="state" decoration-success="state == 'approved'"
decoration-danger="state == 'rejected'"
decoration-warning="state == 'pending'"/>
</list>
</field>
</page>
<page string="Streaks" name="fclk_sub_streaks">
<field name="x_fclk_activity_log_ids" nolabel="1" colspan="2"
domain="[('log_type', '=', 'streak_milestone')]">
<list create="false" delete="false" limit="20" default_order="log_date desc">
<field name="log_date"/>
<field name="description"/>
</list>
</field>
</page>
</notebook>
</page>
</xpath>
</field>
</record>
</odoo>