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,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,
)