fix: comprehensive permission overhaul for fusion_faxes and fusion_ringcentral

Users without fax/RC groups could not open Sale Orders, Invoices, or
Contacts because the One2many computed fields triggered AccessError
on fusion.fax. Now base.group_user gets read-only access so computed
fields work silently, while all UI elements (smart buttons, header
buttons, menus, partner fields, settings) are restricted to the
proper security groups. Both modules now use Odoo 19 privilege
pattern for the user settings dropdown.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
gsinghpal
2026-02-25 11:52:06 -05:00
parent e71bc503f9
commit 34e5b46025
11 changed files with 112 additions and 38 deletions

View File

@@ -1,8 +1,10 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_fusion_fax_base,fusion.fax.base.read,model_fusion_fax,base.group_user,1,0,0,0
access_fusion_fax_user,fusion.fax.user,model_fusion_fax,group_fax_user,1,1,1,0 access_fusion_fax_user,fusion.fax.user,model_fusion_fax,group_fax_user,1,1,1,0
access_fusion_fax_manager,fusion.fax.manager,model_fusion_fax,group_fax_manager,1,1,1,1 access_fusion_fax_manager,fusion.fax.manager,model_fusion_fax,group_fax_manager,1,1,1,1
access_fusion_send_fax_wizard_user,fusion.send.fax.wizard.user,model_fusion_faxes_send_fax_wizard,group_fax_user,1,1,1,1 access_fusion_send_fax_wizard_user,fusion.send.fax.wizard.user,model_fusion_faxes_send_fax_wizard,group_fax_user,1,1,1,1
access_fusion_send_fax_wizard_line_user,fusion.send.fax.wizard.line.user,model_fusion_faxes_send_fax_wizard_line,group_fax_user,1,1,1,1 access_fusion_send_fax_wizard_line_user,fusion.send.fax.wizard.line.user,model_fusion_faxes_send_fax_wizard_line,group_fax_user,1,1,1,1
access_fusion_fax_document_base,fusion.fax.document.base.read,model_fusion_fax_document,base.group_user,1,0,0,0
access_fusion_fax_document_user,fusion.fax.document.user,model_fusion_fax_document,group_fax_user,1,1,1,0 access_fusion_fax_document_user,fusion.fax.document.user,model_fusion_fax_document,group_fax_user,1,1,1,0
access_fusion_fax_document_manager,fusion.fax.document.manager,model_fusion_fax_document,group_fax_manager,1,1,1,1 access_fusion_fax_document_manager,fusion.fax.document.manager,model_fusion_fax_document,group_fax_manager,1,1,1,1
access_fusion_fax_dashboard_user,fusion.fax.dashboard.user,model_fusion_fax_dashboard,group_fax_user,1,1,1,1 access_fusion_fax_dashboard_user,fusion.fax.dashboard.user,model_fusion_fax_dashboard,group_fax_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_fusion_fax_base fusion.fax.base.read model_fusion_fax base.group_user 1 0 0 0
3 access_fusion_fax_user fusion.fax.user model_fusion_fax group_fax_user 1 1 1 0
4 access_fusion_fax_manager fusion.fax.manager model_fusion_fax group_fax_manager 1 1 1 1
5 access_fusion_send_fax_wizard_user fusion.send.fax.wizard.user model_fusion_faxes_send_fax_wizard group_fax_user 1 1 1 1
6 access_fusion_send_fax_wizard_line_user fusion.send.fax.wizard.line.user model_fusion_faxes_send_fax_wizard_line group_fax_user 1 1 1 1
7 access_fusion_fax_document_base fusion.fax.document.base.read model_fusion_fax_document base.group_user 1 0 0 0
8 access_fusion_fax_document_user fusion.fax.document.user model_fusion_fax_document group_fax_user 1 1 1 0
9 access_fusion_fax_document_manager fusion.fax.document.manager model_fusion_fax_document group_fax_manager 1 1 1 1
10 access_fusion_fax_dashboard_user fusion.fax.dashboard.user model_fusion_fax_dashboard group_fax_user 1 1 1 1

View File

@@ -1,22 +1,51 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<odoo> <odoo>
<data noupdate="0">
<!-- Module category (appears in Settings > Users) -->
<record id="module_category_fusion_faxes" model="ir.module.category">
<field name="name">Fusion Faxes</field>
<field name="sequence">47</field>
</record>
<!-- Privilege (Odoo 19 user settings dropdown) -->
<record id="res_groups_privilege_fusion_faxes" model="res.groups.privilege">
<field name="name">Fusion Faxes</field>
<field name="sequence">47</field>
<field name="category_id" ref="module_category_fusion_faxes"/>
</record>
<!-- User group: can send faxes and view own fax history --> <!-- User group: can send faxes and view own fax history -->
<record id="group_fax_user" model="res.groups"> <record id="group_fax_user" model="res.groups">
<field name="name">Fusion Faxes / User</field> <field name="name">User</field>
<field name="sequence">10</field>
<field name="implied_ids" eval="[(4, ref('base.group_user'))]"/> <field name="implied_ids" eval="[(4, ref('base.group_user'))]"/>
<field name="privilege_id" ref="res_groups_privilege_fusion_faxes"/>
</record> </record>
<!-- Manager group: can view all faxes and configure settings --> <!-- Manager group: can view all faxes and configure settings -->
<record id="group_fax_manager" model="res.groups"> <record id="group_fax_manager" model="res.groups">
<field name="name">Fusion Faxes / Manager</field> <field name="name">Manager</field>
<field name="sequence">20</field>
<field name="implied_ids" eval="[(4, ref('group_fax_user'))]"/> <field name="implied_ids" eval="[(4, ref('group_fax_user'))]"/>
<field name="privilege_id" ref="res_groups_privilege_fusion_faxes"/>
<field name="user_ids" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
</record> </record>
<!-- Record rules --> <data noupdate="0">
<!-- Users see only their own faxes --> <!-- Base users can read fax records (needed for One2many computed fields on sale.order etc.) -->
<record id="rule_fax_base_read" model="ir.rule">
<field name="name">Fax: all internal users read-only</field>
<field name="model_id" ref="model_fusion_fax"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
<!-- Fax users see only their own faxes (full CRUD minus unlink) -->
<record id="rule_fax_user_own" model="ir.rule"> <record id="rule_fax_user_own" model="ir.rule">
<field name="name">Fax: user sees own faxes</field> <field name="name">Fax: user sees own faxes</field>
<field name="model_id" ref="model_fusion_fax"/> <field name="model_id" ref="model_fusion_fax"/>

View File

@@ -8,16 +8,17 @@
<field name="inherit_id" ref="account.view_move_form"/> <field name="inherit_id" ref="account.view_move_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<!-- Smart button for fax count --> <!-- Smart button for fax count (fax users only) -->
<xpath expr="//div[@name='button_box']" position="inside"> <xpath expr="//div[@name='button_box']" position="inside">
<button name="action_view_faxes" type="object" <button name="action_view_faxes" type="object"
class="oe_stat_button" icon="fa-fax" class="oe_stat_button" icon="fa-fax"
invisible="x_ff_fax_count == 0"> invisible="x_ff_fax_count == 0"
groups="fusion_faxes.group_fax_user">
<field name="x_ff_fax_count" widget="statinfo" string="Faxes"/> <field name="x_ff_fax_count" widget="statinfo" string="Faxes"/>
</button> </button>
</xpath> </xpath>
<!-- Send Fax header button --> <!-- Send Fax header button (fax users only) -->
<xpath expr="//header" position="inside"> <xpath expr="//header" position="inside">
<button name="action_send_fax" string="Send Fax" <button name="action_send_fax" string="Send Fax"
type="object" class="btn-secondary" type="object" class="btn-secondary"

View File

@@ -232,10 +232,11 @@
<field name="search_view_id" ref="view_fusion_fax_search"/> <field name="search_view_id" ref="view_fusion_fax_search"/>
</record> </record>
<!-- Top-level Faxes menu --> <!-- Top-level Faxes menu (fax users only) -->
<menuitem id="menu_fusion_faxes_root" <menuitem id="menu_fusion_faxes_root"
name="Faxes" name="Faxes"
web_icon="fusion_faxes,static/description/icon.png" web_icon="fusion_faxes,static/description/icon.png"
groups="group_fax_user"
sequence="45"/> sequence="45"/>
<menuitem id="menu_fusion_fax_list" <menuitem id="menu_fusion_fax_list"

View File

@@ -8,28 +8,31 @@
<field name="inherit_id" ref="base.view_partner_form"/> <field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<!-- Add fax number on its own row below phone --> <!-- Add fax number on its own row below phone (fax users only) -->
<xpath expr="//field[@name='phone']/.." position="after"> <xpath expr="//field[@name='phone']/.." position="after">
<div class="d-flex align-items-baseline w-md-50"> <div class="d-flex align-items-baseline w-md-50"
groups="fusion_faxes.group_fax_user">
<i class="fa fa-fw me-1 fa-fax text-primary" title="Fax"/> <i class="fa fa-fw me-1 fa-fax text-primary" title="Fax"/>
<field name="x_ff_fax_number" class="w-100" widget="phone" <field name="x_ff_fax_number" class="w-100" widget="phone"
placeholder="Fax number..."/> placeholder="Fax number..."/>
</div> </div>
</xpath> </xpath>
<!-- Add smart button for fax count --> <!-- Add smart button for fax count (fax users only) -->
<xpath expr="//div[@name='button_box']" position="inside"> <xpath expr="//div[@name='button_box']" position="inside">
<button name="action_view_faxes" type="object" <button name="action_view_faxes" type="object"
class="oe_stat_button" icon="fa-fax" class="oe_stat_button" icon="fa-fax"
invisible="x_ff_fax_count == 0"> invisible="x_ff_fax_count == 0"
groups="fusion_faxes.group_fax_user">
<field name="x_ff_fax_count" widget="statinfo" string="Faxes"/> <field name="x_ff_fax_count" widget="statinfo" string="Faxes"/>
</button> </button>
</xpath> </xpath>
<!-- Add Fax History tab --> <!-- Add Fax History tab (fax users only) -->
<xpath expr="//page[@name='internal_notes']" position="after"> <xpath expr="//page[@name='internal_notes']" position="after">
<page string="Fax History" name="fax_history" <page string="Fax History" name="fax_history"
invisible="x_ff_fax_count == 0"> invisible="x_ff_fax_count == 0"
groups="fusion_faxes.group_fax_user">
<field name="x_ff_fax_ids" readonly="1"> <field name="x_ff_fax_ids" readonly="1">
<list> <list>
<field name="name"/> <field name="name"/>

View File

@@ -8,16 +8,17 @@
<field name="inherit_id" ref="sale.view_order_form"/> <field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<!-- Smart button for fax count --> <!-- Smart button for fax count (fax users only) -->
<xpath expr="//div[@name='button_box']" position="inside"> <xpath expr="//div[@name='button_box']" position="inside">
<button name="action_view_faxes" type="object" <button name="action_view_faxes" type="object"
class="oe_stat_button" icon="fa-fax" class="oe_stat_button" icon="fa-fax"
invisible="x_ff_fax_count == 0"> invisible="x_ff_fax_count == 0"
groups="fusion_faxes.group_fax_user">
<field name="x_ff_fax_count" widget="statinfo" string="Faxes"/> <field name="x_ff_fax_count" widget="statinfo" string="Faxes"/>
</button> </button>
</xpath> </xpath>
<!-- Send Fax header button --> <!-- Send Fax header button (fax users only) -->
<xpath expr="//header" position="inside"> <xpath expr="//header" position="inside">
<button name="action_send_fax" string="Send Fax" <button name="action_send_fax" string="Send Fax"
type="object" class="btn-secondary" type="object" class="btn-secondary"

View File

@@ -1,9 +1,11 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_rc_config_manager,rc.config.manager,model_rc_config,group_rc_manager,1,1,1,1 access_rc_config_manager,rc.config.manager,model_rc_config,group_rc_manager,1,1,1,1
access_rc_call_history_base,rc.call.history.base.read,model_rc_call_history,base.group_user,1,0,0,0
access_rc_call_history_user,rc.call.history.user,model_rc_call_history,group_rc_user,1,1,1,0 access_rc_call_history_user,rc.call.history.user,model_rc_call_history,group_rc_user,1,1,1,0
access_rc_call_history_company,rc.call.history.company,model_rc_call_history,group_rc_company_user,1,1,1,0 access_rc_call_history_company,rc.call.history.company,model_rc_call_history,group_rc_company_user,1,1,1,0
access_rc_call_history_manager,rc.call.history.manager,model_rc_call_history,group_rc_manager,1,1,1,1 access_rc_call_history_manager,rc.call.history.manager,model_rc_call_history,group_rc_manager,1,1,1,1
access_rc_call_dashboard_user,rc.call.dashboard.user,model_rc_call_dashboard,group_rc_user,1,1,1,1 access_rc_call_dashboard_user,rc.call.dashboard.user,model_rc_call_dashboard,group_rc_user,1,1,1,1
access_rc_voicemail_base,rc.voicemail.base.read,model_rc_voicemail,base.group_user,1,0,0,0
access_rc_voicemail_user,rc.voicemail.user,model_rc_voicemail,group_rc_user,1,1,1,0 access_rc_voicemail_user,rc.voicemail.user,model_rc_voicemail,group_rc_user,1,1,1,0
access_rc_voicemail_company,rc.voicemail.company,model_rc_voicemail,group_rc_company_user,1,1,1,0 access_rc_voicemail_company,rc.voicemail.company,model_rc_voicemail,group_rc_company_user,1,1,1,0
access_rc_voicemail_manager,rc.voicemail.manager,model_rc_voicemail,group_rc_manager,1,1,1,1 access_rc_voicemail_manager,rc.voicemail.manager,model_rc_voicemail,group_rc_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_rc_config_manager rc.config.manager model_rc_config group_rc_manager 1 1 1 1
3 access_rc_call_history_base rc.call.history.base.read model_rc_call_history base.group_user 1 0 0 0
4 access_rc_call_history_user rc.call.history.user model_rc_call_history group_rc_user 1 1 1 0
5 access_rc_call_history_company rc.call.history.company model_rc_call_history group_rc_company_user 1 1 1 0
6 access_rc_call_history_manager rc.call.history.manager model_rc_call_history group_rc_manager 1 1 1 1
7 access_rc_call_dashboard_user rc.call.dashboard.user model_rc_call_dashboard group_rc_user 1 1 1 1
8 access_rc_voicemail_base rc.voicemail.base.read model_rc_voicemail base.group_user 1 0 0 0
9 access_rc_voicemail_user rc.voicemail.user model_rc_voicemail group_rc_user 1 1 1 0
10 access_rc_voicemail_company rc.voicemail.company model_rc_voicemail group_rc_company_user 1 1 1 0
11 access_rc_voicemail_manager rc.voicemail.manager model_rc_voicemail group_rc_manager 1 1 1 1

View File

@@ -50,6 +50,33 @@
<data noupdate="0"> <data noupdate="0">
<!-- ============================================================ -->
<!-- Base read rules (allow computed fields on res.partner to -->
<!-- work for users without RC groups) -->
<!-- ============================================================ -->
<record id="rule_call_base_read" model="ir.rule">
<field name="name">RC Call: all internal users read-only</field>
<field name="model_id" ref="model_rc_call_history"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
<record id="rule_voicemail_base_read" model="ir.rule">
<field name="name">RC Voicemail: all internal users read-only</field>
<field name="model_id" ref="model_rc_voicemail"/>
<field name="domain_force">[(1, '=', 1)]</field>
<field name="groups" eval="[(4, ref('base.group_user'))]"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
<field name="perm_create" eval="False"/>
<field name="perm_unlink" eval="False"/>
</record>
<!-- ============================================================ --> <!-- ============================================================ -->
<!-- Call History record rules --> <!-- Call History record rules -->
<!-- ============================================================ --> <!-- ============================================================ -->

View File

@@ -8,34 +8,39 @@
<field name="inherit_id" ref="fusion_faxes.view_fusion_fax_form"/> <field name="inherit_id" ref="fusion_faxes.view_fusion_fax_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<!-- Add Forward and Send New buttons to header --> <!-- Add Forward and Send New buttons to header (RC users only) -->
<xpath expr="//button[@name='action_resend']" position="after"> <xpath expr="//button[@name='action_resend']" position="after">
<button name="action_forward_fax" string="Forward Fax" <button name="action_forward_fax" string="Forward Fax"
type="object" class="btn-secondary" type="object" class="btn-secondary"
icon="fa-share" icon="fa-share"
invisible="direction != 'inbound' or state != 'received'"/> invisible="direction != 'inbound' or state != 'received'"
groups="fusion_ringcentral.group_rc_user"/>
<button name="action_send_new_fax" string="Send New Fax" <button name="action_send_new_fax" string="Send New Fax"
type="object" class="btn-secondary" type="object" class="btn-secondary"
icon="fa-fax"/> icon="fa-fax"
groups="fusion_ringcentral.group_rc_user"/>
</xpath> </xpath>
<!-- Add Contact, Sales Orders, Invoices smart buttons --> <!-- Add Contact, Sales Orders, Invoices smart buttons (RC users only) -->
<xpath expr="//div[@name='button_box']" position="inside"> <xpath expr="//div[@name='button_box']" position="inside">
<button name="action_view_contact" type="object" <button name="action_view_contact" type="object"
class="oe_stat_button" icon="fa-user" class="oe_stat_button" icon="fa-user"
invisible="not partner_id"> invisible="not partner_id"
groups="fusion_ringcentral.group_rc_user">
<div class="o_stat_info"> <div class="o_stat_info">
<span class="o_stat_text"><field name="partner_id" readonly="1" nolabel="1" class="o_text_overflow"/></span> <span class="o_stat_text"><field name="partner_id" readonly="1" nolabel="1" class="o_text_overflow"/></span>
</div> </div>
</button> </button>
<button name="action_view_sale_orders" type="object" <button name="action_view_sale_orders" type="object"
class="oe_stat_button" icon="fa-shopping-cart" class="oe_stat_button" icon="fa-shopping-cart"
invisible="not partner_id"> invisible="not partner_id"
groups="fusion_ringcentral.group_rc_user">
<field name="sale_order_count" widget="statinfo" string="Sales Orders"/> <field name="sale_order_count" widget="statinfo" string="Sales Orders"/>
</button> </button>
<button name="action_view_invoices" type="object" <button name="action_view_invoices" type="object"
class="oe_stat_button" icon="fa-file-text-o" class="oe_stat_button" icon="fa-file-text-o"
invisible="not partner_id"> invisible="not partner_id"
groups="fusion_ringcentral.group_rc_user">
<field name="invoice_count" widget="statinfo" string="Invoices"/> <field name="invoice_count" widget="statinfo" string="Invoices"/>
</button> </button>
</xpath> </xpath>

View File

@@ -7,7 +7,8 @@
<field name="inherit_id" ref="base.res_config_settings_view_form"/> <field name="inherit_id" ref="base.res_config_settings_view_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//form" position="inside"> <xpath expr="//form" position="inside">
<app data-string="Fusion RingCentral" string="Fusion RingCentral" name="fusion_ringcentral"> <app data-string="Fusion RingCentral" string="Fusion RingCentral" name="fusion_ringcentral"
groups="fusion_ringcentral.group_rc_manager">
<h2>AI Transcription</h2> <h2>AI Transcription</h2>
<div class="row mt-4 o_settings_container"> <div class="row mt-4 o_settings_container">
<div class="col-12 col-lg-6 o_setting_box"> <div class="col-12 col-lg-6 o_setting_box">

View File

@@ -8,19 +8,21 @@
<field name="inherit_id" ref="base.view_partner_form"/> <field name="inherit_id" ref="base.view_partner_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<!-- Smart button for call count --> <!-- Smart button for call count (RC users only) -->
<xpath expr="//div[@name='button_box']" position="inside"> <xpath expr="//div[@name='button_box']" position="inside">
<button name="action_view_rc_calls" type="object" <button name="action_view_rc_calls" type="object"
class="oe_stat_button" icon="fa-phone" class="oe_stat_button" icon="fa-phone"
invisible="rc_call_count == 0"> invisible="rc_call_count == 0"
groups="fusion_ringcentral.group_rc_user">
<field name="rc_call_count" widget="statinfo" string="Calls"/> <field name="rc_call_count" widget="statinfo" string="Calls"/>
</button> </button>
</xpath> </xpath>
<!-- RingCentral Calls tab --> <!-- RingCentral Calls tab (RC users only) -->
<xpath expr="//page[@name='internal_notes']" position="after"> <xpath expr="//page[@name='internal_notes']" position="after">
<page string="RingCentral Calls" name="rc_calls" <page string="RingCentral Calls" name="rc_calls"
invisible="rc_call_count == 0"> invisible="rc_call_count == 0"
groups="fusion_ringcentral.group_rc_user">
<field name="rc_call_ids" readonly="1"> <field name="rc_call_ids" readonly="1">
<list decoration-danger="status in ('missed', 'no_answer')" <list decoration-danger="status in ('missed', 'no_answer')"
decoration-success="status == 'answered'"> decoration-success="status == 'answered'">