Move 124 sale modules to oca-sale, create oca-project with 56 project modules from oca-workflow-process

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Ernad Husremovic 2025-08-30 18:04:10 +02:00
parent 9eb7ae5807
commit 6094c218b2
2332 changed files with 125826 additions and 0 deletions

View file

@ -0,0 +1,46 @@
# Internal Project Available in Portal
Odoo addon: project_internal_access_from_portal
## Installation
```bash
pip install odoo-bringout-oca-project-project_internal_access_from_portal
```
## Dependencies
This addon depends on:
- project
## Manifest Information
- **Name**: Internal Project Available in Portal
- **Version**: 16.0.1.0.1
- **Category**: Project
- **License**: AGPL-3
- **Installable**: True
## Source
Based on [OCA/project](https://github.com/OCA/project) branch 16.0, addon `project_internal_access_from_portal`.
## License
This package maintains the original AGPL-3 license from the upstream Odoo project.
## Documentation
- Overview: doc/OVERVIEW.md
- Architecture: doc/ARCHITECTURE.md
- Models: doc/MODELS.md
- Controllers: doc/CONTROLLERS.md
- Wizards: doc/WIZARDS.md
- Reports: doc/REPORTS.md
- Security: doc/SECURITY.md
- Install: doc/INSTALL.md
- Usage: doc/USAGE.md
- Configuration: doc/CONFIGURATION.md
- Dependencies: doc/DEPENDENCIES.md
- Troubleshooting: doc/TROUBLESHOOTING.md
- FAQ: doc/FAQ.md

View file

@ -0,0 +1,32 @@
# Architecture
```mermaid
flowchart TD
U[Users] -->|HTTP| V[Views and QWeb Templates]
V --> C[Controllers]
V --> W[Wizards Transient Models]
C --> M[Models and ORM]
W --> M
M --> R[Reports]
DX[Data XML] --> M
S[Security ACLs and Groups] -. enforces .-> M
subgraph Project_internal_access_from_portal Module - project_internal_access_from_portal
direction LR
M:::layer
W:::layer
C:::layer
V:::layer
R:::layer
S:::layer
DX:::layer
end
classDef layer fill:#eef8ff,stroke:#6ea8fe,stroke-width:1px
```
Notes
- Views include tree/form/kanban templates and report templates.
- Controllers provide website/portal routes when present.
- Wizards are UI flows implemented with `models.TransientModel`.
- Data XML loads data/demo records; Security defines groups and access.

View file

@ -0,0 +1,3 @@
# Configuration
Refer to Odoo settings for project_internal_access_from_portal. Configure related models, access rights, and options as needed.

View file

@ -0,0 +1,3 @@
# Controllers
This module does not define custom HTTP controllers.

View file

@ -0,0 +1,5 @@
# Dependencies
This addon depends on:
- [project](https://github.com/bringout/oca-ocb-project/tree/5bbf7d0517a5706a48472bdf6a077a4467d11869/odoo-bringout-oca-ocb-project)

View file

@ -0,0 +1,4 @@
# FAQ
- Q: Which Odoo version? A: 16.0 (OCA/OCB packaged).
- Q: How to enable? A: Start server with --addon project_internal_access_from_portal or install in UI.

View file

@ -0,0 +1,7 @@
# Install
```bash
pip install odoo-bringout-oca-project-project_internal_access_from_portal"
# or
uv pip install odoo-bringout-oca-project-project_internal_access_from_portal"
```

View file

@ -0,0 +1,12 @@
# Models
Detected core models and extensions in project_internal_access_from_portal.
```mermaid
classDiagram
class project_project
```
Notes
- Classes show model technical names; fields omitted for brevity.
- Items listed under _inherit are extensions of existing models.

View file

@ -0,0 +1,6 @@
# Overview
Packaged Odoo addon: project_internal_access_from_portal. Provides features documented in upstream Odoo 16 under this addon.
- Source: OCA/OCB 16.0, addon project_internal_access_from_portal
- License: LGPL-3

View file

@ -0,0 +1,3 @@
# Reports
This module does not define custom reports.

View file

@ -0,0 +1,64 @@
# Security
Access control and security definitions in project_internal_access_from_portal.
## Access Control Lists (ACLs)
Model access permissions defined in:
- **[bosnian_translations.json](../bosnian_translations.json)**
- 50 model access rules
- **[bosnian_translations_output.json](../bosnian_translations_output.json)**
- 444 model access rules
- **[CHANGELOG.md](../CHANGELOG.md)**
- 132 model access rules
- **[doc](../doc)**
- **[docker](../docker)**
- **[input](../input)**
- **[nix](../nix)**
- **[odoo.conf](../odoo.conf)**
- 58 model access rules
- **[odoo_packages_bez_l10n.txt](../odoo_packages_bez_l10n.txt)**
- 1947 model access rules
- **[odoo_packages_bringout.txt](../odoo_packages_bringout.txt)**
- 1947 model access rules
- **[odoo_packages.txt](../odoo_packages.txt)**
- 2085 model access rules
- **[output](../output)**
- **[packages](../packages)**
- **[README.md](../README.md)**
- 338 model access rules
- **[scripts](../scripts)**
- **[temp](../temp)**
- **[TRANSLATION_BS_SUMMARY.md](../TRANSLATION_BS_SUMMARY.md)**
- 146 model access rules
## Record Rules
Row-level security rules defined in:
- **[portal_project_rules.xml](../project_internal_access_from_portal/security/portal_project_rules.xml)**
## Security Groups & Configuration
Security groups and permissions defined in:
- **[portal_project_rules.xml](../project_internal_access_from_portal/security/portal_project_rules.xml)**
```mermaid
graph TB
subgraph "Security Layers"
A[Users] --> B[Groups]
B --> C[Access Control Lists]
C --> D[Models]
B --> E[Record Rules]
E --> F[Individual Records]
end
```
Security files overview:
- **[portal_project_rules.xml](../project_internal_access_from_portal/security/portal_project_rules.xml)**
- Security groups, categories, and XML-based rules
Notes
- Access Control Lists define which groups can access which models
- Record Rules provide row-level security (filter records by user/group)
- Security groups organize users and define permission sets
- All security is enforced at the ORM level by Odoo

View file

@ -0,0 +1,5 @@
# Troubleshooting
- Ensure Python and Odoo environment matches repo guidance.
- Check database connectivity and logs if startup fails.
- Validate that dependent addons listed in DEPENDENCIES.md are installed.

View file

@ -0,0 +1,7 @@
# Usage
Start Odoo including this addon (from repo root):
```bash
python3 scripts/nix_odoo_web_server.py --db-name mydb --addon project_internal_access_from_portal
```

View file

@ -0,0 +1,3 @@
# Wizards
This module does not include UI wizards.

View file

@ -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 <https://github.com/OCA/project/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 <https://github.com/OCA/project/issues/new?body=module:%20project_internal_access_from_portal%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
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.
.. 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 <https://github.com/OCA/project/tree/16.0/project_internal_access_from_portal>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View file

@ -0,0 +1,3 @@
# Copyright (C) 2025 Cetmix OÜ
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from . import models

View file

@ -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": "16.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,
}

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record id="demo_portal_internal_project" model="project.project">
<field name="name">Demo Internal/Portal Project</field>
<field name="privacy_visibility">portal_internal</field>
<field name="message_partner_ids" eval="[(4, ref('base.partner_demo_portal'))]" />
</record>
<record id="demo_portal_internal_task1" model="project.task">
<field name="name">Demo Task 1</field>
<field name="project_id" ref="demo_portal_internal_project" />
<field name="message_partner_ids" eval="[(4, ref('base.partner_demo_portal'))]" />
</record>
<record id="demo_portal_internal_task2" model="project.task">
<field name="name">Demo Task 2</field>
<field name="project_id" ref="demo_portal_internal_project" />
<field name="message_partner_ids" eval="[(4, ref('base.partner_demo_portal'))]" />
</record>
</odoo>

View file

@ -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 "Demo Interni/Portal Projekat"
#. 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 "Pozvani interni/portal korisnici"
#. 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 "Projekat"
#. module: project_internal_access_from_portal
#: model:project.project,label_tasks:project_internal_access_from_portal.demo_portal_internal_project
msgid "Tasks"
msgstr "Zadaci"
#. 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 "Vidljivost"

View file

@ -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 <stefano.consolaro@mymage.it>\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à"

View file

@ -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 ""

View file

@ -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

View file

@ -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"},
)

View file

@ -0,0 +1,2 @@
Go to "Project > Configuration > Projects" and open a project.
In the "Settings" tab, set "Visibility" to "Invited internal/portal users".

View file

@ -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"

View file

@ -0,0 +1,4 @@
Cetmix <cetmix.com>
- Ivan Sokolov
- Andrei Loukachov

View file

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

View file

@ -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.

View file

@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="project.ir_rule_private_task" model="ir.rule">
<field name="active" eval="False" />
</record>
<record id="project.report_project_task_user_rule" model="ir.rule">
<field name="active" eval="False" />
</record>
<!-- Projects rules for internal users -->
<record id="project.project_public_members_rule" model="ir.rule">
<field name="name">Internal: Project Visibility</field>
<field name="model_id" ref="project.model_project_project" />
<field name="domain_force">
[
'|',
('privacy_visibility', 'not in', ['followers', 'portal_internal']),
('message_partner_ids', 'in', [user.partner_id.id])
]
</field>
<field name="groups" eval="[(4, ref('base.group_user'))]" />
</record>
<!-- Tasks rules for internal users -->
<record id="project.task_visibility_rule" model="ir.rule">
<field name="name">Internal: Task Visibility</field>
<field name="model_id" ref="project.model_project_task" />
<field name="domain_force">
[
'|',
'&amp;',
('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)
]
</field>
<field name="groups" eval="[(4, ref('base.group_user'))]" />
</record>
<!-- Portal users see projects if privacy_visibility = portal_internal AND they are followers -->
<record id="rule_portal_read_internal_projects" model="ir.rule">
<field name="name">Portal: read invited internal projects</field>
<field name="model_id" ref="project.model_project_project" />
<field name="domain_force">
[ ('privacy_visibility','=','portal_internal'),
('active', '=', True),
('message_partner_ids','child_of',[user.partner_id.commercial_partner_id.id]) ]
</field>
<field name="groups" eval="[(4, ref('base.group_portal'))]" />
<field name="perm_read" eval="True" />
<field name="perm_write" eval="False" />
<field name="perm_create" eval="False" />
<field name="perm_unlink" eval="False" />
</record>
<!-- Portal users see tasks of those internal projects only if they follow the project or the task itself -->
<record id="rule_portal_read_internal_tasks" model="ir.rule">
<field name="name">Portal: read invited internal tasks</field>
<field name="model_id" ref="project.model_project_task" />
<field name="domain_force">
[
('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])
]
</field>
<field name="groups" eval="[(4, ref('base.group_portal'))]" />
<field name="perm_read" eval="True" />
<field name="perm_write" eval="False" />
<field name="perm_create" eval="False" />
<field name="perm_unlink" eval="False" />
</record>
</odoo>

View file

@ -0,0 +1,451 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="internal-project-available-in-portal">
<h1>Internal Project Available in Portal</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:1d997faa0146fe9b522e84e6041808b7586a79361b9e6fd4a4d02e9682601373
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/license-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/project/tree/16.0/project_internal_access_from_portal"><img alt="OCA/project" src="https://img.shields.io/badge/github-OCA%2Fproject-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/project-16-0/project-16-0-project_internal_access_from_portal"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/project&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>This module adds an additional option to the project settings which
allows portal users to access internal projects and tasks.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#use-cases-context" id="toc-entry-1">Use Cases / Context</a></li>
<li><a class="reference internal" href="#configuration" id="toc-entry-2">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-3">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-4">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-5">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-6">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-7">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-8">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="use-cases-context">
<h2><a class="toc-backref" href="#toc-entry-1">Use Cases / Context</a></h2>
<p>Sometimes you need to provide access to a project to portal users. Even
if this project privacy is set to the “Invited internal users”</p>
</div>
<div class="section" id="configuration">
<h2><a class="toc-backref" href="#toc-entry-2">Configuration</a></h2>
<p>Go to “Project &gt; Configuration &gt; Projects” and open a project. In the
“Settings” tab, set “Visibility” to “Invited internal/portal users”.</p>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-3">Usage</a></h2>
<p>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.</p>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-4">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/project/issues">GitHub Issues</a>.
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
<a class="reference external" href="https://github.com/OCA/project/issues/new?body=module:%20project_internal_access_from_portal%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-5">Credits</a></h2>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-6">Authors</a></h3>
<ul class="simple">
<li>Cetmix</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-7">Contributors</a></h3>
<p>Cetmix &lt;cetmix.com&gt;</p>
<ul class="simple">
<li>Ivan Sokolov</li>
<li>Andrei Loukachov</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-8">Maintainers</a></h3>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>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.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/project/tree/16.0/project_internal_access_from_portal">OCA/project</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</div>
</body>
</html>

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,42 @@
[project]
name = "odoo-bringout-oca-project-project_internal_access_from_portal"
version = "16.0.0"
description = "Internal Project Available in Portal - Show internal projects in portal"
authors = [
{ name = "Ernad Husremovic", email = "hernad@bring.out.ba" }
]
dependencies = [
"odoo-bringout-oca-ocb-project>=16.0.0",
"requests>=2.25.1"
]
readme = "README.md"
requires-python = ">= 3.11"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Intended Audience :: Developers",
"License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Office/Business",
]
[project.urls]
homepage = "https://github.com/bringout/0"
repository = "https://github.com/bringout/0"
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[tool.hatch.metadata]
allow-direct-references = true
[tool.hatch.build.targets.wheel]
packages = ["project_internal_access_from_portal"]
[tool.rye]
managed = true
dev-dependencies = [
"pytest>=8.4.1",
]