Module
dms, dms_field
Describe the bug
While implementing a custom module that embeds dms_directory_ids on a generic model (project.project) via the standard dms.field.mixin pattern (same approach as the existing hr_dms_field/sale_dms_field submodules), we found four distinct issues in the dms_list widget and the "Embedded DMS Template" mechanism. Filing them together since they all surfaced while testing the same feature end-to-end, but each has an independent root cause.
Bug 1 — New/unsaved record with an embedded dms_list field crashes (OwlError)
Steps to reproduce (pure dms + dms_field, no custom module needed):
- Documents > Configuration > Storages > New.
- The form crashes immediately with an uncaught
OwlError:
Root cause: in dms_list_controller.esm.js, processProps() does:
storage_domain = [["id", "in", this.model.root.records.map((record) => record.resId)]];
this.model.root.records only exists on a list datapoint. On a single, unsaved record form, this.model.root is a Record datapoint with no .records property → undefined.map() throws.
The same unguarded pattern exists in the dms.field.template branch (this.model.root.data.dms_directory_ids.records.map(...)), which can equally throw if that field isn't loaded yet.
Expected behavior: opening/creating a new record with an embedded dms_list field should render an empty state, not crash.
Bug 2 — Autocompute domain doesn't filter by res_model, mixing directories across unrelated models
Steps to reproduce:
- Create a
dms.field.template for a generic model (e.g. project.project).
- Ensure a
project.project record ends up sharing the same numeric id as an existing dms.field.template record (easy to hit on a small/demo DB — e.g. both id=2).
- Open that project's form, go to the embedded
dms_list field.
- The widget shows the template's own root directory (named after the template) instead of the project's own directory.
Root cause: in buildDMSDomain(), when autocompute_directory is true (used for any generic model via the mixin), the domain only filters by res_id:
result = Domain.and([result, new Domain([["res_id", "=", this.model.root.resId]])]);
No res_model term is added. Since dms.directory.res_id is a plain Integer (paired with res_model for a polymorphic reference, not a Many2one), two records of different models sharing the same numeric id get mixed together.
Expected behavior: the autocompute domain should also filter by res_model (the real model of the record being viewed).
Bug 3 — Defining a Template's folder structure creates real, live dms.directory records instead of a pure template definition
Steps to reproduce:
- Documents > Configuration > Embedded DMS Templates > New, set Storage + Model, save.
- In the "Documents" tab, use the add-directory toolbar action to define the hierarchy (e.g. "Legal", "Technical", "Financial").
Each directory defined here is persisted as a real, standalone dms.directory row (res_model = 'dms.field.template', res_id = <template id> for the root; no res_model/res_id at all for its children — see _prepare_child_directory_vals()). As a result these show up as ordinary, "live" folders throughout the rest of Documents (General Folders menu, the Files sidebar category tree, etc.), which is confusing since they're meant to be a template blueprint, not folders anyone should browse/use directly.
We attempted a view-level mitigation (overriding the domain of action_dms_directory/action_dms_file and the directory_id searchpanel field to exclude root_directory_id.res_model = 'dms.field.template'). This works for the main "Folders" window action, but not for the hierarchical <searchpanel> category tree used in the Files view — that tree is built via a separate mechanism that ignores the field's domain attribute, so template-owned folders still show up there even with 0 files inside. We could not find a safe, view-only way to hide them from every entry point without risking hiding the same directories from the Template's own editing screen (which legitimately needs to see them).
Expected behavior: the template's folder hierarchy should be stored as lightweight template configuration, not as real dms.directory records that leak into every general-purpose folder browser in the system.
Bug 4 — A directory (or its owning Template) with no Access Group ends up invisible to every user, including admins, with zero errors or warnings
Steps to reproduce:
- Create an Embedded DMS Template, leave "Groups" empty, save.
- Click the add-root-directory action.
- The directory is created successfully in the database (confirmed via direct SQL — correct
res_id/res_model/storage_id), but it never appears anywhere in the UI — not in the template's own embedded view, not in Folders/Files, not even after a hard page reload — for any user, including a database administrator.
Root cause: dms.security.mixin._get_access_groups_query() grants access to a directory exclusively through membership in a dms.access.group linked to it (dms_directory_complete_groups_rel). With zero groups linked, that subquery is always empty, so the resulting access domain matches nothing for anyone. There is no "no groups = open to everyone" fallback, and no validation preventing you from creating/using a template without any group — the record is created silently and just never shows up anywhere, with no error message pointing at the actual cause.
Note: this specific risk is limited to the Template's own root directory. Directories created for the records that use the template (e.g. an actual project.project) are safe by comparison, since _prepare_directory_vals() always adds an auto-generated per-record group via _get_autogenerated_group() — but the Template's own directory-creation branch (res_model == "dms.field.template") does not have this safety net.
Expected behavior: either (a) creating a directory/template with no Access Group should be blocked with a clear validation error, or (b) directories with no group should fall back to some sane default visibility, so this doesn't fail completely silently.
Affected versions
18.0 (branch 18.0, tested 2026-07)
Additional context
We worked around Bugs 1 and 2 locally with a patch()-based JS override (no core files modified) adding defensive guards before every .map() call in processProps(), and an explicit res_model term in buildDMSDomain(). For Bug 4, we added group_ids = fields.Many2many(required=True) as a field-level override on dms.field.template, which is simple and doesn't touch the widget's save cycle. Bug 3 we left unresolved beyond a partial, view-only cosmetic mitigation, since a proper fix would require changes to how template structures are stored. Happy to share the patches or open a PR if useful.
Module
dms, dms_field
Describe the bug
While implementing a custom module that embeds
dms_directory_idson a generic model (project.project) via the standarddms.field.mixinpattern (same approach as the existinghr_dms_field/sale_dms_fieldsubmodules), we found four distinct issues in thedms_listwidget and the "Embedded DMS Template" mechanism. Filing them together since they all surfaced while testing the same feature end-to-end, but each has an independent root cause.Bug 1 — New/unsaved record with an embedded
dms_listfield crashes (OwlError)Steps to reproduce (pure
dms+dms_field, no custom module needed):OwlError:Root cause: in
dms_list_controller.esm.js,processProps()does:this.model.root.recordsonly exists on a list datapoint. On a single, unsaved record form,this.model.rootis a Record datapoint with no.recordsproperty →undefined.map()throws.The same unguarded pattern exists in the
dms.field.templatebranch (this.model.root.data.dms_directory_ids.records.map(...)), which can equally throw if that field isn't loaded yet.Expected behavior: opening/creating a new record with an embedded
dms_listfield should render an empty state, not crash.Bug 2 — Autocompute domain doesn't filter by
res_model, mixing directories across unrelated modelsSteps to reproduce:
dms.field.templatefor a generic model (e.g.project.project).project.projectrecord ends up sharing the same numericidas an existingdms.field.templaterecord (easy to hit on a small/demo DB — e.g. bothid=2).dms_listfield.Root cause: in
buildDMSDomain(), whenautocompute_directoryistrue(used for any generic model via the mixin), the domain only filters byres_id:No
res_modelterm is added. Sincedms.directory.res_idis a plain Integer (paired withres_modelfor a polymorphic reference, not aMany2one), two records of different models sharing the same numeric id get mixed together.Expected behavior: the autocompute domain should also filter by
res_model(the real model of the record being viewed).Bug 3 — Defining a Template's folder structure creates real, live
dms.directoryrecords instead of a pure template definitionSteps to reproduce:
Each directory defined here is persisted as a real, standalone
dms.directoryrow (res_model = 'dms.field.template',res_id = <template id>for the root; nores_model/res_idat all for its children — see_prepare_child_directory_vals()). As a result these show up as ordinary, "live" folders throughout the rest of Documents (General Folders menu, the Files sidebar category tree, etc.), which is confusing since they're meant to be a template blueprint, not folders anyone should browse/use directly.We attempted a view-level mitigation (overriding the domain of
action_dms_directory/action_dms_fileand thedirectory_idsearchpanel field to excluderoot_directory_id.res_model = 'dms.field.template'). This works for the main "Folders" window action, but not for the hierarchical<searchpanel>category tree used in the Files view — that tree is built via a separate mechanism that ignores the field'sdomainattribute, so template-owned folders still show up there even with 0 files inside. We could not find a safe, view-only way to hide them from every entry point without risking hiding the same directories from the Template's own editing screen (which legitimately needs to see them).Expected behavior: the template's folder hierarchy should be stored as lightweight template configuration, not as real
dms.directoryrecords that leak into every general-purpose folder browser in the system.Bug 4 — A directory (or its owning Template) with no Access Group ends up invisible to every user, including admins, with zero errors or warnings
Steps to reproduce:
res_id/res_model/storage_id), but it never appears anywhere in the UI — not in the template's own embedded view, not in Folders/Files, not even after a hard page reload — for any user, including a database administrator.Root cause:
dms.security.mixin._get_access_groups_query()grants access to a directory exclusively through membership in adms.access.grouplinked to it (dms_directory_complete_groups_rel). With zero groups linked, that subquery is always empty, so the resulting access domain matches nothing for anyone. There is no "no groups = open to everyone" fallback, and no validation preventing you from creating/using a template without any group — the record is created silently and just never shows up anywhere, with no error message pointing at the actual cause.Note: this specific risk is limited to the Template's own root directory. Directories created for the records that use the template (e.g. an actual
project.project) are safe by comparison, since_prepare_directory_vals()always adds an auto-generated per-record group via_get_autogenerated_group()— but the Template's own directory-creation branch (res_model == "dms.field.template") does not have this safety net.Expected behavior: either (a) creating a directory/template with no Access Group should be blocked with a clear validation error, or (b) directories with no group should fall back to some sane default visibility, so this doesn't fail completely silently.
Affected versions
18.0 (branch
18.0, tested 2026-07)Additional context
We worked around Bugs 1 and 2 locally with a
patch()-based JS override (no core files modified) adding defensive guards before every.map()call inprocessProps(), and an explicitres_modelterm inbuildDMSDomain(). For Bug 4, we addedgroup_ids = fields.Many2many(required=True)as a field-level override ondms.field.template, which is simple and doesn't touch the widget's save cycle. Bug 3 we left unresolved beyond a partial, view-only cosmetic mitigation, since a proper fix would require changes to how template structures are stored. Happy to share the patches or open a PR if useful.