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,46 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Extend the Fax Form: add smart buttons, forward, and send new -->
<record id="view_fusion_fax_form_inherit_rc" model="ir.ui.view">
<field name="name">fusion.fax.form.inherit.rc</field>
<field name="model">fusion.fax</field>
<field name="inherit_id" ref="fusion_faxes.view_fusion_fax_form"/>
<field name="arch" type="xml">
<!-- Add Forward and Send New buttons to header -->
<xpath expr="//button[@name='action_resend']" position="after">
<button name="action_forward_fax" string="Forward Fax"
type="object" class="btn-secondary"
icon="fa-share"
invisible="direction != 'inbound' or state != 'received'"/>
<button name="action_send_new_fax" string="Send New Fax"
type="object" class="btn-secondary"
icon="fa-fax"/>
</xpath>
<!-- Add Contact, Sales Orders, Invoices smart buttons -->
<xpath expr="//div[@name='button_box']" position="inside">
<button name="action_view_contact" type="object"
class="oe_stat_button" icon="fa-user"
invisible="not partner_id">
<div class="o_stat_info">
<span class="o_stat_text"><field name="partner_id" readonly="1" nolabel="1" class="o_text_overflow"/></span>
</div>
</button>
<button name="action_view_sale_orders" type="object"
class="oe_stat_button" icon="fa-shopping-cart"
invisible="not partner_id">
<field name="sale_order_count" widget="statinfo" string="Sales Orders"/>
</button>
<button name="action_view_invoices" type="object"
class="oe_stat_button" icon="fa-file-text-o"
invisible="not partner_id">
<field name="invoice_count" widget="statinfo" string="Invoices"/>
</button>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,282 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Call Dashboard Form View -->
<record id="view_rc_call_dashboard_form" model="ir.ui.view">
<field name="name">rc.call.dashboard.form</field>
<field name="model">rc.call.dashboard</field>
<field name="arch" type="xml">
<form string="Call Dashboard" create="0" delete="0" edit="0">
<sheet>
<field name="name" invisible="1"/>
<div class="mb-4">
<span class="text-muted">RingCentral call activity overview and analytics</span>
</div>
<!-- KPI Stat Cards -->
<div class="d-flex flex-nowrap gap-3 mb-4">
<div class="flex-fill">
<button name="action_open_all" type="object"
class="btn btn-primary w-100 p-0 border-0 rounded-3">
<div class="text-center py-3 px-2">
<div class="fw-bold" style="font-size: 2rem;">
<field name="total_count"/>
</div>
<div style="font-size: 0.85rem;">Total Calls</div>
</div>
</button>
</div>
<div class="flex-fill">
<button name="action_open_inbound" type="object"
class="btn btn-success w-100 p-0 border-0 rounded-3">
<div class="text-center py-3 px-2">
<div class="fw-bold" style="font-size: 2rem;">
<field name="inbound_count"/>
</div>
<div style="font-size: 0.85rem;">Inbound</div>
</div>
</button>
</div>
<div class="flex-fill">
<button name="action_open_outbound" type="object"
class="btn btn-info w-100 p-0 border-0 rounded-3">
<div class="text-center py-3 px-2">
<div class="fw-bold" style="font-size: 2rem;">
<field name="outbound_count"/>
</div>
<div style="font-size: 0.85rem;">Outbound</div>
</div>
</button>
</div>
<div class="flex-fill">
<button name="action_open_missed" type="object"
class="btn btn-danger w-100 p-0 border-0 rounded-3"
invisible="missed_count == 0">
<div class="text-center py-3 px-2">
<div class="fw-bold" style="font-size: 2rem;">
<field name="missed_count"/>
</div>
<div style="font-size: 0.85rem;">Missed</div>
</div>
</button>
</div>
</div>
<!-- Secondary stats -->
<div class="d-flex flex-nowrap gap-3 mb-4">
<div class="flex-fill border rounded-3 p-3 text-center">
<div class="text-muted" style="font-size: 0.85rem;">Success Rate</div>
<div class="fw-bold" style="font-size: 1.5rem;">
<field name="success_rate"/>%
</div>
</div>
<div class="flex-fill border rounded-3 p-3 text-center">
<div class="text-muted" style="font-size: 0.85rem;">Avg Duration</div>
<div class="fw-bold" style="font-size: 1.5rem;">
<field name="avg_duration"/> min
</div>
</div>
<div class="flex-fill border rounded-3 p-3 text-center">
<div class="text-muted" style="font-size: 0.85rem;">Today</div>
<div class="fw-bold" style="font-size: 1.5rem;">
<field name="today_count"/>
</div>
</div>
<div class="flex-fill border rounded-3 p-3 text-center">
<div class="text-muted" style="font-size: 0.85rem;">This Week</div>
<div class="fw-bold" style="font-size: 1.5rem;">
<field name="week_count"/>
</div>
</div>
<div class="flex-fill border rounded-3 p-3 text-center">
<div class="text-muted" style="font-size: 0.85rem;">This Month</div>
<div class="fw-bold" style="font-size: 1.5rem;">
<field name="month_count"/>
</div>
</div>
<div class="flex-fill">
<button name="action_open_unread_voicemails" type="object"
class="btn btn-warning w-100 p-0 border-0 rounded-3">
<div class="text-center py-3 px-2">
<div class="fw-bold" style="font-size: 1.5rem;">
<field name="unread_voicemail_count"/>
</div>
<div style="font-size: 0.85rem;">Voicemails</div>
<div class="text-muted" style="font-size: 0.75rem;">
<field name="voicemail_count"/> total
</div>
</div>
</button>
</div>
</div>
<!-- Quick Actions -->
<div class="d-flex gap-2 mb-4">
<button name="action_quick_call" type="object"
class="btn btn-primary" icon="fa-phone">
Call
</button>
<button name="action_quick_sms" type="object"
class="btn btn-success" icon="fa-comment">
Message
</button>
<button name="action_quick_fax" type="object"
class="btn btn-info" icon="fa-fax">
Fax
</button>
<div class="ms-auto d-flex gap-2">
<button name="action_open_all" type="object"
class="btn btn-secondary" icon="fa-list">
View All Calls
</button>
<button name="action_open_graph" type="object"
class="btn btn-secondary" icon="fa-bar-chart">
Analytics
</button>
</div>
</div>
<!-- Recent Call History -->
<separator string="Recent Calls"/>
<field name="recent_call_ids" nolabel="1" readonly="1">
<list decoration-danger="status in ('missed', 'no_answer')"
decoration-success="status == 'answered'">
<field name="name"/>
<field name="direction" widget="badge"
decoration-info="direction == 'outbound'"
decoration-success="direction == 'inbound'"/>
<field name="start_time"/>
<field name="from_number"/>
<field name="to_number"/>
<field name="partner_id"/>
<field name="duration_display" string="Duration"/>
<field name="status" widget="badge"
decoration-success="status == 'answered'"
decoration-danger="status in ('missed', 'no_answer')"/>
<field name="has_recording" string="Rec" widget="boolean"/>
</list>
</field>
</sheet>
</form>
</field>
</record>
<!-- Voicemail Dashboard Form View -->
<record id="view_rc_voicemail_dashboard_form" model="ir.ui.view">
<field name="name">rc.call.dashboard.voicemail.form</field>
<field name="model">rc.call.dashboard</field>
<field name="arch" type="xml">
<form string="Voicemails" create="0" delete="0" edit="0">
<sheet>
<field name="name" invisible="1"/>
<!-- Quick Action Buttons -->
<div class="d-flex gap-2 mb-4">
<button name="action_quick_call" type="object"
class="btn btn-primary" icon="fa-phone">
Call
</button>
<button name="action_quick_sms" type="object"
class="btn btn-success" icon="fa-comment">
Message
</button>
<button name="action_quick_fax" type="object"
class="btn btn-info" icon="fa-fax">
Fax
</button>
<div class="ms-auto d-flex gap-2">
<button name="action_open_voicemails" type="object"
class="btn btn-secondary" icon="fa-list">
All Voicemails
</button>
</div>
</div>
<!-- KPI Row -->
<div class="d-flex flex-nowrap gap-3 mb-4">
<div class="flex-fill border rounded-3 p-3 text-center">
<div class="text-muted" style="font-size: 0.85rem;">Total</div>
<div class="fw-bold" style="font-size: 1.5rem;">
<field name="voicemail_count"/>
</div>
</div>
<div class="flex-fill border rounded-3 p-3 text-center"
style="border-color: #dc3545 !important;">
<div class="text-muted" style="font-size: 0.85rem;">Unread</div>
<div class="fw-bold text-danger" style="font-size: 1.5rem;">
<field name="unread_voicemail_count"/>
</div>
</div>
</div>
<!-- Recent Voicemails (Past Week) -->
<separator string="Last 7 Days"/>
<field name="recent_voicemail_ids" nolabel="1" readonly="1">
<list decoration-danger="read_status == 'Unread'"
decoration-muted="read_status == 'Read'"
open_form_view="True">
<field name="name"/>
<field name="direction" widget="badge"
decoration-success="direction == 'inbound'"
decoration-info="direction == 'outbound'"/>
<field name="received_date"/>
<field name="caller_number"/>
<field name="caller_name" optional="show"/>
<field name="partner_id"/>
<field name="duration_display" string="Duration"/>
<field name="read_status" widget="badge"
decoration-danger="read_status == 'Unread'"
decoration-success="read_status == 'Read'"/>
</list>
</field>
<!-- Older Voicemails -->
<separator string="Older Voicemails"/>
<field name="older_voicemail_ids" nolabel="1" readonly="1">
<list decoration-danger="read_status == 'Unread'"
decoration-muted="read_status == 'Read'"
open_form_view="True">
<field name="name"/>
<field name="direction" widget="badge"
decoration-success="direction == 'inbound'"
decoration-info="direction == 'outbound'"/>
<field name="received_date"/>
<field name="caller_number"/>
<field name="caller_name" optional="show"/>
<field name="partner_id"/>
<field name="duration_display" string="Duration"/>
<field name="read_status" widget="badge"
decoration-danger="read_status == 'Unread'"
decoration-success="read_status == 'Read'"/>
</list>
</field>
</sheet>
</form>
</field>
</record>
<!-- Dashboard Action -->
<record id="action_rc_call_dashboard" model="ir.actions.act_window">
<field name="name">Dashboard</field>
<field name="res_model">rc.call.dashboard</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_rc_call_dashboard_form"/>
<field name="target">current</field>
</record>
<!-- Dashboard Menu -->
<menuitem id="menu_rc_dashboard"
name="Dashboard"
parent="menu_rc_root"
action="action_rc_call_dashboard"
sequence="1"/>
</odoo>

View File

@@ -0,0 +1,264 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Call History Form View -->
<record id="view_rc_call_history_form" model="ir.ui.view">
<field name="name">rc.call.history.form</field>
<field name="model">rc.call.history</field>
<field name="arch" type="xml">
<form string="Call Record">
<header>
<button name="action_make_call" type="object"
string="Call" icon="fa-phone"
class="btn-primary"/>
<button name="action_send_sms" type="object"
string="Text" icon="fa-comment"
class="btn-secondary"/>
<button name="action_send_fax" type="object"
string="Fax" icon="fa-fax"
class="btn-secondary"
invisible="not partner_id"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="action_view_contact" type="object"
class="oe_stat_button" icon="fa-user"
invisible="not partner_id">
<div class="o_stat_info">
<span class="o_stat_text"><field name="partner_id" readonly="1" nolabel="1"/></span>
</div>
</button>
<button name="action_view_sale_orders" type="object"
class="oe_stat_button" icon="fa-shopping-cart"
invisible="not partner_id">
<field name="sale_order_count" widget="statinfo"
string="Sales Orders"/>
</button>
<button name="action_view_invoices" type="object"
class="oe_stat_button" icon="fa-file-text-o"
invisible="not partner_id">
<field name="invoice_count" widget="statinfo"
string="Invoices"/>
</button>
</div>
<div class="oe_title">
<h1><field name="name" readonly="1"/></h1>
</div>
<div class="mb-3">
<field name="direction" widget="badge"
decoration-info="direction == 'outbound'"
decoration-success="direction == 'inbound'"
readonly="1"/>
<field name="status" widget="badge"
decoration-success="status == 'answered'"
decoration-danger="status in ('missed', 'no_answer')"
decoration-warning="status == 'busy'"
readonly="1" class="ms-2"/>
</div>
<group>
<group string="Call Details">
<field name="from_number" widget="phone"/>
<field name="to_number" widget="phone"/>
<field name="start_time"/>
<field name="duration_display" string="Duration"/>
<field name="duration" invisible="1"/>
</group>
<group string="Linking">
<field name="partner_id"/>
<field name="user_id"/>
<field name="sale_order_id" options="{'no_create': True}"/>
</group>
</group>
<!-- Recording section -->
<group string="Recording" invisible="not has_recording">
<div>
<button name="action_play_recording" type="object"
string="Play Recording" icon="fa-play-circle"
class="btn-primary"/>
</div>
<field name="has_recording" invisible="1"/>
<field name="recording_content_uri" invisible="1"/>
</group>
<!-- Transcript section -->
<group string="Transcript" invisible="not has_transcript">
<field name="transcript_text" nolabel="1" readonly="1"/>
<field name="has_transcript" invisible="1"/>
</group>
<!-- Notes -->
<group string="Notes">
<field name="notes" nolabel="1" placeholder="Add notes about this call..."/>
</group>
<group>
<field name="rc_session_id" readonly="1" string="RingCentral Session ID"/>
</group>
</sheet>
<chatter/>
</form>
</field>
</record>
<!-- Call History List View -->
<record id="view_rc_call_history_list" model="ir.ui.view">
<field name="name">rc.call.history.list</field>
<field name="model">rc.call.history</field>
<field name="arch" type="xml">
<list string="Call History"
decoration-danger="status in ('missed', 'no_answer')"
decoration-success="status == 'answered'"
decoration-warning="status == 'busy'">
<field name="name"/>
<field name="direction" widget="badge"
decoration-info="direction == 'outbound'"
decoration-success="direction == 'inbound'"/>
<field name="start_time"/>
<field name="from_number"/>
<field name="to_number"/>
<field name="partner_id"/>
<field name="duration_display" string="Duration"/>
<field name="status" widget="badge"
decoration-success="status == 'answered'"
decoration-danger="status in ('missed', 'no_answer')"
decoration-warning="status == 'busy'"/>
<field name="has_recording" string="Rec" widget="boolean"/>
<field name="has_transcript" string="Trans" widget="boolean" optional="show"/>
<field name="user_id" optional="hide"/>
</list>
</field>
</record>
<!-- Call History Search View -->
<record id="view_rc_call_history_search" model="ir.ui.view">
<field name="name">rc.call.history.search</field>
<field name="model">rc.call.history</field>
<field name="arch" type="xml">
<search string="Search Calls">
<field name="name"/>
<field name="partner_id"/>
<field name="from_number"/>
<field name="to_number"/>
<separator/>
<filter string="Inbound" name="filter_inbound"
domain="[('direction', '=', 'inbound')]"/>
<filter string="Outbound" name="filter_outbound"
domain="[('direction', '=', 'outbound')]"/>
<separator/>
<filter string="Answered" name="filter_answered"
domain="[('status', '=', 'answered')]"/>
<filter string="Missed" name="filter_missed"
domain="[('status', 'in', ('missed', 'no_answer'))]"/>
<filter string="Has Recording" name="filter_recording"
domain="[('has_recording', '=', True)]"/>
<filter string="Has Transcript" name="filter_transcript"
domain="[('has_transcript', '=', True)]"/>
<separator/>
<filter string="Today" name="filter_today"
domain="[('start_time', '>=', (context_today()).strftime('%%Y-%%m-%%d'))]"/>
<filter string="This Week" name="filter_week"
domain="[('start_time', '>=', (context_today() - relativedelta(weeks=1)).strftime('%%Y-%%m-%%d'))]"/>
<filter string="This Month" name="filter_month"
domain="[('start_time', '>=', (context_today() - relativedelta(months=1)).strftime('%%Y-%%m-%%d'))]"/>
<separator/>
<filter string="Direction" name="group_direction"
context="{'group_by': 'direction'}"/>
<filter string="Status" name="group_status"
context="{'group_by': 'status'}"/>
<filter string="Contact" name="group_partner"
context="{'group_by': 'partner_id'}"/>
<filter string="Date" name="group_date"
context="{'group_by': 'start_time:month'}"/>
</search>
</field>
</record>
<!-- Graph View -->
<record id="view_rc_call_history_graph" model="ir.ui.view">
<field name="name">rc.call.history.graph</field>
<field name="model">rc.call.history</field>
<field name="arch" type="xml">
<graph string="Call Analytics" type="bar">
<field name="start_time" interval="month"/>
<field name="direction"/>
<field name="duration" type="measure"/>
</graph>
</field>
</record>
<!-- Pivot View -->
<record id="view_rc_call_history_pivot" model="ir.ui.view">
<field name="name">rc.call.history.pivot</field>
<field name="model">rc.call.history</field>
<field name="arch" type="xml">
<pivot string="Call Analytics">
<field name="partner_id" type="row"/>
<field name="direction" type="col"/>
<field name="duration" type="measure"/>
</pivot>
</field>
</record>
<!-- Actions -->
<record id="action_rc_call_history" model="ir.actions.act_window">
<field name="name">Call History</field>
<field name="res_model">rc.call.history</field>
<field name="path">calls</field>
<field name="view_mode">list,form,graph,pivot</field>
<field name="search_view_id" ref="view_rc_call_history_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No call history yet
</p>
<p>
Call history is automatically synced from RingCentral every 15 minutes.
Connect your RingCentral account in Configuration to start.
</p>
</field>
</record>
<record id="action_rc_call_inbound" model="ir.actions.act_window">
<field name="name">Inbound Calls</field>
<field name="res_model">rc.call.history</field>
<field name="view_mode">list,form,graph</field>
<field name="domain">[('direction', '=', 'inbound')]</field>
<field name="search_view_id" ref="view_rc_call_history_search"/>
</record>
<record id="action_rc_call_outbound" model="ir.actions.act_window">
<field name="name">Outbound Calls</field>
<field name="res_model">rc.call.history</field>
<field name="view_mode">list,form,graph</field>
<field name="domain">[('direction', '=', 'outbound')]</field>
<field name="search_view_id" ref="view_rc_call_history_search"/>
</record>
<!-- Phone menu items (under Fusion RingCentral root) -->
<menuitem id="menu_rc_phone"
name="Phone"
parent="menu_rc_root"
sequence="20"/>
<menuitem id="menu_rc_call_history"
name="Call History"
parent="menu_rc_phone"
action="action_rc_call_history"
sequence="10"/>
<menuitem id="menu_rc_call_inbound"
name="Inbound Calls"
parent="menu_rc_phone"
action="action_rc_call_inbound"
sequence="20"/>
<menuitem id="menu_rc_call_outbound"
name="Outbound Calls"
parent="menu_rc_phone"
action="action_rc_call_outbound"
sequence="30"/>
</odoo>

View File

@@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- RC Config Form View -->
<record id="view_rc_config_form" model="ir.ui.view">
<field name="name">rc.config.form</field>
<field name="model">rc.config</field>
<field name="arch" type="xml">
<form string="RingCentral Configuration">
<header>
<button name="action_test_connection" type="object"
string="Test Connection" icon="fa-plug"
class="btn-secondary"/>
<button name="action_oauth_connect" type="object"
string="Connect (OAuth)" icon="fa-sign-in"
class="btn-primary"
invisible="state == 'connected'"
confirm="You will be redirected to RingCentral to authorize. Continue?"/>
<button name="action_disconnect" type="object"
string="Disconnect" icon="fa-sign-out"
class="btn-danger"
invisible="state != 'connected'"
confirm="This will revoke the RingCentral connection. Continue?"/>
<button name="action_import_historical_calls" type="object"
string="Import Call History (12 months)"
icon="fa-history"
class="btn-secondary"
invisible="state != 'connected'"
confirm="This will import up to 12 months of call history from RingCentral. This may take a few minutes. Continue?"/>
<button name="action_import_historical_voicemails" type="object"
string="Import Voicemails (12 months)"
icon="fa-voicemail"
class="btn-secondary"
invisible="state != 'connected'"
confirm="This will import up to 12 months of voicemails from RingCentral. This runs in the background. Continue?"/>
<button name="action_backfill_voicemail_media" type="object"
string="Download Voicemail Audio"
icon="fa-download"
class="btn-secondary"
invisible="state != 'connected'"
confirm="This will download audio files and transcriptions for voicemails that are missing them. Continue?"/>
<button name="action_rematch_contacts" type="object"
string="Re-match Contacts"
icon="fa-users"
class="btn-secondary"
invisible="state != 'connected'"/>
<field name="state" widget="statusbar"
statusbar_visible="draft,connected"/>
</header>
<sheet>
<div class="oe_title">
<h1>
<field name="name" placeholder="Configuration Name"/>
</h1>
</div>
<group>
<group string="Application Credentials">
<field name="client_id" placeholder="Client ID from RingCentral Developer Console"/>
<field name="client_secret" password="True"
placeholder="Client Secret"/>
<field name="server_url"
placeholder="https://platform.ringcentral.com"/>
</group>
<group string="Connection Status">
<field name="extension_name" invisible="state != 'connected'"/>
<field name="phone_widget_enabled"/>
<field name="webhook_subscription_id" invisible="not webhook_subscription_id"/>
<field name="token_expiry" invisible="state != 'connected'"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
</group>
<group string="Proxy Settings (Optional)" col="4">
<field name="proxy_url" placeholder="http://proxy.company.com"/>
<field name="proxy_port" placeholder="8080"/>
<field name="ssl_verify"/>
</group>
<div class="alert alert-info" role="alert"
invisible="state == 'connected'">
<strong>Setup Instructions:</strong>
<ol class="mb-0">
<li>Enter your Client ID and Client Secret from the
<a href="https://developers.ringcentral.com/my-account.html"
target="_blank">RingCentral Developer Console</a>.</li>
<li>Click <strong>Test Connection</strong> to verify connectivity.</li>
<li>Click <strong>Connect (OAuth)</strong> to authorize with your RingCentral account.</li>
<li>Add these redirect URIs to your RingCentral app:
<ul>
<li><code>{your-odoo-url}/ringcentral/oauth</code></li>
<li><code>https://apps.ringcentral.com/integration/ringcentral-embeddable/latest/redirect.html</code></li>
</ul>
</li>
</ol>
</div>
<div class="alert alert-success" role="alert"
invisible="state != 'connected'">
<i class="fa fa-check-circle"/> Connected to RingCentral as
<strong><field name="extension_name" nolabel="1" readonly="1" class="d-inline"/></strong>.
The phone widget, call history sync, and webhooks are active.
</div>
</sheet>
</form>
</field>
</record>
<!-- RC Config List View -->
<record id="view_rc_config_list" model="ir.ui.view">
<field name="name">rc.config.list</field>
<field name="model">rc.config</field>
<field name="arch" type="xml">
<list string="RingCentral Configurations">
<field name="name"/>
<field name="server_url"/>
<field name="extension_name"/>
<field name="phone_widget_enabled"/>
<field name="state" widget="badge"
decoration-success="state == 'connected'"
decoration-warning="state == 'draft'"
decoration-danger="state == 'error'"/>
</list>
</field>
</record>
<!-- Action and Menu -->
<record id="action_rc_config" model="ir.actions.act_window">
<field name="name">RingCentral Settings</field>
<field name="res_model">rc.config</field>
<field name="view_mode">list,form</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Configure RingCentral Integration
</p>
<p>
Create a configuration record to connect Odoo with your RingCentral account.
</p>
</field>
</record>
<!-- Top-level menu: Fusion Connect -->
<menuitem id="menu_rc_root"
name="Fusion Connect"
web_icon="fusion_ringcentral,static/description/icon.png"
sequence="44"/>
<!-- Configuration submenu -->
<menuitem id="menu_rc_configuration"
name="Configuration"
parent="menu_rc_root"
sequence="90"/>
<menuitem id="menu_rc_config_settings"
name="RingCentral Settings"
parent="menu_rc_configuration"
action="action_rc_config"
sequence="10"/>
</odoo>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Hide the standalone Fusion Faxes top-level menu -->
<record id="fusion_faxes.menu_fusion_faxes_root" model="ir.ui.menu">
<field name="active" eval="False"/>
</record>
<!-- Faxes submenu under Fusion RingCentral -->
<menuitem id="menu_rc_faxes"
name="Faxes"
parent="menu_rc_root"
sequence="30"/>
<menuitem id="menu_rc_fax_all"
name="All Faxes"
parent="menu_rc_faxes"
action="fusion_faxes.action_fusion_fax"
sequence="10"/>
<menuitem id="menu_rc_fax_received"
name="Received Faxes"
parent="menu_rc_faxes"
action="fusion_faxes.action_fusion_fax_received"
sequence="20"/>
<menuitem id="menu_rc_fax_sent"
name="Sent Faxes"
parent="menu_rc_faxes"
action="fusion_faxes.action_fusion_fax_sent"
sequence="30"/>
<menuitem id="menu_rc_fax_send"
name="Send Fax"
parent="menu_rc_faxes"
action="fusion_faxes.action_send_fax_wizard"
sequence="5"/>
<menuitem id="menu_rc_fax_dashboard"
name="Fax Dashboard"
parent="menu_rc_faxes"
action="fusion_faxes.action_fusion_fax_dashboard"
sequence="40"/>
</odoo>

View File

@@ -0,0 +1,217 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Voicemail Form View -->
<record id="view_rc_voicemail_form" model="ir.ui.view">
<field name="name">rc.voicemail.form</field>
<field name="model">rc.voicemail</field>
<field name="arch" type="xml">
<form string="Voicemail">
<header>
<button name="action_make_call" type="object"
string="Call" icon="fa-phone"
class="btn-primary"/>
<button name="action_send_sms" type="object"
string="Message" icon="fa-comment"
class="btn-success"/>
<button name="action_send_fax" type="object"
string="Fax" icon="fa-fax"
class="btn-secondary"
invisible="not partner_id"/>
<button name="action_transcribe" type="object"
string="Transcribe" icon="fa-file-text"
class="btn-secondary"
invisible="not audio_attachment_id or transcription_text"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button name="action_view_contact" type="object"
class="oe_stat_button" icon="fa-user"
invisible="not partner_id">
<div class="o_stat_info">
<span class="o_stat_text">
<field name="partner_id" readonly="1"
nolabel="1" class="o_text_overflow"/>
</span>
</div>
</button>
<button name="action_view_sale_orders" type="object"
class="oe_stat_button" icon="fa-shopping-cart"
invisible="not partner_id">
<field name="sale_order_count" widget="statinfo"
string="Sales Orders"/>
</button>
<button name="action_view_invoices" type="object"
class="oe_stat_button" icon="fa-file-text-o"
invisible="not partner_id">
<field name="invoice_count" widget="statinfo"
string="Invoices"/>
</button>
</div>
<field name="audio_attachment_id" invisible="1"/>
<div class="oe_title">
<h1><field name="name" readonly="1"/></h1>
</div>
<div class="mb-3">
<field name="direction" widget="badge"
decoration-success="direction == 'inbound'"
decoration-info="direction == 'outbound'"
readonly="1"/>
<field name="read_status" widget="badge"
decoration-danger="read_status == 'Unread'"
decoration-success="read_status == 'Read'"
readonly="1" class="ms-2"/>
</div>
<group>
<group string="Voicemail Details">
<field name="caller_number" widget="phone"/>
<field name="caller_name"/>
<field name="received_date"/>
<field name="duration_display" string="Duration"/>
<field name="duration" invisible="1"/>
</group>
<group string="Linking">
<field name="partner_id"/>
<field name="transcription_status"/>
</group>
</group>
<group string="Transcription"
invisible="not transcription_text">
<group>
<field name="transcription_language"
widget="badge" decoration-info="1"/>
</group>
<field name="transcription_text" nolabel="1"
readonly="1"/>
</group>
<group>
<field name="rc_message_id" readonly="1"
string="RingCentral Message ID"/>
</group>
</sheet>
<chatter/>
</form>
</field>
</record>
<!-- Voicemail List View -->
<record id="view_rc_voicemail_list" model="ir.ui.view">
<field name="name">rc.voicemail.list</field>
<field name="model">rc.voicemail</field>
<field name="arch" type="xml">
<list string="Voicemails"
decoration-danger="read_status == 'Unread'"
decoration-muted="read_status == 'Read'">
<field name="name"/>
<field name="direction" widget="badge"
decoration-success="direction == 'inbound'"
decoration-info="direction == 'outbound'"/>
<field name="received_date"/>
<field name="caller_number"/>
<field name="caller_name" optional="show"/>
<field name="partner_id"/>
<field name="duration_display" string="Duration"/>
<field name="read_status" widget="badge"
decoration-danger="read_status == 'Unread'"
decoration-success="read_status == 'Read'"/>
<field name="has_transcription" string="Transcript"
widget="boolean"/>
</list>
</field>
</record>
<!-- Voicemail Search View -->
<record id="view_rc_voicemail_search" model="ir.ui.view">
<field name="name">rc.voicemail.search</field>
<field name="model">rc.voicemail</field>
<field name="arch" type="xml">
<search string="Search Voicemails">
<field name="name"/>
<field name="partner_id"/>
<field name="caller_number"/>
<field name="caller_name"/>
<separator/>
<filter string="Unread" name="filter_unread"
domain="[('read_status', '=', 'Unread')]"/>
<filter string="Read" name="filter_read"
domain="[('read_status', '=', 'Read')]"/>
<separator/>
<filter string="Inbound" name="filter_inbound"
domain="[('direction', '=', 'inbound')]"/>
<filter string="Outbound" name="filter_outbound"
domain="[('direction', '=', 'outbound')]"/>
<separator/>
<filter string="Today" name="filter_today"
domain="[('received_date', '>=', (context_today()).strftime('%%Y-%%m-%%d'))]"/>
<filter string="This Week" name="filter_week"
domain="[('received_date', '>=', (context_today() - relativedelta(weeks=1)).strftime('%%Y-%%m-%%d'))]"/>
<filter string="This Month" name="filter_month"
domain="[('received_date', '>=', (context_today() - relativedelta(months=1)).strftime('%%Y-%%m-%%d'))]"/>
<separator/>
<filter string="Contact" name="group_partner"
context="{'group_by': 'partner_id'}"/>
<filter string="Date" name="group_date"
context="{'group_by': 'received_date:month'}"/>
<filter string="Read Status" name="group_read"
context="{'group_by': 'read_status'}"/>
</search>
</field>
</record>
<!-- Actions -->
<record id="action_rc_voicemail" model="ir.actions.act_window">
<field name="name">Voicemails</field>
<field name="res_model">rc.voicemail</field>
<field name="path">voicemails</field>
<field name="view_mode">list,form</field>
<field name="search_view_id" ref="view_rc_voicemail_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No voicemails yet
</p>
<p>
Voicemails are automatically synced from RingCentral.
</p>
</field>
</record>
<!-- Voicemail Dashboard Action -->
<record id="action_rc_voicemail_dashboard" model="ir.actions.act_window">
<field name="name">Voicemail Dashboard</field>
<field name="res_model">rc.call.dashboard</field>
<field name="view_mode">form</field>
<field name="view_id" ref="view_rc_voicemail_dashboard_form"/>
<field name="target">current</field>
</record>
<!-- Voicemail top-level menu -->
<menuitem id="menu_rc_voicemail_root"
name="Voicemail"
parent="menu_rc_root"
sequence="30"/>
<menuitem id="menu_rc_voicemail_dashboard"
name="Dashboard"
parent="menu_rc_voicemail_root"
action="action_rc_voicemail_dashboard"
sequence="10"/>
<menuitem id="menu_rc_voicemails"
name="Voicemails"
parent="menu_rc_voicemail_root"
action="action_rc_voicemail"
sequence="20"/>
<!-- Keep voicemails accessible under Phone too -->
<menuitem id="menu_rc_voicemails_phone"
name="Voicemails"
parent="menu_rc_phone"
action="action_rc_voicemail"
sequence="15"/>
</odoo>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="res_config_settings_view_form_rc" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.fusion.rc</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 RingCentral" string="Fusion RingCentral" name="fusion_ringcentral">
<h2>AI Transcription</h2>
<div class="row mt-4 o_settings_container">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_right_pane">
<span class="o_form_label">OpenAI API Key</span>
<div class="text-muted">Used for automatic voicemail transcription via Whisper.</div>
<div class="mt-2">
<field name="rc_openai_api_key" password="True"/>
</div>
</div>
</div>
</div>
</app>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Add RingCentral calls tab and smart button to contact form -->
<record id="view_partner_form_inherit_rc" model="ir.ui.view">
<field name="name">res.partner.form.inherit.fusion_ringcentral</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml">
<!-- Smart button for call count -->
<xpath expr="//div[@name='button_box']" position="inside">
<button name="action_view_rc_calls" type="object"
class="oe_stat_button" icon="fa-phone"
invisible="rc_call_count == 0">
<field name="rc_call_count" widget="statinfo" string="Calls"/>
</button>
</xpath>
<!-- RingCentral Calls tab -->
<xpath expr="//page[@name='internal_notes']" position="after">
<page string="RingCentral Calls" name="rc_calls"
invisible="rc_call_count == 0">
<field name="rc_call_ids" readonly="1">
<list decoration-danger="status in ('missed', 'no_answer')"
decoration-success="status == 'answered'">
<field name="name"/>
<field name="direction" widget="badge"
decoration-info="direction == 'outbound'"
decoration-success="direction == 'inbound'"/>
<field name="start_time"/>
<field name="from_number"/>
<field name="to_number"/>
<field name="duration_display" string="Duration"/>
<field name="status" widget="badge"
decoration-success="status == 'answered'"
decoration-danger="status in ('missed', 'no_answer')"/>
<field name="has_recording" string="Rec" widget="boolean"/>
</list>
</field>
</page>
</xpath>
</field>
</record>
</odoo>