folder rename

This commit is contained in:
gsinghpal
2026-04-16 20:53:53 -04:00
parent 3f3ddcbab4
commit 7c7ef06057
634 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,64 @@
# Fusion Plating — Documents Bridge (Enterprise)
Part of the Fusion Plating product family by Nexa Systems Inc.
## What this module does
When both `fusion_plating_quality` and Odoo Enterprise's `documents` module are
installed, this bridge **auto-installs** and wires the two together so that
every attachment dropped on a Fusion Plating quality record (NCR, CAPA, FAIR,
or Controlled Document) is automatically mirrored into a dedicated Documents
workspace with the right tag applied.
No manual file uploads, no duplicate copies to keep in sync — the bridge takes
care of it on `ir.attachment.create()`.
## What it creates
* **Workspace**: `Plating — Quality` (a `documents.document` with `type='folder'`)
* **Facet**: `Record Type` (a `documents.facet` scoped to the workspace)
* **Tags**: `NCR`, `CAPA`, `FAIR`, `Doc Control` under that facet
* **Smart button**: a `Documents` stat button on every NCR / CAPA / FAIR /
Controlled Document form view, opening the filtered Documents kanban for
that record
## How it works
1. A user attaches a file to an NCR (or any of the other supported records)
via the chatter or a wizard.
2. The bridge's `ir.attachment.create()` override inspects `res_model` and,
if it matches one of the supported quality models, creates a mirror
`documents.document` record:
* Placed inside the `Plating — Quality` workspace
* Tagged with the corresponding record type tag
* Linked back to the original `ir.attachment` via `attachment_id`
3. On the quality record form, the smart button reads a computed Many2many
(`x_fc_document_ids`) that searches `documents.document` by the underlying
attachment's `res_model` + `res_id` — no duplication, no storage overhead.
## Safety & robustness
* The bridge never blocks attachment creation. Any exception raised while
creating the mirror `documents.document` record is caught and logged — the
user's upload always succeeds.
* All references to the workspace folder and tags use
`env.ref(..., raise_if_not_found=False)`. If the data records are ever
removed or renamed, the bridge degrades gracefully (no mirror created,
logged warning).
* The bridge never modifies `fusion_plating`, `fusion_plating_quality`, or
the EE `documents` module. It is purely additive.
## Dependencies
* `fusion_plating_quality`
* `documents` (Odoo Enterprise)
## Auto-install
`auto_install = True` — the bridge installs automatically whenever both
dependencies are present in the same database, and stays dormant otherwise.
## Copyright
Copyright 2026 Nexa Systems Inc. All rights reserved.
Licensed under OPL-1 (Odoo Proprietary License v1.0).

View File

@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from . import models

View File

@@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
{
'name': 'Fusion Plating — Documents Bridge (EE)',
'version': '19.0.1.0.0',
'category': 'Manufacturing/Plating',
'summary': 'Enterprise bridge: auto-promotes Fusion Plating quality attachments '
'(NCR, CAPA, FAIR, Doc Control) into Odoo EE Documents with a tagged '
'workspace. Auto-installs when both modules are present.',
'description': """
Fusion Plating — Documents Bridge (Enterprise)
==============================================
Part of the Fusion Plating product family by Nexa Systems Inc.
This bridge module connects the native Fusion Plating QMS (`fusion_plating_quality`)
with the Odoo Enterprise `documents` module. When both modules are installed the
bridge installs automatically and takes care of the plumbing so that every
attachment dropped on an NCR, CAPA, FAIR, or Controlled Document record is
promoted into a dedicated "Plating — Quality" workspace and tagged by record
type for easy retrieval, review, and audit export.
What it does
------------
* Creates a dedicated Documents workspace: "Plating — Quality"
* Creates a "Record Type" facet with four tags: NCR, CAPA, FAIR, Doc Control
* Overrides `ir.attachment.create()` so attachments added to supported quality
records are silently mirrored as `documents.document` records in the
workspace and tagged with the appropriate record type
* Adds a "Documents" smart button on each NCR, CAPA, FAIR, and Doc Control form
view that opens the filtered Documents kanban for that record
* Ships with `auto_install = True` so no manual install step is required — the
bridge activates as soon as both pre-requisite modules are present
Why this module exists
----------------------
The Community-Edition-compatible `fusion_plating_quality` module intentionally
does NOT depend on the Enterprise `documents` module. On Enterprise deployments
this bridge provides the richer Documents-app experience (workspaces, tags,
bulk download, preview, sharing) without ever touching the core or quality
modules — both stay CE-safe and upgradable.
Copyright (c) 2026 Nexa Systems Inc. All rights reserved.
""",
'author': 'Nexa Systems Inc.',
'website': 'https://www.nexasystems.ca',
'maintainer': 'Nexa Systems Inc.',
'support': 'support@nexasystems.ca',
'license': 'OPL-1',
'price': 0.00,
'currency': 'CAD',
'depends': [
'fusion_plating_quality',
'documents',
],
'data': [
'security/ir.model.access.csv',
'data/documents_folder_data.xml',
'data/documents_tag_data.xml',
'views/fp_ncr_views.xml',
'views/fp_capa_views.xml',
'views/fp_fair_views.xml',
'views/fp_doc_control_views.xml',
],
'installable': True,
'application': False,
'auto_install': True,
}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Part of the Fusion Plating product family.
Workspace folder for Fusion Plating QMS attachments.
Odoo 19 EE unified the Documents data model: folders are simply
``documents.document`` records with ``type = 'folder'`` and a null
``folder_id`` (root) or a parent folder reference. This matches the
Odoo 18.x / 19.x behaviour where the Documents kanban is driven by
a single model rather than a separate ``documents.folder`` model.
-->
<odoo noupdate="1">
<record id="documents_folder_plating_quality" model="documents.document">
<field name="name">Plating — Quality</field>
<field name="type">folder</field>
<field name="folder_id" eval="False"/>
</record>
</odoo>

View File

@@ -0,0 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Part of the Fusion Plating product family.
Tags used by the bridge to categorise mirrored quality attachments.
Odoo 19 EE organises tags under a two-level hierarchy:
documents.facet (a.k.a. "category" — scoped to a folder/workspace)
└── documents.tag (individual tags, required to have a facet_id)
We create one facet called "Record Type" inside the Plating — Quality
workspace, then four tags beneath it — one per supported quality
record type.
-->
<odoo noupdate="1">
<record id="documents_facet_record_type" model="documents.facet">
<field name="name">Record Type</field>
<field name="folder_id" ref="documents_folder_plating_quality"/>
<field name="sequence">10</field>
</record>
<record id="documents_tag_ncr" model="documents.tag">
<field name="name">NCR</field>
<field name="facet_id" ref="documents_facet_record_type"/>
<field name="sequence">10</field>
</record>
<record id="documents_tag_capa" model="documents.tag">
<field name="name">CAPA</field>
<field name="facet_id" ref="documents_facet_record_type"/>
<field name="sequence">20</field>
</record>
<record id="documents_tag_fair" model="documents.tag">
<field name="name">FAIR</field>
<field name="facet_id" ref="documents_facet_record_type"/>
<field name="sequence">30</field>
</record>
<record id="documents_tag_doc_control" model="documents.tag">
<field name="name">Doc Control</field>
<field name="facet_id" ref="documents_facet_record_type"/>
<field name="sequence">40</field>
</record>
</odoo>

View File

@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from . import ir_attachment
from . import fp_ncr
from . import fp_capa
from . import fp_fair
from . import fp_doc_control

View File

@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import _, api, fields, models
_FOLDER_XMLID = 'fusion_plating_bridge_documents.documents_folder_plating_quality'
class FpCapa(models.Model):
"""Bridge extension: expose Documents workspace on CAPAs."""
_inherit = 'fusion.plating.capa'
x_fc_document_ids = fields.Many2many(
'documents.document',
'fp_bridge_capa_document_rel',
'capa_id',
'document_id',
string='Quality Documents',
compute='_compute_x_fc_document_ids',
store=False,
help='Documents in the Plating — Quality workspace mirrored from '
'attachments on this CAPA.',
)
x_fc_document_count = fields.Integer(
string='# Documents',
compute='_compute_x_fc_document_ids',
store=False,
)
@api.depends('message_attachment_count')
def _compute_x_fc_document_ids(self):
Document = self.env.get('documents.document') if 'documents.document' in self.env else None
for rec in self:
if not Document:
rec.x_fc_document_ids = False
rec.x_fc_document_count = 0
continue
docs = Document.sudo().search([
('attachment_id.res_model', '=', 'fusion.plating.capa'),
('attachment_id.res_id', '=', rec.id),
])
rec.x_fc_document_ids = docs
rec.x_fc_document_count = len(docs)
def action_view_documents(self):
self.ensure_one()
folder_id = self._get_default_folder_id()
ctx = {}
if folder_id:
ctx['default_folder_id'] = folder_id
ctx['searchpanel_default_folder_id'] = folder_id
return {
'name': _('Quality Documents'),
'type': 'ir.actions.act_window',
'res_model': 'documents.document',
'view_mode': 'kanban,list,form',
'domain': [('id', 'in', self.x_fc_document_ids.ids)],
'context': ctx,
}
def _get_default_folder_id(self):
folder = self.env.ref(_FOLDER_XMLID, raise_if_not_found=False)
return folder.id if folder else 0

View File

@@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import _, api, fields, models
_FOLDER_XMLID = 'fusion_plating_bridge_documents.documents_folder_plating_quality'
class FpDocControl(models.Model):
"""Bridge extension: expose Documents workspace on Controlled Documents.
Doc Control already carries a native ``attachment_ids`` Many2many; the
bridge additionally exposes the `documents.document` mirror so users can
jump straight into the Documents app to use its preview, tag, share,
and lock features.
"""
_inherit = 'fusion.plating.doc.control'
x_fc_document_ids = fields.Many2many(
'documents.document',
'fp_bridge_doc_control_document_rel',
'doc_id',
'document_id',
string='Quality Documents',
compute='_compute_x_fc_document_ids',
store=False,
help='Documents in the Plating — Quality workspace mirrored from '
'attachments on this controlled document record.',
)
x_fc_document_count = fields.Integer(
string='# Documents',
compute='_compute_x_fc_document_ids',
store=False,
)
@api.depends('attachment_ids', 'message_attachment_count')
def _compute_x_fc_document_ids(self):
Document = self.env.get('documents.document') if 'documents.document' in self.env else None
for rec in self:
if not Document:
rec.x_fc_document_ids = False
rec.x_fc_document_count = 0
continue
# Pull in both chatter attachments (matched via res_model/res_id) and
# any documents whose underlying ir.attachment is in the native
# attachment_ids M2m on this controlled document record.
native_attachment_ids = rec.attachment_ids.ids
if native_attachment_ids:
domain = [
'|',
'&', ('attachment_id.res_model', '=', 'fusion.plating.doc.control'),
('attachment_id.res_id', '=', rec.id),
('attachment_id', 'in', native_attachment_ids),
]
else:
domain = [
('attachment_id.res_model', '=', 'fusion.plating.doc.control'),
('attachment_id.res_id', '=', rec.id),
]
docs = Document.sudo().search(domain)
rec.x_fc_document_ids = docs
rec.x_fc_document_count = len(docs)
def action_view_documents(self):
self.ensure_one()
folder_id = self._get_default_folder_id()
ctx = {}
if folder_id:
ctx['default_folder_id'] = folder_id
ctx['searchpanel_default_folder_id'] = folder_id
return {
'name': _('Quality Documents'),
'type': 'ir.actions.act_window',
'res_model': 'documents.document',
'view_mode': 'kanban,list,form',
'domain': [('id', 'in', self.x_fc_document_ids.ids)],
'context': ctx,
}
def _get_default_folder_id(self):
folder = self.env.ref(_FOLDER_XMLID, raise_if_not_found=False)
return folder.id if folder else 0

View File

@@ -0,0 +1,65 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import _, api, fields, models
_FOLDER_XMLID = 'fusion_plating_bridge_documents.documents_folder_plating_quality'
class FpFair(models.Model):
"""Bridge extension: expose Documents workspace on FAIRs."""
_inherit = 'fusion.plating.fair'
x_fc_document_ids = fields.Many2many(
'documents.document',
'fp_bridge_fair_document_rel',
'fair_id',
'document_id',
string='Quality Documents',
compute='_compute_x_fc_document_ids',
store=False,
help='Documents in the Plating — Quality workspace mirrored from '
'attachments on this FAIR.',
)
x_fc_document_count = fields.Integer(
string='# Documents',
compute='_compute_x_fc_document_ids',
store=False,
)
@api.depends('message_attachment_count')
def _compute_x_fc_document_ids(self):
Document = self.env.get('documents.document') if 'documents.document' in self.env else None
for rec in self:
if not Document:
rec.x_fc_document_ids = False
rec.x_fc_document_count = 0
continue
docs = Document.sudo().search([
('attachment_id.res_model', '=', 'fusion.plating.fair'),
('attachment_id.res_id', '=', rec.id),
])
rec.x_fc_document_ids = docs
rec.x_fc_document_count = len(docs)
def action_view_documents(self):
self.ensure_one()
folder_id = self._get_default_folder_id()
ctx = {}
if folder_id:
ctx['default_folder_id'] = folder_id
ctx['searchpanel_default_folder_id'] = folder_id
return {
'name': _('Quality Documents'),
'type': 'ir.actions.act_window',
'res_model': 'documents.document',
'view_mode': 'kanban,list,form',
'domain': [('id', 'in', self.x_fc_document_ids.ids)],
'context': ctx,
}
def _get_default_folder_id(self):
folder = self.env.ref(_FOLDER_XMLID, raise_if_not_found=False)
return folder.id if folder else 0

View File

@@ -0,0 +1,71 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
from odoo import _, api, fields, models
_FOLDER_XMLID = 'fusion_plating_bridge_documents.documents_folder_plating_quality'
class FpNcr(models.Model):
"""Bridge extension: expose Documents workspace on NCRs.
Adds a reverse link to any `documents.document` records that were
created by the bridge override on `ir.attachment`, plus a smart
button action that opens the filtered Documents kanban for the
current NCR.
"""
_inherit = 'fusion.plating.ncr'
x_fc_document_ids = fields.Many2many(
'documents.document',
'fp_bridge_ncr_document_rel',
'ncr_id',
'document_id',
string='Quality Documents',
compute='_compute_x_fc_document_ids',
store=False,
help='Documents in the Plating — Quality workspace mirrored from '
'attachments on this NCR.',
)
x_fc_document_count = fields.Integer(
string='# Documents',
compute='_compute_x_fc_document_ids',
store=False,
)
@api.depends('message_attachment_count')
def _compute_x_fc_document_ids(self):
Document = self.env.get('documents.document') if 'documents.document' in self.env else None
for rec in self:
if not Document:
rec.x_fc_document_ids = False
rec.x_fc_document_count = 0
continue
docs = Document.sudo().search([
('attachment_id.res_model', '=', 'fusion.plating.ncr'),
('attachment_id.res_id', '=', rec.id),
])
rec.x_fc_document_ids = docs
rec.x_fc_document_count = len(docs)
def action_view_documents(self):
self.ensure_one()
folder_id = self._get_default_folder_id()
ctx = {}
if folder_id:
ctx['default_folder_id'] = folder_id
ctx['searchpanel_default_folder_id'] = folder_id
return {
'name': _('Quality Documents'),
'type': 'ir.actions.act_window',
'res_model': 'documents.document',
'view_mode': 'kanban,list,form',
'domain': [('id', 'in', self.x_fc_document_ids.ids)],
'context': ctx,
}
def _get_default_folder_id(self):
folder = self.env.ref(_FOLDER_XMLID, raise_if_not_found=False)
return folder.id if folder else 0

View File

@@ -0,0 +1,122 @@
# -*- coding: utf-8 -*-
# Copyright 2026 Nexa Systems Inc.
# License OPL-1 (Odoo Proprietary License v1.0)
# Part of the Fusion Plating product family.
import logging
from odoo import api, models
_logger = logging.getLogger(__name__)
# Map of quality-module res_model -> bridge tag XML id.
# Kept module-level so both create() and write() can consult it cheaply.
_QUALITY_MODELS_TO_TAG = {
'fusion.plating.ncr': 'fusion_plating_bridge_documents.documents_tag_ncr',
'fusion.plating.capa': 'fusion_plating_bridge_documents.documents_tag_capa',
'fusion.plating.fair': 'fusion_plating_bridge_documents.documents_tag_fair',
'fusion.plating.doc.control': 'fusion_plating_bridge_documents.documents_tag_doc_control',
}
_FOLDER_XMLID = 'fusion_plating_bridge_documents.documents_folder_plating_quality'
class IrAttachment(models.Model):
"""Bridge ir.attachment with Odoo EE `documents.document`.
Whenever an attachment is created on one of the Fusion Plating QMS
record types (NCR, CAPA, FAIR, Doc Control) we silently mirror it as
a `documents.document` record inside the "Plating — Quality"
workspace, tagged with the corresponding record type. The original
`ir.attachment` record is untouched and continues to live on the
quality record as before — the bridge is purely additive.
Design notes
------------
* We resolve the folder and tag XML ids via ``env.ref`` with
``raise_if_not_found=False`` so that a partial install, a missing
demo record, or a future schema change can never break attachment
creation on a quality record — the worst case is that the
`documents.document` mirror record isn't created and a line goes
to the log.
* The write is wrapped in a broad try/except for the same reason:
user-facing attachment creation must never be blocked by a bridge
failure.
* We use ``sudo()`` on the `documents.document` create because the
user uploading the attachment may not have write access to the
Documents app — the bridge is a system-level convenience.
"""
_inherit = 'ir.attachment'
@api.model_create_multi
def create(self, vals_list):
attachments = super().create(vals_list)
try:
self._fusion_plating_bridge_promote_to_documents(attachments)
except Exception: # pragma: no cover - defensive only
_logger.exception(
"Fusion Plating Documents bridge: failed to promote attachments %s",
attachments.ids,
)
return attachments
def _fusion_plating_bridge_promote_to_documents(self, attachments):
"""Create `documents.document` mirror records for quality attachments.
Silently skips if:
- the documents module isn't in the registry (defensive, the
manifest already depends on it but this module may be tested
on CE)
- the target folder hasn't been created yet
- the attachment is not attached to a quality record
"""
if 'documents.document' not in self.env:
return
folder = self.env.ref(_FOLDER_XMLID, raise_if_not_found=False)
if not folder:
_logger.warning(
"Fusion Plating Documents bridge: target folder %s not found",
_FOLDER_XMLID,
)
return
Document = self.env['documents.document'].sudo()
# Cache tag lookups across the batch so we don't hit env.ref per attachment.
tag_cache = {}
for att in attachments:
if att.res_model not in _QUALITY_MODELS_TO_TAG:
continue
# Skip attachments linked to a specific field (e.g. image_1920) —
# those are UI artefacts, not user-uploaded docs.
if att.res_field:
continue
# Skip records that have no concrete res_id (drafts being built).
if not att.res_id:
continue
tag_xmlid = _QUALITY_MODELS_TO_TAG[att.res_model]
if tag_xmlid not in tag_cache:
tag = self.env.ref(tag_xmlid, raise_if_not_found=False)
tag_cache[tag_xmlid] = tag.id if tag else False
tag_id = tag_cache[tag_xmlid]
doc_vals = {
'attachment_id': att.id,
'folder_id': folder.id,
'name': att.name or 'Untitled',
}
if tag_id:
doc_vals['tag_ids'] = [(4, tag_id)]
try:
Document.create(doc_vals)
except Exception: # pragma: no cover - defensive only
_logger.exception(
"Fusion Plating Documents bridge: could not create "
"documents.document for attachment id=%s (res_model=%s, res_id=%s)",
att.id, att.res_model, att.res_id,
)

View File

@@ -0,0 +1,8 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_documents_document_fp_operator,documents.document.fp.operator,documents.model_documents_document,fusion_plating.group_fusion_plating_operator,1,0,0,0
access_documents_document_fp_supervisor,documents.document.fp.supervisor,documents.model_documents_document,fusion_plating.group_fusion_plating_supervisor,1,1,1,0
access_documents_document_fp_manager,documents.document.fp.manager,documents.model_documents_document,fusion_plating.group_fusion_plating_manager,1,1,1,1
access_documents_tag_fp_operator,documents.tag.fp.operator,documents.model_documents_tag,fusion_plating.group_fusion_plating_operator,1,0,0,0
access_documents_tag_fp_manager,documents.tag.fp.manager,documents.model_documents_tag,fusion_plating.group_fusion_plating_manager,1,1,1,1
access_documents_facet_fp_operator,documents.facet.fp.operator,documents.model_documents_facet,fusion_plating.group_fusion_plating_operator,1,0,0,0
access_documents_facet_fp_manager,documents.facet.fp.manager,documents.model_documents_facet,fusion_plating.group_fusion_plating_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_documents_document_fp_operator documents.document.fp.operator documents.model_documents_document fusion_plating.group_fusion_plating_operator 1 0 0 0
3 access_documents_document_fp_supervisor documents.document.fp.supervisor documents.model_documents_document fusion_plating.group_fusion_plating_supervisor 1 1 1 0
4 access_documents_document_fp_manager documents.document.fp.manager documents.model_documents_document fusion_plating.group_fusion_plating_manager 1 1 1 1
5 access_documents_tag_fp_operator documents.tag.fp.operator documents.model_documents_tag fusion_plating.group_fusion_plating_operator 1 0 0 0
6 access_documents_tag_fp_manager documents.tag.fp.manager documents.model_documents_tag fusion_plating.group_fusion_plating_manager 1 1 1 1
7 access_documents_facet_fp_operator documents.facet.fp.operator documents.model_documents_facet fusion_plating.group_fusion_plating_operator 1 0 0 0
8 access_documents_facet_fp_manager documents.facet.fp.manager documents.model_documents_facet fusion_plating.group_fusion_plating_manager 1 1 1 1

View File

@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Part of the Fusion Plating product family.
The upstream fusion_plating_quality CAPA form does not ship with a
button_box. We inject one at the top of the <sheet> so our stat button
has somewhere to live. Adding via the sheet xpath keeps the core form
untouched.
-->
<odoo>
<record id="view_fp_capa_form_bridge_documents" model="ir.ui.view">
<field name="name">fp.capa.form.bridge.documents</field>
<field name="model">fusion.plating.capa</field>
<field name="inherit_id" ref="fusion_plating_quality.view_fp_capa_form"/>
<field name="arch" type="xml">
<xpath expr="//sheet/div[hasclass('oe_title')]" position="before">
<div class="oe_button_box" name="button_box">
<button name="action_view_documents" type="object"
class="oe_stat_button" icon="fa-folder-open">
<field name="x_fc_document_count" widget="statinfo" string="Documents"/>
</button>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Part of the Fusion Plating product family.
The upstream fusion_plating_quality Doc Control form does not ship
with a button_box. We inject one at the top of the <sheet> so our
stat button has somewhere to live.
-->
<odoo>
<record id="view_fp_doc_control_form_bridge_documents" model="ir.ui.view">
<field name="name">fp.doc.control.form.bridge.documents</field>
<field name="model">fusion.plating.doc.control</field>
<field name="inherit_id" ref="fusion_plating_quality.view_fp_doc_control_form"/>
<field name="arch" type="xml">
<xpath expr="//sheet/div[hasclass('oe_title')]" position="before">
<div class="oe_button_box" name="button_box">
<button name="action_view_documents" type="object"
class="oe_stat_button" icon="fa-folder-open">
<field name="x_fc_document_count" widget="statinfo" string="Documents"/>
</button>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Part of the Fusion Plating product family.
The upstream fusion_plating_quality FAIR form does not ship with a
button_box. We inject one at the top of the <sheet> so our stat button
has somewhere to live.
-->
<odoo>
<record id="view_fp_fair_form_bridge_documents" model="ir.ui.view">
<field name="name">fp.fair.form.bridge.documents</field>
<field name="model">fusion.plating.fair</field>
<field name="inherit_id" ref="fusion_plating_quality.view_fp_fair_form"/>
<field name="arch" type="xml">
<xpath expr="//sheet/div[hasclass('oe_title')]" position="before">
<div class="oe_button_box" name="button_box">
<button name="action_view_documents" type="object"
class="oe_stat_button" icon="fa-folder-open">
<field name="x_fc_document_count" widget="statinfo" string="Documents"/>
</button>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2026 Nexa Systems Inc.
License OPL-1 (Odoo Proprietary License v1.0)
Part of the Fusion Plating product family.
-->
<odoo>
<record id="view_fp_ncr_form_bridge_documents" model="ir.ui.view">
<field name="name">fp.ncr.form.bridge.documents</field>
<field name="model">fusion.plating.ncr</field>
<field name="inherit_id" ref="fusion_plating_quality.view_fp_ncr_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button name="action_view_documents" type="object"
class="oe_stat_button" icon="fa-folder-open">
<field name="x_fc_document_count" widget="statinfo" string="Documents"/>
</button>
</xpath>
</field>
</record>
</odoo>