This commit is contained in:
gsinghpal
2026-02-27 14:32:32 -05:00
parent b649246e81
commit b925766966
80 changed files with 7831 additions and 1041 deletions

View File

@@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Activity Log List View -->
<record id="view_fusion_clock_activity_log_list" model="ir.ui.view">
<field name="name">fusion.clock.activity.log.list</field>
<field name="model">fusion.clock.activity.log</field>
<field name="arch" type="xml">
<list>
<field name="log_date"/>
<field name="employee_id"/>
<field name="log_type"/>
<field name="description"/>
<field name="location_id" optional="show"/>
<field name="source" optional="show"/>
<field name="distance" optional="hide"/>
</list>
</field>
</record>
<!-- Activity Log Form View -->
<record id="view_fusion_clock_activity_log_form" model="ir.ui.view">
<field name="name">fusion.clock.activity.log.form</field>
<field name="model">fusion.clock.activity.log</field>
<field name="arch" type="xml">
<form create="false" edit="false">
<sheet>
<group>
<group>
<field name="employee_id"/>
<field name="log_type"/>
<field name="log_date"/>
<field name="source"/>
</group>
<group>
<field name="location_id"/>
<field name="attendance_id"/>
<field name="latitude"/>
<field name="longitude"/>
<field name="distance"/>
</group>
</group>
<group>
<field name="description"/>
</group>
<!-- Clock-in attempt location map -->
<group string="Clock Attempt Location"
invisible="not latitude or not longitude">
<field name="attempt_map_url" widget="image_url" nolabel="1" colspan="2"/>
</group>
</sheet>
</form>
</field>
</record>
<!-- Activity Log Search View -->
<record id="view_fusion_clock_activity_log_search" model="ir.ui.view">
<field name="name">fusion.clock.activity.log.search</field>
<field name="model">fusion.clock.activity.log</field>
<field name="arch" type="xml">
<search>
<field name="employee_id"/>
<field name="log_type"/>
<separator/>
<filter name="clock_events" string="Clock Events" domain="[('log_type', 'in', ['clock_in', 'clock_out'])]"/>
<filter name="penalties" string="Penalties" domain="[('log_type', 'in', ['late_clock_in', 'early_clock_out'])]"/>
<filter name="geofence" string="Geofence" domain="[('log_type', 'in', ['outside_geofence', 'ip_fallback'])]"/>
<filter name="system" string="System" domain="[('log_type', 'in', ['auto_clock_out', 'missed_clock_out'])]"/>
<filter name="absences" string="Absences" domain="[('log_type', '=', 'absent')]"/>
<filter name="leaves" string="Leaves" domain="[('log_type', '=', 'leave_request')]"/>
<filter name="overtime" string="Overtime" domain="[('log_type', '=', 'overtime')]"/>
<separator/>
<filter name="group_employee" string="Employee" context="{'group_by': 'employee_id'}"/>
<filter name="group_type" string="Type" context="{'group_by': 'log_type'}"/>
<filter name="group_date" string="Date" context="{'group_by': 'log_date:day'}"/>
</search>
</field>
</record>
<!-- Activity Log Action -->
<record id="action_fusion_clock_activity_log" model="ir.actions.act_window">
<field name="name">Activity Logs</field>
<field name="res_model">fusion.clock.activity.log</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_fusion_clock_activity_log_search"/>
</record>
</odoo>

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Correction Request List View -->
<record id="view_fusion_clock_correction_list" model="ir.ui.view">
<field name="name">fusion.clock.correction.list</field>
<field name="model">fusion.clock.correction</field>
<field name="arch" type="xml">
<list>
<field name="create_date"/>
<field name="employee_id"/>
<field name="original_check_in"/>
<field name="original_check_out"/>
<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'" widget="badge"/>
</list>
</field>
</record>
<!-- Correction Request Form View -->
<record id="view_fusion_clock_correction_form" model="ir.ui.view">
<field name="name">fusion.clock.correction.form</field>
<field name="model">fusion.clock.correction</field>
<field name="arch" type="xml">
<form>
<header>
<button name="action_approve" string="Approve" type="object"
class="btn-primary" invisible="state != 'pending'"
groups="fusion_clock.group_fusion_clock_manager"/>
<button name="action_reject" string="Reject" type="object"
class="btn-danger" invisible="state != 'pending'"
groups="fusion_clock.group_fusion_clock_manager"/>
<field name="state" widget="statusbar"/>
</header>
<sheet>
<group>
<group string="Employee">
<field name="employee_id"/>
<field name="attendance_id"/>
</group>
<group string="Review">
<field name="reviewed_by" readonly="1"/>
<field name="reviewed_date" readonly="1"/>
</group>
</group>
<group>
<group string="Original Times">
<field name="original_check_in"/>
<field name="original_check_out"/>
</group>
<group string="Requested Correction">
<field name="requested_check_in"/>
<field name="requested_check_out"/>
</group>
</group>
<group>
<field name="reason"/>
</group>
</sheet>
<chatter/>
</form>
</field>
</record>
<!-- Correction Request Action -->
<record id="action_fusion_clock_correction" model="ir.actions.act_window">
<field name="name">Correction Requests</field>
<field name="res_model">fusion.clock.correction</field>
<field name="view_mode">list,form</field>
</record>
</odoo>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Dashboard Client Action -->
<record id="action_fusion_clock_dashboard" model="ir.actions.client">
<field name="name">Dashboard</field>
<field name="tag">fusion_clock.Dashboard</field>
</record>
</odoo>

View File

@@ -0,0 +1,56 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Leave Request List View -->
<record id="view_fusion_clock_leave_request_list" model="ir.ui.view">
<field name="name">fusion.clock.leave.request.list</field>
<field name="model">fusion.clock.leave.request</field>
<field name="arch" type="xml">
<list>
<field name="leave_date"/>
<field name="employee_id"/>
<field name="reason"/>
<field name="state" decoration-success="state == 'reviewed'"
decoration-info="state == 'auto_approved'" widget="badge"/>
<field name="created_from"/>
</list>
</field>
</record>
<!-- Leave Request Form View -->
<record id="view_fusion_clock_leave_request_form" model="ir.ui.view">
<field name="name">fusion.clock.leave.request.form</field>
<field name="model">fusion.clock.leave.request</field>
<field name="arch" type="xml">
<form>
<header>
<button name="action_mark_reviewed" string="Mark Reviewed" type="object"
class="btn-primary" invisible="state == 'reviewed'"
groups="fusion_clock.group_fusion_clock_manager"/>
<field name="state" widget="statusbar"/>
</header>
<sheet>
<group>
<group>
<field name="employee_id"/>
<field name="leave_date"/>
<field name="created_from"/>
</group>
<group>
<field name="reason"/>
</group>
</group>
</sheet>
<chatter/>
</form>
</field>
</record>
<!-- Leave Request Action -->
<record id="action_fusion_clock_leave_request" model="ir.actions.act_window">
<field name="name">Leave Requests</field>
<field name="res_model">fusion.clock.leave.request</field>
<field name="view_mode">list,form</field>
</record>
</odoo>

View File

@@ -29,7 +29,8 @@
</div>
<group>
<group string="Address">
<field name="address" placeholder="123 Business Ave, San Francisco, CA"/>
<field name="address" widget="fclk_places_autocomplete"
placeholder="Start typing an address..."/>
<field name="timezone"/>
</group>
<group string="Geofence">
@@ -45,6 +46,15 @@
<field name="employee_ids" widget="many2many_tags"
invisible="all_employees"/>
</group>
<group string="Verification">
<field name="require_photo"/>
</group>
</group>
<group>
<group string="IP Whitelist">
<field name="ip_whitelist" nolabel="1" colspan="2"
placeholder="One IP or CIDR per line, e.g.&#10;192.168.1.0/24&#10;10.0.0.100"/>
</group>
<group string="Other">
<field name="company_id" groups="base.group_multi_company"/>
<field name="active" invisible="1"/>
@@ -52,9 +62,9 @@
</group>
</group>
<!-- Google Maps Static Preview -->
<group string="Map Preview" invisible="not latitude or not longitude">
<field name="map_url" widget="image_url" string="Map" invisible="not map_url"/>
<!-- Interactive Map Preview -->
<group string="Map Preview">
<field name="map_url" widget="fclk_location_map" nolabel="1" colspan="2"/>
</group>
<group string="Notes">

View File

@@ -8,7 +8,15 @@
sequence="45"
groups="group_fusion_clock_user"/>
<!-- Dashboard / Attendance Sub-Menu -->
<!-- Dashboard -->
<menuitem id="menu_fusion_clock_dashboard"
name="Dashboard"
parent="menu_fusion_clock_root"
action="action_fusion_clock_dashboard"
sequence="5"
groups="group_fusion_clock_manager,group_fusion_clock_team_lead"/>
<!-- Attendance Sub-Menu -->
<menuitem id="menu_fusion_clock_attendance"
name="Attendance"
parent="menu_fusion_clock_root"
@@ -19,8 +27,23 @@
parent="menu_fusion_clock_attendance"
action="hr_attendance.hr_attendance_action"
sequence="10"
groups="group_fusion_clock_manager,group_fusion_clock_team_lead"/>
<menuitem id="menu_fusion_clock_corrections"
name="Correction Requests"
parent="menu_fusion_clock_attendance"
action="action_fusion_clock_correction"
sequence="20"
groups="group_fusion_clock_manager"/>
<!-- Activity Logs -->
<menuitem id="menu_fusion_clock_activity_logs"
name="Activity Logs"
parent="menu_fusion_clock_root"
action="action_fusion_clock_activity_log"
sequence="15"
groups="group_fusion_clock_manager,group_fusion_clock_team_lead"/>
<!-- Locations Sub-Menu -->
<menuitem id="menu_fusion_clock_locations"
name="Locations"
@@ -35,6 +58,14 @@
parent="menu_fusion_clock_root"
action="action_fusion_clock_penalty"
sequence="30"
groups="group_fusion_clock_manager,group_fusion_clock_team_lead"/>
<!-- Leave Requests -->
<menuitem id="menu_fusion_clock_leaves"
name="Leave Requests"
parent="menu_fusion_clock_root"
action="action_fusion_clock_leave_request"
sequence="35"
groups="group_fusion_clock_manager"/>
<!-- Reports Sub-Menu -->
@@ -52,13 +83,28 @@
sequence="90"
groups="group_fusion_clock_manager"/>
<record id="action_fusion_clock_config_settings" model="ir.actions.act_window">
<field name="name">Fusion Clock Settings</field>
<field name="res_model">res.config.settings</field>
<field name="view_mode">form</field>
<field name="target">current</field>
<field name="context">{'module': 'fusion_clock', 'bin_size': False}</field>
</record>
<menuitem id="menu_fusion_clock_settings"
name="Settings"
parent="menu_fusion_clock_config"
action="base.res_config_setting_act_window"
action="action_fusion_clock_config_settings"
sequence="10"
groups="group_fusion_clock_manager"/>
<menuitem id="menu_fusion_clock_shifts"
name="Shifts"
parent="menu_fusion_clock_config"
action="action_fusion_clock_shift"
sequence="15"
groups="group_fusion_clock_manager"/>
<menuitem id="menu_fusion_clock_locations_config"
name="Locations"
parent="menu_fusion_clock_config"

View File

@@ -13,6 +13,7 @@
<field name="scheduled_time" widget="datetime"/>
<field name="actual_time" widget="datetime"/>
<field name="difference_minutes" string="Diff (min)"/>
<field name="penalty_minutes" string="Deducted (min)"/>
<field name="company_id" groups="base.group_multi_company"/>
</list>
</field>
@@ -36,6 +37,7 @@
<field name="scheduled_time"/>
<field name="actual_time"/>
<field name="difference_minutes"/>
<field name="penalty_minutes"/>
</group>
</group>
<group>

View File

@@ -40,6 +40,10 @@
string="Send Report" class="btn-secondary"
invisible="state != 'generated'"
icon="fa-envelope"/>
<button name="action_export_csv" type="object"
string="Export CSV" class="btn-secondary"
invisible="state == 'draft'"
icon="fa-download"/>
<field name="state" widget="statusbar" statusbar_visible="draft,generated,sent"/>
</header>
<sheet>

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Shift List View -->
<record id="view_fusion_clock_shift_list" model="ir.ui.view">
<field name="name">fusion.clock.shift.list</field>
<field name="model">fusion.clock.shift</field>
<field name="arch" type="xml">
<list editable="bottom">
<field name="sequence" widget="handle"/>
<field name="name"/>
<field name="start_time" widget="float_time"/>
<field name="end_time" widget="float_time"/>
<field name="break_minutes"/>
<field name="employee_count"/>
<field name="company_id" groups="base.group_multi_company"/>
</list>
</field>
</record>
<!-- Shift Form View -->
<record id="view_fusion_clock_shift_form" model="ir.ui.view">
<field name="name">fusion.clock.shift.form</field>
<field name="model">fusion.clock.shift</field>
<field name="arch" type="xml">
<form>
<sheet>
<group>
<group>
<field name="name"/>
<field name="start_time" widget="float_time"/>
<field name="end_time" widget="float_time"/>
<field name="break_minutes"/>
</group>
<group>
<field name="sequence"/>
<field name="active"/>
<field name="color" widget="color"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
</group>
<group string="Assigned Employees">
<field name="employee_ids" nolabel="1" colspan="2">
<list>
<field name="name"/>
<field name="department_id"/>
<field name="job_title"/>
</list>
</field>
</group>
</sheet>
</form>
</field>
</record>
<!-- Shift Action -->
<record id="action_fusion_clock_shift" model="ir.actions.act_window">
<field name="name">Shifts</field>
<field name="res_model">fusion.clock.shift</field>
<field name="view_mode">list,form</field>
</record>
</odoo>

View File

@@ -10,9 +10,12 @@
<xpath expr="//field[@name='worked_hours']" position="after">
<field name="x_fclk_net_hours" string="Net Hours" widget="float_time" optional="show"/>
<field name="x_fclk_break_minutes" string="Break (min)" optional="show"/>
<field name="x_fclk_overtime_hours" string="Overtime (h)" widget="float_time" optional="show"
decoration-danger="x_fclk_is_overtime"/>
<field name="x_fclk_location_id" string="Location" optional="show"/>
<field name="x_fclk_clock_source" string="Source" optional="hide"/>
<field name="x_fclk_auto_clocked_out" string="Auto Out" optional="hide"/>
<field name="x_fclk_is_overtime" column_invisible="True"/>
</xpath>
</field>
</record>
@@ -30,14 +33,20 @@
<field name="x_fclk_clock_source"/>
<field name="x_fclk_break_minutes"/>
<field name="x_fclk_net_hours" widget="float_time"/>
<field name="x_fclk_overtime_hours" widget="float_time"/>
</group>
<group>
<field name="x_fclk_in_distance"/>
<field name="x_fclk_out_distance"/>
<field name="x_fclk_auto_clocked_out"/>
<field name="x_fclk_grace_used"/>
<field name="x_fclk_is_overtime"/>
</group>
</group>
<group string="Check-In Photo" name="fusion_clock_photo"
invisible="not x_fclk_checkin_photo">
<field name="x_fclk_checkin_photo" widget="image" class="oe_avatar"/>
</group>
<group string="Penalties" name="fusion_clock_penalties"
invisible="not x_fclk_penalty_ids">
<field name="x_fclk_penalty_ids" nolabel="1" colspan="2">
@@ -46,6 +55,7 @@
<field name="scheduled_time"/>
<field name="actual_time"/>
<field name="difference_minutes"/>
<field name="penalty_minutes"/>
</list>
</field>
</group>
@@ -62,11 +72,13 @@
<xpath expr="//search" position="inside">
<field name="x_fclk_location_id"/>
<separator/>
<filter name="fclk_portal" string="Portal" domain="[('x_fclk_clock_source', '=', 'portal')]"/>
<filter name="fclk_systray" string="Systray" domain="[('x_fclk_clock_source', '=', 'systray')]"/>
<filter name="fclk_portal" string="Portal" domain="[('x_fclk_clock_source', 'in', ['portal', 'portal_fab'])]"/>
<filter name="fclk_systray" string="Systray/Backend" domain="[('x_fclk_clock_source', 'in', ['systray', 'backend_fab'])]"/>
<filter name="fclk_kiosk" string="Kiosk" domain="[('x_fclk_clock_source', '=', 'kiosk')]"/>
<filter name="fclk_auto" string="Auto Clock-Out" domain="[('x_fclk_auto_clocked_out', '=', True)]"/>
<separator/>
<filter name="fclk_has_penalty" string="Has Penalty" domain="[('x_fclk_penalty_ids', '!=', False)]"/>
<filter name="fclk_has_overtime" string="Has Overtime" domain="[('x_fclk_is_overtime', '=', True)]"/>
<separator/>
<filter name="group_location" string="Location" context="{'group_by': 'x_fclk_location_id'}"/>
<filter name="group_source" string="Source" context="{'group_by': 'x_fclk_clock_source'}"/>

View File

@@ -0,0 +1,167 @@
<?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">
<!-- Summary Stats -->
<group>
<group string="Configuration">
<field name="x_fclk_enable_clock"/>
<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"/>
</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>
<separator string="Activity Logs"/>
<!-- Clock Events -->
<group string="Clock Events" name="fclk_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">
<field name="log_date"/>
<field name="log_type"/>
<field name="description"/>
<field name="location_id"/>
<field name="source"/>
</list>
</field>
</group>
<!-- Penalties -->
<group string="Penalties" name="fclk_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">
<field name="log_date"/>
<field name="log_type"/>
<field name="description"/>
</list>
</field>
</group>
<!-- Geofence Violations -->
<group string="Geofence Violations" name="fclk_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">
<field name="log_date"/>
<field name="log_type"/>
<field name="description"/>
<field name="latitude"/>
<field name="longitude"/>
<field name="distance"/>
</list>
</field>
</group>
<!-- System Actions -->
<group string="System Actions" name="fclk_system_actions">
<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">
<field name="log_date"/>
<field name="log_type"/>
<field name="description"/>
<field name="attendance_id"/>
</list>
</field>
</group>
<!-- Absences -->
<group string="Absences" name="fclk_absences">
<field name="x_fclk_activity_log_ids" nolabel="1" colspan="2"
domain="[('log_type', '=', 'absent')]">
<list create="false" delete="false" limit="20">
<field name="log_date"/>
<field name="description"/>
</list>
</field>
</group>
<!-- Leave Requests -->
<group string="Leave Requests" name="fclk_leaves">
<field name="x_fclk_leave_request_ids" nolabel="1" colspan="2">
<list create="false" delete="false" limit="20">
<field name="leave_date"/>
<field name="reason"/>
<field name="state"/>
<field name="created_from"/>
</list>
</field>
</group>
<!-- Reason Submissions -->
<group string="Reason Submissions" name="fclk_reasons">
<field name="x_fclk_activity_log_ids" nolabel="1" colspan="2"
domain="[('log_type', '=', 'reason_provided')]">
<list create="false" delete="false" limit="20">
<field name="log_date"/>
<field name="description"/>
</list>
</field>
</group>
<!-- Overtime -->
<group string="Overtime" name="fclk_overtime">
<field name="x_fclk_activity_log_ids" nolabel="1" colspan="2"
domain="[('log_type', '=', 'overtime')]">
<list create="false" delete="false" limit="20">
<field name="log_date"/>
<field name="description"/>
<field name="attendance_id"/>
</list>
</field>
</group>
<!-- Correction Requests -->
<group string="Correction Requests" name="fclk_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>
</group>
<!-- Streak Milestones -->
<group string="Streak Milestones" name="fclk_streaks">
<field name="x_fclk_activity_log_ids" nolabel="1" colspan="2"
domain="[('log_type', '=', 'streak_milestone')]">
<list create="false" delete="false" limit="20">
<field name="log_date"/>
<field name="description"/>
</list>
</field>
</group>
</page>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Kiosk Page Template -->
<template id="kiosk_page" name="Fusion Clock Kiosk">
<t t-call="web.frontend_layout">
<t t-set="no_header" t-value="True"/>
<t t-set="no_footer" t-value="True"/>
<div id="fclk-kiosk" class="container-fluid vh-100 d-flex flex-column align-items-center justify-content-center"
style="background: var(--o-main-bg-color, #f8f9fa);"
t-att-data-pin-required="'true' if pin_required else 'false'">
<!-- Header -->
<div class="text-center mb-4">
<h1 style="font-size: 2.5rem;">Fusion Clock</h1>
<p class="text-muted" style="font-size: 1.2rem;">Kiosk Mode</p>
<div id="fclk-kiosk-time" style="font-size: 3rem; font-weight: 300;"></div>
</div>
<!-- Search / PIN Entry -->
<div class="card shadow-sm" style="width: 100%; max-width: 500px;">
<div class="card-body p-4">
<!-- Step 1: Search Employee -->
<div id="fclk-kiosk-search">
<label class="form-label fw-bold">Employee Name</label>
<input type="text" id="fclk-kiosk-query" class="form-control form-control-lg mb-3"
placeholder="Type your name..." autocomplete="off"/>
<div id="fclk-kiosk-results" class="list-group mb-3" style="max-height: 300px; overflow-y: auto;"></div>
</div>
<!-- Step 2: PIN (if required) -->
<div id="fclk-kiosk-pin" style="display: none;">
<h4 id="fclk-kiosk-emp-name" class="text-center mb-3"></h4>
<t t-if="pin_required">
<label class="form-label fw-bold">Enter PIN</label>
<input type="password" id="fclk-kiosk-pin-input" class="form-control form-control-lg text-center mb-3"
maxlength="6" placeholder="------"/>
</t>
<div class="d-grid gap-2">
<button id="fclk-kiosk-clock-btn" class="btn btn-lg btn-primary">
Clock In / Out
</button>
<button id="fclk-kiosk-back-btn" class="btn btn-outline-secondary">
Back
</button>
</div>
</div>
<!-- Step 3: Result -->
<div id="fclk-kiosk-result" style="display: none;">
<div id="fclk-kiosk-result-msg" class="text-center py-4" style="font-size: 1.3rem;"></div>
</div>
<!-- Error message -->
<div id="fclk-kiosk-error" class="alert alert-danger mt-3" style="display: none;"></div>
</div>
</div>
</div>
</t>
</template>
</odoo>

View File

@@ -1,17 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Add Clock link to portal home -->
<!-- Clock link removed from portal home - now handled by fusion_authorizer_portal -->
<template id="portal_my_home_clock" name="Portal My Home: Clock"
inherit_id="portal.portal_my_home" priority="60">
<xpath expr="//div[hasclass('o_portal_docs')]" position="inside">
<t t-call="portal.portal_docs_entry">
<t t-set="icon" t-value="'/fusion_clock/static/description/icon.png'"/>
<t t-set="title">Clock In / Out</t>
<t t-set="url" t-value="'/my/clock'"/>
<t t-set="text">Punch in, view timesheets, and download attendance reports</t>
<t t-set="placeholder_count" t-value="'clock_count'"/>
</t>
</xpath>
</template>
@@ -22,8 +15,8 @@
<template id="portal_layout_clock_fab" name="Portal Clock FAB"
inherit_id="portal.portal_layout">
<xpath expr="//div[@id='wrap']" position="after">
<t t-set="__fclk_emp" t-value="fclk_employee if fclk_employee is defined else False"/>
<t t-if="__fclk_emp and __fclk_emp.x_fclk_enable_clock">
<t t-set="fclk_emp" t-value="fclk_employee if fclk_employee is defined else False"/>
<t t-if="fclk_emp and fclk_emp.x_fclk_enable_clock">
<div id="fclk-portal-fab"
t-att-data-checked-in="'true' if fclk_checked_in else 'false'"
t-att-data-check-in-time="fclk_check_in_time or ''"
@@ -208,6 +201,20 @@
</div>
</div>
<!-- Request Leave -->
<button id="fclk-leave-btn" class="fclk-leave-btn">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/>
<line x1="16" y1="2" x2="16" y2="6"/>
<line x1="8" y1="2" x2="8" y2="6"/>
<line x1="3" y1="10" x2="21" y2="10"/>
</svg>
Request Leave
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" class="fclk-leave-btn-arrow">
<polyline points="9 18 15 12 9 6"/>
</svg>
</button>
<!-- Recent Activity -->
<div class="fclk-recent-section">
<div class="fclk-recent-header">
@@ -278,6 +285,127 @@
</div>
<!-- Reason Modal (missed clock-out) -->
<div class="fclk-wizard-overlay" id="fclk-reason-modal" style="display:none;">
<div class="fclk-wizard-backdrop" data-dismiss="fclk-reason-modal"></div>
<div class="fclk-wizard-dialog">
<div class="fclk-wizard-header fclk-wizard-header--warning">
<div class="fclk-wizard-header-icon">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/>
<line x1="12" y1="9" x2="12" y2="13"/>
<line x1="12" y1="17" x2="12.01" y2="17"/>
</svg>
</div>
<h3 class="fclk-wizard-title">Missed Clock-Out</h3>
<p class="fclk-wizard-subtitle">You didn't clock out on your last shift. Please provide details before continuing.</p>
</div>
<div class="fclk-wizard-body">
<div class="fclk-wizard-field">
<label class="fclk-wizard-label" for="fclk-reason-text">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
Reason <span class="fclk-wizard-required">*</span>
</label>
<textarea id="fclk-reason-text" class="fclk-wizard-input fclk-wizard-textarea" rows="3"
placeholder="Please explain why you didn't clock out..."></textarea>
</div>
<div class="fclk-wizard-field">
<label class="fclk-wizard-label" for="fclk-reason-time">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
Departure Time
</label>
<input type="datetime-local" id="fclk-reason-time" class="fclk-wizard-input"/>
<span class="fclk-wizard-hint">When did you actually leave? (optional)</span>
</div>
</div>
<div class="fclk-wizard-footer">
<button class="fclk-wizard-btn fclk-wizard-btn--secondary" data-dismiss="fclk-reason-modal">Cancel</button>
<button id="fclk-reason-submit" class="fclk-wizard-btn fclk-wizard-btn--primary">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
Submit Reason
</button>
</div>
</div>
</div>
<!-- Clock-Out Confirmation Modal -->
<div class="fclk-wizard-overlay" id="fclk-clockout-confirm-modal" style="display:none;">
<div class="fclk-wizard-backdrop" data-dismiss="fclk-clockout-confirm-modal"></div>
<div class="fclk-wizard-dialog fclk-wizard-dialog--compact">
<div class="fclk-wizard-header fclk-wizard-header--danger">
<div class="fclk-wizard-header-icon">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="4" y="4" width="16" height="16" rx="2"/>
</svg>
</div>
<h3 class="fclk-wizard-title">Clock Out?</h3>
<p class="fclk-wizard-subtitle">Are you sure you want to end your current shift?</p>
</div>
<div class="fclk-wizard-body">
<div class="fclk-clockout-summary">
<div class="fclk-clockout-summary-row">
<span class="fclk-clockout-summary-label">Clocked in at</span>
<span class="fclk-clockout-summary-value" id="fclk-confirm-checkin-time">--</span>
</div>
<div class="fclk-clockout-summary-row">
<span class="fclk-clockout-summary-label">Duration</span>
<span class="fclk-clockout-summary-value" id="fclk-confirm-duration">--</span>
</div>
</div>
</div>
<div class="fclk-wizard-footer">
<button class="fclk-wizard-btn fclk-wizard-btn--secondary" data-dismiss="fclk-clockout-confirm-modal">Cancel</button>
<button id="fclk-clockout-confirm-btn" class="fclk-wizard-btn fclk-wizard-btn--danger">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="4" y="4" width="16" height="16" rx="2"/></svg>
Confirm Clock Out
</button>
</div>
</div>
</div>
<!-- Leave Request Modal -->
<div class="fclk-wizard-overlay" id="fclk-leave-modal" style="display:none;">
<div class="fclk-wizard-backdrop" data-dismiss="fclk-leave-modal"></div>
<div class="fclk-wizard-dialog">
<div class="fclk-wizard-header fclk-wizard-header--info">
<div class="fclk-wizard-header-icon">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/>
<line x1="16" y1="2" x2="16" y2="6"/>
<line x1="8" y1="2" x2="8" y2="6"/>
<line x1="3" y1="10" x2="21" y2="10"/>
</svg>
</div>
<h3 class="fclk-wizard-title">Request Leave</h3>
<p class="fclk-wizard-subtitle">Submit a leave request for your manager to review.</p>
</div>
<div class="fclk-wizard-body">
<div class="fclk-wizard-field">
<label class="fclk-wizard-label" for="fclk-leave-date">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
Leave Date <span class="fclk-wizard-required">*</span>
</label>
<input type="date" id="fclk-leave-date" class="fclk-wizard-input"/>
</div>
<div class="fclk-wizard-field">
<label class="fclk-wizard-label" for="fclk-leave-reason">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
Reason <span class="fclk-wizard-required">*</span>
</label>
<textarea id="fclk-leave-reason" class="fclk-wizard-input fclk-wizard-textarea" rows="3"
placeholder="Please provide a reason for your leave request..."></textarea>
</div>
</div>
<div class="fclk-wizard-footer">
<button class="fclk-wizard-btn fclk-wizard-btn--secondary" data-dismiss="fclk-leave-modal">Cancel</button>
<button id="fclk-leave-submit" class="fclk-wizard-btn fclk-wizard-btn--primary">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="20 6 9 17 4 12"/></svg>
Submit Leave Request
</button>
</div>
</div>
</div>
<!-- Location Picker Modal -->
<div class="fclk-modal" id="fclk-location-modal" style="display:none;">
<div class="fclk-modal-backdrop" onclick="document.getElementById('fclk-location-modal').style.display='none'"></div>

View File

@@ -62,6 +62,7 @@
<th>Break</th>
<th>Net</th>
<th>Location</th>
<th></th>
</tr>
</thead>
<tbody>
@@ -92,6 +93,15 @@
<td style="color:#9ca3af; font-size:12px;">
<t t-esc="att.x_fclk_location_id.name or ''"/>
</td>
<td>
<a href="#" class="fclk-correction-link"
t-att-data-att-id="att.id"
t-att-data-check-in="att.check_in.strftime('%Y-%m-%d %H:%M:%S') if att.check_in else ''"
t-att-data-check-out="att.check_out.strftime('%Y-%m-%d %H:%M:%S') if att.check_out else ''"
style="font-size:11px; color:#6b7280;">
Correct
</a>
</td>
</tr>
</t>
</tbody>

View File

@@ -12,7 +12,7 @@
<!-- Work Schedule -->
<block title="Work Schedule" name="fclk_work_schedule">
<setting string="Default Clock-In Time" help="The scheduled start time for employees.">
<setting string="Default Clock-In Time" help="The scheduled start time for employees (used when no shift is assigned).">
<div class="content-group">
<div class="row mt16">
<label for="fclk_default_clock_in_time" class="col-lg-3"/>
@@ -70,7 +70,7 @@
<!-- Penalties -->
<block title="Penalty Tracking" name="fclk_penalties">
<setting string="Enable Penalties" help="Track late clock-in and early clock-out.">
<setting string="Enable Penalties" help="Track late clock-in and early clock-out with automatic deductions.">
<field name="fclk_enable_penalties"/>
<div class="content-group" invisible="not fclk_enable_penalties">
<div class="row mt16">
@@ -78,10 +78,104 @@
<field name="fclk_penalty_grace_minutes"/>
<span class="ms-1">minutes grace before penalty</span>
</div>
<div class="row mt8">
<label for="fclk_penalty_deduction_minutes" class="col-lg-3"/>
<field name="fclk_penalty_deduction_minutes"/>
<span class="ms-1">minutes deducted per penalty</span>
</div>
</div>
</setting>
</block>
<!-- Office User & Notifications -->
<block title="Office User &amp; Notifications" name="fclk_notifications">
<setting string="Office User" help="User who receives all attendance-related activity notifications.">
<div class="content-group">
<div class="row mt16">
<label for="fclk_office_user_id" class="col-lg-3"/>
<field name="fclk_office_user_id"/>
</div>
<div class="row mt8">
<label for="fclk_very_late_threshold_minutes" class="col-lg-3"/>
<field name="fclk_very_late_threshold_minutes"/>
<span class="ms-1">minutes late before office user is notified</span>
</div>
<div class="row mt8">
<label for="fclk_max_monthly_absences" class="col-lg-3"/>
<field name="fclk_max_monthly_absences"/>
<span class="ms-1">absences before office user is alerted</span>
</div>
</div>
</setting>
<setting string="Employee Notifications" help="Send clock-in/out reminders to employees.">
<field name="fclk_enable_employee_notifications"/>
<div class="content-group" invisible="not fclk_enable_employee_notifications">
<div class="row mt16">
<label for="fclk_reminder_before_shift_minutes" class="col-lg-3"/>
<field name="fclk_reminder_before_shift_minutes"/>
<span class="ms-1">minutes after shift start to remind</span>
</div>
<div class="row mt8">
<label for="fclk_reminder_before_end_minutes" class="col-lg-3"/>
<field name="fclk_reminder_before_end_minutes"/>
<span class="ms-1">minutes before shift end to remind</span>
</div>
</div>
</setting>
<setting string="Weekly Summary" help="Send weekly attendance summary to employees on Monday.">
<field name="fclk_send_weekly_summary"/>
</setting>
</block>
<!-- Overtime -->
<block title="Overtime" name="fclk_overtime">
<setting string="Enable Overtime Tracking" help="Track hours beyond scheduled shift.">
<field name="fclk_enable_overtime"/>
<div class="content-group" invisible="not fclk_enable_overtime">
<div class="row mt16">
<label for="fclk_daily_overtime_threshold" class="col-lg-3"/>
<field name="fclk_daily_overtime_threshold" widget="float_time"/>
<span class="ms-1">daily net hours threshold</span>
</div>
<div class="row mt8">
<label for="fclk_weekly_overtime_threshold" class="col-lg-3"/>
<field name="fclk_weekly_overtime_threshold" widget="float_time"/>
<span class="ms-1">weekly net hours threshold</span>
</div>
</div>
</setting>
</block>
<!-- Location & Verification -->
<block title="Location &amp; Verification" name="fclk_location_verification">
<setting string="IP Fallback" help="Allow IP-based verification when GPS is unavailable.">
<field name="fclk_enable_ip_fallback"/>
</setting>
<setting string="Photo Verification" help="Require selfie on clock-in (controlled per location).">
<field name="fclk_enable_photo_verification"/>
</setting>
</block>
<!-- Kiosk -->
<block title="Kiosk Mode" name="fclk_kiosk">
<setting string="Enable Kiosk" help="Allow shared-device clock-in/out.">
<field name="fclk_enable_kiosk"/>
<div class="content-group" invisible="not fclk_enable_kiosk">
<div class="row mt16">
<field name="fclk_kiosk_pin_required"/>
<label for="fclk_kiosk_pin_required" class="ms-1"/>
</div>
</div>
</setting>
</block>
<!-- Corrections -->
<block title="Corrections" name="fclk_corrections">
<setting string="Enable Correction Requests" help="Allow employees to request timesheet corrections.">
<field name="fclk_enable_correction_requests"/>
</setting>
</block>
<!-- Pay Period -->
<block title="Pay Period" name="fclk_pay_period">
<setting string="Pay Period Schedule" help="Defines how often reports are generated.">
@@ -110,7 +204,15 @@
<div class="content-group">
<div class="row mt16">
<label for="fclk_report_recipient_emails" class="col-lg-3"/>
<field name="fclk_report_recipient_emails" class="o_input" placeholder="manager@company.com, hr@company.com"/>
<field name="fclk_report_recipient_emails" class="o_input" placeholder="manager@company.com"/>
</div>
</div>
</setting>
<setting string="CSV Column Mapping" help="Custom column names for CSV export (JSON format).">
<div class="content-group">
<div class="row mt16">
<label for="fclk_csv_column_mapping" class="col-lg-3"/>
<field name="fclk_csv_column_mapping" class="o_input"/>
</div>
</div>
</setting>
@@ -118,7 +220,7 @@
<!-- Clock Locations -->
<block title="Clock Locations" name="fclk_locations">
<setting string="Manage Locations" help="Configure geofenced clock-in/out locations with GPS coordinates and radius.">
<setting string="Manage Locations" help="Configure geofenced clock-in/out locations.">
<div class="content-group">
<div class="mt16">
<button name="%(fusion_clock.action_fusion_clock_location)d" type="action"