Initial commit

This commit is contained in:
gsinghpal
2026-02-22 01:22:18 -05:00
commit 5200d5baf0
2394 changed files with 386834 additions and 0 deletions

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!-- ================================================================= -->
<!-- VENDOR BILL FORM: Email creation info -->
<!-- ================================================================= -->
<record id="view_move_form_fusion_accounts" model="ir.ui.view">
<field name="name">account.move.form.fusion.accounts</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_move_form"/>
<field name="priority">80</field>
<field name="arch" type="xml">
<!-- Add email creation badge -->
<xpath expr="//div[hasclass('oe_title')]" position="before">
<field name="x_fa_created_from_email" invisible="1"/>
<div class="float-end" invisible="not x_fa_created_from_email or move_type != 'in_invoice'">
<span class="badge text-bg-info">
<i class="fa fa-envelope me-1"/>Created from Email
</span>
<field name="x_fa_ai_extracted" invisible="1"/>
<span class="badge text-bg-primary ms-1" invisible="not x_fa_ai_extracted">
<i class="fa fa-magic me-1"/>AI Extracted
</span>
</div>
</xpath>
<!-- Add email origin info in notebook -->
<xpath expr="//notebook" position="inside">
<field name="x_fa_created_from_email" invisible="1"/>
<page string="Email Origin" name="fa_email_origin"
invisible="not x_fa_created_from_email or move_type != 'in_invoice'">
<group>
<group string="Email Details">
<field name="x_fa_original_sender" readonly="1"/>
<field name="x_fa_match_level" widget="badge" readonly="1"/>
</group>
<group string="Processing">
<field name="x_fa_ai_extracted" readonly="1"/>
</group>
</group>
</page>
</xpath>
</field>
</record>
<!-- ================================================================= -->
<!-- VENDOR BILL SEARCH: Add email filter -->
<!-- ================================================================= -->
<record id="view_move_search_fusion_accounts" model="ir.ui.view">
<field name="name">account.move.search.fusion.accounts</field>
<field name="model">account.move</field>
<field name="inherit_id" ref="account.view_account_invoice_filter"/>
<field name="priority">80</field>
<field name="arch" type="xml">
<xpath expr="//search" position="inside">
<separator/>
<filter string="From Email" name="from_email"
domain="[('x_fa_created_from_email', '=', True)]"/>
<filter string="AI Extracted" name="ai_extracted"
domain="[('x_fa_ai_extracted', '=', True)]"/>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!-- ================================================================= -->
<!-- DASHBOARD ACTION -->
<!-- ================================================================= -->
<record id="action_fusion_accounts_dashboard" model="ir.actions.act_window">
<field name="name">Dashboard</field>
<field name="res_model">fusion.accounts.log</field>
<field name="view_mode">kanban,list,form</field>
<field name="context">{'search_default_filter_date': 1, 'search_default_group_action': 1}</field>
<field name="search_view_id" ref="view_fusion_accounts_log_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Fusion Accounts Dashboard
</p>
<p>
Email processing activity will appear here.
Configure your email aliases and AI settings under Configuration.
</p>
</field>
</record>
<!-- ================================================================= -->
<!-- DASHBOARD KANBAN VIEW -->
<!-- ================================================================= -->
<record id="view_fusion_accounts_log_kanban" model="ir.ui.view">
<field name="name">fusion.accounts.log.kanban</field>
<field name="model">fusion.accounts.log</field>
<field name="arch" type="xml">
<kanban class="o_kanban_dashboard" create="0" edit="0"
group_create="0" group_delete="0" group_edit="0"
default_group_by="action_taken">
<field name="email_from"/>
<field name="email_subject"/>
<field name="vendor_id"/>
<field name="match_level"/>
<field name="action_taken"/>
<field name="bill_id"/>
<field name="ai_extracted"/>
<field name="create_date"/>
<field name="vendor_blocked"/>
<templates>
<t t-name="card">
<div class="d-flex flex-column">
<strong class="fs-5 mb-1">
<field name="email_subject"/>
</strong>
<div class="text-muted small mb-1">
<i class="fa fa-envelope-o me-1"/>
<field name="email_from"/>
</div>
<div class="d-flex align-items-center gap-2 mb-1">
<field name="match_level" widget="badge"
decoration-info="match_level == 'exact_email'"
decoration-success="match_level == 'domain'"
decoration-warning="match_level == 'name'"
decoration-danger="match_level == 'no_match'"/>
<span t-if="record.ai_extracted.raw_value" class="badge text-bg-primary">
<i class="fa fa-magic me-1"/>AI
</span>
</div>
<div t-if="record.vendor_id.value" class="text-muted small">
<i class="fa fa-building-o me-1"/>
<field name="vendor_id"/>
</div>
<div t-if="record.bill_id.value" class="small mt-1">
<i class="fa fa-file-text-o me-1"/>
<field name="bill_id"/>
</div>
<div class="text-muted small mt-1">
<field name="create_date" widget="datetime"/>
</div>
<!-- Block/Enable buttons -->
<div t-if="record.vendor_id.value" class="mt-2 d-flex gap-1">
<button t-if="!record.vendor_blocked.raw_value"
name="action_block_vendor" type="object"
class="btn btn-sm btn-outline-danger">
<i class="fa fa-ban me-1"/>Block Vendor
</button>
<button t-if="record.vendor_blocked.raw_value"
name="action_enable_vendor" type="object"
class="btn btn-sm btn-outline-success">
<i class="fa fa-check me-1"/>Enable Vendor
</button>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,130 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!-- ================================================================= -->
<!-- ACTIVITY LOG - LIST VIEW -->
<!-- ================================================================= -->
<record id="view_fusion_accounts_log_list" model="ir.ui.view">
<field name="name">fusion.accounts.log.list</field>
<field name="model">fusion.accounts.log</field>
<field name="arch" type="xml">
<list string="Email Processing Log" create="0" edit="0"
decoration-success="action_taken == 'bill_created'"
decoration-warning="action_taken == 'blocked'"
decoration-danger="action_taken == 'failed'"
decoration-muted="action_taken == 'no_vendor'">
<header>
<button name="action_block_vendor" type="object"
string="Block Vendor" class="btn-secondary"
icon="fa-ban"/>
<button name="action_enable_vendor" type="object"
string="Enable Vendor" class="btn-secondary"
icon="fa-check"/>
</header>
<field name="create_date" string="Date"/>
<field name="email_from"/>
<field name="email_subject"/>
<field name="vendor_id"/>
<field name="match_level" widget="badge"
decoration-info="match_level == 'exact_email'"
decoration-success="match_level == 'domain'"
decoration-warning="match_level == 'name'"
decoration-danger="match_level == 'no_match'"/>
<field name="action_taken" widget="badge"
decoration-success="action_taken == 'bill_created'"
decoration-warning="action_taken == 'blocked'"
decoration-danger="action_taken == 'failed'"/>
<field name="bill_id"/>
<field name="ai_extracted" widget="boolean"/>
<field name="vendor_blocked" string="Blocked" widget="boolean"/>
</list>
</field>
</record>
<!-- ================================================================= -->
<!-- ACTIVITY LOG - FORM VIEW -->
<!-- ================================================================= -->
<record id="view_fusion_accounts_log_form" model="ir.ui.view">
<field name="name">fusion.accounts.log.form</field>
<field name="model">fusion.accounts.log</field>
<field name="arch" type="xml">
<form string="Email Processing Log" create="0" edit="0">
<header>
<button name="action_block_vendor" type="object"
string="Block Vendor" class="btn-secondary"
icon="fa-ban"
invisible="not vendor_id or vendor_blocked"/>
<button name="action_enable_vendor" type="object"
string="Enable Vendor" class="btn-secondary"
icon="fa-check"
invisible="not vendor_id or not vendor_blocked"/>
</header>
<sheet>
<div class="oe_title">
<h1><field name="email_subject" readonly="1"/></h1>
</div>
<group>
<group string="Email Details">
<field name="email_from"/>
<field name="email_date"/>
<field name="create_date" string="Processed At"/>
</group>
<group string="Processing Result">
<field name="vendor_id"/>
<field name="vendor_blocked" string="Vendor Blocked"/>
<field name="match_level" widget="badge"/>
<field name="action_taken" widget="badge"/>
<field name="bill_id"/>
<field name="ai_extracted"/>
</group>
</group>
<group string="AI Extraction Result" invisible="not ai_extracted">
<field name="ai_result" widget="text" nolabel="1" colspan="2"/>
</group>
<group string="Notes" invisible="not notes">
<field name="notes" nolabel="1" colspan="2"/>
</group>
</sheet>
</form>
</field>
</record>
<!-- ================================================================= -->
<!-- ACTIVITY LOG - SEARCH VIEW -->
<!-- ================================================================= -->
<record id="view_fusion_accounts_log_search" model="ir.ui.view">
<field name="name">fusion.accounts.log.search</field>
<field name="model">fusion.accounts.log</field>
<field name="arch" type="xml">
<search string="Activity Log">
<field name="email_from"/>
<field name="email_subject"/>
<field name="vendor_id"/>
<separator/>
<!-- Action Filters -->
<filter string="Bills Created" name="bill_created" domain="[('action_taken', '=', 'bill_created')]"/>
<filter string="Blocked" name="blocked" domain="[('action_taken', '=', 'blocked')]"/>
<filter string="Failed" name="failed" domain="[('action_taken', '=', 'failed')]"/>
<separator/>
<filter string="AI Extracted" name="ai_extracted" domain="[('ai_extracted', '=', True)]"/>
<separator/>
<!-- Time Period Filter (Odoo date filter with period selector) -->
<filter string="Date" name="filter_date" date="create_date"/>
<separator/>
<!-- Match Level Filters -->
<filter string="Exact Email Match" name="exact" domain="[('match_level', '=', 'exact_email')]"/>
<filter string="Domain Match" name="domain_match" domain="[('match_level', '=', 'domain')]"/>
<filter string="Name Match" name="name_match" domain="[('match_level', '=', 'name')]"/>
<filter string="No Match" name="no_match" domain="[('match_level', '=', 'no_match')]"/>
<group>
<filter string="Action" name="group_action" context="{'group_by': 'action_taken'}"/>
<filter string="Match Level" name="group_match" context="{'group_by': 'match_level'}"/>
<filter string="Vendor" name="group_vendor" context="{'group_by': 'vendor_id'}"/>
<filter string="Day" name="group_day" context="{'group_by': 'create_date:day'}"/>
<filter string="Week" name="group_week" context="{'group_by': 'create_date:week'}"/>
<filter string="Month" name="group_month" context="{'group_by': 'create_date:month'}"/>
<filter string="Year" name="group_year" context="{'group_by': 'create_date:year'}"/>
</group>
</search>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,190 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!-- ================================================================= -->
<!-- WINDOW ACTIONS (must be before menus) -->
<!-- ================================================================= -->
<!-- Bills from Email -->
<record id="action_bills_from_email" model="ir.actions.act_window">
<field name="name">Bills from Email</field>
<field name="res_model">account.move</field>
<field name="view_mode">list,form</field>
<field name="domain">[('move_type', '=', 'in_invoice'), ('x_fa_created_from_email', '=', True)]</field>
<field name="context">{'default_move_type': 'in_invoice'}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No bills from email yet
</p>
<p>
Bills will appear here when incoming emails create vendor bills automatically.
</p>
</field>
</record>
<!-- All Vendor Bills -->
<record id="action_all_vendor_bills" model="ir.actions.act_window">
<field name="name">All Vendor Bills</field>
<field name="res_model">account.move</field>
<field name="view_mode">list,form</field>
<field name="domain">[('move_type', '=', 'in_invoice')]</field>
<field name="context">{'default_move_type': 'in_invoice'}</field>
</record>
<!-- Blocked Vendors -->
<record id="action_blocked_vendors" model="ir.actions.act_window">
<field name="name">Blocked Vendors</field>
<field name="res_model">res.partner</field>
<field name="view_mode">list,form</field>
<field name="view_id" ref="view_partner_list_fusion_accounts"/>
<field name="domain">[('x_fa_block_email_bill', '=', True)]</field>
<field name="context">{'default_x_fa_block_email_bill': True}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No blocked vendors
</p>
<p>
Vendors blocked from automatic email bill creation will appear here.
Block vendors whose bills should be created through Purchase Orders instead.
</p>
</field>
</record>
<!-- Vendors with Active POs -->
<record id="action_vendors_with_po" model="ir.actions.act_window">
<field name="name">Vendors with Active POs</field>
<field name="res_model">res.partner</field>
<field name="view_mode">list,form</field>
<field name="view_id" ref="view_partner_list_fusion_accounts"/>
<field name="domain">[('purchase_line_ids', '!=', False), ('supplier_rank', '>', 0)]</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No vendors with purchase orders
</p>
<p>
Vendors with Purchase Orders appear here.
Consider blocking these vendors from automatic email bill creation.
</p>
</field>
</record>
<!-- All Vendors -->
<record id="action_all_vendors" model="ir.actions.act_window">
<field name="name">All Vendors</field>
<field name="res_model">res.partner</field>
<field name="view_mode">list,form</field>
<field name="view_id" ref="view_partner_list_fusion_accounts"/>
<field name="domain">[('supplier_rank', '>', 0)]</field>
</record>
<!-- Activity Log -->
<record id="action_fusion_accounts_log" model="ir.actions.act_window">
<field name="name">Activity Log</field>
<field name="res_model">fusion.accounts.log</field>
<field name="view_mode">list,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No activity logged yet
</p>
<p>
Email processing activity will be logged here automatically.
</p>
</field>
</record>
<!-- Settings -->
<record id="action_fusion_accounts_settings" model="ir.actions.act_window">
<field name="name">Fusion Accounts 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_accounts'}</field>
</record>
<!-- ================================================================= -->
<!-- TOP-LEVEL APP MENU -->
<!-- ================================================================= -->
<menuitem id="menu_fusion_accounts_root"
name="Fusion Accounts"
web_icon="fusion_accounts,static/description/icon.png"
sequence="35"
groups="group_fusion_accounts_user"/>
<!-- ================================================================= -->
<!-- DASHBOARD -->
<!-- ================================================================= -->
<menuitem id="menu_fusion_accounts_dashboard"
name="Dashboard"
parent="menu_fusion_accounts_root"
action="action_fusion_accounts_dashboard"
sequence="10"/>
<!-- ================================================================= -->
<!-- BILLS -->
<!-- ================================================================= -->
<menuitem id="menu_fusion_accounts_bills"
name="Bills"
parent="menu_fusion_accounts_root"
sequence="20"/>
<menuitem id="menu_fusion_accounts_bills_email"
name="Bills from Email"
parent="menu_fusion_accounts_bills"
action="action_bills_from_email"
sequence="10"/>
<menuitem id="menu_fusion_accounts_bills_all"
name="All Vendor Bills"
parent="menu_fusion_accounts_bills"
action="action_all_vendor_bills"
sequence="20"/>
<!-- ================================================================= -->
<!-- VENDORS -->
<!-- ================================================================= -->
<menuitem id="menu_fusion_accounts_vendors"
name="Vendors"
parent="menu_fusion_accounts_root"
sequence="30"/>
<menuitem id="menu_fusion_accounts_vendors_blocked"
name="Blocked Vendors"
parent="menu_fusion_accounts_vendors"
action="action_blocked_vendors"
sequence="10"/>
<menuitem id="menu_fusion_accounts_vendors_with_po"
name="Vendors with Active POs"
parent="menu_fusion_accounts_vendors"
action="action_vendors_with_po"
sequence="15"/>
<menuitem id="menu_fusion_accounts_vendors_all"
name="All Vendors"
parent="menu_fusion_accounts_vendors"
action="action_all_vendors"
sequence="20"/>
<!-- ================================================================= -->
<!-- ACTIVITY LOG -->
<!-- ================================================================= -->
<menuitem id="menu_fusion_accounts_logs"
name="Activity Log"
parent="menu_fusion_accounts_root"
action="action_fusion_accounts_log"
sequence="40"/>
<!-- ================================================================= -->
<!-- CONFIGURATION -->
<!-- ================================================================= -->
<menuitem id="menu_fusion_accounts_config"
name="Configuration"
parent="menu_fusion_accounts_root"
sequence="90"
groups="group_fusion_accounts_manager"/>
<menuitem id="menu_fusion_accounts_settings"
name="Settings"
parent="menu_fusion_accounts_config"
action="action_fusion_accounts_settings"
sequence="10"/>
</odoo>

View File

@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!-- ================================================================= -->
<!-- SETTINGS PAGE -->
<!-- ================================================================= -->
<record id="view_res_config_settings_fusion_accounts" model="ir.ui.view">
<field name="name">res.config.settings.fusion.accounts</field>
<field name="model">res.config.settings</field>
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//form" position="inside">
<app data-string="Fusion Accounts" string="Fusion Accounts"
name="fusion_accounts"
groups="fusion_accounts.group_fusion_accounts_manager">
<!-- AI SETTINGS -->
<block title="AI Data Extraction" name="fa_ai_settings">
<setting id="fa_ai_enabled" string="Enable AI Extraction"
help="Use OpenAI to automatically extract bill data from emails and PDF attachments.">
<field name="x_fa_ai_enabled"/>
</setting>
<setting id="fa_openai_key" string="OpenAI API Key"
help="Your OpenAI API key for bill data extraction."
invisible="not x_fa_ai_enabled">
<field name="x_fa_openai_api_key" password="True"/>
</setting>
<setting id="fa_ai_model" string="AI Model"
help="Select the OpenAI model. GPT-4o Mini is faster and cheaper, GPT-4o is more accurate."
invisible="not x_fa_ai_enabled">
<field name="x_fa_ai_model"/>
</setting>
<setting id="fa_ai_max_pages" string="Max PDF Pages"
help="Maximum number of PDF pages to send to AI for extraction. More pages = higher cost."
invisible="not x_fa_ai_enabled">
<field name="x_fa_ai_max_pages"/>
</setting>
</block>
<!-- VENDOR MATCHING SETTINGS -->
<block title="Vendor Matching" name="fa_matching_settings">
<setting id="fa_domain_match" string="Domain Matching (Level 2)"
help="Match vendors by email domain when exact email is not found.">
<field name="x_fa_enable_domain_match"/>
</setting>
<setting id="fa_name_match" string="Name Matching (Level 3)"
help="Match vendors by sender display name when email and domain don't match.">
<field name="x_fa_enable_name_match"/>
</setting>
<setting id="fa_auto_block" string="Auto-Block PO Vendors"
help="Automatically block email bill creation for vendors that have active Purchase Orders.">
<field name="x_fa_auto_block_po_vendors"/>
</setting>
</block>
<!-- GENERAL SETTINGS -->
<block title="General" name="fa_general_settings">
<setting id="fa_log_retention" string="Log Retention"
help="Number of days to keep activity logs. Set to 0 to keep forever.">
<div class="content-group">
<div class="row mt8">
<label for="x_fa_log_retention_days" string="Keep logs for" class="col-3"/>
<field name="x_fa_log_retention_days" class="col-1"/>
<span class="col-2"> days</span>
</div>
</div>
</setting>
</block>
</app>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<!-- ================================================================= -->
<!-- VENDOR FORM: Add Block Email Bill checkbox -->
<!-- ================================================================= -->
<record id="view_partner_form_fusion_accounts" model="ir.ui.view">
<field name="name">res.partner.form.fusion.accounts</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="priority">50</field>
<field name="arch" type="xml">
<xpath expr="//page[@name='internal_notes']" position="before">
<page string="Fusion Accounts" name="fusion_accounts">
<group>
<group string="Email Bill Settings">
<field name="x_fa_block_email_bill" widget="boolean_toggle"/>
<div class="alert alert-info" role="alert" colspan="2"
invisible="not x_fa_block_email_bill">
<i class="fa fa-info-circle"/>
Emails from this vendor will <strong>not</strong> create vendor bills automatically.
Bills for this vendor should be created through Purchase Orders.
</div>
</group>
</group>
</page>
</xpath>
</field>
</record>
<!-- ================================================================= -->
<!-- FUSION ACCOUNTS: Custom Vendor List View (clean actions) -->
<!-- ================================================================= -->
<record id="view_partner_list_fusion_accounts" model="ir.ui.view">
<field name="name">res.partner.list.fusion.accounts</field>
<field name="model">res.partner</field>
<field name="priority">99</field>
<field name="arch" type="xml">
<list string="Vendors" multi_edit="1">
<header>
<button name="action_fa_block_vendors" type="object"
string="Block Email Bills" class="btn-secondary"
icon="fa-ban"/>
<button name="action_fa_enable_vendors" type="object"
string="Enable Email Bills" class="btn-secondary"
icon="fa-check"/>
</header>
<field name="name"/>
<field name="email"/>
<field name="phone"/>
<field name="x_fa_block_email_bill" string="Blocked" widget="boolean_toggle"/>
<field name="supplier_rank" column_invisible="True"/>
</list>
</field>
</record>
<!-- ================================================================= -->
<!-- Add block/enable methods to res.partner -->
<!-- ================================================================= -->
</odoo>