[ADD] document_page_project_task: add new module

This commit is contained in:
Marcel Savegnago
2025-11-06 19:06:29 -03:00
parent 97e75cd3c0
commit c6b64112a1
19 changed files with 1437 additions and 0 deletions

View File

@@ -0,0 +1,267 @@
==========================
Document Page Project Task
==========================
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:792f27beb9e30e02336420e9ffafb95a1a3027dc4b2ff368b4d350c683de3b01
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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%2Fknowledge-lightgray.png?logo=github
:target: https://github.com/OCA/knowledge/tree/16.0/document_page_project_task
:alt: OCA/knowledge
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/knowledge-16-0/knowledge-16-0-document_page_project_task
: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/knowledge&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
This module extends the document page (wiki) functionality by allowing
you to link them directly to project tasks.
Main Features
-------------
- **Link Wiki Pages to Tasks**: Allows associating document pages to
specific project tasks
- **Automatic Project Filling**: When a task is selected, the related
project is automatically filled
- **Consistency Validation**: Ensures that the wiki page's project is
always the same as the linked task's project
- **Smart Filtering**: When a project is defined, only tasks from that
project are displayed for selection
- **Page Counter**: Displays the number of wiki pages linked to each
task directly in the task view
Benefits
--------
- Organize project documentation hierarchically (Project → Task → Wiki)
- Keep documentation close to the work context (tasks)
- Avoid inconsistencies between projects and tasks through automatic
validations
- Quickly access documentation related to a specific task
Dependencies
------------
This module requires:
- ``document_page_project``: Module that links document pages to
projects
- ``project``: Odoo's project management module
**Table of contents**
.. contents::
:local:
Configuration
=============
This module does not require additional configuration after
installation. It works automatically once installed.
Installation
------------
1. Go to the **Apps** menu
2. Remove the "Apps" filter if necessary
3. Search for "Document Page Project Task"
4. Click **Install**
Prerequisites
-------------
Make sure the following modules are installed:
- **Project** (base project module)
- **Document Page Project** (links wiki pages to projects)
The system will automatically install the necessary dependencies during
installation.
Permissions
-----------
The module uses the same access permissions as the base modules:
- Users with access to **Projects** can view and create wiki pages
linked to tasks
- Users with access to **Documents/Knowledge** can manage wiki page
content
No additional permission configuration is required.
Usage
=====
This guide explains how to use the Document Page Project Task module to
link wiki pages to project tasks.
Create a Wiki Page from a Task
------------------------------
**Method 1: From the Task**
1. Go to the **Projects** module
2. Open the desired project
3. Select a task
4. In the task view, locate the **Wiki Pages** button (book icon)
5. Click the button to see linked pages or create a new one
6. Click **Create** to add a new wiki page
7. The task and project will be automatically filled
**Method 2: From the Wiki Page**
1. Go to the **Knowledge** or **Documents** module
2. Create a new wiki page or edit an existing one
3. In the page form, you will see the fields:
- **Project**: Select the project
- **Task**: Select the task (only tasks from the selected project
will be displayed)
4. When you select a task, the project will be automatically filled
5. Save the page
Automatic Behaviors
-------------------
**Automatic Project Filling**
When you select a task:
- The **Project** field is automatically filled with the task's project
- This ensures consistency between task and project
**Task Filtering**
When a project is selected:
- Only tasks from that project appear in the selection list
- This prevents selecting tasks from different projects
**Consistency Validation**
The system automatically validates that:
- If a task is linked, the project must also be defined
- The wiki page's project must be the same as the linked task's project
- If you try to link a task to a different project, the system will
prevent the operation
**Automatic Cleanup**
When you change the project:
- If the linked task does not belong to the new project, it is
automatically removed
- This maintains data consistency
View Wiki Pages of a Task
-------------------------
1. Access a project task
2. At the top of the form, you will see the **Wiki Pages** button with a
counter
3. The displayed number indicates how many wiki pages are linked to the
task
4. Click the button to see all linked pages
Usage Examples
--------------
**Example 1: Requirements Documentation**
1. Create a task "Define System Requirements"
2. From the task, create a wiki page "Functional Requirements"
3. Document the requirements in the wiki page
4. The page will be linked to the task and project
**Example 2: Meeting Notes**
1. Create a task "Planning Meeting"
2. Create a wiki page "Meeting Minutes"
3. Document the discussed points
4. The documentation will be organized and easy to find
**Example 3: Technical Specifications**
1. Create a task "Develop Module X"
2. Create a wiki page "Technical Specification"
3. Document the architecture and technical decisions
4. Keep the documentation close to the task work
Tips
----
- Use wiki pages to maintain contextual documentation related to
specific tasks
- The page counter on the task helps quickly identify tasks with
documentation
- When creating a page from the task, fields are automatically filled,
saving time
- Organize project documentation hierarchically: Project → Task → Wiki
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/knowledge/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/knowledge/issues/new?body=module:%20document_page_project_task%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
-------
* Escodoo
Contributors
------------
- `ESCODOO <https://escodoo.com.br>`__:
- Marcel Savegnago <marcel.savegnago@escodoo.com.br>
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.
.. |maintainer-marcelsavegnago| image:: https://github.com/marcelsavegnago.png?size=40px
:target: https://github.com/marcelsavegnago
:alt: marcelsavegnago
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-marcelsavegnago|
This module is part of the `OCA/knowledge <https://github.com/OCA/knowledge/tree/16.0/document_page_project_task>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1 @@
from . import models

View File

@@ -0,0 +1,16 @@
# Copyright 2025 Escodoo <https://escodoo.com.br>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
{
"name": "Document Page Project Task",
"summary": "This module links document pages to project tasks",
"version": "16.0.1.0.0",
"category": "Project",
"author": "Escodoo, Odoo Community Association (OCA)",
"maintainers": ["marcelsavegnago"],
"website": "https://github.com/OCA/knowledge",
"license": "AGPL-3",
"depends": ["document_page_project"],
"data": ["views/document_page_views.xml", "views/project_task_views.xml"],
"installable": True,
}

View File

@@ -0,0 +1,2 @@
from . import document_page
from . import project_task

View File

@@ -0,0 +1,67 @@
# Copyright 2025 Marcel Savegnago - Escodoo <https://escodoo.com.br>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class DocumentPage(models.Model):
_inherit = "document.page"
task_id = fields.Many2one(string="Task", comodel_name="project.task")
@api.onchange("task_id")
def _onchange_task_id(self):
"""Fill the project_id field with the task's related project."""
if self.task_id and self.task_id.project_id:
self.project_id = self.task_id.project_id
@api.onchange("project_id")
def _onchange_project_id(self):
"""Clear the task if the project is removed or changed and does not
match the task's project."""
if self.task_id:
if not self.project_id or self.task_id.project_id != self.project_id:
self.task_id = False
@api.constrains("task_id", "project_id")
def _check_task_project_consistency(self):
"""Ensure that the project is the same as the task's project when a
task is defined."""
for record in self:
# If there is a task, there must be a project
if record.task_id and not record.project_id:
raise ValidationError(
_(
"When a task is linked, the project must be defined. "
"Task '%(task)s' requires that the project be defined.",
task=record.task_id.name,
)
)
# If there is a task and project, they must be from the same project
if record.task_id and record.project_id:
if record.task_id.project_id != record.project_id:
raise ValidationError(
_(
"The wiki document's project must be the same as the "
"task's project. Task '%(task)s' belongs to project "
"'%(task_project)s', but the document is associated "
"with project '%(doc_project)s'.",
task=record.task_id.name,
task_project=record.task_id.project_id.name,
doc_project=record.project_id.name,
)
)
@api.model
def default_get(self, fields_list):
"""Fill the project_id when the wiki is created with default_task_id
in the context."""
res = super().default_get(fields_list)
if "default_task_id" in self.env.context and "project_id" in fields_list:
task = self.env["project.task"].browse(
self.env.context.get("default_task_id")
)
if task.exists() and task.project_id:
res["project_id"] = task.project_id.id
return res

View File

@@ -0,0 +1,18 @@
# Copyright 2025 Marcel Savegnago - Escodoo <https://escodoo.com.br>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import api, fields, models
class ProjectTask(models.Model):
_inherit = "project.task"
document_page_ids = fields.One2many(
string="Wiki Pages", comodel_name="document.page", inverse_name="task_id"
)
document_page_count = fields.Integer(compute="_compute_document_page_count")
@api.depends("document_page_ids")
def _compute_document_page_count(self):
for rec in self:
rec.document_page_count = len(rec.document_page_ids)

View File

@@ -0,0 +1,24 @@
This module does not require additional configuration after installation. It works automatically once installed.
## Installation
1. Go to the **Apps** menu
2. Remove the "Apps" filter if necessary
3. Search for "Document Page Project Task"
4. Click **Install**
## Prerequisites
Make sure the following modules are installed:
* **Project** (base project module)
* **Document Page Project** (links wiki pages to projects)
The system will automatically install the necessary dependencies during installation.
## Permissions
The module uses the same access permissions as the base modules:
* Users with access to **Projects** can view and create wiki pages linked to tasks
* Users with access to **Documents/Knowledge** can manage wiki page content
No additional permission configuration is required.

View File

@@ -0,0 +1,2 @@
- [ESCODOO](https://escodoo.com.br):
- Marcel Savegnago \<<marcel.savegnago@escodoo.com.br>\>

View File

@@ -0,0 +1,22 @@
This module extends the document page (wiki) functionality by allowing you to link them directly to project tasks.
## Main Features
* **Link Wiki Pages to Tasks**: Allows associating document pages to specific project tasks
* **Automatic Project Filling**: When a task is selected, the related project is automatically filled
* **Consistency Validation**: Ensures that the wiki page's project is always the same as the linked task's project
* **Smart Filtering**: When a project is defined, only tasks from that project are displayed for selection
* **Page Counter**: Displays the number of wiki pages linked to each task directly in the task view
## Benefits
* Organize project documentation hierarchically (Project → Task → Wiki)
* Keep documentation close to the work context (tasks)
* Avoid inconsistencies between projects and tasks through automatic validations
* Quickly access documentation related to a specific task
## Dependencies
This module requires:
* `document_page_project`: Module that links document pages to projects
* `project`: Odoo's project management module

View File

@@ -0,0 +1,87 @@
This guide explains how to use the Document Page Project Task module to link wiki pages to project tasks.
## Create a Wiki Page from a Task
**Method 1: From the Task**
1. Go to the **Projects** module
2. Open the desired project
3. Select a task
4. In the task view, locate the **Wiki Pages** button (book icon)
5. Click the button to see linked pages or create a new one
6. Click **Create** to add a new wiki page
7. The task and project will be automatically filled
**Method 2: From the Wiki Page**
1. Go to the **Knowledge** or **Documents** module
2. Create a new wiki page or edit an existing one
3. In the page form, you will see the fields:
* **Project**: Select the project
* **Task**: Select the task (only tasks from the selected project will be displayed)
4. When you select a task, the project will be automatically filled
5. Save the page
## Automatic Behaviors
**Automatic Project Filling**
When you select a task:
* The **Project** field is automatically filled with the task's project
* This ensures consistency between task and project
**Task Filtering**
When a project is selected:
* Only tasks from that project appear in the selection list
* This prevents selecting tasks from different projects
**Consistency Validation**
The system automatically validates that:
* If a task is linked, the project must also be defined
* The wiki page's project must be the same as the linked task's project
* If you try to link a task to a different project, the system will prevent the operation
**Automatic Cleanup**
When you change the project:
* If the linked task does not belong to the new project, it is automatically removed
* This maintains data consistency
## View Wiki Pages of a Task
1. Access a project task
2. At the top of the form, you will see the **Wiki Pages** button with a counter
3. The displayed number indicates how many wiki pages are linked to the task
4. Click the button to see all linked pages
## Usage Examples
**Example 1: Requirements Documentation**
1. Create a task "Define System Requirements"
2. From the task, create a wiki page "Functional Requirements"
3. Document the requirements in the wiki page
4. The page will be linked to the task and project
**Example 2: Meeting Notes**
1. Create a task "Planning Meeting"
2. Create a wiki page "Meeting Minutes"
3. Document the discussed points
4. The documentation will be organized and easy to find
**Example 3: Technical Specifications**
1. Create a task "Develop Module X"
2. Create a wiki page "Technical Specification"
3. Document the architecture and technical decisions
4. Keep the documentation close to the task work
## Tips
* Use wiki pages to maintain contextual documentation related to specific tasks
* The page counter on the task helps quickly identify tasks with documentation
* When creating a page from the task, fields are automatically filled, saving time
* Organize project documentation hierarchically: Project → Task → Wiki

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,600 @@
<!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>Document Page Project Task</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" id="document-page-project-task">
<h1 class="title">Document Page Project Task</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:792f27beb9e30e02336420e9ffafb95a1a3027dc4b2ff368b4d350c683de3b01
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<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/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/knowledge/tree/16.0/document_page_project_task"><img alt="OCA/knowledge" src="https://img.shields.io/badge/github-OCA%2Fknowledge-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/knowledge-16-0/knowledge-16-0-document_page_project_task"><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/knowledge&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 extends the document page (wiki) functionality by allowing
you to link them directly to project tasks.</p>
<div class="section" id="main-features">
<h1>Main Features</h1>
<ul class="simple">
<li><strong>Link Wiki Pages to Tasks</strong>: Allows associating document pages to
specific project tasks</li>
<li><strong>Automatic Project Filling</strong>: When a task is selected, the related
project is automatically filled</li>
<li><strong>Consistency Validation</strong>: Ensures that the wiki pages project is
always the same as the linked tasks project</li>
<li><strong>Smart Filtering</strong>: When a project is defined, only tasks from that
project are displayed for selection</li>
<li><strong>Page Counter</strong>: Displays the number of wiki pages linked to each
task directly in the task view</li>
</ul>
</div>
<div class="section" id="benefits">
<h1>Benefits</h1>
<ul class="simple">
<li>Organize project documentation hierarchically (Project → Task → Wiki)</li>
<li>Keep documentation close to the work context (tasks)</li>
<li>Avoid inconsistencies between projects and tasks through automatic
validations</li>
<li>Quickly access documentation related to a specific task</li>
</ul>
</div>
<div class="section" id="dependencies">
<h1>Dependencies</h1>
<p>This module requires:</p>
<ul class="simple">
<li><tt class="docutils literal">document_page_project</tt>: Module that links document pages to
projects</li>
<li><tt class="docutils literal">project</tt>: Odoos project management module</li>
</ul>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
</ul>
</div>
<div class="section" id="configuration">
<h2><a class="toc-backref" href="#toc-entry-1">Configuration</a></h2>
<p>This module does not require additional configuration after
installation. It works automatically once installed.</p>
</div>
</div>
<div class="section" id="installation">
<h1>Installation</h1>
<ol class="arabic simple">
<li>Go to the <strong>Apps</strong> menu</li>
<li>Remove the “Apps” filter if necessary</li>
<li>Search for “Document Page Project Task”</li>
<li>Click <strong>Install</strong></li>
</ol>
</div>
<div class="section" id="prerequisites">
<h1>Prerequisites</h1>
<p>Make sure the following modules are installed:</p>
<ul class="simple">
<li><strong>Project</strong> (base project module)</li>
<li><strong>Document Page Project</strong> (links wiki pages to projects)</li>
</ul>
<p>The system will automatically install the necessary dependencies during
installation.</p>
</div>
<div class="section" id="permissions">
<h1>Permissions</h1>
<p>The module uses the same access permissions as the base modules:</p>
<ul class="simple">
<li>Users with access to <strong>Projects</strong> can view and create wiki pages
linked to tasks</li>
<li>Users with access to <strong>Documents/Knowledge</strong> can manage wiki page
content</li>
</ul>
<p>No additional permission configuration is required.</p>
<div class="section" id="usage">
<h2>Usage</h2>
<p>This guide explains how to use the Document Page Project Task module to
link wiki pages to project tasks.</p>
</div>
</div>
<div class="section" id="create-a-wiki-page-from-a-task">
<h1>Create a Wiki Page from a Task</h1>
<p><strong>Method 1: From the Task</strong></p>
<ol class="arabic simple">
<li>Go to the <strong>Projects</strong> module</li>
<li>Open the desired project</li>
<li>Select a task</li>
<li>In the task view, locate the <strong>Wiki Pages</strong> button (book icon)</li>
<li>Click the button to see linked pages or create a new one</li>
<li>Click <strong>Create</strong> to add a new wiki page</li>
<li>The task and project will be automatically filled</li>
</ol>
<p><strong>Method 2: From the Wiki Page</strong></p>
<ol class="arabic simple">
<li>Go to the <strong>Knowledge</strong> or <strong>Documents</strong> module</li>
<li>Create a new wiki page or edit an existing one</li>
<li>In the page form, you will see the fields:<ul>
<li><strong>Project</strong>: Select the project</li>
<li><strong>Task</strong>: Select the task (only tasks from the selected project
will be displayed)</li>
</ul>
</li>
<li>When you select a task, the project will be automatically filled</li>
<li>Save the page</li>
</ol>
</div>
<div class="section" id="automatic-behaviors">
<h1>Automatic Behaviors</h1>
<p><strong>Automatic Project Filling</strong></p>
<p>When you select a task:</p>
<ul class="simple">
<li>The <strong>Project</strong> field is automatically filled with the tasks project</li>
<li>This ensures consistency between task and project</li>
</ul>
<p><strong>Task Filtering</strong></p>
<p>When a project is selected:</p>
<ul class="simple">
<li>Only tasks from that project appear in the selection list</li>
<li>This prevents selecting tasks from different projects</li>
</ul>
<p><strong>Consistency Validation</strong></p>
<p>The system automatically validates that:</p>
<ul class="simple">
<li>If a task is linked, the project must also be defined</li>
<li>The wiki pages project must be the same as the linked tasks project</li>
<li>If you try to link a task to a different project, the system will
prevent the operation</li>
</ul>
<p><strong>Automatic Cleanup</strong></p>
<p>When you change the project:</p>
<ul class="simple">
<li>If the linked task does not belong to the new project, it is
automatically removed</li>
<li>This maintains data consistency</li>
</ul>
</div>
<div class="section" id="view-wiki-pages-of-a-task">
<h1>View Wiki Pages of a Task</h1>
<ol class="arabic simple">
<li>Access a project task</li>
<li>At the top of the form, you will see the <strong>Wiki Pages</strong> button with a
counter</li>
<li>The displayed number indicates how many wiki pages are linked to the
task</li>
<li>Click the button to see all linked pages</li>
</ol>
</div>
<div class="section" id="usage-examples">
<h1>Usage Examples</h1>
<p><strong>Example 1: Requirements Documentation</strong></p>
<ol class="arabic simple">
<li>Create a task “Define System Requirements”</li>
<li>From the task, create a wiki page “Functional Requirements”</li>
<li>Document the requirements in the wiki page</li>
<li>The page will be linked to the task and project</li>
</ol>
<p><strong>Example 2: Meeting Notes</strong></p>
<ol class="arabic simple">
<li>Create a task “Planning Meeting”</li>
<li>Create a wiki page “Meeting Minutes”</li>
<li>Document the discussed points</li>
<li>The documentation will be organized and easy to find</li>
</ol>
<p><strong>Example 3: Technical Specifications</strong></p>
<ol class="arabic simple">
<li>Create a task “Develop Module X”</li>
<li>Create a wiki page “Technical Specification”</li>
<li>Document the architecture and technical decisions</li>
<li>Keep the documentation close to the task work</li>
</ol>
</div>
<div class="section" id="tips">
<h1>Tips</h1>
<ul class="simple">
<li>Use wiki pages to maintain contextual documentation related to
specific tasks</li>
<li>The page counter on the task helps quickly identify tasks with
documentation</li>
<li>When creating a page from the task, fields are automatically filled,
saving time</li>
<li>Organize project documentation hierarchically: Project → Task → Wiki</li>
</ul>
<div class="section" id="bug-tracker">
<h2>Bug Tracker</h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/knowledge/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/knowledge/issues/new?body=module:%20document_page_project_task%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>Credits</h2>
</div>
</div>
<div class="section" id="authors">
<h1>Authors</h1>
<ul class="simple">
<li>Escodoo</li>
</ul>
</div>
<div class="section" id="contributors">
<h1>Contributors</h1>
<ul class="simple">
<li><a class="reference external" href="https://escodoo.com.br">ESCODOO</a>:<ul>
<li>Marcel Savegnago &lt;<a class="reference external" href="mailto:marcel.savegnago&#64;escodoo.com.br">marcel.savegnago&#64;escodoo.com.br</a>&gt;</li>
</ul>
</li>
</ul>
</div>
<div class="section" id="maintainers">
<h1>Maintainers</h1>
<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>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external image-reference" href="https://github.com/marcelsavegnago"><img alt="marcelsavegnago" src="https://github.com/marcelsavegnago.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/knowledge/tree/16.0/document_page_project_task">OCA/knowledge</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>
</body>
</html>

View File

@@ -0,0 +1,2 @@
from . import test_project_task
from . import test_document_page

View File

@@ -0,0 +1,192 @@
# Copyright 2025 Marcel Savegnago - Escodoo <https://escodoo.com.br>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo.exceptions import ValidationError
from odoo.tests import common
class TestDocumentPage(common.TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.Page = cls.env["document.page"]
cls.Project = cls.env["project.project"]
cls.Task = cls.env["project.task"]
cls.project1 = cls.Project.create({"name": "Project 1"})
cls.project2 = cls.Project.create({"name": "Project 2"})
cls.task1 = cls.Task.create({"name": "Task 1", "project_id": cls.project1.id})
cls.task2 = cls.Task.create({"name": "Task 2", "project_id": cls.project2.id})
def test_onchange_task_id_fills_project(self):
"""Test that when selecting a task, the project is automatically
filled."""
page = self.Page.new({"name": "Test Page"})
page.task_id = self.task1
page._onchange_task_id()
self.assertEqual(
page.project_id,
self.project1,
"The project should be automatically filled with the task's project",
)
def test_onchange_project_id_clears_task(self):
"""Test that when changing the project to a different one, the task
is cleared."""
page = self.Page.new(
{
"name": "Test Page",
"task_id": self.task1.id,
"project_id": self.project1.id,
}
)
page.project_id = self.project2
page._onchange_project_id()
self.assertFalse(
page.task_id,
"The task should be cleared when the project changes to a different one",
)
def test_onchange_project_id_clears_task_when_removed(self):
"""Test that when removing the project, the task is cleared."""
page = self.Page.new(
{
"name": "Test Page",
"task_id": self.task1.id,
"project_id": self.project1.id,
}
)
page.project_id = False
page._onchange_project_id()
self.assertFalse(
page.task_id,
"The task should be cleared when the project is removed",
)
def test_default_get_with_task_in_context(self):
"""Test that default_get fills the project when there is default_task_id
in the context."""
context = {"default_task_id": self.task1.id}
fields_list = ["name", "project_id", "task_id"]
defaults = self.Page.with_context(**context).default_get(fields_list)
self.assertEqual(
defaults.get("project_id"),
self.project1.id,
"The project_id should be filled with the task's project in context",
)
self.assertEqual(
defaults.get("task_id"),
self.task1.id,
"The task_id should be filled with the task from context",
)
def test_constraint_task_project_consistency_valid(self):
"""Test that the constraint allows task and project from the same
project."""
page = self.Page.create(
{
"name": "Test Page",
"task_id": self.task1.id,
"project_id": self.project1.id,
}
)
# Should not raise an exception
self.assertTrue(page.exists())
def test_constraint_task_project_consistency_invalid(self):
"""Test that the constraint prevents task and project from different
projects."""
page = self.Page.create(
{
"name": "Test Page",
"task_id": self.task1.id,
"project_id": self.project1.id,
}
)
# Trying to change the project to a different one should raise
# ValidationError
with self.assertRaises(ValidationError):
page.project_id = self.project2
def test_constraint_task_project_consistency_no_task(self):
"""Test that the constraint does not prevent when there is no task."""
page = self.Page.create({"name": "Test Page", "project_id": self.project1.id})
# Should not raise an exception when there is no task
self.assertTrue(page.exists())
def test_constraint_task_project_consistency_no_project(self):
"""Test that the constraint prevents when there is a task but no
project."""
# Trying to create a page with a task but without a project should
# raise ValidationError
with self.assertRaises(ValidationError):
self.Page.create({"name": "Test Page", "task_id": self.task1.id})
def test_constraint_task_requires_project(self):
"""Test that when removing the project when there is a task, it should
raise an error."""
page = self.Page.create(
{
"name": "Test Page",
"task_id": self.task1.id,
"project_id": self.project1.id,
}
)
# Trying to remove the project when there is a task should raise
# ValidationError
with self.assertRaises(ValidationError):
page.project_id = False
def test_create_page_with_task_sets_project(self):
"""Test that when creating a page with a task, the project is set
automatically."""
# Create page with task_id and project_id defined together
# (the onchange fills the project_id when task_id is defined in the
# form)
page = self.Page.create(
{
"name": "Test Page",
"task_id": self.task1.id,
"project_id": self.project1.id,
}
)
self.assertEqual(
page.project_id,
self.project1,
"The project should be the same as the task",
)
self.assertEqual(
page.task_id,
self.task1,
"The task should be correctly associated",
)
def test_write_task_changes_project(self):
"""Test that when changing the task, the project should be updated."""
page = self.Page.create(
{
"name": "Test Page",
"task_id": self.task1.id,
"project_id": self.project1.id,
}
)
# Change the task - the project should be updated to match
page.write({"task_id": self.task2.id, "project_id": self.task2.project_id.id})
page.invalidate_recordset()
self.assertEqual(
page.project_id,
self.project2,
"The project should match the new task's project",
)

View File

@@ -0,0 +1,70 @@
# Copyright 2025 Marcel Savegnago - Escodoo <https://escodoo.com.br>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo.tests import common
class TestProjectTask(common.TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.Page = cls.env["document.page"]
cls.Project = cls.env["project.project"]
cls.Task = cls.env["project.task"]
cls.project = cls.Project.create({"name": "Test Project"})
cls.task = cls.Task.create({"name": "Test Task", "project_id": cls.project.id})
cls.default_page = cls.Page.create({"name": "My page"})
def test_page_count(self):
"""Test the page counter on the task."""
self.assertEqual(
self.task.document_page_count,
0,
"Initial page count should be zero",
)
# Set task_id and project_id together (the onchange fills project_id)
self.default_page.write(
{"task_id": self.task.id, "project_id": self.task.project_id.id}
)
self.task._compute_document_page_count()
self.assertEqual(
self.task.document_page_count,
1,
"After associating task to document, the count should be one",
)
self.assertIn(
self.default_page,
self.task.document_page_ids,
"The page should be in the list of document pages for the task",
)
def test_page_count_multiple_pages(self):
"""Test the counter with multiple pages."""
page2 = self.Page.create(
{
"name": "Second page",
"task_id": self.task.id,
"project_id": self.task.project_id.id,
}
)
page3 = self.Page.create(
{
"name": "Third page",
"task_id": self.task.id,
"project_id": self.task.project_id.id,
}
)
self.task._compute_document_page_count()
self.assertEqual(
self.task.document_page_count,
2,
"Count should be two with two pages associated",
)
self.assertIn(page2, self.task.document_page_ids)
self.assertIn(page3, self.task.document_page_ids)

View File

@@ -0,0 +1,36 @@
<?xml version="1.0" ?>
<odoo>
<record id="view_wiki_form_task" model="ir.ui.view">
<field name="name">document.page.form - document_page_project_task</field>
<field name="model">document.page</field>
<field name="inherit_id" ref="document_page.view_wiki_form" />
<field name="arch" type="xml">
<field name="project_id" position="after">
<field name="task_id" domain="[('project_id', '=', project_id)]" />
</field>
</field>
</record>
<record id="action_document_page_tasks" model="ir.actions.act_window">
<field name="name">Task Wiki</field>
<field name="res_model">document.page</field>
<field
name="domain"
>[('type','=','content'), ('task_id', '=', active_id)]</field>
<field name="context">{
'default_type': 'content',
'default_task_id': active_id}</field>
<field name="view_mode">tree,form</field>
<field
name="view_ids"
eval="[(5,0,0),
(0,0,{'view_mode':'tree', 'view_id': ref('document_page.view_wiki_tree')}),
(0,0,{'view_mode':'form', 'view_id': ref('document_page.view_wiki_form')})]"
/>
<field name="search_view_id" ref="document_page.view_wiki_filter" />
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a new web page.
</p>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record id="view_task_form2" model="ir.ui.view">
<field name="name">project.task.form - document_page_project_task</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_form2" />
<field name="arch" type="xml">
<div name="button_box" position="inside">
<button
class="oe_stat_button"
type="action"
name="%(action_document_page_tasks)d"
icon="fa-book"
>
<field
string="Wiki Pages"
name="document_page_count"
widget="statinfo"
/>
</button>
</div>
</field>
</record>
</odoo>

View File

@@ -0,0 +1 @@
../../../../document_page_project_task

View File

@@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)