fix(plating-sec): move cross-module implied_ids out of fp_security_v2.xml
The previous commit (a53b0326) added implied_ids in fp_security_v2.xml
that referenced 5 xmlids from downstream modules (configurator/receiving/
invoicing/cgp). Since fusion_plating is the BASE module and loads first
at fresh install, those refs raised External-ID-not-found at install.
Fix: relocate the 5 cross-module implications into each downstream module's
own security file via additive (4, ref()) writes to the core group's
implied_ids. Odoo's XML data loader treats these as additive updates so
they stack cleanly across install + -u cycles.
Also: drop redundant <data noupdate="0"> wrapper in fp_security_v2.xml
to match sibling fp_security.xml's bare <odoo> shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -208,6 +208,14 @@ Use only: `name`, `model_id`, `state`, `code` (or `function`/`model`), `interval
|
||||
11. **XML data ordering**: Window actions must be defined BEFORE `<menuitem>` elements that reference them in the same file.
|
||||
12. **Module install on new modules**: Use `--update=base` alongside `-i MODULE` to ensure Odoo rescans the addons path and finds the new module directory.
|
||||
13. **Implied group cascade**: `implied_ids` on `res.groups` does NOT reliably propagate to users on module install. Always include `user_ids` to explicitly assign admin, or fix via SQL post-install.
|
||||
13a. **Cross-module xmlid refs — base modules CANNOT forward-ref downstream xmlids**: A BASE module's data XML cannot `ref('downstream_module.some_xmlid')` because at fresh install, the base module loads FIRST and `ir.model.data` has no row for the downstream xmlid yet → `ValueError: External ID not found`. This bites on entech (existing DB has the row) but breaks fresh CI/test/demo/new-client installs. **Fix pattern: relocate the cross-module link to the downstream module's own security/data file, using an additive write to the BASE module's record:**
|
||||
```xml
|
||||
<!-- In downstream module's security XML -->
|
||||
<record id="fusion_plating.group_fp_sales_rep" model="res.groups">
|
||||
<field name="implied_ids" eval="[(4, ref('fusion_plating_configurator.group_fp_estimator'))]"/>
|
||||
</record>
|
||||
```
|
||||
Odoo's XML loader treats `id="other_module.xmlid"` as an additive update to the existing record, and `(4, ref(...))` (Command.link) stacks idempotently across install/-u cycles. Use this whenever a base module group/record needs to imply or reference something defined in a downstream module. Caught 2026-05-24 when `fusion_plating/security/fp_security_v2.xml` referenced groups from configurator/receiving/invoicing/cgp — worked on entech, would have broken fresh installs.
|
||||
14a. **FP report palette + border rendering**: `fusion_plating_reports/report/report_base_styles.xml` uses **`#c1c1c1`** for section-header backgrounds and **`#1d1f1e`** (th text on grey) / **`#4e4e4e`** (h2/h4 on white) — NOT `res.company.primary_color`. Per-customer request (2026-05-17) the FP reports stopped following the company brand colour so every shop gets the same neutral look. The `fp_primary` template variable is still computed in the styles block so per-report templates can opt back in if needed, but the default `.fp-report` / `.fp-landscape` rules use the hardcoded greys. **Don't "fix" this back to `fp_primary` without confirming.**
|
||||
|
||||
**Border-rendering gotcha** (entech wkhtmltopdf): with the standard `border-collapse: collapse` + `border: 1px solid #000` pattern, vertical borders can render slightly softer than horizontal borders because of how wkhtmltopdf rounds sub-pixels in its collapse-adjudication. Cells with a `background-color` also paint over the border edge unless clipped. Mitigations in place:
|
||||
|
||||
@@ -1,85 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="0">
|
||||
<!-- Phase 1 Permissions Overhaul: 8 consolidated roles -->
|
||||
<!-- See docs/superpowers/specs/2026-05-23-permissions-overhaul-design.md -->
|
||||
<!-- Phase 1 Permissions Overhaul: 8 consolidated roles -->
|
||||
<!-- See docs/superpowers/specs/2026-05-23-permissions-overhaul-design.md -->
|
||||
<!-- Cross-module implications (estimator, receiving, accounting, cgp_officer,
|
||||
cgp_designated_official) live in the downstream modules' security files
|
||||
to avoid fresh-install forward-ref errors. -->
|
||||
|
||||
<record id="group_fp_technician" model="res.groups">
|
||||
<field name="name">Technician</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="privilege_id" ref="fusion_plating.res_groups_privilege_fusion_plating"/>
|
||||
<field name="implied_ids" eval="[
|
||||
(4, ref('base.group_user')),
|
||||
(4, ref('fusion_plating.group_fusion_plating_operator')),
|
||||
]"/>
|
||||
</record>
|
||||
<record id="group_fp_technician" model="res.groups">
|
||||
<field name="name">Technician</field>
|
||||
<field name="sequence">10</field>
|
||||
<field name="privilege_id" ref="fusion_plating.res_groups_privilege_fusion_plating"/>
|
||||
<field name="implied_ids" eval="[
|
||||
(4, ref('base.group_user')),
|
||||
(4, ref('fusion_plating.group_fusion_plating_operator')),
|
||||
]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_fp_sales_rep" model="res.groups">
|
||||
<field name="name">Sales Representative</field>
|
||||
<field name="sequence">20</field>
|
||||
<field name="privilege_id" ref="fusion_plating.res_groups_privilege_fusion_plating"/>
|
||||
<field name="implied_ids" eval="[
|
||||
(4, ref('base.group_user')),
|
||||
(4, ref('fusion_plating_configurator.group_fp_estimator')),
|
||||
]"/>
|
||||
</record>
|
||||
<record id="group_fp_sales_rep" model="res.groups">
|
||||
<field name="name">Sales Representative</field>
|
||||
<field name="sequence">20</field>
|
||||
<field name="privilege_id" ref="fusion_plating.res_groups_privilege_fusion_plating"/>
|
||||
<field name="implied_ids" eval="[
|
||||
(4, ref('base.group_user')),
|
||||
]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_fp_shop_manager_v2" model="res.groups">
|
||||
<field name="name">Shop Manager</field>
|
||||
<field name="sequence">30</field>
|
||||
<field name="privilege_id" ref="fusion_plating.res_groups_privilege_fusion_plating"/>
|
||||
<field name="implied_ids" eval="[
|
||||
(4, ref('group_fp_technician')),
|
||||
(4, ref('fusion_plating.group_fusion_plating_supervisor')),
|
||||
(4, ref('fusion_plating_receiving.group_fp_receiving')),
|
||||
]"/>
|
||||
</record>
|
||||
<record id="group_fp_shop_manager_v2" model="res.groups">
|
||||
<field name="name">Shop Manager</field>
|
||||
<field name="sequence">30</field>
|
||||
<field name="privilege_id" ref="fusion_plating.res_groups_privilege_fusion_plating"/>
|
||||
<field name="implied_ids" eval="[
|
||||
(4, ref('group_fp_technician')),
|
||||
(4, ref('fusion_plating.group_fusion_plating_supervisor')),
|
||||
]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_fp_sales_manager" model="res.groups">
|
||||
<field name="name">Sales Manager</field>
|
||||
<field name="sequence">40</field>
|
||||
<field name="privilege_id" ref="fusion_plating.res_groups_privilege_fusion_plating"/>
|
||||
<field name="implied_ids" eval="[
|
||||
(4, ref('group_fp_sales_rep')),
|
||||
]"/>
|
||||
</record>
|
||||
<record id="group_fp_sales_manager" model="res.groups">
|
||||
<field name="name">Sales Manager</field>
|
||||
<field name="sequence">40</field>
|
||||
<field name="privilege_id" ref="fusion_plating.res_groups_privilege_fusion_plating"/>
|
||||
<field name="implied_ids" eval="[
|
||||
(4, ref('group_fp_sales_rep')),
|
||||
]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_fp_manager" model="res.groups">
|
||||
<field name="name">Manager</field>
|
||||
<field name="sequence">50</field>
|
||||
<field name="privilege_id" ref="fusion_plating.res_groups_privilege_fusion_plating"/>
|
||||
<field name="implied_ids" eval="[
|
||||
(4, ref('group_fp_shop_manager_v2')),
|
||||
(4, ref('group_fp_sales_manager')),
|
||||
(4, ref('fusion_plating.group_fusion_plating_manager')),
|
||||
(4, ref('fusion_plating_invoicing.group_fp_accounting')),
|
||||
]"/>
|
||||
</record>
|
||||
<record id="group_fp_manager" model="res.groups">
|
||||
<field name="name">Manager</field>
|
||||
<field name="sequence">50</field>
|
||||
<field name="privilege_id" ref="fusion_plating.res_groups_privilege_fusion_plating"/>
|
||||
<field name="implied_ids" eval="[
|
||||
(4, ref('group_fp_shop_manager_v2')),
|
||||
(4, ref('group_fp_sales_manager')),
|
||||
(4, ref('fusion_plating.group_fusion_plating_manager')),
|
||||
]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_fp_quality_manager" model="res.groups">
|
||||
<field name="name">Quality Manager</field>
|
||||
<field name="sequence">60</field>
|
||||
<field name="privilege_id" ref="fusion_plating.res_groups_privilege_fusion_plating"/>
|
||||
<field name="implied_ids" eval="[
|
||||
(4, ref('group_fp_manager')),
|
||||
(4, ref('fusion_plating_cgp.group_fusion_plating_cgp_officer')),
|
||||
]"/>
|
||||
</record>
|
||||
<record id="group_fp_quality_manager" model="res.groups">
|
||||
<field name="name">Quality Manager</field>
|
||||
<field name="sequence">60</field>
|
||||
<field name="privilege_id" ref="fusion_plating.res_groups_privilege_fusion_plating"/>
|
||||
<field name="implied_ids" eval="[
|
||||
(4, ref('group_fp_manager')),
|
||||
]"/>
|
||||
</record>
|
||||
|
||||
<record id="group_fp_owner" model="res.groups">
|
||||
<field name="name">Owner</field>
|
||||
<field name="sequence">70</field>
|
||||
<field name="privilege_id" ref="fusion_plating.res_groups_privilege_fusion_plating"/>
|
||||
<field name="implied_ids" eval="[
|
||||
(4, ref('group_fp_quality_manager')),
|
||||
(4, ref('fusion_plating.group_fusion_plating_admin')),
|
||||
(4, ref('fusion_plating_cgp.group_fusion_plating_cgp_designated_official')),
|
||||
(4, ref('base.group_system')),
|
||||
]"/>
|
||||
<field name="user_ids" eval="[
|
||||
(4, ref('base.user_root')),
|
||||
(4, ref('base.user_admin')),
|
||||
]"/>
|
||||
</record>
|
||||
</data>
|
||||
<record id="group_fp_owner" model="res.groups">
|
||||
<field name="name">Owner</field>
|
||||
<field name="sequence">70</field>
|
||||
<field name="privilege_id" ref="fusion_plating.res_groups_privilege_fusion_plating"/>
|
||||
<field name="implied_ids" eval="[
|
||||
(4, ref('group_fp_quality_manager')),
|
||||
(4, ref('fusion_plating.group_fusion_plating_admin')),
|
||||
(4, ref('base.group_system')),
|
||||
]"/>
|
||||
<field name="user_ids" eval="[
|
||||
(4, ref('base.user_root')),
|
||||
(4, ref('base.user_admin')),
|
||||
]"/>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Controlled Goods Program',
|
||||
'version': '19.0.1.1.0',
|
||||
'version': '19.0.1.2.0',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Canadian Controlled Goods Program (CGP) compliance for plating '
|
||||
'shops handling defence work: registration, authorized individuals, '
|
||||
|
||||
@@ -35,6 +35,15 @@
|
||||
eval="[(4, ref('group_fusion_plating_cgp_officer'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Backward-compat: new Quality Manager implies old CGP Officer; new Owner implies old CGP DO. -->
|
||||
<record id="fusion_plating.group_fp_quality_manager" model="res.groups">
|
||||
<field name="implied_ids" eval="[(4, ref('fusion_plating_cgp.group_fusion_plating_cgp_officer'))]"/>
|
||||
</record>
|
||||
|
||||
<record id="fusion_plating.group_fp_owner" model="res.groups">
|
||||
<field name="implied_ids" eval="[(4, ref('fusion_plating_cgp.group_fusion_plating_cgp_designated_official'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- RECORD RULES -->
|
||||
<!-- -->
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Configurator',
|
||||
'version': '19.0.21.7.2',
|
||||
'version': '19.0.21.8.0',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Quotation configurator with part catalog, coating configs, and formula-based pricing engine.',
|
||||
'description': """
|
||||
|
||||
@@ -24,4 +24,10 @@
|
||||
<field name="user_ids" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Backward-compat: new Sales Rep role implies old Estimator group so existing ACLs still resolve.
|
||||
Lives here (not in fusion_plating core) to avoid fresh-install forward-ref. -->
|
||||
<record id="fusion_plating.group_fp_sales_rep" model="res.groups">
|
||||
<field name="implied_ids" eval="[(4, ref('fusion_plating_configurator.group_fp_estimator'))]"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Invoicing',
|
||||
'version': '19.0.3.5.0',
|
||||
'version': '19.0.3.6.0',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Invoice strategy engine with deposit, progress billing, net terms, COD/prepay, and account holds.',
|
||||
'description': """
|
||||
|
||||
@@ -14,4 +14,9 @@
|
||||
<field name="user_ids" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Backward-compat: new Manager role implies old Accounting group. -->
|
||||
<record id="fusion_plating.group_fp_manager" model="res.groups">
|
||||
<field name="implied_ids" eval="[(4, ref('fusion_plating_invoicing.group_fp_accounting'))]"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
{
|
||||
'name': 'Fusion Plating — Receiving & Inspection',
|
||||
'version': '19.0.3.27.0',
|
||||
'version': '19.0.3.28.0',
|
||||
'category': 'Manufacturing/Plating',
|
||||
'summary': 'Parts receiving, inspection, damage logging, and manufacturing gate.',
|
||||
'description': """
|
||||
|
||||
@@ -14,4 +14,9 @@
|
||||
<field name="user_ids" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/>
|
||||
</record>
|
||||
|
||||
<!-- Backward-compat: new Shop Manager v2 role implies old Receiving group. -->
|
||||
<record id="fusion_plating.group_fp_shop_manager_v2" model="res.groups">
|
||||
<field name="implied_ids" eval="[(4, ref('fusion_plating_receiving.group_fp_receiving'))]"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user