-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
TA#72205 [16.0][MIG] project_time_range
- Loading branch information
1 parent
d94eab5
commit b355230
Showing
22 changed files
with
698 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
Project Time Range | ||
================== | ||
|
||
.. contents:: Table of Contents | ||
|
||
Tasks | ||
----- | ||
The module adds the fields `Min` and `Max` under the `Timesheets` tab of a task. | ||
|
||
.. image:: static/description/task_form.png | ||
|
||
These fields contain the minimum and maximum time initialy estimated on the task. | ||
The field `Initially Planned Hours` is renamed `Ideal`. | ||
|
||
The fields are also added to the list view of tasks. | ||
|
||
.. image:: static/description/task_list.png | ||
|
||
They are also added to the portal view of a task. | ||
|
||
.. image:: static/description/portal_task.png | ||
|
||
Subtasks | ||
-------- | ||
Also, if a task has subtasks, the sum of time estimated on subtasks is also displayed. | ||
|
||
.. image:: static/description/task_form_with_subtasks.png | ||
|
||
Projects | ||
-------- | ||
On projects, a ``Management`` tab is added. | ||
|
||
This tab contains the sum of hours of all tasks under the project. | ||
|
||
.. image:: static/description/project_form.png | ||
|
||
The time ``Spent`` is the sum of hours on analytic lines. | ||
|
||
The time ``Remaining`` is the ``Ideal`` time minus the time ``Spent``. | ||
|
||
The fields are also added to the list view of projects. | ||
|
||
.. image:: static/description/project_list.png | ||
|
||
Contributors | ||
------------ | ||
* Numigi (tm) and all its contributors (https://bit.ly/numigiens) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Copyright 2023 Numigi (tm) and all its contributors (https://bit.ly/numigiens) | ||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). | ||
|
||
from . import models | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Copyright 2023 Numigi (tm) and all its contributors (https://bit.ly/numigiens) | ||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). | ||
|
||
{ | ||
"name": "Project Time Range", | ||
"version": "16.0.1.0.0", | ||
"author": "Numigi", | ||
"maintainer": "Numigi", | ||
"website": "https://bit.ly/numigi-com", | ||
"license": "LGPL-3", | ||
"category": "Project", | ||
"summary": "Add fields Min and Max on project tasks.", | ||
"depends": ["hr_timesheet"], | ||
"data": ["views/portal.xml", "views/project_task.xml", "views/project_project.xml"], | ||
"installable": True, | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
# Translation of Odoo Server. | ||
# This file contains the translation of the following modules: | ||
# * project_task_time_range | ||
# | ||
msgid "" | ||
msgstr "" | ||
"Project-Id-Version: Odoo Server 16.0\n" | ||
"Report-Msgid-Bugs-To: \n" | ||
"POT-Creation-Date: 2021-02-24 14:59+0000\n" | ||
"PO-Revision-Date: 2021-02-24 10:06-0500\n" | ||
"Last-Translator: <>\n" | ||
"Language-Team: \n" | ||
"Language: fr\n" | ||
"MIME-Version: 1.0\n" | ||
"Content-Type: text/plain; charset=UTF-8\n" | ||
"Content-Transfer-Encoding: 8bit\n" | ||
"Plural-Forms: \n" | ||
"X-Generator: Poedit 2.0.6\n" | ||
|
||
#. module: project_time_range | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.portal_my_task_with_time_range | ||
msgid "<strong>Ideal:</strong>" | ||
msgstr "<strong>Idéal:</strong>" | ||
|
||
#. module: project_time_range | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.portal_my_task_with_time_range | ||
msgid "<strong>Max:</strong>" | ||
msgstr "<strong>Max:</strong>" | ||
|
||
#. module: project_time_range | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.portal_my_task_with_time_range | ||
msgid "<strong>Min:</strong>" | ||
msgstr "<strong>Min:</strong>" | ||
|
||
#. module: project_time_range | ||
#: model:ir.model.fields,field_description:project_time_range.field_project_project__consumed_hours | ||
msgid "Consumed Hours" | ||
msgstr "Heures consommées" | ||
|
||
#. module: project_time_range | ||
#: code:addons/project_time_range/models/project_task.py:51 | ||
#, python-format | ||
msgid "Hours must be positive numbers." | ||
msgstr "Les heures doivent être des nombres positives." | ||
|
||
#. module: project_time_range | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_form | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_list | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_task_form | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_task_list | ||
msgid "Ideal" | ||
msgstr "Idéal" | ||
|
||
#. module: project_time_range | ||
#: model:ir.model.fields,field_description:project_time_range.field_project_project__planned_hours | ||
#: model:ir.model.fields,field_description:project_time_range.field_project_task__planned_hours | ||
msgid "Ideal Planned Hours" | ||
msgstr "Heures planifiées idéal" | ||
|
||
#. module: project_time_range | ||
#: model:ir.model.fields,help:project_time_range.field_project_task__planned_hours | ||
msgid "" | ||
"It is the time planned to achieve the task. If this document has sub-tasks, " | ||
"it means the time needed to achieve this tasks and its childs." | ||
msgstr "" | ||
|
||
#. module: project_time_range | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_form | ||
msgid "Management" | ||
msgstr "Pilotage" | ||
|
||
#. module: project_time_range | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_form | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_list | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_task_form | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_task_list | ||
msgid "Max" | ||
msgstr "Max" | ||
|
||
#. module: project_time_range | ||
#: code:addons/project_time_range/models/project_task.py:38 | ||
#, python-format | ||
msgid "Max Hours must be greater than the planned hours." | ||
msgstr "Les heures Max doivent être supérieures aux heures idéales." | ||
|
||
#. module: project_time_range | ||
#: model:ir.model.fields,field_description:project_time_range.field_project_project__max_hours | ||
#: model:ir.model.fields,field_description:project_time_range.field_project_task__max_hours | ||
msgid "Maximum Planned Hours" | ||
msgstr "Heures maximales plannifiées" | ||
|
||
#. module: project_time_range | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_form | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_list | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_task_form | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_task_list | ||
msgid "Min" | ||
msgstr "Min" | ||
|
||
#. module: project_time_range | ||
#: code:addons/project_time_range/models/project_task.py:45 | ||
#, python-format | ||
msgid "Min Hours must be lesser than the planned hours." | ||
msgstr "Les heures Min doivent être inférieures aux heures idéales." | ||
|
||
#. module: project_time_range | ||
#: model:ir.model.fields,field_description:project_time_range.field_project_project__min_hours | ||
#: model:ir.model.fields,field_description:project_time_range.field_project_task__min_hours | ||
msgid "Minimum Planned Hours" | ||
msgstr "Heures minimales plannifiées" | ||
|
||
#. module: project_time_range | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_task_form | ||
msgid "Planned Hours" | ||
msgstr "Heures planifiées idéales" | ||
|
||
#. module: project_time_range | ||
#: model:ir.model,name:project_time_range.model_project_project | ||
msgid "Project" | ||
msgstr "Projet" | ||
|
||
#. module: project_time_range | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_form | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_list | ||
msgid "Remaining" | ||
msgstr "Restant" | ||
|
||
#. module: project_time_range | ||
#: model:ir.model.fields,field_description:project_time_range.field_project_project__remaining_hours | ||
msgid "Remaining Hours" | ||
msgstr "Heures restantes" | ||
|
||
#. module: project_time_range | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_form | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_list | ||
msgid "Spent" | ||
msgstr "Consommé" | ||
|
||
#. module: project_time_range | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_task_form | ||
msgid "Sub-tasks Hours" | ||
msgstr "Heures planifiées des sous-tâches" | ||
|
||
#. module: project_time_range | ||
#: model:ir.model.fields,field_description:project_time_range.field_project_task__subtask_max_hours | ||
msgid "Sub-tasks Max" | ||
msgstr "Heures maximales des sous-tâches" | ||
|
||
#. module: project_time_range | ||
#: model:ir.model.fields,field_description:project_time_range.field_project_task__subtask_min_hours | ||
msgid "Sub-tasks Min" | ||
msgstr "Heures minimales des sous-tâches" | ||
|
||
#. module: project_time_range | ||
#: model:ir.model,name:project_time_range.model_project_task | ||
msgid "Task" | ||
msgstr "Tâche" | ||
|
||
#. module: project_time_range | ||
#: model_terms:ir.ui.view,arch_db:project_time_range.project_form | ||
msgid "Time Range" | ||
msgstr "Intervalle de temps" | ||
|
||
#~ msgid "Initially Planned Hours" | ||
#~ msgstr "Heures planifiées initialement" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Copyright 2023 Numigi (tm) and all its contributors (https://bit.ly/numigiens) | ||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). | ||
|
||
from . import project_task, project_project | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
# Copyright 2023 Numigi (tm) and all its contributors (https://bit.ly/numigiens) | ||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). | ||
|
||
from odoo import fields, models, api | ||
|
||
|
||
class ProjectProject(models.Model): | ||
|
||
_inherit = "project.project" | ||
|
||
min_hours = fields.Float( | ||
"Minimum Planned Hours", compute="_compute_time_range", store=True | ||
) | ||
max_hours = fields.Float( | ||
"Maximum Planned Hours", compute="_compute_time_range", store=True | ||
) | ||
planned_hours = fields.Float( | ||
string="Ideal Planned Hours", compute="_compute_time_range", store=True | ||
) | ||
consumed_hours = fields.Float( | ||
string="Consumed Hours", compute="_compute_consumed_remaining_hours" | ||
) | ||
remaining_hours = fields.Float( | ||
string="Remaining Hours", compute="_compute_consumed_remaining_hours" | ||
) | ||
|
||
@api.depends( | ||
"task_ids", | ||
"task_ids.min_hours", | ||
"task_ids.max_hours", | ||
"task_ids.planned_hours", | ||
"task_ids.active", | ||
"task_ids.parent_id", | ||
) | ||
def _compute_time_range(self): | ||
for project in self: | ||
tasks = project._get_parent_tasks() | ||
project.min_hours = sum(tasks.mapped("min_hours")) | ||
project.max_hours = sum(tasks.mapped("max_hours")) | ||
project.planned_hours = sum(tasks.mapped("planned_hours")) | ||
|
||
def _get_parent_tasks(self): | ||
return ( | ||
self.env["project.task"] | ||
.with_context({}) | ||
.search([("project_id", "=", self.id), ("parent_id", "=", False)]) | ||
) | ||
|
||
def _compute_consumed_remaining_hours(self): | ||
consumed_hours = self._get_consumed_hours_per_project() | ||
for project in self: | ||
project.consumed_hours = consumed_hours.get(project.id, 0) | ||
project.remaining_hours = project.planned_hours - project.consumed_hours | ||
|
||
def _get_consumed_hours_per_project(self): | ||
self._cr.execute( | ||
"SELECT project_id, sum(unit_amount) " | ||
"FROM account_analytic_line " | ||
"WHERE project_id in %s " | ||
"GROUP BY project_id", | ||
(tuple(self.ids),), | ||
) | ||
return dict(self._cr.fetchall()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# Copyright 2023 Numigi (tm) and all its contributors (https://bit.ly/numigiens) | ||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). | ||
|
||
|
||
from odoo import fields, models, api, _ | ||
from odoo.exceptions import ValidationError | ||
from .util import time_range_constraint | ||
|
||
|
||
class ProjectTask(models.Model): | ||
|
||
_inherit = "project.task" | ||
|
||
min_hours = fields.Float("Minimum Planned Hours") | ||
max_hours = fields.Float("Maximum Planned Hours") | ||
planned_hours = fields.Float(string="Ideal Planned Hours") | ||
|
||
subtask_min_hours = fields.Float( | ||
"Sub-tasks Min", compute="_compute_subtask_min_hours" | ||
) | ||
subtask_max_hours = fields.Float( | ||
"Sub-tasks Max", compute="_compute_subtask_max_hours" | ||
) | ||
|
||
def _compute_subtask_min_hours(self): | ||
for task in self: | ||
task.subtask_min_hours = sum(task.child_ids.mapped("min_hours")) | ||
|
||
def _compute_subtask_max_hours(self): | ||
for task in self: | ||
task.subtask_max_hours = sum(task.child_ids.mapped("max_hours")) | ||
|
||
@api.constrains("planned_hours", "max_hours") | ||
@time_range_constraint | ||
def _check_max_hours(self): | ||
if self.max_hours < self.planned_hours: | ||
raise ValidationError( | ||
_("Max Hours must be greater than the planned hours.") | ||
) | ||
|
||
@api.constrains("planned_hours", "min_hours") | ||
@time_range_constraint | ||
def _check_min_hours(self): | ||
if self.min_hours > self.planned_hours: | ||
raise ValidationError(_("Min Hours must be lesser than the planned hours.")) | ||
|
||
@api.constrains("planned_hours", "min_hours", "max_hours") | ||
@time_range_constraint | ||
def _check_positive_hours(self): | ||
if any(h < 0 for h in [self.min_hours, self.planned_hours, self.max_hours]): | ||
raise ValidationError(_("Hours must be positive numbers.")) | ||
|
||
@api.model | ||
def create(self, vals): | ||
if "max_hours" not in vals: | ||
vals["max_hours"] = vals.get("planned_hours") | ||
return super().create(vals) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# Copyright 2023 Numigi (tm) and all its contributors (https://bit.ly/numigiens) | ||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). | ||
|
||
import threading | ||
import functools | ||
|
||
|
||
def time_range_constraint(method): | ||
@functools.wraps(method) | ||
def wrapper(self): | ||
is_testing = getattr(threading.currentThread(), "testing", False) | ||
if self._context.get("enable_task_max_hours_constraint") or not is_testing: | ||
for task in self: | ||
method(task) | ||
|
||
return wrapper |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file.
Oops, something went wrong.