This commit is contained in:
gsinghpal
2026-03-16 08:14:56 -04:00
parent fdca9518ab
commit e56974d46f
196 changed files with 19739 additions and 3471 deletions

View File

@@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Conversation List View -->
<record id="view_ai_conversation_list" model="ir.ui.view">
<field name="name">fusion.clock.ai.conversation.list</field>
<field name="model">fusion.clock.ai.conversation</field>
<field name="arch" type="xml">
<list string="AI Conversations" default_order="create_date desc" create="false">
<field name="title"/>
<field name="user_id"/>
<field name="conversation_type"/>
<field name="create_date"/>
</list>
</field>
</record>
<!-- Conversation Form View -->
<record id="view_ai_conversation_form" model="ir.ui.view">
<field name="name">fusion.clock.ai.conversation.form</field>
<field name="model">fusion.clock.ai.conversation</field>
<field name="arch" type="xml">
<form string="AI Conversation" create="false" edit="false">
<sheet>
<group>
<group>
<field name="title"/>
<field name="user_id"/>
</group>
<group>
<field name="conversation_type"/>
<field name="create_date"/>
</group>
</group>
<separator string="Messages"/>
<field name="message_ids" nolabel="1">
<list create="false" delete="false">
<field name="role" decoration-info="role == 'user'"
decoration-success="role == 'assistant'"
decoration-muted="role == 'system'" widget="badge"/>
<field name="content"/>
<field name="create_date"/>
</list>
</field>
</sheet>
</form>
</field>
</record>
<!-- Conversation Search View -->
<record id="view_ai_conversation_search" model="ir.ui.view">
<field name="name">fusion.clock.ai.conversation.search</field>
<field name="model">fusion.clock.ai.conversation</field>
<field name="arch" type="xml">
<search string="Search Conversations">
<field name="title"/>
<field name="user_id"/>
<filter name="manager_query" string="Manager Queries"
domain="[('conversation_type', '=', 'manager_query')]"/>
<filter name="employee_chat" string="Employee Chats"
domain="[('conversation_type', '=', 'employee_chat')]"/>
<filter name="config" string="Configuration"
domain="[('conversation_type', '=', 'config')]"/>
<separator/>
<filter name="group_type" string="Type"
context="{'group_by': 'conversation_type'}"/>
<filter name="group_user" string="User"
context="{'group_by': 'user_id'}"/>
</search>
</field>
</record>
<!-- Conversation Action -->
<record id="action_ai_conversation" model="ir.actions.act_window">
<field name="name">Chat History</field>
<field name="res_model">fusion.clock.ai.conversation</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_ai_conversation_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No AI conversations yet
</p>
<p>
Conversations with the AI assistant will appear here.
</p>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- AI Sub-Menu under Fusion Clock -->
<menuitem id="menu_fusion_clock_ai"
name="AI"
parent="fusion_clock.menu_fusion_clock_root"
sequence="50"
groups="fusion_clock.group_fusion_clock_manager"/>
<menuitem id="menu_fusion_clock_ai_chat"
name="Chat History"
parent="menu_fusion_clock_ai"
action="action_ai_conversation"
sequence="10"/>
<menuitem id="menu_fusion_clock_ai_prompts"
name="Prompt Templates"
parent="menu_fusion_clock_ai"
action="action_ai_prompt"
sequence="20"/>
<menuitem id="menu_fusion_clock_ai_usage"
name="Usage Dashboard"
parent="menu_fusion_clock_ai"
action="action_ai_usage"
sequence="30"/>
</odoo>

View File

@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Prompt List View -->
<record id="view_ai_prompt_list" model="ir.ui.view">
<field name="name">fusion.clock.ai.prompt.list</field>
<field name="model">fusion.clock.ai.prompt</field>
<field name="arch" type="xml">
<list string="AI Prompt Templates" default_order="key">
<field name="key"/>
<field name="name"/>
<field name="feature_category" widget="badge"/>
<field name="active"/>
</list>
</field>
</record>
<!-- Prompt Form View -->
<record id="view_ai_prompt_form" model="ir.ui.view">
<field name="name">fusion.clock.ai.prompt.form</field>
<field name="model">fusion.clock.ai.prompt</field>
<field name="arch" type="xml">
<form string="AI Prompt Template">
<sheet>
<div class="oe_button_box" name="button_box">
<field name="active" widget="boolean_button"
options='{"terminology": "archive"}'/>
</div>
<group>
<group>
<field name="key"/>
<field name="name"/>
</group>
<group>
<field name="feature_category"/>
</group>
</group>
<group string="Description">
<field name="description" nolabel="1" colspan="2"
placeholder="Explain when this prompt is used and what variables are available..."/>
</group>
<group string="Prompt Content">
<field name="content" nolabel="1" colspan="2"
placeholder="Enter the system prompt template..."
widget="text"/>
</group>
</sheet>
</form>
</field>
</record>
<!-- Prompt Search View -->
<record id="view_ai_prompt_search" model="ir.ui.view">
<field name="name">fusion.clock.ai.prompt.search</field>
<field name="model">fusion.clock.ai.prompt</field>
<field name="arch" type="xml">
<search string="Search Prompts">
<field name="key"/>
<field name="name"/>
<filter name="active" string="Active" domain="[('active', '=', True)]"/>
<filter name="archived" string="Archived" domain="[('active', '=', False)]"/>
<separator/>
<filter name="group_category" string="Category"
context="{'group_by': 'feature_category'}"/>
</search>
</field>
</record>
<!-- Prompt Action -->
<record id="action_ai_prompt" model="ir.actions.act_window">
<field name="name">Prompt Templates</field>
<field name="res_model">fusion.clock.ai.prompt</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_ai_prompt_search"/>
<field name="context">{'search_default_active': 1}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No prompt templates defined
</p>
<p>
Create prompt templates to customize AI behavior for different features.
</p>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Usage List View -->
<record id="view_ai_usage_list" model="ir.ui.view">
<field name="name">fusion.clock.ai.usage.list</field>
<field name="model">fusion.clock.ai.usage</field>
<field name="arch" type="xml">
<list string="AI Usage" default_order="date desc" create="false">
<field name="date"/>
<field name="feature"/>
<field name="model_name"/>
<field name="request_count" sum="Total Requests"/>
<field name="prompt_tokens" sum="Total Prompt"/>
<field name="completion_tokens" sum="Total Completion"/>
<field name="total_tokens" sum="Total Tokens"/>
<field name="estimated_cost_usd" sum="Total Cost" widget="monetary"
options="{'currency_id': False}"/>
</list>
</field>
</record>
<!-- Usage Search View -->
<record id="view_ai_usage_search" model="ir.ui.view">
<field name="name">fusion.clock.ai.usage.search</field>
<field name="model">fusion.clock.ai.usage</field>
<field name="arch" type="xml">
<search string="Search Usage">
<field name="feature"/>
<field name="model_name"/>
<filter name="today" string="Today"
domain="[('date', '=', context_today().strftime('%Y-%m-%d'))]"/>
<filter name="this_week" string="This Week"
domain="[('date', '>=', (context_today() - datetime.timedelta(days=7)).strftime('%Y-%m-%d'))]"/>
<filter name="this_month" string="This Month"
domain="[('date', '>=', context_today().strftime('%Y-%m-01'))]"/>
<separator/>
<filter name="group_date" string="Date"
context="{'group_by': 'date'}"/>
<filter name="group_feature" string="Feature"
context="{'group_by': 'feature'}"/>
<filter name="group_model" string="Model"
context="{'group_by': 'model_name'}"/>
</search>
</field>
</record>
<!-- Usage Graph View -->
<record id="view_ai_usage_graph" model="ir.ui.view">
<field name="name">fusion.clock.ai.usage.graph</field>
<field name="model">fusion.clock.ai.usage</field>
<field name="arch" type="xml">
<graph string="AI Usage" type="line">
<field name="date" interval="day"/>
<field name="estimated_cost_usd" type="measure"/>
</graph>
</field>
</record>
<!-- Usage Pivot View -->
<record id="view_ai_usage_pivot" model="ir.ui.view">
<field name="name">fusion.clock.ai.usage.pivot</field>
<field name="model">fusion.clock.ai.usage</field>
<field name="arch" type="xml">
<pivot string="AI Usage">
<field name="date" interval="week" type="row"/>
<field name="feature" type="col"/>
<field name="total_tokens" type="measure"/>
<field name="estimated_cost_usd" type="measure"/>
<field name="request_count" type="measure"/>
</pivot>
</field>
</record>
<!-- Usage Action -->
<record id="action_ai_usage" model="ir.actions.act_window">
<field name="name">Usage Dashboard</field>
<field name="res_model">fusion.clock.ai.usage</field>
<field name="view_mode">graph,pivot,list</field>
<field name="search_view_id" ref="view_ai_usage_search"/>
<field name="context">{'search_default_this_month': 1}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No AI usage recorded yet
</p>
<p>
Usage statistics are tracked automatically when AI features are used.
</p>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Correction Form: AI Advice Button and Field -->
<record id="view_fusion_clock_correction_form_ai" model="ir.ui.view">
<field name="name">fusion.clock.correction.form.ai</field>
<field name="model">fusion.clock.correction</field>
<field name="inherit_id" ref="fusion_clock.view_fusion_clock_correction_form"/>
<field name="arch" type="xml">
<!-- Add AI Advice button in header -->
<xpath expr="//button[@name='action_reject']" position="after">
<button name="action_get_ai_advice" type="object"
string="Get AI Advice" class="btn-secondary"
invisible="state != 'pending'"
groups="fusion_clock.group_fusion_clock_manager"
icon="fa-magic"/>
</xpath>
<!-- Add AI Advice section before chatter -->
<xpath expr="//chatter" position="before">
<group string="AI Review Advice" invisible="not x_fclk_ai_advice">
<field name="x_fclk_ai_advice" nolabel="1" colspan="2" readonly="1"/>
</group>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Report Form: AI Narrative Button and Field -->
<record id="view_fusion_clock_report_form_ai" model="ir.ui.view">
<field name="name">fusion.clock.report.form.ai</field>
<field name="model">fusion.clock.report</field>
<field name="inherit_id" ref="fusion_clock.view_fusion_clock_report_form"/>
<field name="arch" type="xml">
<!-- Add AI Narrative button in header -->
<xpath expr="//button[@name='action_export_csv']" position="after">
<button name="action_generate_ai_narrative" type="object"
string="Generate AI Narrative" class="btn-secondary"
invisible="state == 'draft'"
icon="fa-magic"/>
</xpath>
<!-- Add AI Narrative tab in notebook -->
<xpath expr="//page[@name='attendances']" position="after">
<page string="AI Narrative" name="ai_narrative"
invisible="not x_fclk_ai_narrative">
<field name="x_fclk_ai_narrative" nolabel="1" readonly="1"/>
</page>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Employee Form: AI Insights Tab -->
<record id="view_employee_form_fusion_clock_ai" model="ir.ui.view">
<field name="name">hr.employee.form.fusion.clock.ai</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="fusion_clock.view_employee_form_fusion_clock"/>
<field name="arch" type="xml">
<xpath expr="//page[@name='fusion_clock_tab']" position="after">
<page string="AI Insights" name="fusion_clock_ai_tab"
groups="fusion_clock.group_fusion_clock_manager,fusion_clock.group_fusion_clock_team_lead">
<group>
<div class="d-flex gap-2 mb-3">
<button name="action_generate_ai_summary" type="object"
string="Generate AI Summary" class="btn-primary"
icon="fa-magic"/>
<button name="action_generate_coach_tip" type="object"
string="Generate Coach Tip" class="btn-secondary"
icon="fa-lightbulb-o"/>
</div>
</group>
<group string="AI Attendance Summary">
<field name="x_fclk_ai_summary" nolabel="1" colspan="2"/>
<field name="x_fclk_ai_summary_date"/>
</group>
<group string="AI Coach Tip">
<field name="x_fclk_ai_coach_tip" nolabel="1" colspan="2"/>
</group>
</page>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2026 Nexa Systems Inc. -->
<!-- License OPL-1 (Odoo Proprietary License v1.0) -->
<odoo>
<!-- Inject the AI chat widget into the Fusion Clock portal page.
The JS Interaction class attaches to .fclk-ai-portal-chat and
builds the floating chat bubble + panel dynamically. -->
<template id="portal_clock_ai_chat"
name="Portal Clock AI Chat Widget"
inherit_id="fusion_clock.portal_clock_page">
<xpath expr="//script[@id='fclk-locations-data']" position="after">
<div class="fclk-ai-portal-chat"/>
</xpath>
</template>
</odoo>

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="res_config_settings_view_form_fusion_clock_ai" model="ir.ui.view">
<field name="name">res.config.settings.view.form.fusion.clock.ai</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="fusion_clock.res_config_settings_view_form_fusion_clock"/>
<field name="priority">96</field>
<field name="arch" type="xml">
<xpath expr="//app[@name='fusion_clock']" position="inside">
<!-- ============================================================
AI Settings - API Configuration
============================================================ -->
<block title="AI Configuration" name="fclk_ai_config">
<setting string="OpenAI API Key" help="Required. Your OpenAI API key for all AI features.">
<div class="content-group">
<div class="row mt16">
<label for="fclk_ai_openai_api_key" string="API Key" class="col-lg-5 o_light_label"/>
<field name="fclk_ai_openai_api_key" class="o_input" placeholder="sk-..." password="True"/>
</div>
</div>
</setting>
<setting string="AI Model" help="Choose the GPT model for AI features. Higher quality costs more per request.">
<div class="content-group">
<div class="row mt16">
<label for="fclk_ai_openai_model" string="Model" class="col-lg-5 o_light_label"/>
<field name="fclk_ai_openai_model"/>
</div>
</div>
</setting>
</block>
<!-- ============================================================
AI Settings - Limits
============================================================ -->
<block title="AI Limits" name="fclk_ai_limits">
<setting string="Monthly Budget" help="Maximum monthly spend on AI API calls. Set to 0 for unlimited.">
<div class="content-group">
<div class="row mt16">
<label for="fclk_ai_monthly_budget_usd" string="Budget (USD)" class="col-lg-5 o_light_label"/>
<field name="fclk_ai_monthly_budget_usd"/>
</div>
</div>
</setting>
<setting string="Max Response Tokens" help="Maximum tokens per AI response. Higher values allow longer replies but cost more.">
<div class="content-group">
<div class="row mt16">
<label for="fclk_ai_max_response_tokens" string="Max Tokens" class="col-lg-5 o_light_label"/>
<field name="fclk_ai_max_response_tokens"/>
</div>
</div>
</setting>
<setting string="Cache TTL" help="How long to cache AI responses to avoid duplicate API calls.">
<div class="content-group">
<div class="row mt16">
<label for="fclk_ai_cache_ttl_minutes" string="TTL (min)" class="col-lg-5 o_light_label"/>
<field name="fclk_ai_cache_ttl_minutes"/>
</div>
</div>
</setting>
</block>
<!-- ============================================================
AI Settings - Core Features (enabled by default)
============================================================ -->
<block title="AI Core Features" name="fclk_ai_core_features">
<setting string="Manager AI Chat" help="Natural language dashboard queries for managers. Ask questions about attendance in plain English.">
<field name="fclk_ai_enable_manager_chat"/>
</setting>
<setting string="Employee AI Assistant" help="Portal chatbot for employees to check hours, request leave, and ask schedule questions.">
<field name="fclk_ai_enable_employee_chat"/>
</setting>
<setting string="AI Narrative Reports" help="Generate human-readable weekly and monthly attendance summaries.">
<field name="fclk_ai_enable_narrative_reports"/>
</setting>
<setting string="Payroll Anomaly Detection" help="GPT flags suspicious attendance patterns before payroll runs.">
<field name="fclk_ai_enable_anomaly_detection"/>
</setting>
<setting string="Attendance Coach" help="Personalized weekly attendance tips and insights per employee.">
<field name="fclk_ai_enable_coach"/>
</setting>
<setting string="Correction Review Advisor" help="AI provides context and recommendation when reviewing correction requests.">
<field name="fclk_ai_enable_correction_advisor"/>
</setting>
<setting string="Incident Auto-Explain" help="Automatically generate human-readable explanations for activity log entries.">
<field name="fclk_ai_enable_incident_explain"/>
</setting>
</block>
<!-- ============================================================
AI Settings - Advanced Features (disabled by default)
============================================================ -->
<block title="AI Advanced Features" name="fclk_ai_advanced_features">
<setting string="Predictive Alerts" help="Forecast absence likelihood by day using historical patterns.">
<field name="fclk_ai_enable_predictions"/>
</setting>
<setting string="Shift Optimization" help="Data-driven shift reassignment suggestions based on attendance patterns.">
<field name="fclk_ai_enable_shift_suggestions"/>
</setting>
<setting string="Compliance Checks" help="Automated labor law violation alerts based on worked hours and schedules.">
<field name="fclk_ai_enable_compliance"/>
</setting>
<setting string="Natural Language Config" help="Describe attendance policies in English and let AI map them to settings.">
<field name="fclk_ai_enable_smart_config"/>
</setting>
<setting string="Geofence Tuning" help="AI analyzes clock-in GPS data and suggests optimal geofence radius adjustments.">
<field name="fclk_ai_enable_geofence_tuning"/>
</setting>
</block>
</xpath>
</field>
</record>
</odoo>