mirror of
https://github.com/bringout/oca-technical.git
synced 2026-04-20 03:52:03 +02:00
Initial commit: OCA Technical packages (595 packages)
This commit is contained in:
commit
2cc02aac6e
24950 changed files with 2318079 additions and 0 deletions
|
|
@ -0,0 +1,8 @@
|
|||
from . import dms_field_mixin
|
||||
from . import ir_actions_act_window_view
|
||||
from . import ir_ui_view
|
||||
from . import dms_access_group
|
||||
from . import dms_storage
|
||||
from . import dms_directory
|
||||
from . import dms_field_template
|
||||
from . import res_partner
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
# Copyright 2024 Tecnativa - Víctor Martínez
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class DmsAccessGroups(models.Model):
|
||||
_inherit = "dms.access.group"
|
||||
|
||||
dms_field_ref = fields.Reference(
|
||||
selection="_selection_reference_value",
|
||||
string="DMS field reference",
|
||||
)
|
||||
company_id = fields.Many2one(
|
||||
compute="_compute_company_id",
|
||||
comodel_name="res.company",
|
||||
string="Company",
|
||||
store=True,
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _selection_reference_value(self):
|
||||
models = (
|
||||
self.env["ir.model"]
|
||||
.sudo()
|
||||
.search([("transient", "=", False)], order="name asc")
|
||||
)
|
||||
return [(model.model, model.name) for model in models]
|
||||
|
||||
@api.depends("dms_field_ref")
|
||||
def _compute_company_id(self):
|
||||
self.company_id = False
|
||||
for item in self.filtered("dms_field_ref"):
|
||||
item.company_id = (
|
||||
item.dms_field_ref.company_id
|
||||
if "company_id" in item.dms_field_ref._fields
|
||||
else False
|
||||
)
|
||||
|
||||
def _get_item_from_dms_field_ref(self, record):
|
||||
return self.env["dms.access.group"].search(
|
||||
[("dms_field_ref", "=", "%s,%s" % (record._name, record.id))]
|
||||
)
|
||||
|
||||
@api.constrains("dms_field_ref")
|
||||
def _check_dms_field_ref(self):
|
||||
for item in self.filtered("dms_field_ref"):
|
||||
dms_field_ref = "%s,%s" % (item.dms_field_ref._name, item.dms_field_ref.id)
|
||||
if self.search(
|
||||
[("dms_field_ref", "=", dms_field_ref), ("id", "!=", item.id)]
|
||||
):
|
||||
raise UserError(
|
||||
_("There is already an access group created for this record.")
|
||||
)
|
||||
|
|
@ -0,0 +1,166 @@
|
|||
# Copyright 2020 Creu Blanca
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.osv import expression
|
||||
|
||||
|
||||
class DmsDirectory(models.Model):
|
||||
_inherit = "dms.directory"
|
||||
|
||||
parent_id = fields.Many2one(default=lambda self: self._default_parent())
|
||||
|
||||
@api.model
|
||||
def _default_parent(self):
|
||||
return self.env.context.get("default_parent_directory_id", False)
|
||||
|
||||
@api.constrains("res_id", "is_root_directory", "storage_id", "res_model")
|
||||
def _check_resource(self):
|
||||
for directory in self:
|
||||
if directory.storage_id.save_type == "attachment":
|
||||
continue
|
||||
if (
|
||||
directory.is_root_directory
|
||||
and directory.storage_id.model_ids
|
||||
and not directory.res_id
|
||||
):
|
||||
raise ValidationError(
|
||||
_("Directories of this storage must be related to a record")
|
||||
)
|
||||
if not directory.res_id:
|
||||
continue
|
||||
if self.search(
|
||||
[
|
||||
("storage_id", "=", directory.storage_id.id),
|
||||
("id", "!=", directory.id),
|
||||
("res_id", "=", directory.res_id),
|
||||
("res_model", "=", directory.res_model),
|
||||
],
|
||||
limit=1,
|
||||
):
|
||||
raise ValidationError(
|
||||
_("This record is already related in this storage")
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _build_documents_view_directory(self, directory):
|
||||
return {
|
||||
"id": "directory_%s" % directory.id,
|
||||
"text": directory.name,
|
||||
"icon": "fa fa-folder-o",
|
||||
"type": "directory",
|
||||
"data": {"odoo_id": directory.id, "odoo_model": "dms.directory"},
|
||||
"children": directory.count_elements > 0,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def _check_parent_field(self):
|
||||
if self._parent_name not in self._fields:
|
||||
raise TypeError("The parent (%s) field does not exist." % self._parent_name)
|
||||
|
||||
@api.model
|
||||
def search_read_parents(
|
||||
self, domain=False, fields=None, offset=0, limit=None, order=None
|
||||
):
|
||||
"""This method finds the top level elements of the hierarchy
|
||||
for a given search query.
|
||||
|
||||
:param domain: a search domain <reference/orm/domains> (default: empty list)
|
||||
:param fields: a list of fields to read (default: all fields of the model)
|
||||
:param offset: the number of results to ignore (default: none)
|
||||
:param limit: maximum number of records to return (default: all)
|
||||
:param order: a string to define the sort order of the query
|
||||
(default: none)
|
||||
:returns: the top level elements for the given search query
|
||||
"""
|
||||
if not domain:
|
||||
domain = []
|
||||
records = self.search_parents(
|
||||
domain=domain, offset=offset, limit=limit, order=order
|
||||
)
|
||||
if not records:
|
||||
return []
|
||||
if fields and fields == ["id"]:
|
||||
return [{"id": record.id} for record in records]
|
||||
result = records.read(fields)
|
||||
if len(result) <= 1:
|
||||
return result
|
||||
index = {vals["id"]: vals for vals in result}
|
||||
return [index[record.id] for record in records if record.id in index]
|
||||
|
||||
@api.model
|
||||
def search_parents(
|
||||
self, domain=False, offset=0, limit=None, order=None, count=False
|
||||
):
|
||||
"""This method finds the top level elements of the
|
||||
hierarchy for a given search query.
|
||||
|
||||
:param domain: a search domain <reference/orm/domains> (default: empty list)
|
||||
:param offset: the number of results to ignore (default: none)
|
||||
:param limit: maximum number of records to return (default: all)
|
||||
:param order: a string to define the sort order of the query
|
||||
(default: none)
|
||||
:param count: counts and returns the number of matching records
|
||||
(default: False)
|
||||
:returns: the top level elements for the given search query
|
||||
"""
|
||||
if not domain:
|
||||
domain = []
|
||||
res = self._search_parents(
|
||||
domain=domain, offset=offset, limit=limit, order=order, count=count
|
||||
)
|
||||
return res if count else self.browse(res)
|
||||
|
||||
@api.model
|
||||
def _search_parents(
|
||||
self, domain=False, offset=0, limit=None, order=None, count=False
|
||||
):
|
||||
if not domain:
|
||||
domain = []
|
||||
self._check_parent_field()
|
||||
self.check_access_rights("read")
|
||||
if expression.is_false(self, domain):
|
||||
return []
|
||||
query = self._where_calc(domain)
|
||||
self._apply_ir_rules(query, "read")
|
||||
from_clause, where_clause, where_clause_arguments = query.get_sql()
|
||||
parent_where = where_clause and (" WHERE %s" % where_clause) or ""
|
||||
parent_query = 'SELECT "%s".id FROM ' % self._table + from_clause + parent_where
|
||||
no_parent_clause = '"{table}"."{field}" IS NULL'.format(
|
||||
table=self._table, field=self._parent_name
|
||||
)
|
||||
no_access_clause = '"{table}"."{field}" NOT IN ({query})'.format(
|
||||
table=self._table, field=self._parent_name, query=parent_query
|
||||
)
|
||||
parent_clause = "({} OR {})".format(no_parent_clause, no_access_clause)
|
||||
order_by = self._generate_order_by(order, query)
|
||||
from_clause, where_clause, where_clause_params = query.get_sql()
|
||||
where_str = (
|
||||
where_clause
|
||||
and (" WHERE {} AND {}".format(where_clause, parent_clause))
|
||||
or (" WHERE %s" % parent_clause)
|
||||
)
|
||||
if count:
|
||||
# pylint: disable=sql-injection
|
||||
query_str = "SELECT count(1) FROM " + from_clause + where_str
|
||||
self._cr.execute(query_str, where_clause_params)
|
||||
return self._cr.fetchone()[0]
|
||||
limit_str = limit and " limit %s" or ""
|
||||
offset_str = offset and " offset %s" or ""
|
||||
query_str = (
|
||||
'SELECT "%s".id FROM ' % (self._table)
|
||||
+ from_clause
|
||||
+ where_str
|
||||
+ order_by
|
||||
+ limit_str
|
||||
+ offset_str
|
||||
)
|
||||
complete_where_clause_params = where_clause_params + where_clause_arguments
|
||||
if limit:
|
||||
complete_where_clause_params.append(limit)
|
||||
if offset:
|
||||
complete_where_clause_params.append(offset)
|
||||
# pylint: disable=sql-injection
|
||||
self._cr.execute(query_str, complete_where_clause_params)
|
||||
return list({x[0] for x in self._cr.fetchall()})
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
# Copyright 2020 Creu Blanca
|
||||
# Copyright 2024 Tecnativa - Víctor Martínez
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools import config
|
||||
|
||||
|
||||
class DMSFieldMixin(models.AbstractModel):
|
||||
_name = "dms.field.mixin"
|
||||
_description = "Mixin to use DMS Field"
|
||||
|
||||
dms_directory_ids = fields.One2many(
|
||||
"dms.directory",
|
||||
"res_id",
|
||||
string="DMS Directories",
|
||||
domain=lambda self: [
|
||||
("res_model", "=", self._name),
|
||||
("storage_id.save_type", "!=", "attachment"),
|
||||
],
|
||||
auto_join=True,
|
||||
)
|
||||
|
||||
@api.model
|
||||
def models_to_track_dms_field_template(self):
|
||||
"""Models to be tracked for dms field templates
|
||||
:args:
|
||||
:returns: list of models
|
||||
"""
|
||||
return self.env["dms.field.template"].sudo().search([]).mapped("model_id.model")
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
"""Create a dms directory when creating the record if exist a template.
|
||||
We need to avoid applying a template except when testing functionality
|
||||
with dms_field* modules to avoid the error that a directory with the same
|
||||
name already exists (example: create partner).
|
||||
"""
|
||||
result = super().create(vals_list)
|
||||
test_condition = not config["test_enable"] or self.env.context.get(
|
||||
"test_dms_field"
|
||||
)
|
||||
if (
|
||||
test_condition
|
||||
and not self.env.context.get("skip_track_dms_field_template")
|
||||
and self._name in self.models_to_track_dms_field_template()
|
||||
):
|
||||
template = self.env["dms.field.template"].with_context(res_model=self._name)
|
||||
for item in result:
|
||||
template.with_context(res_id=item.id).create_dms_directory()
|
||||
return result
|
||||
|
||||
def write(self, vals):
|
||||
"""When modifying a record that has linked directories and changing the
|
||||
user_id field it is necessary to update the auto-generated access group
|
||||
(name and explicit_user_ids).
|
||||
"""
|
||||
res = super().write(vals)
|
||||
# Apply sudo() in case the user does not have access to the directory
|
||||
for item in self.sudo().filtered("dms_directory_ids"):
|
||||
if "user_id" in vals:
|
||||
template = self.env["dms.field.template"]._get_template_from_model(
|
||||
item._name
|
||||
)
|
||||
if template:
|
||||
template.sudo()._get_autogenerated_group(item)
|
||||
return res
|
||||
|
||||
def unlink(self):
|
||||
"""When deleting a record, we also delete the linked directories and the
|
||||
auto-generated access group.
|
||||
"""
|
||||
# Apply sudo() in case the user does not have access to the directory
|
||||
for record in self.sudo().filtered("dms_directory_ids"):
|
||||
group = (
|
||||
self.env["dms.access.group"].sudo()._get_item_from_dms_field_ref(record)
|
||||
)
|
||||
record.sudo().dms_directory_ids.unlink()
|
||||
group.unlink()
|
||||
return super().unlink()
|
||||
|
|
@ -0,0 +1,202 @@
|
|||
# Copyright 2024 Tecnativa - Víctor Martínez
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
|
||||
|
||||
class DmsFieldTemplate(models.Model):
|
||||
_name = "dms.field.template"
|
||||
_inherit = "dms.field.mixin"
|
||||
_description = "Dms Field Template"
|
||||
|
||||
name = fields.Char(required=True)
|
||||
company_id = fields.Many2one(
|
||||
comodel_name="res.company",
|
||||
string="Company",
|
||||
store=True,
|
||||
index=True,
|
||||
)
|
||||
storage_id = fields.Many2one(
|
||||
comodel_name="dms.storage",
|
||||
domain=[("save_type", "!=", "attachment")],
|
||||
string="Storage",
|
||||
)
|
||||
parent_directory_id = fields.Many2one(
|
||||
comodel_name="dms.directory",
|
||||
domain="[('storage_id', '=', storage_id)]",
|
||||
string="Parent directory",
|
||||
)
|
||||
model_id = fields.Many2one(
|
||||
comodel_name="ir.model",
|
||||
string="Model",
|
||||
domain=[("transient", "=", False), ("model", "!=", "dms.field.template")],
|
||||
index=True,
|
||||
)
|
||||
model = fields.Char(
|
||||
compute="_compute_model", compute_sudo=True, store=True, string="Model name"
|
||||
)
|
||||
group_ids = fields.Many2many(
|
||||
comodel_name="dms.access.group",
|
||||
string="Groups",
|
||||
)
|
||||
user_field_id = fields.Many2one(
|
||||
comodel_name="ir.model.fields",
|
||||
domain="[('model_id', '=', model_id),('relation', '=', 'res.users')]",
|
||||
string="User field",
|
||||
)
|
||||
directory_format_name = fields.Char(
|
||||
string="Directory format name",
|
||||
default="{{object.display_name}}",
|
||||
help="""You can set expressions to be used for the directory name,
|
||||
e.g.: {{object.name}}""",
|
||||
)
|
||||
|
||||
@api.depends("model_id")
|
||||
def _compute_model(self):
|
||||
for item in self:
|
||||
item.model = item.model_id.model
|
||||
|
||||
def _get_template_from_model(self, model):
|
||||
return self.search([("model", "=", model)], limit=1)
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
"""Create dms directory automatically in the creation in install mode."""
|
||||
result = super().create(vals_list)
|
||||
if self.env.context.get("install_mode"):
|
||||
for item in result:
|
||||
item_ctx = item.with_context(res_model=item._name, res_id=item.id)
|
||||
item_ctx.create_dms_directory()
|
||||
return result
|
||||
|
||||
@api.model
|
||||
def create_dms_directory(self):
|
||||
"""According to the model, create the directory linked to that record
|
||||
and the subdirectories."""
|
||||
res_model = self.env.context.get("res_model")
|
||||
res_id = self.env.context.get("res_id")
|
||||
record = self.env[res_model].browse(res_id)
|
||||
directory_model = self.env["dms.directory"].sudo()
|
||||
if res_model == "dms.field.template":
|
||||
return directory_model.create(
|
||||
{
|
||||
"storage_id": record.storage_id.id,
|
||||
"res_id": record.id,
|
||||
"res_model": record._name,
|
||||
"is_root_directory": True,
|
||||
"name": record.display_name,
|
||||
"group_ids": record.group_ids.ids,
|
||||
}
|
||||
)
|
||||
template = self._get_template_from_model(res_model).sudo()
|
||||
if not template:
|
||||
raise ValidationError(_("There is no template linked to this model"))
|
||||
total_directories = directory_model.search_count(
|
||||
[
|
||||
("parent_id", "=", self.parent_directory_id.id),
|
||||
("res_model", "=", res_model),
|
||||
("res_id", "=", res_id),
|
||||
]
|
||||
)
|
||||
if total_directories > 0:
|
||||
raise ValidationError(_("There is already a linked directory created."))
|
||||
# Create root directory + files
|
||||
dms_directory_ids = template.dms_directory_ids
|
||||
new_directory = directory_model.create(
|
||||
template._prepare_directory_vals(dms_directory_ids, record)
|
||||
)
|
||||
self._copy_files_from_directory(dms_directory_ids, new_directory)
|
||||
# Create child directories
|
||||
self._create_child_directories(new_directory, dms_directory_ids)
|
||||
return new_directory
|
||||
|
||||
def _copy_files_from_directory(self, directory, new_directory):
|
||||
for file in directory.file_ids:
|
||||
file.copy({"directory_id": new_directory.id})
|
||||
|
||||
def _prepare_autogenerated_group(self, record):
|
||||
group_name = _("Autogenerated group from %(model)s (%(name)s) #%(id)s") % {
|
||||
"model": record._description,
|
||||
"name": record.display_name,
|
||||
"id": record.id,
|
||||
}
|
||||
vals = {
|
||||
"name": group_name,
|
||||
# We need to set all the permissions so that the user can manage their
|
||||
# documents (directories and files)
|
||||
"perm_create": True,
|
||||
"perm_write": True,
|
||||
"perm_unlink": True,
|
||||
"dms_field_ref": "%s,%s" % (record._name, record.id),
|
||||
"explicit_user_ids": [(5, 0)],
|
||||
}
|
||||
# Apply sudo() because the user may not have permissions to access
|
||||
# ir.model.fields.
|
||||
user_field = self.sudo().user_field_id
|
||||
if user_field:
|
||||
user = record[user_field.name]
|
||||
if user:
|
||||
vals["explicit_user_ids"] += [(4, user.id)]
|
||||
return vals
|
||||
|
||||
def _get_autogenerated_group(self, record):
|
||||
"""Get the existing auto-generated group or create a new one.
|
||||
The permissions of the auto-generated group should be changed
|
||||
to make sure you have the correct data.
|
||||
"""
|
||||
group_model = self.env["dms.access.group"]
|
||||
group_ref = group_model._get_item_from_dms_field_ref(record)
|
||||
if group_ref:
|
||||
group_ref.write(self._prepare_autogenerated_group(record))
|
||||
return group_ref
|
||||
# Create the autogenerated group linked to the record
|
||||
return group_model.create(self._prepare_autogenerated_group(record))
|
||||
|
||||
def _create_child_directories(self, parent, directory):
|
||||
# Create child directories (all leves) + files
|
||||
directory_model = self.env["dms.directory"].sudo()
|
||||
for child_directory in directory.child_directory_ids:
|
||||
child = directory_model.create(
|
||||
{
|
||||
"name": child_directory.name,
|
||||
"is_root_directory": False,
|
||||
"parent_id": parent.id,
|
||||
}
|
||||
)
|
||||
self._copy_files_from_directory(child_directory, child)
|
||||
self._create_child_directories(child, child_directory)
|
||||
|
||||
def _prepare_directory_vals(self, directory, record):
|
||||
# Groups of the new directory will be those of the template + auto-generate
|
||||
groups = directory.group_ids
|
||||
groups += self._get_autogenerated_group(record)
|
||||
directory_name = self.env["mail.render.mixin"]._render_template(
|
||||
self.directory_format_name,
|
||||
record._name,
|
||||
record.ids,
|
||||
engine="inline_template",
|
||||
)[record.id]
|
||||
vals = {
|
||||
"storage_id": directory.storage_id.id,
|
||||
"res_id": record.id,
|
||||
"res_model": record._name,
|
||||
"name": directory_name,
|
||||
"group_ids": [(4, group.id) for group in groups],
|
||||
}
|
||||
if not self.parent_directory_id:
|
||||
vals.update({"is_root_directory": True})
|
||||
else:
|
||||
vals.update(
|
||||
{"parent_id": self.parent_directory_id.id, "inherit_group_ids": False}
|
||||
)
|
||||
return vals
|
||||
|
||||
@api.constrains("model_id")
|
||||
def _check_model_id(self):
|
||||
for template in self:
|
||||
if self.env["dms.field.template"].search(
|
||||
[("model_id", "=", template.model_id.id), ("id", "!=", template.id)]
|
||||
):
|
||||
raise UserError(
|
||||
_("There is already a template created for this model.")
|
||||
)
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
# Copyright 2020 Creu Blanca
|
||||
# Copyright 2024 Tecnativa - Víctor Martínez
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
|
||||
class DmsStorage(models.Model):
|
||||
_inherit = "dms.storage"
|
||||
|
||||
field_template_ids = fields.One2many(
|
||||
comodel_name="dms.field.template",
|
||||
inverse_name="storage_id",
|
||||
string="File templated ids",
|
||||
)
|
||||
|
||||
@api.model
|
||||
def _build_documents_storage(self, storage):
|
||||
storage_directories = []
|
||||
model = self.env["dms.directory"]
|
||||
directories = model.search_parents([["storage_id", "=", storage.id]])
|
||||
for record in directories:
|
||||
storage_directories.append(model._build_documents_view_directory(record))
|
||||
return {
|
||||
"id": "storage_%s" % storage.id,
|
||||
"text": storage.name,
|
||||
"icon": "fa fa-database",
|
||||
"type": "storage",
|
||||
"data": {"odoo_id": storage.id, "odoo_model": "dms.storage"},
|
||||
"children": storage_directories,
|
||||
}
|
||||
|
||||
@api.model
|
||||
def get_js_tree_data(self):
|
||||
return [record._build_documents_storage(record) for record in self.search([])]
|
||||
|
||||
@api.constrains("model_ids", "save_type")
|
||||
def _constrain_model_ids(self):
|
||||
for storage in self:
|
||||
if storage.save_type == "attachment":
|
||||
continue
|
||||
if self.env["dms.directory"].search(
|
||||
[
|
||||
("storage_id", "=", storage.id),
|
||||
("is_root_directory", "=", True),
|
||||
(
|
||||
"res_model",
|
||||
"not in",
|
||||
storage.mapped("model_ids.model"),
|
||||
),
|
||||
]
|
||||
):
|
||||
raise ValidationError(
|
||||
_("Some directories are inconsistent with the storage models")
|
||||
)
|
||||
if storage.model_ids and self.env["dms.directory"].search(
|
||||
[
|
||||
("storage_id", "=", storage.id),
|
||||
("is_root_directory", "=", True),
|
||||
("res_model", "=", False),
|
||||
]
|
||||
):
|
||||
raise ValidationError(
|
||||
_("There are directories not associated to a record")
|
||||
)
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
# Copyright 2020 Creu Blanca
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class IrActionsActWindowView(models.Model):
|
||||
|
||||
_inherit = "ir.actions.act_window.view"
|
||||
|
||||
view_mode = fields.Selection(
|
||||
selection_add=[("dms_list", "DMS Tree")], ondelete={"dms_list": "cascade"}
|
||||
)
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
# Copyright 2020 Creu Blanca
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from odoo import _, fields, models
|
||||
|
||||
from odoo.addons.base.models.ir_ui_view import NameManager
|
||||
|
||||
|
||||
class IrUiView(models.Model):
|
||||
|
||||
_inherit = "ir.ui.view"
|
||||
|
||||
type = fields.Selection(selection_add=[("dms_list", "DMS Tree")])
|
||||
|
||||
def _postprocess_tag_dms_list(self, node, name_manager, node_info):
|
||||
parent = node.getparent()
|
||||
if parent_name := parent and parent.get("name"):
|
||||
field = name_manager.model._fields.get(parent_name)
|
||||
if field:
|
||||
model_name = field.comodel_name
|
||||
if model_name not in self.env:
|
||||
self._raise_view_error(
|
||||
_("Model not found: %(model)s", model=model_name), node
|
||||
)
|
||||
model = self.env[model_name]
|
||||
new_name_manager = NameManager(model, parent=name_manager)
|
||||
root_info = {
|
||||
"view_type": node.tag,
|
||||
"view_editable": self._editable_node(node, name_manager),
|
||||
"view_modifiers_from_model": self._modifiers_from_model(node),
|
||||
}
|
||||
new_node_info = dict(
|
||||
root_info,
|
||||
modifiers={},
|
||||
editable=self._editable_node(node, new_name_manager),
|
||||
)
|
||||
for child in node:
|
||||
self._postprocess_tag_field(child, new_name_manager, new_node_info)
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
# Copyright 2024 Tecnativa - Carlos Roca
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from odoo import models
|
||||
|
||||
|
||||
class ResPartner(models.Model):
|
||||
_name = "res.partner"
|
||||
_inherit = ["res.partner", "dms.field.mixin"]
|
||||
Loading…
Add table
Add a link
Reference in a new issue