feat(plating-menu): Layer 1+2 — explicit groups on top-level menus + submenus
Phase D Tasks D1-D4 of permissions overhaul. Adds explicit groups= attributes to: - 9 top-level Plating menus (matrix per spec Section 2.E) - Quality submenus: Audits, Customer Specs, AVL → QM-only - Compliance hub child submenus (CGP, General, Safety, Aerospace, Nuclear) → QM-only - Operations submenus: Maintenance, Move Log, Labor History → Shop Manager+; Replenishment Suggestions → Manager+ Replaces fragile inheritance + action-ACL-based visibility with explicit per-menu gates. Now every role's menu tree is deterministic. Also adds fusion_plating/tests/test_menu_visibility.py — per-role matrix tests using ir.ui.menu.search_count with the test user. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating',
|
||||
'version': '19.0.21.0.2',
|
||||
'version': '19.0.21.0.3',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Core plating / metal finishing ERP: facilities, processes, tanks, baths, jobs, operators.',
|
||||
'description': """
|
||||
|
||||
@@ -6,3 +6,4 @@ from . import test_simple_recipe_flatten
|
||||
from . import test_role_groups
|
||||
from . import test_acl_migration
|
||||
from . import test_quality_split
|
||||
from . import test_menu_visibility
|
||||
|
||||
85
fusion_plating/fusion_plating/tests/test_menu_visibility.py
Normal file
85
fusion_plating/fusion_plating/tests/test_menu_visibility.py
Normal file
@@ -0,0 +1,85 @@
|
||||
from odoo.tests.common import TransactionCase, tagged
|
||||
|
||||
|
||||
@tagged('-at_install', 'post_install', 'fp_perms')
|
||||
class TestMenuVisibility(TransactionCase):
|
||||
"""Section 2.F of spec: per-role menu render matrix."""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
Users = self.env['res.users'].with_context(no_reset_password=True)
|
||||
def mk(name, xmlid):
|
||||
return Users.create({
|
||||
'login': f'menu_{name}', 'name': f'Menu Test {name}',
|
||||
'email': f'menu_{name}@example.com',
|
||||
'groups_id': [(6, 0, [self.env.ref(xmlid).id])] if xmlid else [(6, 0, [])],
|
||||
})
|
||||
# "No" user has only base.group_user — no plating group
|
||||
no_user = Users.create({
|
||||
'login': 'menu_no', 'name': 'Menu Test no',
|
||||
'email': 'menu_no@example.com',
|
||||
})
|
||||
no_user.write({'groups_id': [(6, 0, [self.env.ref('base.group_user').id])]})
|
||||
self.u_no = no_user
|
||||
self.u_tech = mk('tech', 'fusion_plating.group_fp_technician')
|
||||
self.u_sr = mk('sr', 'fusion_plating.group_fp_sales_rep')
|
||||
self.u_sm = mk('sm', 'fusion_plating.group_fp_shop_manager_v2')
|
||||
self.u_smg = mk('smg', 'fusion_plating.group_fp_sales_manager')
|
||||
self.u_mgr = mk('mgr', 'fusion_plating.group_fp_manager')
|
||||
self.u_qm = mk('qm', 'fusion_plating.group_fp_quality_manager')
|
||||
self.u_owner = mk('owner', 'fusion_plating.group_fp_owner')
|
||||
|
||||
def _visible(self, user, menu_xmlid):
|
||||
menu = self.env.ref(menu_xmlid, raise_if_not_found=False)
|
||||
if not menu:
|
||||
return None # menu not installed
|
||||
# An "invisible" menu is one the user can't read
|
||||
return bool(self.env['ir.ui.menu'].with_user(user).search_count([('id', '=', menu.id)]))
|
||||
|
||||
def test_no_sees_no_plating_root(self):
|
||||
result = self._visible(self.u_no, 'fusion_plating.menu_fp_root')
|
||||
if result is None:
|
||||
self.skipTest('Plating root menu not found')
|
||||
self.assertFalse(result, '"No" role must not see Plating root')
|
||||
|
||||
def test_technician_sees_shop_floor(self):
|
||||
result = self._visible(self.u_tech, 'fusion_plating_shopfloor.menu_fp_shopfloor_root')
|
||||
if result is None:
|
||||
self.skipTest('Shop Floor menu not found')
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_technician_does_not_see_sales(self):
|
||||
result = self._visible(self.u_tech, 'fusion_plating_configurator.menu_fp_sales_root')
|
||||
if result is None:
|
||||
self.skipTest('Sales menu not found')
|
||||
self.assertFalse(result, 'Technician must not see Sales & Quoting')
|
||||
|
||||
def test_sales_rep_sees_sales(self):
|
||||
result = self._visible(self.u_sr, 'fusion_plating_configurator.menu_fp_sales_root')
|
||||
if result is None:
|
||||
self.skipTest('Sales menu not found')
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_sales_rep_does_not_see_shop_floor(self):
|
||||
result = self._visible(self.u_sr, 'fusion_plating_shopfloor.menu_fp_shopfloor_root')
|
||||
if result is None:
|
||||
self.skipTest('Shop Floor menu not found')
|
||||
self.assertFalse(result, 'Sales Rep must not see Shop Floor')
|
||||
|
||||
def test_manager_sees_quality(self):
|
||||
result = self._visible(self.u_mgr, 'fusion_plating_quality.menu_fp_quality')
|
||||
if result is None:
|
||||
self.skipTest('Quality menu not found')
|
||||
self.assertTrue(result)
|
||||
|
||||
def test_manager_does_not_see_compliance(self):
|
||||
result = self._visible(self.u_mgr, 'fusion_plating_compliance.menu_fp_compliance_hub')
|
||||
if result is None:
|
||||
self.skipTest('Compliance hub not found')
|
||||
self.assertFalse(result, 'Manager must not see Compliance hub')
|
||||
|
||||
def test_qm_sees_compliance(self):
|
||||
result = self._visible(self.u_qm, 'fusion_plating_compliance.menu_fp_compliance_hub')
|
||||
if result is None:
|
||||
self.skipTest('Compliance hub not found')
|
||||
self.assertTrue(result)
|
||||
@@ -116,13 +116,13 @@
|
||||
</record>
|
||||
|
||||
<!-- Phase 1 — under Operations.
|
||||
Phase 3 — supervisor+ only. Operators see their own moves on
|
||||
the tablet; this is an audit view of every move. -->
|
||||
Phase D (perms v2) — Shop Manager+ only. Operators see their
|
||||
own moves on the tablet; this is an audit view of every move. -->
|
||||
<menuitem id="menu_fp_job_step_move"
|
||||
name="Parts & Rack Move Log"
|
||||
parent="menu_fp_operations"
|
||||
action="action_fp_job_step_move"
|
||||
sequence="90"
|
||||
groups="fusion_plating.group_fusion_plating_supervisor"/>
|
||||
groups="fusion_plating.group_fp_shop_manager_v2"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -133,10 +133,12 @@
|
||||
</record>
|
||||
|
||||
<!-- Phase 1 — re-parented under Operations. -->
|
||||
<!-- Phase D (perms v2) — Shop Manager+ only. Payroll/billing audit. -->
|
||||
<menuitem id="menu_fp_labor_history"
|
||||
name="Labor History"
|
||||
parent="menu_fp_operations"
|
||||
action="action_fp_labor_history"
|
||||
sequence="95"/>
|
||||
sequence="95"
|
||||
groups="fusion_plating.group_fp_shop_manager_v2"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -22,14 +22,14 @@
|
||||
sequence="46"
|
||||
web_icon="fusion_plating,static/description/icon.png"
|
||||
action="action_fp_resolve_plating_landing"
|
||||
groups="group_fusion_plating_operator"/>
|
||||
groups="fusion_plating.group_fp_technician,fusion_plating.group_fp_sales_rep"/>
|
||||
|
||||
<!-- ===== 2. CONFIGURATION + 7 Phase-2 buckets ===== -->
|
||||
<menuitem id="menu_fp_config"
|
||||
name="Configuration"
|
||||
parent="menu_fp_root"
|
||||
sequence="90"
|
||||
groups="group_fusion_plating_manager"/>
|
||||
groups="fusion_plating.group_fp_manager"/>
|
||||
|
||||
<menuitem id="menu_fp_config_shop_setup"
|
||||
name="Shop Setup"
|
||||
@@ -71,13 +71,14 @@
|
||||
name="Compliance"
|
||||
parent="menu_fp_root"
|
||||
sequence="50"
|
||||
groups="group_fusion_plating_supervisor"/>
|
||||
groups="fusion_plating.group_fp_quality_manager"/>
|
||||
|
||||
<!-- ===== 4. OPERATIONS ===== -->
|
||||
<menuitem id="menu_fp_operations"
|
||||
name="Operations"
|
||||
parent="menu_fp_root"
|
||||
sequence="18"/>
|
||||
sequence="18"
|
||||
groups="fusion_plating.group_fp_technician"/>
|
||||
|
||||
<!-- ===== 5. CHILD MENUS ===== -->
|
||||
|
||||
@@ -112,13 +113,13 @@
|
||||
action="action_fp_rack"
|
||||
sequence="35"/>
|
||||
|
||||
<!-- Phase 3 — supervisor+: replenishment is a purchasing decision. -->
|
||||
<!-- Phase D (perms v2) — Manager+: replenishment is a purchasing decision. -->
|
||||
<menuitem id="menu_fp_replenishment_suggestions"
|
||||
name="Replenishment Suggestions"
|
||||
parent="menu_fp_operations"
|
||||
action="action_fp_replenishment_suggestion"
|
||||
sequence="40"
|
||||
groups="fusion_plating.group_fusion_plating_supervisor"/>
|
||||
groups="fusion_plating.group_fp_manager"/>
|
||||
|
||||
<!-- Configuration children (referencing the 7 buckets above) -->
|
||||
<menuitem id="menu_fp_replenishment_rules"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Aerospace (AS9100 + Nadcap)',
|
||||
'version': '19.0.1.1.1',
|
||||
'version': '19.0.1.1.2',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Aerospace industry pack: AS9100 Rev D clause library, Nadcap AC7108 '
|
||||
'audits, counterfeit parts prevention, config management, risk register, '
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
<odoo>
|
||||
|
||||
<!-- Phase 1 — re-parented under Plating → Compliance hub. -->
|
||||
<!-- Phase D (perms v2) — QM-only under compliance hub. -->
|
||||
<menuitem id="menu_fp_aerospace"
|
||||
name="Aerospace (AS9100 / Nadcap)"
|
||||
parent="fusion_plating.menu_fp_compliance_hub"
|
||||
sequence="30"
|
||||
groups="fusion_plating.group_fusion_plating_operator"/>
|
||||
groups="fusion_plating.group_fp_quality_manager"/>
|
||||
|
||||
<menuitem id="menu_fp_aerospace_as9100"
|
||||
name="AS9100 Clauses"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Maintenance Bridge',
|
||||
'version': '19.0.1.2.1',
|
||||
'version': '19.0.1.2.2',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Bridge standard Odoo Maintenance with Fusion Plating equipment, '
|
||||
'plans, checklists, and sensor integration.',
|
||||
|
||||
@@ -3,11 +3,13 @@
|
||||
|
||||
<!-- Phase 1 — re-parented under Plating → Operations. Maintenance
|
||||
is an Operations concern, not a separate top-level. -->
|
||||
<!-- Phase D (perms v2) — Shop Manager+ only. Operators don't need
|
||||
to schedule plans or browse the equipment registry. -->
|
||||
<menuitem id="menu_fp_maintenance"
|
||||
name="Maintenance"
|
||||
parent="fusion_plating.menu_fp_operations"
|
||||
sequence="80"
|
||||
groups="fusion_plating.group_fusion_plating_operator"/>
|
||||
groups="fusion_plating.group_fp_shop_manager_v2"/>
|
||||
|
||||
<menuitem id="menu_fp_maintenance_active"
|
||||
name="Active Events"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Controlled Goods Program',
|
||||
'version': '19.0.1.2.1',
|
||||
'version': '19.0.1.2.2',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Canadian Controlled Goods Program (CGP) compliance for plating '
|
||||
'shops handling defence work: registration, authorized individuals, '
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
<odoo>
|
||||
|
||||
<!-- Phase 1 — re-parented under Plating → Compliance hub. -->
|
||||
<!-- Phase D (perms v2) — QM-only under compliance hub. -->
|
||||
<menuitem id="menu_fp_cgp"
|
||||
name="Controlled Goods (CGP)"
|
||||
parent="fusion_plating.menu_fp_compliance_hub"
|
||||
sequence="50"
|
||||
groups="group_fusion_plating_cgp_officer"/>
|
||||
groups="fusion_plating.group_fp_quality_manager"/>
|
||||
|
||||
<menuitem id="menu_fp_cgp_registration"
|
||||
name="Registration"
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
{
|
||||
'name': 'Fusion Plating - Compliance (Framework)',
|
||||
'version': '19.0.1.3.1',
|
||||
'version': '19.0.1.3.2',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Jurisdiction-agnostic compliance framework: permits, discharge monitoring, waste manifests, pollutant inventory, compliance calendar, spill register.',
|
||||
'description': 'Generic compliance framework. Region packs load jurisdiction-specific data.',
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
<!-- Phase 1 — re-parented under fusion_plating.menu_fp_compliance_hub
|
||||
and renamed to 'General' since the hub is now the top-level Compliance. -->
|
||||
<menuitem id="menu_fp_compliance_root" name="General"
|
||||
parent="fusion_plating.menu_fp_compliance_hub" sequence="10"/>
|
||||
parent="fusion_plating.menu_fp_compliance_hub" sequence="10"
|
||||
groups="fusion_plating.group_fp_quality_manager"/>
|
||||
|
||||
<menuitem id="menu_fp_compliance_permit" name="Permits" parent="menu_fp_compliance_root" action="action_fp_permit" sequence="10"/>
|
||||
<menuitem id="menu_fp_compliance_discharge_sample" name="Discharge Samples" parent="menu_fp_compliance_root" action="action_fp_discharge_sample" sequence="20"/>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Configurator',
|
||||
'version': '19.0.21.8.1',
|
||||
'version': '19.0.21.8.2',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Quotation configurator with part catalog, coating configs, and formula-based pricing engine.',
|
||||
'description': """
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
name="Sales"
|
||||
parent="fusion_plating.menu_fp_root"
|
||||
sequence="5"
|
||||
groups="group_fp_estimator,fusion_plating.group_fusion_plating_supervisor"/>
|
||||
groups="fusion_plating.group_fp_sales_rep"/>
|
||||
|
||||
<!-- === New Quote — top-of-menu entry point for a fresh quote === -->
|
||||
<menuitem id="menu_fp_new_quote"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — KPI Dashboard',
|
||||
'version': '19.0.1.1.1',
|
||||
'version': '19.0.1.1.2',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Configurable KPI dashboards for plating operations.',
|
||||
'author': 'Nexa Systems Inc.',
|
||||
|
||||
@@ -6,12 +6,12 @@
|
||||
-->
|
||||
<odoo>
|
||||
|
||||
<!-- Phase 3 — supervisor+ only. Operators don't need dashboards. -->
|
||||
<!-- Phase D (perms v2) — Manager+ only. Operators don't need dashboards. -->
|
||||
<menuitem id="menu_fp_dashboard"
|
||||
name="KPIs"
|
||||
parent="fusion_plating.menu_fp_root"
|
||||
sequence="85"
|
||||
groups="fusion_plating.group_fusion_plating_supervisor"/>
|
||||
groups="fusion_plating.group_fp_manager"/>
|
||||
|
||||
<menuitem id="menu_fp_kpis"
|
||||
name="KPIs"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Nuclear (CSA N299, NQA-1)',
|
||||
'version': '19.0.1.2.1',
|
||||
'version': '19.0.1.2.2',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Nuclear industry pack: CSA N299 Levels 1-4, NQA-1 awareness, '
|
||||
'CNSC licence tracking, 10 CFR Part 21 reporting, ITPs, '
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
<odoo>
|
||||
|
||||
<!-- Phase 1 — re-parented under Plating → Compliance hub. -->
|
||||
<!-- Phase D (perms v2) — QM-only under compliance hub. -->
|
||||
<menuitem id="menu_fp_nuclear"
|
||||
name="Nuclear (CSA N299 / CNSC)"
|
||||
parent="fusion_plating.menu_fp_compliance_hub"
|
||||
sequence="40"
|
||||
groups="fusion_plating.group_fusion_plating_operator"/>
|
||||
groups="fusion_plating.group_fp_quality_manager"/>
|
||||
|
||||
<menuitem id="menu_fp_nuclear_program"
|
||||
name="N299 Programs"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Quality (QMS)',
|
||||
'version': '19.0.6.6.2',
|
||||
'version': '19.0.6.6.3',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Native QMS for plating shops: NCR, CAPA, calibration, AVL, FAIR, '
|
||||
'internal audits, customer specs, document control. CE + EE compatible.',
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
name="Quality"
|
||||
parent="fusion_plating.menu_fp_root"
|
||||
sequence="30"
|
||||
groups="fusion_plating.group_fusion_plating_operator"/>
|
||||
groups="fusion_plating.group_fp_manager"/>
|
||||
|
||||
<menuitem id="menu_fp_quality_hold"
|
||||
name="Quality Holds"
|
||||
@@ -47,7 +47,8 @@
|
||||
name="Internal Audits"
|
||||
parent="menu_fp_quality"
|
||||
action="action_fp_audit"
|
||||
sequence="40"/>
|
||||
sequence="40"
|
||||
groups="fusion_plating.group_fp_quality_manager"/>
|
||||
|
||||
<menuitem id="menu_fp_quality_doc_control"
|
||||
name="Document Control"
|
||||
@@ -79,12 +80,14 @@
|
||||
name="Specifications"
|
||||
parent="menu_fp_quality"
|
||||
action="action_fp_customer_spec"
|
||||
sequence="70"/>
|
||||
sequence="70"
|
||||
groups="fusion_plating.group_fp_quality_manager"/>
|
||||
|
||||
<menuitem id="menu_fp_config_avl"
|
||||
name="Approved Vendor List"
|
||||
parent="fusion_plating.menu_fp_config_quality_docs"
|
||||
action="action_fp_avl"
|
||||
sequence="20"/>
|
||||
sequence="20"
|
||||
groups="fusion_plating.group_fp_quality_manager"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Receiving & Inspection',
|
||||
'version': '19.0.3.28.1',
|
||||
'version': '19.0.3.28.2',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Parts receiving, inspection, damage logging, and manufacturing gate.',
|
||||
'description': """
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
name="Shipping & Receiving"
|
||||
parent="fusion_plating.menu_fp_root"
|
||||
sequence="15"
|
||||
groups="group_fp_receiving"/>
|
||||
groups="fusion_plating.group_fp_shop_manager_v2"/>
|
||||
|
||||
<!-- Inbound (sequences 10–30) -->
|
||||
<menuitem id="menu_fp_receiving_all"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Safety (EHS)',
|
||||
'version': '19.0.1.3.1',
|
||||
'version': '19.0.1.3.2',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Occupational health and safety for plating shops: SDS library, '
|
||||
'WHMIS/TDG training, exposure monitoring, JHSC, incidents, PPE, '
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
<odoo>
|
||||
|
||||
<!-- Phase 1 — re-parented under Plating → Compliance hub. -->
|
||||
<!-- Phase D (perms v2) — QM-only under compliance hub. -->
|
||||
<menuitem id="menu_fp_safety_root"
|
||||
name="Safety / WHMIS"
|
||||
parent="fusion_plating.menu_fp_compliance_hub"
|
||||
sequence="20"
|
||||
groups="fusion_plating.group_fusion_plating_operator"/>
|
||||
groups="fusion_plating.group_fp_quality_manager"/>
|
||||
|
||||
<menuitem id="menu_fp_safety_sds"
|
||||
name="SDS Library"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Shop Floor',
|
||||
'version': '19.0.32.0.1',
|
||||
'version': '19.0.32.0.2',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Shop-floor tablet stations, QR scanning, bake window enforcer, '
|
||||
'first-piece inspection gates.',
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
name="Shop Floor"
|
||||
parent="fusion_plating.menu_fp_root"
|
||||
sequence="12"
|
||||
groups="fusion_plating.group_fusion_plating_operator"/>
|
||||
groups="fusion_plating.group_fp_technician"/>
|
||||
|
||||
<!-- Manager Desk — assign workers, swap tanks, cover no-shows -->
|
||||
<record id="action_fp_manager_dashboard" model="ir.actions.client">
|
||||
|
||||
Reference in New Issue
Block a user