diff --git a/fusion_plating/fusion_plating/__init__.py b/fusion_plating/fusion_plating/__init__.py index db9673ad..ff872e85 100644 --- a/fusion_plating/fusion_plating/__init__.py +++ b/fusion_plating/fusion_plating/__init__.py @@ -35,6 +35,86 @@ def post_init_hook(env): _migrate_legacy_uom_columns(env) _seed_starter_recipes_once(env) _fp_post_init_role_migration(env) + _fp_apply_office_user_menu_visibility(env) + + +# Top-level app menus that technicians should NOT see. Each entry is an +# xmlid; env.ref(..., raise_if_not_found=False) silently skips menus +# from uninstalled modules so this is safe across configurations. +# Kept visible to technicians (NOT in this list): Discuss, To-do, +# Plating, AI, Maintenance, Time Off. Settings/Apps/Tests are admin- +# restricted upstream — also not in this list. +# See security/fp_menu_visibility.xml for the design rationale. +MENU_HIDE_FROM_TECHNICIANS = [ + 'calendar.mail_menu_calendar', + 'contacts.menu_contacts', + 'crm.crm_menu_root', + 'sale.sale_menu_root', + 'spreadsheet_dashboard.spreadsheet_dashboard_menu_root', + 'fusion_ringcentral.menu_rc_root', + 'fusion_faxes.menu_fusion_faxes_root', + 'fusion_tasks.menu_field_service_root', + 'fusion_clock.menu_fusion_clock_root', + 'account.menu_finance', + 'accountant.menu_accounting', + 'project.menu_main_pm', + 'hr_timesheet.timesheet_menu_root', + 'planning.planning_menu_root', + 'fusion_shipping.menu_fusion_shipping_root', + 'website.menu_website_configuration', + 'purchase.menu_purchase_root', + 'stock.menu_stock_root', + 'sign.menu_document', + 'hr.menu_hr_root', + 'hr_work_entry_enterprise.menu_hr_payroll_root', + 'hr_attendance.menu_hr_attendance_root', + 'hr_recruitment.menu_hr_recruitment_root', + 'hr_expense.menu_hr_expense_root', + 'iot.iot_menu_root', + 'utm.menu_link_tracker_root', + 'base.menu_management', +] + + +def _fp_apply_office_user_menu_visibility(env): + """Set group_ids = [group_fp_office_user] on every menu in + MENU_HIDE_FROM_TECHNICIANS that exists in this DB. + + Field is `group_ids` on ir.ui.menu in Odoo 19 (was `groups_id` in + earlier versions — Odoo 18 renamed it). Same naming-rename pattern + as res.users (CLAUDE.md Critical Rule 13c). + + Idempotent: if a menu already has only the office_user group, no + change is made. If it has additional groups (e.g. a previous custom + restriction), they're REPLACED — the design accepts this trade-off + because office_user is implied by every fp role above Technician, + so non-fp users keep their access on entech. + + Cross-module xmlids: env.ref(..., raise_if_not_found=False) returns + None for menus from uninstalled modules, which we silently skip. + """ + office = env.ref( + 'fusion_plating.group_fp_office_user', raise_if_not_found=False, + ) + if not office: + _logger.warning( + '[menu-visibility] group_fp_office_user not found; skipping' + ) + return + touched = 0 + for xmlid in MENU_HIDE_FROM_TECHNICIANS: + menu = env.ref(xmlid, raise_if_not_found=False) + if not menu: + continue + current_ids = set(menu.group_ids.ids) + if current_ids == {office.id}: + continue # already locked-down, nothing to do + menu.sudo().group_ids = [(6, 0, [office.id])] + touched += 1 + _logger.info( + '[menu-visibility] restricted %s menu(s) to group_fp_office_user', + touched, + ) def _fp_post_init_role_migration(env): diff --git a/fusion_plating/fusion_plating/__manifest__.py b/fusion_plating/fusion_plating/__manifest__.py index 98947f2e..30bef953 100644 --- a/fusion_plating/fusion_plating/__manifest__.py +++ b/fusion_plating/fusion_plating/__manifest__.py @@ -5,7 +5,7 @@ { 'name': 'Fusion Plating', - 'version': '19.0.21.3.0', + 'version': '19.0.21.4.0', 'category': 'Manufacturing/Plating', 'summary': 'Core plating / metal finishing ERP: facilities, processes, tanks, baths, jobs, operators.', 'description': """ @@ -82,6 +82,13 @@ Copyright (c) 2026 Nexa Systems Inc. All rights reserved. 'security/fp_security.xml', 'security/fp_security_v2.xml', 'security/ir.model.access.csv', + # Menu visibility — loads after fp_security_v2.xml so the role + # group xmlids exist when we add office_user to their + # implied_ids. Loads after fp_menu.xml in spirit BUT references + # cross-module menus (calendar, sale, hr, etc.) which exist by + # the time fusion_plating loads, so safe to load here at + # security-config time. + 'security/fp_menu_visibility.xml', 'data/fp_landing_data.xml', 'data/fp_sequence_data.xml', 'data/fp_job_sequences.xml', diff --git a/fusion_plating/fusion_plating/migrations/19.0.21.4.0/post-migrate.py b/fusion_plating/fusion_plating/migrations/19.0.21.4.0/post-migrate.py new file mode 100644 index 00000000..56b9b8a9 --- /dev/null +++ b/fusion_plating/fusion_plating/migrations/19.0.21.4.0/post-migrate.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +# Copyright 2026 Nexa Systems Inc. +# License OPL-1 (Odoo Proprietary License v1.0) +"""19.0.21.4.0 — Apply office-user menu visibility on -u. + +post_init_hook only fires on FIRST install (CLAUDE.md Rule 13d). +This script runs the same helper on every -u so existing installs +get the menu restrictions applied without needing to uninstall + +reinstall. Idempotent — the helper checks current state and skips +already-restricted menus. +""" +import logging + +from odoo.api import Environment, SUPERUSER_ID + +_logger = logging.getLogger(__name__) + + +def migrate(cr, version): + from odoo.addons.fusion_plating import _fp_apply_office_user_menu_visibility + env = Environment(cr, SUPERUSER_ID, {}) + _fp_apply_office_user_menu_visibility(env) diff --git a/fusion_plating/fusion_plating/security/fp_menu_visibility.xml b/fusion_plating/fusion_plating/security/fp_menu_visibility.xml new file mode 100644 index 00000000..ee86d673 --- /dev/null +++ b/fusion_plating/fusion_plating/security/fp_menu_visibility.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + Plating: Office User (sees back-office menus) + + 90 + Marker group that controls visibility of + non-tablet app menus (Calendar, Sales, Inventory, etc.). + Implied by every fp role above Technician (Owner, Manager, + Quality Manager, Shop Manager, Sales Rep, Estimator). + Pure Technicians don't have it, so they only see the + tablet apps (Plating, Discuss, To-do, AI, Maintenance, + Time Off). + + + + + + + + + + + + + + + + + + + + + + + +