Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
149 changes: 149 additions & 0 deletions project_task_prioritize/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
=======================
Project Task Prioritize
=======================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:5aff01d38fa5ec9f8c3f15740d4a504fbda95d4e2cb95458c98b20ccf582073a
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |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%2Fproject-lightgray.png?logo=github
:target: https://github.com/OCA/project/tree/18.0/project_task_prioritize
:alt: OCA/project
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/project-18-0/project-18-0-project_task_prioritize
: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=18.0
:alt: Try me on Runboat

|badge1| |badge2| |badge3| |badge4| |badge5|

This module enhances project module by adding a flexible task
prioritization system. It allows you to define custom prioritization
criteria and automatically calculate task priorities based on multiple
factors.

Key Features
============

- Define custom prioritization categories (e.g., Impact, Urgency,
Strategic Value)
- Create weighted scoring criteria for each category
- Configure custom priority calculation formulas
- Easy-to-use matrix interface for setting task priorities
- Automatic priority calculation based on defined criteria
- Demo data included with common prioritization templates

Demo Data
=========

The module includes demo data with the following prioritization
categories:

- **Organizational Impact**: Measures how widely the task affects the
organization
- **Frequency of Use**: How often the task or its results are used
- **Risk / Consequences of Not Doing It**: The impact of not completing
the task
- **Strategic Value**: How well the task aligns with strategic goals
- **Productivity Benefit**: The expected productivity gain from
completing the task

**Table of contents**

.. contents::
:local:

Configuration
=============

1. **Set Up Categories**:

- Navigate to Project > Configuration > Prioritizer > Categories
- Create categories that are relevant to your prioritization needs
- Define the possible values for each category with appropriate
weights

2. **Configure Projects**:

- Open a project and go to the "Prioritizer Configuration" tab
- Select the prioritization categories to use for tasks in this
project
- Optionally, customize the priority calculation formula

3. **Prioritize Tasks**:

- Select tasks in the task list view
- Click on "Prioritize Tasks"
- Set the appropriate values for each category in the matrix view
- Click "Validate" to save the priorities

Usage
=====

Priority Calculation
--------------------

The module calculates task priorities using a configurable formula. The
default formula is:

::

prioritizer_sum / (allocated_hours * (max_value - prioritizer_sum + 1))

Available variables in the formula:

- ``prioritizer_sum``: Sum of all selected prioritizer values
- ``max_value``: Sum of maximum values from all selected categories
- ``allocated_hours``: Number of hours allocated to the task
- ``rec``: The task record itself

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_task_prioritize%0Aversion:%2018.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
-------

* ForgeFlow

Contributors
------------

- David Jimenez [email protected]

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/18.0/project_task_prioritize>`_ project on GitHub.

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
2 changes: 2 additions & 0 deletions project_task_prioritize/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from . import models
from . import wizards
21 changes: 21 additions & 0 deletions project_task_prioritize/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Project Task Prioritize",
"summary": "Allows to define a task prioritizer format.",
"version": "18.0.1.0.0",
"category": "Project",
"author": "ForgeFlow, Odoo Community Association (OCA)",
"contributors": ["DavidJForgeFlow"],
"website": "https://github.com/OCA/project",
"license": "AGPL-3",
"depends": ["project", "web_widget_x2many_2d_matrix"],
"data": [
"security/ir.model.access.csv",
"wizards/project_task_prioritizer_view.xml",
"views/project_view.xml",
"views/prioritizer_category_views.xml",
],
"demo": ["demo/prioritizer_category_data.xml"],
"installable": True,
}
100 changes: 100 additions & 0 deletions project_task_prioritize/demo/prioritizer_category_data.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com)
License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
-->
<odoo noupdate="1">
<!-- Prioritizer Categories -->
<record id="pc_organizational_impact" model="prioritizer.category">
<field name="name">Organizational Impact</field>
</record>
<record id="pc_frequency_use" model="prioritizer.category">
<field name="name">Frequency of Use</field>
</record>
<record id="pc_risk" model="prioritizer.category">
<field name="name">Risk / Consequences of Not Doing It</field>
</record>
<record id="pc_strategic_value" model="prioritizer.category">
<field name="name">Strategic Value</field>
</record>
<record id="pc_productivity_benefit" model="prioritizer.category">
<field name="name">Productivity Benefit</field>
</record>

<!-- Prioritizer Categories Lines-->
<record id="pcl_organizational_impact_0" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_organizational_impact" />
<field name="name">Affects only one user</field>
<field name="value">0</field>
</record>
<record id="pcl_organizational_impact_1" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_organizational_impact" />
<field name="name">Affects one full department</field>
<field name="value">1</field>
</record>
<record id="pcl_organizational_impact_2" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_organizational_impact" />
<field name="name">Affects multiple departments / customers</field>
<field name="value">2</field>
</record>
<record id="pcl_frequency_use_0" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_frequency_use" />
<field name="name">Used sporadically (less than once a month)</field>
<field name="value">0</field>
</record>
<record id="pcl_frequency_use_1" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_frequency_use" />
<field name="name">Used regularly (about once a week)</field>
<field name="value">1</field>
</record>
<record id="pcl_frequency_use_2" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_frequency_use" />
<field name="name">Used daily</field>
<field name="value">2</field>
</record>
<record id="pcl_risk_0" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_risk" />
<field name="name">Low risk, only minor inconvenience</field>
<field name="value">0</field>
</record>
<record id="pcl_risk_1" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_risk" />
<field name="name">Medium risk, frequent errors or inefficiency</field>
<field name="value">1</field>
</record>
<record id="pcl_risk_2" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_risk" />
<field name="name">High risk, affects finances, customers, or compliance</field>
<field name="value">2</field>
</record>
<record id="pcl_strategic_value_0" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_strategic_value" />
<field name="name">Minor improvement, not related to strategic goals</field>
<field name="value">0</field>
</record>
<record id="pcl_strategic_value_1" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_strategic_value" />
<field name="name">Aligned with internal improvement objectives</field>
<field name="value">1</field>
</record>
<record id="pcl_strategic_value_2" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_strategic_value" />
<field name="name">Aligned with business strategic goals</field>
<field name="value">2</field>
</record>
<record id="pcl_productivity_benefit_0" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_productivity_benefit" />
<field name="name">Savings &lt; 1h/month</field>
<field name="value">0</field>
</record>
<record id="pcl_productivity_benefit_1" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_productivity_benefit" />
<field name="name">Savings 1–10h/month</field>
<field name="value">1</field>
</record>
<record id="pcl_productivity_benefit_2" model="prioritizer.category.line">
<field name="prioritizer_category_id" ref="pc_productivity_benefit" />
<field name="name">Savings > 10h/month</field>
<field name="value">2</field>
</record>
</odoo>
4 changes: 4 additions & 0 deletions project_task_prioritize/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from . import project
from . import project_task
from . import prioritizer_category_line
from . import prioritizer_category
23 changes: 23 additions & 0 deletions project_task_prioritize/models/prioritizer_category.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import api, fields, models


class PrioritizerCategory(models.Model):
_name = "prioritizer.category"
_description = "Prioritizer Category"

name = fields.Char()
prioritizer_category_line_ids = fields.One2many(
"prioritizer.category.line", inverse_name="prioritizer_category_id"
)
max_value = fields.Integer(compute="_compute_max_value", store=True)

@api.depends("prioritizer_category_line_ids")
def _compute_max_value(self):
for rec in self:
rec.max_value = (
max([pcl.value for pcl in rec.prioritizer_category_line_ids])
if rec.prioritizer_category_line_ids
else 1
)
12 changes: 12 additions & 0 deletions project_task_prioritize/models/prioritizer_category_line.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models


class PrioritizerCategoryLine(models.Model):
_name = "prioritizer.category.line"
_description = "Prioritizer Category Line"

name = fields.Char()
value = fields.Integer()
prioritizer_category_id = fields.Many2one(comodel_name="prioritizer.category")
24 changes: 24 additions & 0 deletions project_task_prioritize/models/project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from odoo import fields, models


class ProjectProject(models.Model):
_inherit = "project.project"

prioritizer_category_ids = fields.Many2many("prioritizer.category")
prioritizer_formula = fields.Text(
default=lambda self: (
"# Python expression to calculate prioritizer_value\n"
"# Available variables:\n"
"# - prioritizer_sum: sum of prioritizer line values\n"
"# - max_value: sum of max values from prioritizer categories\n"
"# - allocated_hours: hours allocated to the project/task\n"
"# - today: datetime.datetime.today()\n"
"# - rec: current record (project/task)\n"
"\n"
"# Example:\n"
"prioritizer_sum / (allocated_hours * (max_value - prioritizer_sum + 1)) "
),
help="Define a Python expression to compute the prioritizer value.",
)
36 changes: 36 additions & 0 deletions project_task_prioritize/models/project_task.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Copyright 2025 ForgeFlow S.L. (https://www.forgeflow.com)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
import datetime
import logging

from odoo import api, fields, models
from odoo.tools.safe_eval import safe_eval

_logger = logging.getLogger(__name__)


class ProjectTask(models.Model):
_inherit = "project.task"

prioritizer_value = fields.Float(compute="_compute_prioritizer_value", store=True)
prioritizer_line_ids = fields.Many2many("prioritizer.category.line")

@api.depends("prioritizer_line_ids", "allocated_hours")
def _compute_prioritizer_value(self):
for rec in self:
prioritizer_sum = sum([pcl.value for pcl in rec.prioritizer_line_ids])
max_value = sum(
[pc.max_value for pc in rec.project_id.prioritizer_category_ids]
)
locals_dict = {
"prioritizer_sum": prioritizer_sum,
"max_value": max_value,
"allocated_hours": rec.allocated_hours,
"today": datetime.datetime.today(),
"rec": rec,
}
formula = rec.project_id.prioritizer_formula or "0"
try:
rec.prioritizer_value = safe_eval(formula, locals_dict=locals_dict)
except Exception:
rec.prioritizer_value = 0
3 changes: 3 additions & 0 deletions project_task_prioritize/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build-system]
requires = ["whool"]
build-backend = "whool.buildapi"
15 changes: 15 additions & 0 deletions project_task_prioritize/readme/CONFIGURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
1. **Set Up Categories**:
- Navigate to Project > Configuration > Prioritizer > Categories
- Create categories that are relevant to your prioritization needs
- Define the possible values for each category with appropriate weights

2. **Configure Projects**:
- Open a project and go to the "Prioritizer Configuration" tab
- Select the prioritization categories to use for tasks in this project
- Optionally, customize the priority calculation formula

3. **Prioritize Tasks**:
- Select tasks in the task list view
- Click on "Prioritize Tasks"
- Set the appropriate values for each category in the matrix view
- Click "Validate" to save the priorities
Loading