Initial commit
This commit is contained in:
118
fusion_clock/views/clock_location_views.xml
Normal file
118
fusion_clock/views/clock_location_views.xml
Normal file
@@ -0,0 +1,118 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Location Form View -->
|
||||
<record id="view_fusion_clock_location_form" model="ir.ui.view">
|
||||
<field name="name">fusion.clock.location.form</field>
|
||||
<field name="model">fusion.clock.location</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Clock Location">
|
||||
<header/>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="action_view_attendances" type="object"
|
||||
class="oe_stat_button" icon="fa-clock-o">
|
||||
<field name="attendance_count" widget="statinfo" string="Attendances"/>
|
||||
</button>
|
||||
</div>
|
||||
<widget name="web_ribbon" title="Archived" bg_color="text-bg-danger" invisible="active"/>
|
||||
<div class="oe_title">
|
||||
<label for="name"/>
|
||||
<h1>
|
||||
<field name="name" placeholder="e.g. Main Office"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group string="Address">
|
||||
<field name="address" widget="fclk_places_address"
|
||||
placeholder="Start typing an address or place name..."/>
|
||||
<field name="timezone"/>
|
||||
</group>
|
||||
<group string="Geofence">
|
||||
<field name="latitude"/>
|
||||
<field name="longitude"/>
|
||||
<field name="radius"/>
|
||||
<field name="color" widget="color"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<group string="Access">
|
||||
<field name="all_employees"/>
|
||||
<field name="employee_ids" widget="many2many_tags"
|
||||
invisible="all_employees"/>
|
||||
</group>
|
||||
<group string="Other">
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="active" invisible="1"/>
|
||||
<field name="sequence"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<!-- Interactive Map (drag pin to adjust, search places) -->
|
||||
<group string="Map">
|
||||
<widget name="fclk_map" colspan="2"/>
|
||||
</group>
|
||||
|
||||
<group string="Notes">
|
||||
<field name="note" nolabel="1" colspan="2" placeholder="Additional notes about this location..."/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Location List View -->
|
||||
<record id="view_fusion_clock_location_list" model="ir.ui.view">
|
||||
<field name="name">fusion.clock.location.list</field>
|
||||
<field name="model">fusion.clock.location</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Clock Locations" default_order="sequence, name">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="address"/>
|
||||
<field name="radius" string="Radius (m)"/>
|
||||
<field name="all_employees"/>
|
||||
<field name="attendance_count" string="Attendances"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
<field name="active" column_invisible="True"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Location Search View -->
|
||||
<record id="view_fusion_clock_location_search" model="ir.ui.view">
|
||||
<field name="name">fusion.clock.location.search</field>
|
||||
<field name="model">fusion.clock.location</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Locations">
|
||||
<field name="name"/>
|
||||
<field name="address"/>
|
||||
<filter name="active" string="Active" domain="[('active', '=', True)]"/>
|
||||
<filter name="archived" string="Archived" domain="[('active', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter name="all_employees" string="All Employees" domain="[('all_employees', '=', True)]"/>
|
||||
<separator/>
|
||||
<filter name="group_company" string="Company" context="{'group_by': 'company_id'}"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Location Action -->
|
||||
<record id="action_fusion_clock_location" model="ir.actions.act_window">
|
||||
<field name="name">Clock Locations</field>
|
||||
<field name="res_model">fusion.clock.location</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fusion_clock_location_search"/>
|
||||
<field name="context">{'search_default_active': 1}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create your first clock location
|
||||
</p>
|
||||
<p>
|
||||
Define geofenced locations where employees can clock in and out.
|
||||
Set the address, radius, and which employees are allowed.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
91
fusion_clock/views/clock_menus.xml
Normal file
91
fusion_clock/views/clock_menus.xml
Normal file
@@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Top-Level Menu -->
|
||||
<menuitem id="menu_fusion_clock_root"
|
||||
name="Fusion Clock"
|
||||
web_icon="fusion_clock,static/description/icon.png"
|
||||
sequence="45"
|
||||
groups="group_fusion_clock_user"/>
|
||||
|
||||
<!-- Dashboard / Attendance Sub-Menu -->
|
||||
<menuitem id="menu_fusion_clock_attendance"
|
||||
name="Attendance"
|
||||
parent="menu_fusion_clock_root"
|
||||
sequence="10"/>
|
||||
|
||||
<menuitem id="menu_fusion_clock_employee_overview"
|
||||
name="Employee Overview"
|
||||
parent="menu_fusion_clock_attendance"
|
||||
action="action_fusion_clock_employee_overview"
|
||||
sequence="5"
|
||||
groups="group_fusion_clock_manager"/>
|
||||
|
||||
<menuitem id="menu_fusion_clock_by_pay_period"
|
||||
name="By Pay Period"
|
||||
parent="menu_fusion_clock_attendance"
|
||||
action="action_fusion_clock_attendance_by_period"
|
||||
sequence="8"
|
||||
groups="group_fusion_clock_manager"/>
|
||||
|
||||
<menuitem id="menu_fusion_clock_attendance_list"
|
||||
name="All Attendances"
|
||||
parent="menu_fusion_clock_attendance"
|
||||
action="hr_attendance.hr_attendance_action"
|
||||
sequence="10"
|
||||
groups="group_fusion_clock_manager"/>
|
||||
|
||||
<!-- Locations Sub-Menu -->
|
||||
<menuitem id="menu_fusion_clock_locations"
|
||||
name="Locations"
|
||||
parent="menu_fusion_clock_root"
|
||||
action="action_fusion_clock_location"
|
||||
sequence="20"
|
||||
groups="group_fusion_clock_manager"/>
|
||||
|
||||
<!-- Penalties Sub-Menu -->
|
||||
<menuitem id="menu_fusion_clock_penalties"
|
||||
name="Penalties"
|
||||
parent="menu_fusion_clock_root"
|
||||
action="action_fusion_clock_penalty"
|
||||
sequence="30"
|
||||
groups="group_fusion_clock_manager"/>
|
||||
|
||||
<!-- Reports Sub-Menu -->
|
||||
<menuitem id="menu_fusion_clock_reports"
|
||||
name="Reports"
|
||||
parent="menu_fusion_clock_root"
|
||||
action="action_fusion_clock_report"
|
||||
sequence="40"
|
||||
groups="group_fusion_clock_manager"/>
|
||||
|
||||
<!-- Configuration Sub-Menu -->
|
||||
<menuitem id="menu_fusion_clock_config"
|
||||
name="Configuration"
|
||||
parent="menu_fusion_clock_root"
|
||||
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'}</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_fusion_clock_settings"
|
||||
name="Settings"
|
||||
parent="menu_fusion_clock_config"
|
||||
action="action_fusion_clock_config_settings"
|
||||
sequence="10"
|
||||
groups="group_fusion_clock_manager"/>
|
||||
|
||||
<menuitem id="menu_fusion_clock_locations_config"
|
||||
name="Locations"
|
||||
parent="menu_fusion_clock_config"
|
||||
action="action_fusion_clock_location"
|
||||
sequence="20"
|
||||
groups="group_fusion_clock_manager"/>
|
||||
|
||||
</odoo>
|
||||
88
fusion_clock/views/clock_penalty_views.xml
Normal file
88
fusion_clock/views/clock_penalty_views.xml
Normal file
@@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Penalty List View -->
|
||||
<record id="view_fusion_clock_penalty_list" model="ir.ui.view">
|
||||
<field name="name">fusion.clock.penalty.list</field>
|
||||
<field name="model">fusion.clock.penalty</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Clock Penalties" default_order="date desc">
|
||||
<field name="date"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="penalty_type"/>
|
||||
<field name="scheduled_time" widget="datetime"/>
|
||||
<field name="actual_time" widget="datetime"/>
|
||||
<field name="difference_minutes" string="Diff (min)"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Penalty Form View -->
|
||||
<record id="view_fusion_clock_penalty_form" model="ir.ui.view">
|
||||
<field name="name">fusion.clock.penalty.form</field>
|
||||
<field name="model">fusion.clock.penalty</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Clock Penalty">
|
||||
<sheet>
|
||||
<group>
|
||||
<group>
|
||||
<field name="employee_id"/>
|
||||
<field name="penalty_type"/>
|
||||
<field name="date"/>
|
||||
<field name="attendance_id"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="scheduled_time"/>
|
||||
<field name="actual_time"/>
|
||||
<field name="difference_minutes"/>
|
||||
</group>
|
||||
</group>
|
||||
<group>
|
||||
<field name="note" nolabel="1" colspan="2" placeholder="Additional notes..."/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Penalty Search View -->
|
||||
<record id="view_fusion_clock_penalty_search" model="ir.ui.view">
|
||||
<field name="name">fusion.clock.penalty.search</field>
|
||||
<field name="model">fusion.clock.penalty</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Penalties">
|
||||
<field name="employee_id"/>
|
||||
<field name="penalty_type"/>
|
||||
<filter name="late_in" string="Late Clock-In" domain="[('penalty_type', '=', 'late_in')]"/>
|
||||
<filter name="early_out" string="Early Clock-Out" domain="[('penalty_type', '=', 'early_out')]"/>
|
||||
<separator/>
|
||||
<filter name="this_week" string="This Week"
|
||||
domain="[('date', '>=', (context_today() - datetime.timedelta(days=context_today().weekday())).strftime('%Y-%m-%d'))]"/>
|
||||
<filter name="this_month" string="This Month"
|
||||
domain="[('date', '>=', context_today().strftime('%Y-%m-01'))]"/>
|
||||
<separator/>
|
||||
<filter name="group_employee" string="Employee" context="{'group_by': 'employee_id'}"/>
|
||||
<filter name="group_type" string="Penalty Type" context="{'group_by': 'penalty_type'}"/>
|
||||
<filter name="group_date" string="Date" context="{'group_by': 'date:month'}"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Penalty Action -->
|
||||
<record id="action_fusion_clock_penalty" model="ir.actions.act_window">
|
||||
<field name="name">Penalties</field>
|
||||
<field name="res_model">fusion.clock.penalty</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fusion_clock_penalty_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
No penalties recorded
|
||||
</p>
|
||||
<p>
|
||||
Penalties are automatically created when employees clock in late or clock out early.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
135
fusion_clock/views/clock_report_views.xml
Normal file
135
fusion_clock/views/clock_report_views.xml
Normal file
@@ -0,0 +1,135 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Report List View -->
|
||||
<record id="view_fusion_clock_report_list" model="ir.ui.view">
|
||||
<field name="name">fusion.clock.report.list</field>
|
||||
<field name="model">fusion.clock.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<list string="Clock Reports" default_order="date_end desc">
|
||||
<field name="name"/>
|
||||
<field name="date_start"/>
|
||||
<field name="date_end"/>
|
||||
<field name="schedule_type"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="is_batch"/>
|
||||
<field name="days_worked"/>
|
||||
<field name="total_hours" widget="float_time"/>
|
||||
<field name="net_hours" widget="float_time"/>
|
||||
<field name="total_penalties"/>
|
||||
<field name="state" widget="badge"
|
||||
decoration-info="state == 'draft'"
|
||||
decoration-success="state == 'generated'"
|
||||
decoration-warning="state == 'sent'"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Report Form View -->
|
||||
<record id="view_fusion_clock_report_form" model="ir.ui.view">
|
||||
<field name="name">fusion.clock.report.form</field>
|
||||
<field name="model">fusion.clock.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Clock Report">
|
||||
<header>
|
||||
<button name="action_generate_report" type="object"
|
||||
string="Generate Report" class="btn-primary"
|
||||
invisible="state != 'draft'"
|
||||
icon="fa-file-pdf-o"/>
|
||||
<button name="action_send_report" type="object"
|
||||
string="Send Report" class="btn-secondary"
|
||||
invisible="state != 'generated'"
|
||||
icon="fa-envelope"/>
|
||||
<field name="state" widget="statusbar" statusbar_visible="draft,generated,sent"/>
|
||||
</header>
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<h1>
|
||||
<field name="name"/>
|
||||
</h1>
|
||||
</div>
|
||||
<group>
|
||||
<group string="Period">
|
||||
<field name="date_start"/>
|
||||
<field name="date_end"/>
|
||||
<field name="schedule_type"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="is_batch"/>
|
||||
<field name="company_id" groups="base.group_multi_company"/>
|
||||
</group>
|
||||
<group string="Summary">
|
||||
<field name="days_worked"/>
|
||||
<field name="total_hours" widget="float_time"/>
|
||||
<field name="net_hours" widget="float_time"/>
|
||||
<field name="total_breaks"/>
|
||||
<field name="total_penalties"/>
|
||||
</group>
|
||||
</group>
|
||||
|
||||
<!-- PDF Download -->
|
||||
<group string="Report File" invisible="not report_pdf">
|
||||
<field name="report_pdf" filename="report_pdf_filename"/>
|
||||
<field name="report_pdf_filename" invisible="1"/>
|
||||
</group>
|
||||
|
||||
<!-- Attendance Records -->
|
||||
<notebook>
|
||||
<page string="Attendance Records" name="attendances">
|
||||
<field name="attendance_ids" nolabel="1">
|
||||
<list>
|
||||
<field name="employee_id"/>
|
||||
<field name="check_in" widget="datetime"/>
|
||||
<field name="check_out" widget="datetime"/>
|
||||
<field name="worked_hours" widget="float_time"/>
|
||||
<field name="x_fclk_break_minutes" string="Break"/>
|
||||
<field name="x_fclk_net_hours" string="Net" widget="float_time"/>
|
||||
<field name="x_fclk_location_id" string="Location"/>
|
||||
</list>
|
||||
</field>
|
||||
</page>
|
||||
</notebook>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Report Search View -->
|
||||
<record id="view_fusion_clock_report_search" model="ir.ui.view">
|
||||
<field name="name">fusion.clock.report.search</field>
|
||||
<field name="model">fusion.clock.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Reports">
|
||||
<field name="employee_id"/>
|
||||
<field name="name"/>
|
||||
<filter name="individual" string="Individual" domain="[('employee_id', '!=', False)]"/>
|
||||
<filter name="batch" string="Batch" domain="[('employee_id', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter name="draft" string="Draft" domain="[('state', '=', 'draft')]"/>
|
||||
<filter name="generated" string="Generated" domain="[('state', '=', 'generated')]"/>
|
||||
<filter name="sent" string="Sent" domain="[('state', '=', 'sent')]"/>
|
||||
<separator/>
|
||||
<filter name="group_employee" string="Employee" context="{'group_by': 'employee_id'}"/>
|
||||
<filter name="group_period" string="Schedule Type" context="{'group_by': 'schedule_type'}"/>
|
||||
<filter name="group_state" string="Status" context="{'group_by': 'state'}"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- Report Action -->
|
||||
<record id="action_fusion_clock_report" model="ir.actions.act_window">
|
||||
<field name="name">Reports</field>
|
||||
<field name="res_model">fusion.clock.report</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_fusion_clock_report_search"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
No reports generated yet
|
||||
</p>
|
||||
<p>
|
||||
Reports are automatically generated at the end of each pay period.
|
||||
You can also create them manually.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
269
fusion_clock/views/hr_attendance_views.xml
Normal file
269
fusion_clock/views/hr_attendance_views.xml
Normal file
@@ -0,0 +1,269 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- ================================================================
|
||||
Extend Attendance List View - add Fusion Clock columns
|
||||
================================================================ -->
|
||||
<record id="view_hr_attendance_list_fusion_clock" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.list.fusion.clock</field>
|
||||
<field name="model">hr.attendance</field>
|
||||
<field name="inherit_id" ref="hr_attendance.view_attendance_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<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_location_id" string="In Location" optional="show"/>
|
||||
<field name="x_fclk_out_location_id" string="Out Location" optional="hide"/>
|
||||
<field name="x_fclk_pay_period" string="Pay Period" optional="hide"/>
|
||||
<field name="x_fclk_clock_source" string="Source" optional="hide"/>
|
||||
<field name="x_fclk_auto_clocked_out" string="Auto Out" optional="hide"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================
|
||||
Dedicated list view for pay-period views
|
||||
Shows the columns needed for attendance detail
|
||||
================================================================ -->
|
||||
<record id="view_hr_attendance_list_by_period" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.list.by.period</field>
|
||||
<field name="model">hr.attendance</field>
|
||||
<field name="arch" type="xml">
|
||||
<list default_order="check_in desc">
|
||||
<field name="employee_id" optional="show"/>
|
||||
<field name="check_in" string="Clock In"/>
|
||||
<field name="check_out" string="Clock Out"/>
|
||||
<field name="x_fclk_location_id" string="In Location" optional="show"/>
|
||||
<field name="x_fclk_out_location_id" string="Out Location" optional="show"/>
|
||||
<field name="worked_hours" string="Total Hours" widget="float_time"/>
|
||||
<field name="x_fclk_break_minutes" string="Break (min)"/>
|
||||
<field name="x_fclk_net_hours" string="Net Hours" widget="float_time"/>
|
||||
<field name="overtime_hours" string="Extra Hours" widget="float_time" optional="show"/>
|
||||
<field name="x_fclk_pay_period" string="Pay Period" optional="show"/>
|
||||
<field name="x_fclk_clock_source" string="Source" optional="hide"/>
|
||||
</list>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================
|
||||
Dedicated search view for pay-period actions
|
||||
Group-by filters in correct order: Pay Period first, Employee second
|
||||
================================================================ -->
|
||||
<record id="view_hr_attendance_search_by_period" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.search.by.period</field>
|
||||
<field name="model">hr.attendance</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Attendance">
|
||||
<field name="employee_id"/>
|
||||
<field name="department_id" operator="child_of"/>
|
||||
<field name="x_fclk_location_id" string="Location"/>
|
||||
<field name="x_fclk_pay_period" string="Pay Period"/>
|
||||
<separator/>
|
||||
<filter string="My Attendances" name="myattendances"
|
||||
domain="[('employee_id.user_id', '=', uid)]"/>
|
||||
<filter string="At Work" name="nocheckout"
|
||||
domain="[('check_out', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="Date" name="check_in_filter" date="check_in"/>
|
||||
<separator/>
|
||||
<filter string="Pay Period" name="group_pay_period"
|
||||
context="{'group_by': 'x_fclk_pay_period'}"/>
|
||||
<filter string="Employee" name="group_employee"
|
||||
context="{'group_by': 'employee_id'}"/>
|
||||
<filter string="Month" name="group_month"
|
||||
context="{'group_by': 'check_in:month'}"/>
|
||||
<filter string="Location" name="group_location"
|
||||
context="{'group_by': 'x_fclk_location_id'}"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================
|
||||
Extend base Attendance Search View - add our extra filters
|
||||
================================================================ -->
|
||||
<record id="view_hr_attendance_search_fusion_clock" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.search.fusion.clock</field>
|
||||
<field name="model">hr.attendance</field>
|
||||
<field name="inherit_id" ref="hr_attendance.hr_attendance_view_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//search" position="inside">
|
||||
<field name="x_fclk_location_id"/>
|
||||
<field name="x_fclk_pay_period" string="Pay Period"/>
|
||||
<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_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)]"/>
|
||||
<separator/>
|
||||
<filter name="group_pay_period" string="Pay Period" context="{'group_by': 'x_fclk_pay_period'}"/>
|
||||
<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'}"/>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================
|
||||
Extend Attendance Form View - add Fusion Clock details
|
||||
================================================================ -->
|
||||
<record id="view_hr_attendance_form_fusion_clock" model="ir.ui.view">
|
||||
<field name="name">hr.attendance.form.fusion.clock</field>
|
||||
<field name="model">hr.attendance</field>
|
||||
<field name="inherit_id" ref="hr_attendance.hr_attendance_view_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//sheet" position="inside">
|
||||
<group string="Fusion Clock Details" name="fusion_clock_details">
|
||||
<group>
|
||||
<field name="x_fclk_location_id"/>
|
||||
<field name="x_fclk_out_location_id"/>
|
||||
<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_pay_period"/>
|
||||
</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"/>
|
||||
</group>
|
||||
</group>
|
||||
<group string="Penalties" name="fusion_clock_penalties"
|
||||
invisible="not x_fclk_penalty_ids">
|
||||
<field name="x_fclk_penalty_ids" nolabel="1" colspan="2">
|
||||
<list>
|
||||
<field name="penalty_type"/>
|
||||
<field name="scheduled_time"/>
|
||||
<field name="actual_time"/>
|
||||
<field name="difference_minutes"/>
|
||||
</list>
|
||||
</field>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================
|
||||
Employee Kanban View - card per employee showing clock stats
|
||||
================================================================ -->
|
||||
<record id="view_hr_employee_kanban_fusion_clock" model="ir.ui.view">
|
||||
<field name="name">hr.employee.kanban.fusion.clock</field>
|
||||
<field name="model">hr.employee</field>
|
||||
<field name="arch" type="xml">
|
||||
<kanban class="o_hr_employee_kanban" sample="1">
|
||||
<field name="id"/>
|
||||
<field name="name"/>
|
||||
<field name="job_title"/>
|
||||
<field name="department_id"/>
|
||||
<field name="attendance_state"/>
|
||||
<field name="x_fclk_period_hours"/>
|
||||
<field name="x_fclk_period_label"/>
|
||||
<field name="avatar_128"/>
|
||||
<templates>
|
||||
<t t-name="card">
|
||||
<div class="d-flex flex-column h-100">
|
||||
<div class="d-flex align-items-center gap-3 mb-3">
|
||||
<field name="avatar_128" widget="image"
|
||||
options="{'size': [48, 48]}"
|
||||
class="rounded-circle flex-shrink-0"/>
|
||||
<div class="flex-grow-1 min-w-0">
|
||||
<div class="d-flex align-items-center gap-2">
|
||||
<field name="name" class="fw-bold text-truncate"/>
|
||||
<span t-if="record.attendance_state.raw_value == 'checked_in'"
|
||||
class="badge rounded-pill text-bg-success flex-shrink-0">IN</span>
|
||||
<span t-if="record.attendance_state.raw_value == 'checked_out'"
|
||||
class="badge rounded-pill text-bg-secondary flex-shrink-0">OUT</span>
|
||||
</div>
|
||||
<div class="text-muted small text-truncate">
|
||||
<field name="job_title"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-2 px-1">
|
||||
<div class="d-flex justify-content-between align-items-baseline">
|
||||
<span class="text-muted small">Period Hours</span>
|
||||
<span class="fw-bold">
|
||||
<field name="x_fclk_period_hours" widget="float_time"/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="text-muted mt-1" style="font-size: 0.75rem;">
|
||||
<field name="x_fclk_period_label"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-grow-1"/>
|
||||
<button name="action_fclk_open_attendance" type="object"
|
||||
class="btn btn-sm btn-outline-primary w-100 mt-2">
|
||||
<i class="fa fa-list-ul me-1"/>View Attendance
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
</templates>
|
||||
</kanban>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================
|
||||
Employee Kanban Search View
|
||||
================================================================ -->
|
||||
<record id="view_hr_employee_search_fusion_clock" model="ir.ui.view">
|
||||
<field name="name">hr.employee.search.fusion.clock</field>
|
||||
<field name="model">hr.employee</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Employees">
|
||||
<field name="name"/>
|
||||
<field name="department_id"/>
|
||||
<field name="job_title"/>
|
||||
<separator/>
|
||||
<filter name="group_department" string="Department"
|
||||
context="{'group_by': 'department_id'}"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================
|
||||
Action: Employee Overview (Kanban cards)
|
||||
================================================================ -->
|
||||
<record id="action_fusion_clock_employee_overview" model="ir.actions.act_window">
|
||||
<field name="name">Employee Overview</field>
|
||||
<field name="res_model">hr.employee</field>
|
||||
<field name="view_mode">kanban</field>
|
||||
<field name="domain">[('x_fclk_enable_clock', '=', True)]</field>
|
||||
<field name="search_view_id" ref="view_hr_employee_search_fusion_clock"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">No clock-enabled employees found.</p>
|
||||
<p>Enable Fusion Clock on employee records to see them here.</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_fusion_clock_employee_overview_kanban" model="ir.actions.act_window.view">
|
||||
<field name="sequence">1</field>
|
||||
<field name="view_mode">kanban</field>
|
||||
<field name="view_id" ref="view_hr_employee_kanban_fusion_clock"/>
|
||||
<field name="act_window_id" ref="action_fusion_clock_employee_overview"/>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================
|
||||
Action: Attendance By Pay Period (list grouped by period then employee)
|
||||
================================================================ -->
|
||||
<record id="action_fusion_clock_attendance_by_period" model="ir.actions.act_window">
|
||||
<field name="name">Attendance by Pay Period</field>
|
||||
<field name="res_model">hr.attendance</field>
|
||||
<field name="view_mode">list,form</field>
|
||||
<field name="search_view_id" ref="view_hr_attendance_search_by_period"/>
|
||||
<field name="context">{
|
||||
'search_default_group_pay_period': 1,
|
||||
'search_default_group_employee': 1,
|
||||
}</field>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">No attendance records found.</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_fusion_clock_attendance_by_period_list" model="ir.actions.act_window.view">
|
||||
<field name="sequence">1</field>
|
||||
<field name="view_mode">list</field>
|
||||
<field name="view_id" ref="view_hr_attendance_list_by_period"/>
|
||||
<field name="act_window_id" ref="action_fusion_clock_attendance_by_period"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
344
fusion_clock/views/portal_clock_templates.xml
Normal file
344
fusion_clock/views/portal_clock_templates.xml
Normal file
@@ -0,0 +1,344 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Add Clock In/Out tile to the Fusion portal home grid -->
|
||||
<template id="portal_my_home_clock" name="Portal My Home: Clock"
|
||||
inherit_id="fusion_authorizer_portal.portal_my_home_authorizer" priority="60">
|
||||
<xpath expr="//div[hasclass('row')][hasclass('g-3')][hasclass('mb-4')]" position="inside">
|
||||
<t t-set="pfab_emp_home" t-value="request.env['hr.employee'].sudo().search([('user_id','=',request.env.uid)], limit=1)"/>
|
||||
<t t-if="pfab_emp_home and pfab_emp_home.x_fclk_enable_clock">
|
||||
<div class="col-md-6">
|
||||
<a href="/my/clock" class="card h-100 border-0 shadow-sm text-decoration-none" style="border-radius: 12px; min-height: 100px;">
|
||||
<div class="card-body d-flex align-items-center p-4">
|
||||
<div class="me-3">
|
||||
<div class="rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px; background: linear-gradient(135deg, #0d9488 0%, #2563eb 100%);">
|
||||
<i class="fa fa-clock-o fa-lg text-white"/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-1 text-dark">Clock In / Out</h5>
|
||||
<small class="text-muted">Punch in, view timesheets, and reports</small>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- ================================================================
|
||||
Portal FAB - Floating clock button on ALL portal pages
|
||||
Injected into portal.portal_layout so it appears everywhere.
|
||||
================================================================ -->
|
||||
<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="pfab_emp" t-value="fclk_employee if fclk_employee is defined else False"/>
|
||||
<t t-if="pfab_emp and pfab_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 ''"
|
||||
t-att-data-location-name="fclk_location_name or ''">
|
||||
|
||||
<!-- Expandable panel (above button) -->
|
||||
<div class="fclk-pfab-panel" style="display:none;">
|
||||
<div class="fclk-pfab-panel-header">
|
||||
<div class="fclk-pfab-panel-title">
|
||||
<span class="fclk-pfab-status-dot"/>
|
||||
<span class="fclk-pfab-status-text">Ready</span>
|
||||
</div>
|
||||
<a href="/my/clock" class="fclk-pfab-open-link" title="Open Full Clock">
|
||||
<i class="fa fa-external-link"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="fclk-pfab-location" style="display:none;">
|
||||
<i class="fa fa-map-marker"/>
|
||||
<span class="fclk-pfab-location-text"/>
|
||||
</div>
|
||||
<div class="fclk-pfab-timer">00:00:00</div>
|
||||
<div class="fclk-pfab-stats">
|
||||
<div class="fclk-pfab-stat">
|
||||
<span class="fclk-pfab-stat-val fclk-pfab-today">0.0h</span>
|
||||
<span class="fclk-pfab-stat-lbl">Today</span>
|
||||
</div>
|
||||
<div class="fclk-pfab-stat-divider"/>
|
||||
<div class="fclk-pfab-stat">
|
||||
<span class="fclk-pfab-stat-val fclk-pfab-week">0.0h</span>
|
||||
<span class="fclk-pfab-stat-lbl">Week</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="fclk-pfab-action fclk-pfab-action--in">
|
||||
<i class="fa fa-play-circle-o"/> Clock In
|
||||
</button>
|
||||
<div class="fclk-pfab-error" style="display:none;">
|
||||
<i class="fa fa-exclamation-triangle"/>
|
||||
<span class="fclk-pfab-error-text"/>
|
||||
</div>
|
||||
<div class="fclk-pfab-panel-arrow"/>
|
||||
</div>
|
||||
|
||||
<!-- Floating button -->
|
||||
<button class="fclk-pfab-btn">
|
||||
<span class="fclk-pfab-ripple-ring fclk-pfab-ripple-ring--1"/>
|
||||
<span class="fclk-pfab-ripple-ring fclk-pfab-ripple-ring--2"/>
|
||||
<span class="fclk-pfab-ripple-ring fclk-pfab-ripple-ring--3"/>
|
||||
<span class="fclk-pfab-icon">
|
||||
<i class="fa fa-clock-o"/>
|
||||
</span>
|
||||
<span class="fclk-pfab-badge" style="display:none;">00:00:00</span>
|
||||
</button>
|
||||
</div>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<!-- Main Clock Page -->
|
||||
<template id="portal_clock_page" name="Fusion Clock Portal">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="breadcrumbs_searchbar" t-value="False"/>
|
||||
<t t-set="no_breadcrumbs" t-value="True"/>
|
||||
<t t-set="no_header" t-value="True"/>
|
||||
|
||||
<div id="fusion-clock-app" class="fclk-app"
|
||||
t-att-data-checked-in="'true' if is_checked_in else 'false'"
|
||||
t-att-data-check-in-time="current_attendance.check_in.isoformat() if current_attendance and current_attendance.check_in else ''"
|
||||
t-att-data-location-name="current_attendance.x_fclk_location_id.name if current_attendance and current_attendance.x_fclk_location_id else ''"
|
||||
t-att-data-enable-sounds="'true' if enable_sounds else 'false'"
|
||||
t-att-data-google-maps-key="google_maps_key or ''">
|
||||
|
||||
<!-- Dark Background Container -->
|
||||
<div class="fclk-container">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="fclk-header">
|
||||
<div class="fclk-date" id="fclk-date-display"></div>
|
||||
<h1 class="fclk-greeting">Hello, <t t-esc="employee.name.split(' ')[0]"/></h1>
|
||||
</div>
|
||||
|
||||
<!-- Status Card -->
|
||||
<div class="fclk-status-card">
|
||||
<div class="fclk-status-row">
|
||||
<div class="fclk-status-indicator">
|
||||
<span class="fclk-dot" id="fclk-status-dot"
|
||||
t-attf-class="fclk-dot {{ 'fclk-dot-active' if is_checked_in else '' }}"></span>
|
||||
<span class="fclk-status-text" id="fclk-status-text">
|
||||
<t t-if="is_checked_in">Clocked In</t>
|
||||
<t t-else="">Not Clocked In</t>
|
||||
</span>
|
||||
</div>
|
||||
<span class="fclk-current-time" id="fclk-current-time"></span>
|
||||
</div>
|
||||
|
||||
<!-- Location Selector -->
|
||||
<div class="fclk-location-card" id="fclk-location-card">
|
||||
<div class="fclk-location-icon">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#10B981" stroke-width="2">
|
||||
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/>
|
||||
<circle cx="12" cy="10" r="3"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="fclk-location-info">
|
||||
<div class="fclk-location-name" id="fclk-location-name">
|
||||
<t t-if="locations">
|
||||
<t t-esc="locations[0].name"/>
|
||||
</t>
|
||||
<t t-else="">No locations configured</t>
|
||||
</div>
|
||||
<div class="fclk-location-address" id="fclk-location-address">
|
||||
<t t-if="locations">
|
||||
<t t-esc="locations[0].address or 'No address'"/>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fclk-location-arrow" t-if="len(locations) > 1">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#6b7280" stroke-width="2">
|
||||
<polyline points="9 18 15 12 9 6"/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Timer Section -->
|
||||
<div class="fclk-timer-section">
|
||||
<div class="fclk-timer-label" id="fclk-timer-label">
|
||||
<t t-if="is_checked_in">Time Elapsed</t>
|
||||
<t t-else="">Ready to Clock In</t>
|
||||
</div>
|
||||
<div class="fclk-timer" id="fclk-timer">00:00:00</div>
|
||||
</div>
|
||||
|
||||
<!-- Clock Button -->
|
||||
<div class="fclk-button-section">
|
||||
<button class="fclk-clock-btn" id="fclk-clock-btn"
|
||||
t-attf-class="fclk-clock-btn {{ 'fclk-clock-btn-out' if is_checked_in else '' }}">
|
||||
<div class="fclk-btn-ripple"></div>
|
||||
<svg id="fclk-btn-icon-play" class="fclk-btn-icon"
|
||||
t-attf-style="display: {{ 'none' if is_checked_in else 'block' }}; margin-left: 4px;"
|
||||
width="40" height="40" viewBox="0 0 24 24" fill="white">
|
||||
<polygon points="6 3 20 12 6 21 6 3"/>
|
||||
</svg>
|
||||
<svg id="fclk-btn-icon-stop" class="fclk-btn-icon"
|
||||
t-attf-style="display: {{ 'block' if is_checked_in else 'none' }}"
|
||||
width="36" height="36" viewBox="0 0 24 24" fill="white">
|
||||
<rect x="4" y="4" width="16" height="16" rx="2"/>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="fclk-btn-label" id="fclk-btn-label">
|
||||
<t t-if="is_checked_in">Tap to Clock Out</t>
|
||||
<t t-else="">Tap to Clock In</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats Cards -->
|
||||
<div class="fclk-stats-row">
|
||||
<div class="fclk-stat-card">
|
||||
<div class="fclk-stat-header">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#10B981" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<polyline points="12 6 12 12 16 14"/>
|
||||
</svg>
|
||||
<span>Today</span>
|
||||
</div>
|
||||
<div class="fclk-stat-value" id="fclk-today-hours">
|
||||
<t t-esc="'%.1f' % today_hours"/>h
|
||||
</div>
|
||||
<div class="fclk-stat-target">of 8h target</div>
|
||||
</div>
|
||||
<div class="fclk-stat-card">
|
||||
<div class="fclk-stat-header">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#10B981" stroke-width="2">
|
||||
<circle cx="12" cy="12" r="10"/>
|
||||
<polyline points="12 6 12 12 16 14"/>
|
||||
</svg>
|
||||
<span>This Week</span>
|
||||
</div>
|
||||
<div class="fclk-stat-value" id="fclk-week-hours">
|
||||
<t t-esc="'%.1f' % week_hours"/>h
|
||||
</div>
|
||||
<div class="fclk-stat-target">of 40h target</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Activity -->
|
||||
<div class="fclk-recent-section">
|
||||
<div class="fclk-recent-header">
|
||||
<h3>Recent Activity</h3>
|
||||
<a href="/my/clock/timesheets" class="fclk-view-all">View All</a>
|
||||
</div>
|
||||
<div class="fclk-recent-list" id="fclk-recent-list">
|
||||
<t t-foreach="recent_attendances" t-as="att">
|
||||
<div class="fclk-recent-item">
|
||||
<div class="fclk-recent-date">
|
||||
<div class="fclk-recent-day-name">
|
||||
<t t-esc="att.check_in.strftime('%a')"/>
|
||||
</div>
|
||||
<div class="fclk-recent-day-num">
|
||||
<t t-esc="att.check_in.strftime('%d')"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fclk-recent-info">
|
||||
<div class="fclk-recent-location">
|
||||
<t t-esc="att.x_fclk_location_id.name or 'Unknown'"/>
|
||||
</div>
|
||||
<div class="fclk-recent-times">
|
||||
<t t-esc="att.check_in.strftime('%I:%M %p')"/>
|
||||
- <t t-esc="att.check_out.strftime('%I:%M %p') if att.check_out else '--'"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fclk-recent-hours">
|
||||
<t t-esc="'%.1f' % (att.x_fclk_net_hours or 0)"/>h
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="not recent_attendances">
|
||||
<div class="fclk-recent-empty">
|
||||
No recent activity
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Bar -->
|
||||
<div class="fclk-nav-bar">
|
||||
<a href="/my/clock" class="fclk-nav-item fclk-nav-active">
|
||||
<svg width="20" height="20" 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>
|
||||
<span>Clock</span>
|
||||
</a>
|
||||
<a href="/my/clock/timesheets" class="fclk-nav-item">
|
||||
<svg width="20" height="20" 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>
|
||||
<span>Timesheets</span>
|
||||
</a>
|
||||
<a href="/my/clock/reports" class="fclk-nav-item">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
<line x1="16" y1="13" x2="8" y2="13"/>
|
||||
<line x1="16" y1="17" x2="8" y2="17"/>
|
||||
</svg>
|
||||
<span>Reports</span>
|
||||
</a>
|
||||
</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>
|
||||
<div class="fclk-modal-content">
|
||||
<h3>Select Location</h3>
|
||||
<div class="fclk-modal-list">
|
||||
<t t-foreach="locations" t-as="loc">
|
||||
<div class="fclk-modal-item"
|
||||
t-att-data-id="loc.id"
|
||||
t-att-data-name="loc.name"
|
||||
t-att-data-address="loc.address or ''"
|
||||
t-att-data-lat="loc.latitude"
|
||||
t-att-data-lng="loc.longitude"
|
||||
t-att-data-radius="loc.radius">
|
||||
<div class="fclk-modal-item-icon">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#10B981" stroke-width="2">
|
||||
<path d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z"/>
|
||||
<circle cx="12" cy="10" r="3"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div class="fclk-modal-item-name"><t t-esc="loc.name"/></div>
|
||||
<div class="fclk-modal-item-addr"><t t-esc="loc.address or 'No address'"/></div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Notification Toast -->
|
||||
<div class="fclk-toast" id="fclk-toast" style="display:none;">
|
||||
<div class="fclk-toast-icon" id="fclk-toast-icon"></div>
|
||||
<div class="fclk-toast-msg" id="fclk-toast-msg"></div>
|
||||
</div>
|
||||
|
||||
<!-- GPS Loading Overlay -->
|
||||
<div class="fclk-gps-overlay" id="fclk-gps-overlay" style="display:none;">
|
||||
<div class="fclk-gps-spinner"></div>
|
||||
<div class="fclk-gps-text">Verifying your location...</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Hidden location data for JS -->
|
||||
<script type="application/json" id="fclk-locations-data">
|
||||
<t t-out="locations_json"/>
|
||||
</script>
|
||||
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
96
fusion_clock/views/portal_report_templates.xml
Normal file
96
fusion_clock/views/portal_report_templates.xml
Normal file
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Reports Portal Page -->
|
||||
<template id="portal_report_page" name="Fusion Clock Reports">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="breadcrumbs_searchbar" t-value="False"/>
|
||||
<t t-set="no_breadcrumbs" t-value="True"/>
|
||||
<t t-set="no_header" t-value="True"/>
|
||||
|
||||
<div class="fclk-app">
|
||||
<div class="fclk-reports-container">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="fclk-ts-header" style="margin-bottom:24px;">
|
||||
<h2>Reports</h2>
|
||||
</div>
|
||||
|
||||
<!-- Reports List -->
|
||||
<t t-if="reports">
|
||||
<t t-foreach="reports" t-as="report">
|
||||
<div class="fclk-report-item">
|
||||
<div class="fclk-report-info">
|
||||
<h4>
|
||||
<t t-esc="report.date_start.strftime('%b %d')"/> -
|
||||
<t t-esc="report.date_end.strftime('%b %d, %Y')"/>
|
||||
</h4>
|
||||
<p>
|
||||
<t t-esc="'%.1f' % report.net_hours"/>h net |
|
||||
<t t-esc="report.days_worked"/> days |
|
||||
<t t-esc="dict([('weekly','Weekly'),('biweekly','Bi-Weekly'),('semi_monthly','Semi-Monthly'),('monthly','Monthly')]).get(report.schedule_type, '')"/>
|
||||
</p>
|
||||
</div>
|
||||
<t t-if="report.report_pdf">
|
||||
<a t-attf-href="/my/clock/reports/#{report.id}/download"
|
||||
class="fclk-report-download">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align:middle; margin-right:4px;">
|
||||
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
||||
<polyline points="7 10 12 15 17 10"/>
|
||||
<line x1="12" y1="15" x2="12" y2="3"/>
|
||||
</svg>
|
||||
PDF
|
||||
</a>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span style="color:#6b7280; font-size:12px;">Pending</span>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="fclk-empty-state">
|
||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#6b7280" stroke-width="1.5">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
</svg>
|
||||
<p>No reports available yet.</p>
|
||||
<p style="font-size:12px; margin-top:4px;">Reports are generated automatically at the end of each pay period.</p>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Navigation Bar -->
|
||||
<div class="fclk-nav-bar">
|
||||
<a href="/my/clock" class="fclk-nav-item">
|
||||
<svg width="20" height="20" 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>
|
||||
<span>Clock</span>
|
||||
</a>
|
||||
<a href="/my/clock/timesheets" class="fclk-nav-item">
|
||||
<svg width="20" height="20" 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>
|
||||
<span>Timesheets</span>
|
||||
</a>
|
||||
<a href="/my/clock/reports" class="fclk-nav-item fclk-nav-active">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
<line x1="16" y1="13" x2="8" y2="13"/>
|
||||
<line x1="16" y1="17" x2="8" y2="17"/>
|
||||
</svg>
|
||||
<span>Reports</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
146
fusion_clock/views/portal_timesheet_templates.xml
Normal file
146
fusion_clock/views/portal_timesheet_templates.xml
Normal file
@@ -0,0 +1,146 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<!-- Timesheet Portal Page -->
|
||||
<template id="portal_timesheet_page" name="Fusion Clock Timesheets">
|
||||
<t t-call="portal.portal_layout">
|
||||
<t t-set="breadcrumbs_searchbar" t-value="False"/>
|
||||
<t t-set="no_breadcrumbs" t-value="True"/>
|
||||
<t t-set="no_header" t-value="True"/>
|
||||
|
||||
<div class="fclk-app">
|
||||
<div class="fclk-timesheet-container">
|
||||
|
||||
<!-- Header -->
|
||||
<div class="fclk-ts-header">
|
||||
<h2>Timesheets</h2>
|
||||
<div class="fclk-ts-period-nav">
|
||||
<a t-attf-href="/my/clock/timesheets?period=last"
|
||||
t-attf-class="fclk-ts-period-btn {{ 'fclk-ts-period-btn-active' if period == 'last' else '' }}">
|
||||
Previous
|
||||
</a>
|
||||
<a t-attf-href="/my/clock/timesheets?period=current"
|
||||
t-attf-class="fclk-ts-period-btn {{ 'fclk-ts-period-btn-active' if period == 'current' else '' }}">
|
||||
Current
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Period Info -->
|
||||
<div style="margin-bottom:16px;">
|
||||
<span class="fclk-ts-period-btn" style="cursor:default; display:inline-block;">
|
||||
<t t-esc="period_start.strftime('%b %d')"/> -
|
||||
<t t-esc="period_end.strftime('%b %d, %Y')"/>
|
||||
(<t t-esc="dict([('weekly','Weekly'),('biweekly','Bi-Weekly'),('semi_monthly','Semi-Monthly'),('monthly','Monthly')]).get(schedule_type, schedule_type)"/>)
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- Summary Cards -->
|
||||
<div class="fclk-ts-summary">
|
||||
<div class="fclk-ts-summary-card">
|
||||
<div class="fclk-ts-summary-value"><t t-esc="total_hours"/>h</div>
|
||||
<div class="fclk-ts-summary-label">Total Hours</div>
|
||||
</div>
|
||||
<div class="fclk-ts-summary-card">
|
||||
<div class="fclk-ts-summary-value" style="color:#10B981;"><t t-esc="net_hours"/>h</div>
|
||||
<div class="fclk-ts-summary-label">Net Hours</div>
|
||||
</div>
|
||||
<div class="fclk-ts-summary-card">
|
||||
<div class="fclk-ts-summary-value"><t t-esc="int(total_breaks)"/>m</div>
|
||||
<div class="fclk-ts-summary-label">Total Breaks</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Attendance Table -->
|
||||
<t t-if="attendances">
|
||||
<table class="fclk-ts-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>In</th>
|
||||
<th>Out</th>
|
||||
<th>Break</th>
|
||||
<th>Net</th>
|
||||
<th>Location</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<t t-foreach="attendances" t-as="att">
|
||||
<tr>
|
||||
<td>
|
||||
<strong><t t-esc="att.check_in.strftime('%a')"/></strong>
|
||||
<span style="color:#9ca3af; margin-left:4px;">
|
||||
<t t-esc="att.check_in.strftime('%b %d')"/>
|
||||
</span>
|
||||
</td>
|
||||
<td><t t-esc="att.check_in.strftime('%I:%M %p')"/></td>
|
||||
<td>
|
||||
<t t-if="att.check_out">
|
||||
<t t-esc="att.check_out.strftime('%I:%M %p')"/>
|
||||
<t t-if="att.x_fclk_auto_clocked_out">
|
||||
<span class="fclk-ts-badge-auto">AUTO</span>
|
||||
</t>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span style="color:#f59e0b;">Active</span>
|
||||
</t>
|
||||
</td>
|
||||
<td><t t-esc="int(att.x_fclk_break_minutes or 0)"/>m</td>
|
||||
<td style="font-weight:600; color:#10B981;">
|
||||
<t t-esc="'%.1f' % (att.x_fclk_net_hours or 0)"/>h
|
||||
</td>
|
||||
<td style="color:#9ca3af; font-size:12px;">
|
||||
<t t-esc="att.x_fclk_location_id.name or ''"/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</tbody>
|
||||
</table>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<div class="fclk-empty-state">
|
||||
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="#6b7280" stroke-width="1.5">
|
||||
<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>
|
||||
<p>No attendance records for this period.</p>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<!-- Navigation Bar -->
|
||||
<div class="fclk-nav-bar">
|
||||
<a href="/my/clock" class="fclk-nav-item">
|
||||
<svg width="20" height="20" 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>
|
||||
<span>Clock</span>
|
||||
</a>
|
||||
<a href="/my/clock/timesheets" class="fclk-nav-item fclk-nav-active">
|
||||
<svg width="20" height="20" 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>
|
||||
<span>Timesheets</span>
|
||||
</a>
|
||||
<a href="/my/clock/reports" class="fclk-nav-item">
|
||||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/>
|
||||
<polyline points="14 2 14 8 20 8"/>
|
||||
<line x1="16" y1="13" x2="8" y2="13"/>
|
||||
<line x1="16" y1="17" x2="8" y2="17"/>
|
||||
</svg>
|
||||
<span>Reports</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
</odoo>
|
||||
166
fusion_clock/views/res_config_settings_views.xml
Normal file
166
fusion_clock/views/res_config_settings_views.xml
Normal file
@@ -0,0 +1,166 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="res_config_settings_view_form_fusion_clock" model="ir.ui.view">
|
||||
<field name="name">res.config.settings.view.form.fusion.clock</field>
|
||||
<field name="model">res.config.settings</field>
|
||||
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
|
||||
<field name="priority">95</field>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//form" position="inside">
|
||||
<app data-string="Fusion Clock" string="Fusion Clock" name="fusion_clock" groups="fusion_clock.group_fusion_clock_manager">
|
||||
|
||||
<!-- Work Schedule -->
|
||||
<block title="Work Schedule" name="fclk_work_schedule">
|
||||
<setting string="Default Clock-In Time" help="Scheduled start of shift. Used for penalty and auto clock-out calculations.">
|
||||
<span class="me-2">Clock-In</span>
|
||||
<field name="fclk_default_clock_in_time" widget="float_time"
|
||||
class="text-center" style="width: 6ch;"/>
|
||||
<span class="ms-2 text-muted">e.g. 09:00 = 9 AM</span>
|
||||
</setting>
|
||||
<setting string="Default Clock-Out Time" help="Scheduled end of shift. Used for penalty and auto clock-out calculations.">
|
||||
<span class="me-2">Clock-Out</span>
|
||||
<field name="fclk_default_clock_out_time" widget="float_time"
|
||||
class="text-center" style="width: 6ch;"/>
|
||||
<span class="ms-2 text-muted">e.g. 17:00 = 5 PM</span>
|
||||
</setting>
|
||||
</block>
|
||||
|
||||
<!-- Break Settings -->
|
||||
<block title="Break Settings" name="fclk_break_settings">
|
||||
<setting string="Auto-Deduct Break" help="Automatically deduct unpaid break from total worked hours. Applies to portal/FAB clock-outs AND manually entered attendance.">
|
||||
<field name="fclk_auto_deduct_break"/>
|
||||
<div invisible="not fclk_auto_deduct_break" class="mt8">
|
||||
<span class="me-2">Break Duration</span>
|
||||
<field name="fclk_default_break_minutes" class="text-center" style="width: 6ch;"/>
|
||||
<span class="ms-2">minutes</span>
|
||||
<span class="ms-3 text-muted">e.g. 30 = half-hour unpaid break</span>
|
||||
<br/>
|
||||
<span class="me-2 mt8 d-inline-block">Threshold</span>
|
||||
<field name="fclk_break_threshold_hours" widget="float_time"
|
||||
class="text-center" style="width: 6ch;"/>
|
||||
<span class="ms-2">hours</span>
|
||||
<span class="ms-3 text-muted">break deducted only if shift exceeds this</span>
|
||||
<br/>
|
||||
<button name="action_backfill_breaks" type="object"
|
||||
string="Apply Break to Past Records"
|
||||
class="btn btn-secondary mt8" icon="fa-clock-o"
|
||||
confirm="This will apply break deduction to all past attendance records that are missing it. Continue?"/>
|
||||
<span class="ms-2 text-muted mt8 d-inline-block">
|
||||
Applies break to historical records that were entered manually or before break was enabled
|
||||
</span>
|
||||
</div>
|
||||
</setting>
|
||||
</block>
|
||||
|
||||
<!-- Auto Clock-Out & Grace Period -->
|
||||
<block title="Auto Clock-Out" name="fclk_auto_clockout">
|
||||
<setting string="Enable Auto Clock-Out" help="Automatically clock out employees who forget to clock out.">
|
||||
<field name="fclk_enable_auto_clockout"/>
|
||||
<div invisible="not fclk_enable_auto_clockout" class="mt8">
|
||||
<span class="me-2">Grace Period</span>
|
||||
<field name="fclk_grace_period_minutes" class="text-center" style="width: 6ch;"/>
|
||||
<span class="ms-2">minutes after scheduled end</span>
|
||||
<br/>
|
||||
<span class="me-2 mt8 d-inline-block">Max Shift</span>
|
||||
<field name="fclk_max_shift_hours" widget="float_time"
|
||||
class="text-center" style="width: 6ch;"/>
|
||||
<span class="ms-2">hours</span>
|
||||
<span class="ms-3 text-muted">safety net: force clock-out after this</span>
|
||||
</div>
|
||||
</setting>
|
||||
</block>
|
||||
|
||||
<!-- Penalties -->
|
||||
<block title="Penalty Tracking" name="fclk_penalties">
|
||||
<setting string="Enable Penalties" help="Track late clock-ins and early clock-outs for each employee.">
|
||||
<field name="fclk_enable_penalties"/>
|
||||
<div invisible="not fclk_enable_penalties" class="mt8">
|
||||
<span class="me-2">Grace</span>
|
||||
<field name="fclk_penalty_grace_minutes" class="text-center" style="width: 6ch;"/>
|
||||
<span class="ms-2">minutes before a penalty is logged</span>
|
||||
</div>
|
||||
</setting>
|
||||
</block>
|
||||
|
||||
<!-- Pay Period -->
|
||||
<block title="Pay Period" name="fclk_pay_period">
|
||||
<setting string="Pay Period Schedule" help="Defines how pay periods are calculated. Weekly and Bi-Weekly require an anchor date below. Semi-Monthly uses 1st-15th and 16th-end. Monthly uses 1st-end.">
|
||||
<div class="mt8">
|
||||
<span class="me-2">Schedule</span>
|
||||
<field name="fclk_pay_period_type" class="d-inline-block"/>
|
||||
</div>
|
||||
</setting>
|
||||
<setting string="Anchor Date" help="The first day of any actual pay period. All periods are calculated forward and backward from this date in weekly or biweekly increments. Not used for Semi-Monthly or Monthly.">
|
||||
<div class="mt8">
|
||||
<field name="fclk_pay_period_start" class="d-inline-block"
|
||||
placeholder="Pick the start of a real pay period"/>
|
||||
<span class="ms-3 text-muted">
|
||||
Pick the first day of any real pay period
|
||||
</span>
|
||||
</div>
|
||||
</setting>
|
||||
<setting string="Current Period Preview" help="Shows the current pay period based on today and the settings above. Use this to verify your anchor date is correct.">
|
||||
<div class="mt8">
|
||||
<field name="fclk_pay_period_preview" class="fw-bold text-primary"/>
|
||||
</div>
|
||||
</setting>
|
||||
</block>
|
||||
|
||||
<!-- Reports -->
|
||||
<block title="Reports & Email" name="fclk_reports">
|
||||
<setting string="Auto-Generate Reports" help="Automatically generate a summary report at the end of each pay period.">
|
||||
<field name="fclk_auto_generate_reports"/>
|
||||
</setting>
|
||||
<setting string="Send Employee Copies" help="Email each employee a copy of their individual timesheet at the end of every pay period.">
|
||||
<field name="fclk_send_employee_reports"/>
|
||||
</setting>
|
||||
<setting string="Manager Report Recipients" help="Comma-separated email addresses. A batch summary of all employees will be sent to these addresses at the end of every pay period.">
|
||||
<div class="mt8">
|
||||
<field name="fclk_report_recipient_emails" class="o_input w-100"
|
||||
placeholder="manager@company.com, hr@company.com"/>
|
||||
</div>
|
||||
</setting>
|
||||
<setting string="Generate Historical Reports" help="Create reports for all past pay periods that have attendance data but no report yet. No emails will be sent for these. Use this after first install or when changing pay period settings.">
|
||||
<div class="mt8">
|
||||
<button name="action_backfill_reports" type="object"
|
||||
string="Generate Historical Reports"
|
||||
class="btn btn-secondary" icon="fa-history"
|
||||
confirm="This will create reports for all past pay periods. No emails will be sent. This may take a few minutes depending on the amount of data. Continue?"/>
|
||||
</div>
|
||||
</setting>
|
||||
</block>
|
||||
|
||||
<!-- Clock Locations -->
|
||||
<block title="Clock Locations" name="fclk_locations">
|
||||
<setting string="Manage Locations" help="Configure geofenced clock-in/out locations with GPS coordinates and allowed radius.">
|
||||
<div class="mt8">
|
||||
<button name="%(fusion_clock.action_fusion_clock_location)d" type="action"
|
||||
string="Manage Locations" class="btn btn-primary" icon="fa-map-marker"/>
|
||||
</div>
|
||||
</setting>
|
||||
</block>
|
||||
|
||||
<!-- Google Maps -->
|
||||
<block title="Google Maps" name="fclk_google_maps">
|
||||
<setting string="Google Maps API Key" help="Required for geocoding addresses and showing map previews on clock locations.">
|
||||
<div class="mt8">
|
||||
<field name="fclk_google_maps_api_key" class="o_input w-100"
|
||||
placeholder="AIza..." password="True"/>
|
||||
</div>
|
||||
</setting>
|
||||
</block>
|
||||
|
||||
<!-- Sounds -->
|
||||
<block title="Sounds" name="fclk_sounds">
|
||||
<setting string="Clock Sounds" help="Play an audio chime on clock-in and a different one on clock-out.">
|
||||
<field name="fclk_enable_sounds"/>
|
||||
</setting>
|
||||
</block>
|
||||
|
||||
</app>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user