diff --git a/project_internal_access_from_portal/README.rst b/project_internal_access_from_portal/README.rst new file mode 100644 index 0000000000..b81f16d7f7 --- /dev/null +++ b/project_internal_access_from_portal/README.rst @@ -0,0 +1,103 @@ +.. image:: https://odoo-community.org/readme-banner-image + :target: https://odoo-community.org/get-involved?utm_source=readme + :alt: Odoo Community Association + +==================================== +Internal Project Available in Portal +==================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:1d997faa0146fe9b522e84e6041808b7586a79361b9e6fd4a4d02e9682601373 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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/license-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%2Fproject-lightgray.png?logo=github + :target: https://github.com/OCA/project/tree/16.0/project_internal_access_from_portal + :alt: OCA/project +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/project-16-0/project-16-0-project_internal_access_from_portal + :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/project&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module adds an additional option to the project settings which +allows portal users to access internal projects and tasks. + +**Table of contents** + +.. contents:: + :local: + +Use Cases / Context +=================== + +Sometimes you need to provide access to a project to portal users. Even +if this project privacy is set to the "Invited internal users" + +Configuration +============= + +Go to "Project > Configuration > Projects" and open a project. In the +"Settings" tab, set "Visibility" to "Invited internal/portal users". + +Usage +===== + +When a portal user a configured project. The user can now access the +project in the portal. When a portal user a configured project task. The +user can now access the task in the portal. + +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 +------- + +* Cetmix + +Contributors +------------ + +Cetmix + +- Ivan Sokolov +- Andrei Loukachov + +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/project `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/project_internal_access_from_portal/__init__.py b/project_internal_access_from_portal/__init__.py new file mode 100644 index 0000000000..2139908b4e --- /dev/null +++ b/project_internal_access_from_portal/__init__.py @@ -0,0 +1,3 @@ +# Copyright (C) 2025 Cetmix OÜ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from . import models diff --git a/project_internal_access_from_portal/__manifest__.py b/project_internal_access_from_portal/__manifest__.py new file mode 100644 index 0000000000..bc74a60cfe --- /dev/null +++ b/project_internal_access_from_portal/__manifest__.py @@ -0,0 +1,18 @@ +# Copyright (C) 2025 Cetmix OÜ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +{ + "name": "Internal Project Available in Portal", + "version": "17.0.1.0.1", + "summary": "Show internal projects in portal", + "author": "Cetmix, Odoo Community Association (OCA)", + "license": "AGPL-3", + "category": "Project", + "website": "https://github.com/OCA/project", + "depends": ["project"], + "data": [ + "security/portal_project_rules.xml", + ], + "demo": ["demo/demo_data.xml"], + "installable": True, + "application": False, +} diff --git a/project_internal_access_from_portal/demo/demo_data.xml b/project_internal_access_from_portal/demo/demo_data.xml new file mode 100644 index 0000000000..49dea7ff94 --- /dev/null +++ b/project_internal_access_from_portal/demo/demo_data.xml @@ -0,0 +1,20 @@ + + + + Demo Internal/Portal Project + portal_internal + + + + + Demo Task 1 + + + + + + Demo Task 2 + + + + diff --git a/project_internal_access_from_portal/i18n/es.po b/project_internal_access_from_portal/i18n/es.po new file mode 100644 index 0000000000..315cf2da9a --- /dev/null +++ b/project_internal_access_from_portal/i18n/es.po @@ -0,0 +1,75 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_internal_access_from_portal +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-06-25 09:25+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.10.4\n" + +#. module: project_internal_access_from_portal +#: model:project.project,name:project_internal_access_from_portal.demo_portal_internal_project +msgid "Demo Internal/Portal Project" +msgstr "Proyecto de demostración interno/portal" + +#. module: project_internal_access_from_portal +#: model:ir.model.fields.selection,name:project_internal_access_from_portal.selection__project_project__privacy_visibility__portal_internal +msgid "Invited internal/portal users" +msgstr "Usuarios internos/portal invitados" + +#. module: project_internal_access_from_portal +#: model:ir.model.fields,help:project_internal_access_from_portal.field_project_project__privacy_visibility +msgid "" +"People to whom this project and its tasks will be visible.\n" +"\n" +"- Invited internal users: when following a project, internal users will get access to all of its tasks without distinction. Otherwise, they will only get access to the specific tasks they are following.\n" +" A user with the project > administrator access right level can still access this project and its tasks, even if they are not explicitly part of the followers.\n" +"\n" +"- All internal users: all internal users can access the project and all of its tasks without distinction.\n" +"\n" +"- Invited portal users and all internal users: all internal users can access the project and all of its tasks without distinction.\n" +"When following a project, portal users will get access to all of its tasks without distinction. Otherwise, they will only get access to the specific tasks they are following.\n" +"\n" +"When a project is shared in read-only, the portal user is redirected to their portal. They can view the tasks, but not edit them.\n" +"When a project is shared in edit, the portal user is redirected to the kanban and list views of the tasks. They can modify a selected number of fields on the tasks.\n" +"\n" +"In any case, an internal user with no project access rights can still access a task, provided that they are given the corresponding URL (and that they are part of the followers if the project is private)." +msgstr "" +"Personas a las que este proyecto y sus tareas serán visibles.\n" +"\n" +"- Usuarios internos invitados: cuando siguen un proyecto, los usuarios internos tendrán acceso a todas sus tareas sin distinción. De lo contrario, solo tendrán acceso a las tareas específicas que están siguiendo.\n" +"Un usuario con el nivel de acceso de administrador de proyectos puede seguir accediendo a este proyecto y sus tareas, incluso si no forma parte explícitamente de los seguidores.\n" +"\n" +"- Todos los usuarios internos: todos los usuarios internos pueden acceder al proyecto y a todas sus tareas sin distinción.\n" +"\n" +"- Usuarios del portal invitados y todos los usuarios internos: todos los usuarios internos pueden acceder al proyecto y a todas sus tareas sin distinción.\n" +"Cuando siguen un proyecto, los usuarios del portal tendrán acceso a todas sus tareas sin distinción. De lo contrario, solo tendrán acceso a las tareas específicas que están siguiendo.\n" +"\n" +"Cuando un proyecto se comparte en solo lectura, el usuario del portal es redirigido a su portal. Puede ver las tareas, pero no modificarlas.\n" +"Cuando un proyecto se comparte en modo edición, el usuario del portal es redirigido a las vistas kanban y lista de las tareas. Puede modificar un número seleccionado de campos en las tareas.\n" +"\n" +"En cualquier caso, un usuario interno sin derechos de acceso al proyecto aún puede acceder a una tarea, siempre que se le proporcione la URL correspondiente (y que forme parte de los seguidores si el proyecto es privado)." + +#. module: project_internal_access_from_portal +#: model:ir.model,name:project_internal_access_from_portal.model_project_project +msgid "Project" +msgstr "Proyecto" + +#. module: project_internal_access_from_portal +#: model:project.project,label_tasks:project_internal_access_from_portal.demo_portal_internal_project +msgid "Tasks" +msgstr "Tareas" + +#. module: project_internal_access_from_portal +#: model:ir.model.fields,field_description:project_internal_access_from_portal.field_project_project__privacy_visibility +msgid "Visibility" +msgstr "Visibilidad" diff --git a/project_internal_access_from_portal/i18n/it.po b/project_internal_access_from_portal/i18n/it.po new file mode 100644 index 0000000000..e746b67553 --- /dev/null +++ b/project_internal_access_from_portal/i18n/it.po @@ -0,0 +1,90 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_internal_access_from_portal +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-06-25 09:25+0000\n" +"Last-Translator: mymage \n" +"Language-Team: none\n" +"Language: it\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.10.4\n" + +#. module: project_internal_access_from_portal +#: model:project.project,name:project_internal_access_from_portal.demo_portal_internal_project +msgid "Demo Internal/Portal Project" +msgstr "Progetto demo interno/portale" + +#. module: project_internal_access_from_portal +#: model:ir.model.fields.selection,name:project_internal_access_from_portal.selection__project_project__privacy_visibility__portal_internal +msgid "Invited internal/portal users" +msgstr "Utenti interni/portale invitati" + +#. module: project_internal_access_from_portal +#: model:ir.model.fields,help:project_internal_access_from_portal.field_project_project__privacy_visibility +msgid "" +"People to whom this project and its tasks will be visible.\n" +"\n" +"- Invited internal users: when following a project, internal users will get access to all of its tasks without distinction. Otherwise, they will only get access to the specific tasks they are following.\n" +" A user with the project > administrator access right level can still access this project and its tasks, even if they are not explicitly part of the followers.\n" +"\n" +"- All internal users: all internal users can access the project and all of its tasks without distinction.\n" +"\n" +"- Invited portal users and all internal users: all internal users can access the project and all of its tasks without distinction.\n" +"When following a project, portal users will get access to all of its tasks without distinction. Otherwise, they will only get access to the specific tasks they are following.\n" +"\n" +"When a project is shared in read-only, the portal user is redirected to their portal. They can view the tasks, but not edit them.\n" +"When a project is shared in edit, the portal user is redirected to the kanban and list views of the tasks. They can modify a selected number of fields on the tasks.\n" +"\n" +"In any case, an internal user with no project access rights can still access a task, provided that they are given the corresponding URL (and that they are part of the followers if the project is private)." +msgstr "" +"Persone a cui questo progetto e le sue attività saranno visibili.\n" +"\n" +"- Utenti interni invitati: quando seguono un progetto, gli utenti interni " +"avranno accesso a tutte le sue attività indistintamente. In caso contrario, " +"avranno accesso solo alle attività specifiche che stanno seguendo.\n" +"Un utente con il livello di accesso \"progetto > amministratore\" può " +"comunque accedere a questo progetto e alle sue attività, anche se non fa " +"esplicitamente parte dei follower.\n" +"\n" +"- Tutti gli utenti interni: tutti gli utenti interni possono accedere al " +"progetto e a tutte le sue attività indistintamente.\n" +"\n" +"- Utenti del portale invitati e tutti gli utenti interni: tutti gli utenti " +"interni possono accedere al progetto e a tutte le sue attività " +"indistintamente.\n" +"Quando seguono un progetto, gli utenti del portale avranno accesso a tutte " +"le sue attività indistintamente. In caso contrario, avranno accesso solo " +"alle attività specifiche che stanno seguendo.\n" +"\n" +"Quando un progetto è condiviso in sola lettura, l'utente del portale viene " +"reindirizzato al proprio portale. Può visualizzare le attività, ma non " +"modificarle.\n" +"Quando un progetto è condiviso in modalità di modifica, l'utente del portale " +"viene reindirizzato alle viste Kanban e Elenco delle attività. Possono " +"modificare un numero selezionato di campi nelle attività.\n" +"\n" +"In ogni caso, un utente interno senza diritti di accesso al progetto può " +"comunque accedere a un'attività, a condizione che gli venga fornito l'URL " +"corrispondente (e che faccia parte dei follower se il progetto è privato)." + +#. module: project_internal_access_from_portal +#: model:ir.model,name:project_internal_access_from_portal.model_project_project +msgid "Project" +msgstr "Progetto" + +#. module: project_internal_access_from_portal +#: model:project.project,label_tasks:project_internal_access_from_portal.demo_portal_internal_project +msgid "Tasks" +msgstr "Lavori" + +#. module: project_internal_access_from_portal +#: model:ir.model.fields,field_description:project_internal_access_from_portal.field_project_project__privacy_visibility +msgid "Visibility" +msgstr "Visibilità" diff --git a/project_internal_access_from_portal/i18n/project_internal_access_from_portal.pot b/project_internal_access_from_portal/i18n/project_internal_access_from_portal.pot new file mode 100644 index 0000000000..8ad27bdd8a --- /dev/null +++ b/project_internal_access_from_portal/i18n/project_internal_access_from_portal.pot @@ -0,0 +1,58 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * project_internal_access_from_portal +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: project_internal_access_from_portal +#: model:project.project,name:project_internal_access_from_portal.demo_portal_internal_project +msgid "Demo Internal/Portal Project" +msgstr "" + +#. module: project_internal_access_from_portal +#: model:ir.model.fields.selection,name:project_internal_access_from_portal.selection__project_project__privacy_visibility__portal_internal +msgid "Invited internal/portal users" +msgstr "" + +#. module: project_internal_access_from_portal +#: model:ir.model.fields,help:project_internal_access_from_portal.field_project_project__privacy_visibility +msgid "" +"People to whom this project and its tasks will be visible.\n" +"\n" +"- Invited internal users: when following a project, internal users will get access to all of its tasks without distinction. Otherwise, they will only get access to the specific tasks they are following.\n" +" A user with the project > administrator access right level can still access this project and its tasks, even if they are not explicitly part of the followers.\n" +"\n" +"- All internal users: all internal users can access the project and all of its tasks without distinction.\n" +"\n" +"- Invited portal users and all internal users: all internal users can access the project and all of its tasks without distinction.\n" +"When following a project, portal users will get access to all of its tasks without distinction. Otherwise, they will only get access to the specific tasks they are following.\n" +"\n" +"When a project is shared in read-only, the portal user is redirected to their portal. They can view the tasks, but not edit them.\n" +"When a project is shared in edit, the portal user is redirected to the kanban and list views of the tasks. They can modify a selected number of fields on the tasks.\n" +"\n" +"In any case, an internal user with no project access rights can still access a task, provided that they are given the corresponding URL (and that they are part of the followers if the project is private)." +msgstr "" + +#. module: project_internal_access_from_portal +#: model:ir.model,name:project_internal_access_from_portal.model_project_project +msgid "Project" +msgstr "" + +#. module: project_internal_access_from_portal +#: model:project.project,label_tasks:project_internal_access_from_portal.demo_portal_internal_project +msgid "Tasks" +msgstr "" + +#. module: project_internal_access_from_portal +#: model:ir.model.fields,field_description:project_internal_access_from_portal.field_project_project__privacy_visibility +msgid "Visibility" +msgstr "" diff --git a/project_internal_access_from_portal/models/__init__.py b/project_internal_access_from_portal/models/__init__.py new file mode 100644 index 0000000000..ca0fe228cf --- /dev/null +++ b/project_internal_access_from_portal/models/__init__.py @@ -0,0 +1,3 @@ +# Copyright (C) 2025 Cetmix OÜ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from . import project_project diff --git a/project_internal_access_from_portal/models/project_project.py b/project_internal_access_from_portal/models/project_project.py new file mode 100644 index 0000000000..0eae28baad --- /dev/null +++ b/project_internal_access_from_portal/models/project_project.py @@ -0,0 +1,12 @@ +# Copyright (C) 2025 Cetmix OÜ +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class ProjectProject(models.Model): + _inherit = "project.project" + + privacy_visibility = fields.Selection( + selection_add=[("portal_internal", "Invited internal/portal users")], + ondelete={"portal_internal": "set default"}, + ) diff --git a/project_internal_access_from_portal/readme/CONFIGURE.md b/project_internal_access_from_portal/readme/CONFIGURE.md new file mode 100644 index 0000000000..93a5e94e84 --- /dev/null +++ b/project_internal_access_from_portal/readme/CONFIGURE.md @@ -0,0 +1,2 @@ +Go to "Project > Configuration > Projects" and open a project. +In the "Settings" tab, set "Visibility" to "Invited internal/portal users". diff --git a/project_internal_access_from_portal/readme/CONTEXT.md b/project_internal_access_from_portal/readme/CONTEXT.md new file mode 100644 index 0000000000..26c735d53f --- /dev/null +++ b/project_internal_access_from_portal/readme/CONTEXT.md @@ -0,0 +1 @@ +Sometimes you need to provide access to a project to portal users. Even if this project privacy is set to the "Invited internal users" diff --git a/project_internal_access_from_portal/readme/CONTRIBUTORS.md b/project_internal_access_from_portal/readme/CONTRIBUTORS.md new file mode 100644 index 0000000000..cbbe1643bf --- /dev/null +++ b/project_internal_access_from_portal/readme/CONTRIBUTORS.md @@ -0,0 +1,4 @@ +Cetmix + +- Ivan Sokolov +- Andrei Loukachov \ No newline at end of file diff --git a/project_internal_access_from_portal/readme/DESCRIPTION.md b/project_internal_access_from_portal/readme/DESCRIPTION.md new file mode 100644 index 0000000000..0a137711a6 --- /dev/null +++ b/project_internal_access_from_portal/readme/DESCRIPTION.md @@ -0,0 +1 @@ +This module adds an additional option to the project settings which allows portal users to access internal projects and tasks. diff --git a/project_internal_access_from_portal/readme/USAGE.md b/project_internal_access_from_portal/readme/USAGE.md new file mode 100644 index 0000000000..8c56e9e6ee --- /dev/null +++ b/project_internal_access_from_portal/readme/USAGE.md @@ -0,0 +1,2 @@ +When a portal user a configured project. The user can now access the project in the portal. +When a portal user a configured project task. The user can now access the task in the portal. diff --git a/project_internal_access_from_portal/security/portal_project_rules.xml b/project_internal_access_from_portal/security/portal_project_rules.xml new file mode 100644 index 0000000000..710799b320 --- /dev/null +++ b/project_internal_access_from_portal/security/portal_project_rules.xml @@ -0,0 +1,82 @@ + + + + + + + + + + + + + + Internal: Project Visibility + + + [ + '|', + ('privacy_visibility', 'not in', ['followers', 'portal_internal']), + ('message_partner_ids', 'in', [user.partner_id.id]) + ] + + + + + + + Internal: Task Visibility + + + [ + '|', + '&', + ('project_id', '!=', False), + '|', + ('project_id.privacy_visibility', 'not in', ['followers', 'portal_internal']), + ('project_id.message_partner_ids', 'in', [user.partner_id.id]), + '|', + ('message_partner_ids', 'in', [user.partner_id.id]), + ('user_ids', 'in', user.id) + ] + + + + + + + Portal: read invited internal projects + + + [ ('privacy_visibility','=','portal_internal'), + ('active', '=', True), + ('message_partner_ids','child_of',[user.partner_id.commercial_partner_id.id]) ] + + + + + + + + + + + Portal: read invited internal tasks + + + [ + ('project_id.privacy_visibility','=','portal_internal'), + ('active','=',True), + '|', + ('project_id.message_partner_ids','child_of',[user.partner_id.commercial_partner_id.id]), + ('message_partner_ids','child_of',[user.partner_id.commercial_partner_id.id]) + ] + + + + + + + + + diff --git a/project_internal_access_from_portal/static/description/icon.png b/project_internal_access_from_portal/static/description/icon.png new file mode 100644 index 0000000000..1dcc49c24f Binary files /dev/null and b/project_internal_access_from_portal/static/description/icon.png differ diff --git a/project_internal_access_from_portal/static/description/index.html b/project_internal_access_from_portal/static/description/index.html new file mode 100644 index 0000000000..520b6d137d --- /dev/null +++ b/project_internal_access_from_portal/static/description/index.html @@ -0,0 +1,451 @@ + + + + + +README.rst + + + +
+ + + +Odoo Community Association + +
+

Internal Project Available in Portal

+ +

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

+

This module adds an additional option to the project settings which +allows portal users to access internal projects and tasks.

+

Table of contents

+ +
+

Use Cases / Context

+

Sometimes you need to provide access to a project to portal users. Even +if this project privacy is set to the “Invited internal users”

+
+
+

Configuration

+

Go to “Project > Configuration > Projects” and open a project. In the +“Settings” tab, set “Visibility” to “Invited internal/portal users”.

+
+
+

Usage

+

When a portal user a configured project. The user can now access the +project in the portal. When a portal user a configured project task. The +user can now access the task in the portal.

+
+
+

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

+
    +
  • Cetmix
  • +
+
+
+

Contributors

+

Cetmix <cetmix.com>

+
    +
  • Ivan Sokolov
  • +
  • Andrei Loukachov
  • +
+
+
+

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/project project on GitHub.

+

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

+
+
+
+
+ + diff --git a/project_internal_access_from_portal/tests/__init__.py b/project_internal_access_from_portal/tests/__init__.py new file mode 100644 index 0000000000..c884409b90 --- /dev/null +++ b/project_internal_access_from_portal/tests/__init__.py @@ -0,0 +1,3 @@ +# Copyright (C) 2025 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import test_portal_internal_access diff --git a/project_internal_access_from_portal/tests/test_portal_internal_access.py b/project_internal_access_from_portal/tests/test_portal_internal_access.py new file mode 100644 index 0000000000..8804a497bf --- /dev/null +++ b/project_internal_access_from_portal/tests/test_portal_internal_access.py @@ -0,0 +1,108 @@ +# Copyright (C) 2025 Cetmix OÜ +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +# -*- coding: utf-8 -*- +from odoo.exceptions import AccessError + +from odoo.addons.project.tests.test_access_rights import TestAccessRights + + +class TestPortalInternalAccess(TestAccessRights): + """ + Tests for the `portal_internal` visibility mode: + - Portal user may read only when subscribed + - Portal user cannot write/create/unlink projects and tasks + """ + + @classmethod + def setUpClass(cls): + super(TestPortalInternalAccess, cls).setUpClass() + # Switch the demo project to the new portal_internal mode + cls.project_pigs.privacy_visibility = "portal_internal" + cls.env.flush_all() + + def test_project_no_read_without_subscription(self): + """Portal user cannot read project before subscribing""" + with self.assertRaises(AccessError): + _ = self.project_pigs.with_user(self.portal).name + + def test_project_read_with_subscription(self): + """Portal user reads project after subscribing""" + self.project_pigs.message_subscribe([self.portal.partner_id.id]) + _ = self.project_pigs.with_user(self.portal).name + + def test_project_write_unlink_forbidden(self): + """Portal user cannot write or unlink at any time""" + # write + with self.assertRaises(AccessError): + self.project_pigs.with_user(self.portal).write({"name": "New Name"}) + # unlink + self.project_pigs.message_subscribe([self.portal.partner_id.id]) + with self.assertRaises(AccessError): + self.project_pigs.with_user(self.portal).unlink() + + def test_task_no_read_without_subscription(self): + """Portal user cannot read task before subscribing""" + with self.assertRaises(AccessError): + _ = self.task.with_user(self.portal).name + + def test_task_read_with_subscription(self): + """Portal user reads task after subscribing""" + self.project_pigs.message_subscribe([self.portal.partner_id.id]) + self.task.flush_model() + _ = self.task.with_user(self.portal).name + + def test_task_write_forbidden(self): + """Portal user cannot write tasks""" + self.project_pigs.message_subscribe([self.portal.partner_id.id]) + self.task.flush_model() + with self.assertRaises(AccessError): + self.task.with_user(self.portal).write({"name": "X"}) + + def test_task_create_forbidden(self): + """Portal user cannot create tasks""" + self.project_pigs.message_subscribe([self.portal.partner_id.id]) + with self.assertRaises(AccessError): + self.env["project.task"].with_user(self.portal).create( + { + "name": "ShouldFail", + "project_id": self.project_pigs.id, + } + ) + + def test_task_unlink_forbidden(self): + """Portal user cannot unlink tasks""" + self.project_pigs.message_subscribe([self.portal.partner_id.id]) + self.task.flush_model() + with self.assertRaises(AccessError): + self.task.with_user(self.portal).unlink() + + def test_internal_user_project_no_read_without_subscription(self): + """Internal user cannot read portal_internal project without subscription""" + with self.assertRaises(AccessError): + _ = self.project_pigs.with_user(self.user).name + + def test_internal_user_project_read_with_subscription(self): + """Internal user can read portal_internal project after subscribing""" + self.project_pigs.message_subscribe([self.user.partner_id.id]) + self.env["project.project"].flush_model() + _ = self.project_pigs.with_user(self.user).name + + def test_internal_user_task_no_read_without_subscription(self): + """Internal user cannot read tasks of portal_internal project without subscription""" + with self.assertRaises(AccessError): + _ = self.task.with_user(self.user).name + + def test_internal_user_task_read_with_subscription(self): + """Internal user can read tasks of portal_internal project after subscribing""" + self.project_pigs.message_subscribe([self.user.partner_id.id]) + self.task.flush_model() + _ = self.task.with_user(self.user).name + + def test_internal_user_task_assigned_user_can_read(self): + """Internal user can read task if assigned in user_ids""" + # Unsubscribe to ensure only assignment grants access + self.project_pigs.message_unsubscribe([self.user.partner_id.id]) + # Assign user to task + self.task.write({"user_ids": [(4, self.user.id)]}) + self.task.flush_model() + _ = self.task.with_user(self.user).name