diff --git a/base_import_manager/README.rst b/base_import_manager/README.rst new file mode 100644 index 0000000..1fb5ee5 --- /dev/null +++ b/base_import_manager/README.rst @@ -0,0 +1,112 @@ +=================== +Base Import Manager +=================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:2b831fb7d42be7ffd6c57a83b6239ed1ba995848e84926554b504ffec2650348 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fserver--ux-lightgray.png?logo=github + :target: https://github.com/OCA/server-ux/tree/18.0/base_import_manager + :alt: OCA/server-ux +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/server-ux-18-0/server-ux-18-0-base_import_manager + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/server-ux&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module extends the import capability: + +1. It adds an "Import Access" permission field on model access rights + (``ir.model.access``). +2. It disables the import option in list and kanban views if the user + does not have import access for the target model. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure import access: + +1. Activate developer mode. +2. Go to **Settings > Users > Groups** and select a user group. +3. Edit the group and go to the **Access Rights** tab. +4. Uncheck the **Import Access** checkbox for the target model and save. + +Alternatively, you can manage this setting directly under **Settings > +Technical > Security > Access Rights**. + +Usage +===== + +Once import access is configured for a group: + +1. Log in as a user who belongs to a group that does not have "Import + Access" for a specific model. +2. Go to the list or kanban view for that model. +3. The "Import" option will not be displayed or accessible in the user + interface. + +Known issues / Roadmap +====================== + +- None. + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* CIT Services + +Contributors +------------ + +- `CIT-Services `__ + + - Prayag + +Maintainers +----------- + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/server-ux `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/base_import_manager/__init__.py b/base_import_manager/__init__.py new file mode 100644 index 0000000..368196f --- /dev/null +++ b/base_import_manager/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2026 CIT Services +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import models diff --git a/base_import_manager/__manifest__.py b/base_import_manager/__manifest__.py new file mode 100644 index 0000000..fff3e4e --- /dev/null +++ b/base_import_manager/__manifest__.py @@ -0,0 +1,19 @@ +# Copyright 2026 CIT Services +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Base Import Manager", + "summary": "Manage Import Access per Group", + "category": "Tools", + "version": "18.0.1.0.0", + "depends": ["base_import"], + "website": "https://github.com/OCA/server-ux", + "author": "CIT Services, Odoo Community Association (OCA)", + "data": [ + "views/ir_model_access.xml", + "views/res_groups.xml", + ], + "installable": True, + "application": False, + "license": "AGPL-3", +} diff --git a/base_import_manager/models/__init__.py b/base_import_manager/models/__init__.py new file mode 100644 index 0000000..e242576 --- /dev/null +++ b/base_import_manager/models/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2026 CIT Services +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import ir_model_access +from . import ir_ui_view diff --git a/base_import_manager/models/ir_model_access.py b/base_import_manager/models/ir_model_access.py new file mode 100644 index 0000000..b7291a0 --- /dev/null +++ b/base_import_manager/models/ir_model_access.py @@ -0,0 +1,10 @@ +# Copyright 2026 CIT Services +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class IrModelAccess(models.Model): + _inherit = "ir.model.access" + + perm_import = fields.Boolean("Import Access", default=True) diff --git a/base_import_manager/models/ir_ui_view.py b/base_import_manager/models/ir_ui_view.py new file mode 100644 index 0000000..88fda7d --- /dev/null +++ b/base_import_manager/models/ir_ui_view.py @@ -0,0 +1,43 @@ +# Copyright 2026 CIT Services +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class IrUiView(models.Model): + _inherit = "ir.ui.view" + + def _get_import_group_ids(self): + """Helper to get user group IDs for import check. + Overridden by role integration.""" + return self.env.user.groups_id.ids + + def _postprocess_access_rights(self, tree): + """Disable the import action based on the user's + effective model access rights.""" + target_model = tree.get("model_access_rights") + tree = super()._postprocess_access_rights(tree) + + if not target_model or tree.tag not in ("list", "kanban"): + return tree + + group_ids = self._get_import_group_ids() + has_import = bool( + self.env["ir.model.access"] + .sudo() + .search( + [ + ("model_id.model", "=", target_model), + ("perm_import", "=", True), + "|", + ("group_id", "=", False), + ("group_id", "in", group_ids), + ], + limit=1, + ) + ) + + if not has_import: + tree.set("import", "0") + + return tree diff --git a/base_import_manager/pyproject.toml b/base_import_manager/pyproject.toml new file mode 100644 index 0000000..4231d0c --- /dev/null +++ b/base_import_manager/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/base_import_manager/readme/CONFIGURE.md b/base_import_manager/readme/CONFIGURE.md new file mode 100644 index 0000000..443bdf7 --- /dev/null +++ b/base_import_manager/readme/CONFIGURE.md @@ -0,0 +1,8 @@ +To configure import access: + +1. Activate developer mode. +2. Go to **Settings > Users > Groups** and select a user group. +3. Edit the group and go to the **Access Rights** tab. +4. Uncheck the **Import Access** checkbox for the target model and save. + +Alternatively, you can manage this setting directly under **Settings > Technical > Security > Access Rights**. diff --git a/base_import_manager/readme/CONTRIBUTORS.md b/base_import_manager/readme/CONTRIBUTORS.md new file mode 100644 index 0000000..4b54f31 --- /dev/null +++ b/base_import_manager/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +- [CIT-Services](cit-services.eu) + - Prayag \<\> diff --git a/base_import_manager/readme/DESCRIPTION.md b/base_import_manager/readme/DESCRIPTION.md new file mode 100644 index 0000000..e89ec0b --- /dev/null +++ b/base_import_manager/readme/DESCRIPTION.md @@ -0,0 +1,4 @@ +This module extends the import capability: + +1. It adds an "Import Access" permission field on model access rights (`ir.model.access`). +2. It disables the import option in list and kanban views if the user does not have import access for the target model. diff --git a/base_import_manager/readme/ROADMAP.md b/base_import_manager/readme/ROADMAP.md new file mode 100644 index 0000000..1ac5b36 --- /dev/null +++ b/base_import_manager/readme/ROADMAP.md @@ -0,0 +1 @@ +- None. diff --git a/base_import_manager/readme/USAGE.md b/base_import_manager/readme/USAGE.md new file mode 100644 index 0000000..6a06b7c --- /dev/null +++ b/base_import_manager/readme/USAGE.md @@ -0,0 +1,6 @@ +Once import access is configured for a group: + +1. Log in as a user who belongs to a group that does not have "Import Access" for a specific model. +2. Go to the list or kanban view for that model. +3. The "Import" option will not be displayed or accessible in the user interface. + diff --git a/base_import_manager/static/description/index.html b/base_import_manager/static/description/index.html new file mode 100644 index 0000000..ff124f7 --- /dev/null +++ b/base_import_manager/static/description/index.html @@ -0,0 +1,464 @@ + + + + + +Base Import Manager + + + +
+

Base Import Manager

+ + +

Beta License: AGPL-3 OCA/server-ux Translate me on Weblate Try me on Runboat

+

This module extends the import capability:

+
    +
  1. It adds an “Import Access” permission field on model access rights +(ir.model.access).
  2. +
  3. It disables the import option in list and kanban views if the user +does not have import access for the target model.
  4. +
+

Table of contents

+ +
+

Configuration

+

To configure import access:

+
    +
  1. Activate developer mode.
  2. +
  3. Go to Settings > Users > Groups and select a user group.
  4. +
  5. Edit the group and go to the Access Rights tab.
  6. +
  7. Uncheck the Import Access checkbox for the target model and save.
  8. +
+

Alternatively, you can manage this setting directly under Settings > +Technical > Security > Access Rights.

+
+
+

Usage

+

Once import access is configured for a group:

+
    +
  1. Log in as a user who belongs to a group that does not have “Import +Access” for a specific model.
  2. +
  3. Go to the list or kanban view for that model.
  4. +
  5. The “Import” option will not be displayed or accessible in the user +interface.
  6. +
+
+
+

Known issues / Roadmap

+
    +
  • None.
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • CIT Services
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+ +Odoo Community Association + +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/server-ux project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/base_import_manager/tests/__init__.py b/base_import_manager/tests/__init__.py new file mode 100644 index 0000000..4031b17 --- /dev/null +++ b/base_import_manager/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2026 CIT Services +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_base_import_manager diff --git a/base_import_manager/tests/test_base_import_manager.py b/base_import_manager/tests/test_base_import_manager.py new file mode 100644 index 0000000..b8bb23c --- /dev/null +++ b/base_import_manager/tests/test_base_import_manager.py @@ -0,0 +1,119 @@ +# Copyright 2026 CIT Services +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from lxml import etree + +from odoo import Command +from odoo.tests.common import TransactionCase + + +class TestBaseImportManager(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + + polluted_columns = [ + ("res_partner", "autopost_bills"), + ("res_users", "notification_type"), + ] + for table, column in polluted_columns: + cls.env.cr.execute(f""" + SELECT column_name + FROM information_schema.columns + WHERE table_name='{table}' AND column_name='{column}' + """) + if cls.env.cr.fetchone(): + cls.env.cr.execute( + f"ALTER TABLE {table} ALTER COLUMN {column} DROP NOT NULL" + ) + + cls.test_group_1 = cls.env["res.groups"].create({"name": "Test Group 1"}) + cls.test_group_2 = cls.env["res.groups"].create({"name": "Test Group 2"}) + + user_vals = { + "name": "Test User", + "login": "test_user_import_1", + "groups_id": [ + Command.set([cls.test_group_1.id, cls.env.ref("base.group_user").id]) + ], + } + cls.test_user = cls.env["res.users"].create(user_vals) + + cls.model_res_partner = cls.env["ir.model"]._get_id("res.partner") + + cls.env["ir.model.access"].search( + [("model_id", "=", cls.model_res_partner)] + ).write({"perm_import": False}) + + cls.access_group_1 = cls.env["ir.model.access"].create( + { + "name": "Access Group 1", + "model_id": cls.model_res_partner, + "group_id": cls.test_group_1.id, + "perm_read": True, + "perm_import": False, + } + ) + + cls.access_group_2 = cls.env["ir.model.access"].create( + { + "name": "Access Group 2", + "model_id": cls.model_res_partner, + "group_id": cls.test_group_2.id, + "perm_read": True, + "perm_import": True, + } + ) + + def _get_tree(self, tag="list", target_model="res.partner"): + tree = etree.Element(tag) + if target_model: + tree.set("model_access_rights", target_model) + return tree + + def test_ir_model_access_default(self): + access = self.env["ir.model.access"].create( + { + "name": "Test Access Default", + "model_id": self.model_res_partner, + } + ) + self.assertTrue(access.perm_import) + + def test_ir_ui_view_no_target_model(self): + tree = self._get_tree(target_model=False) + processed_tree = ( + self.env["ir.ui.view"] + .with_user(self.test_user) + ._postprocess_access_rights(tree) + ) + self.assertNotIn("import", processed_tree.attrib) + + def test_ir_ui_view_wrong_tag(self): + tree = self._get_tree(tag="form") + processed_tree = ( + self.env["ir.ui.view"] + .with_user(self.test_user) + ._postprocess_access_rights(tree) + ) + self.assertNotIn("import", processed_tree.attrib) + + def test_ir_ui_view_no_import_rights_groups(self): + tree = self._get_tree() + processed_tree = ( + self.env["ir.ui.view"] + .with_user(self.test_user) + ._postprocess_access_rights(tree) + ) + self.assertEqual(processed_tree.get("import"), "0") + + def test_ir_ui_view_with_import_rights_groups(self): + self.test_user.write({"groups_id": [Command.link(self.test_group_2.id)]}) + tree = self._get_tree() + processed_tree = ( + self.env["ir.ui.view"] + .with_user(self.test_user) + ._postprocess_access_rights(tree) + ) + self.assertNotIn("import", processed_tree.attrib) diff --git a/base_import_manager/views/ir_model_access.xml b/base_import_manager/views/ir_model_access.xml new file mode 100644 index 0000000..526849d --- /dev/null +++ b/base_import_manager/views/ir_model_access.xml @@ -0,0 +1,33 @@ + + + + ir.model.access.list.base_import_manager + ir.model.access + + + + + + + + + ir.model.access.list.edition.base_import_manager + ir.model.access + + + + + + + + + ir.model.access.form.base_import_manager + ir.model.access + + + + + + + + diff --git a/base_import_manager/views/res_groups.xml b/base_import_manager/views/res_groups.xml new file mode 100644 index 0000000..a3ba472 --- /dev/null +++ b/base_import_manager/views/res_groups.xml @@ -0,0 +1,16 @@ + + + + res.groups.form.base_import_manager + res.groups + + + + + + + +