forked from OCA/project
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[14.0][ADD] project_milestone_time_kpi
- Loading branch information
Showing
13 changed files
with
311 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
Project Milestone time KPI | ||
========================== | ||
|
||
.. contents:: Table of Contents | ||
|
||
Context | ||
------- | ||
The module has just enriched the milestones tab on the project, in order to offer 3 new fields: | ||
|
||
* Total Estimated Hours. | ||
* Total Hours Spent. | ||
* Total Remaining Work. | ||
|
||
This option is advantageous because it allows you to compare the total hours spent on the project with the estimated hours of each milestone in order to be able to weight an overrun on a batch phase, and to see the time remaining on the batch by also having visual status of milestones. | ||
|
||
These fields are added to the list view of projects, in order to facilitate monitoring. | ||
|
||
|
||
Overview | ||
-------- | ||
The module inherits two modules that have been added to allow to define an estimated time on the milestone | ||
`(project_milestone_estimated_hours)`, and to calculate the times spent on a milestone `(project_milestone_spent_hours)` | ||
|
||
The module add 4 new fields to project: | ||
|
||
* **Total Estimated Hours**: Field calculated by taking into account the total of the 'Estimated hours' fields of each milestone associated with the project. | ||
* **Total Spent Hours**: Field calculated by taking into account the total of the 'Hours spent' fields of each milestone associated with the project. | ||
* **Remaining Estimated Hours**: Calculated field based on the following calculation: Total Estimated Hours - Total Hours Spent. | ||
* **Total Remaining Work**: Field calculated based on the total of the ‘Remaining hours’ fields of the tasks associated with a project. | ||
|
||
**Total Estimated Hours**, **Total Spent Hours** and **Total Remaining Work** are visible under the milestone tab | ||
in form view and visible in tree view after milestones column. | ||
|
||
Usage | ||
----- | ||
As a user, from a project whose **Use milestones** box is checked, go to the tab of a project. | ||
|
||
1. Add 2 milestones: | ||
|
||
* Analysis (estimated 10h). | ||
|
||
.. image:: project_milestone_time_kpi/static/description/milestone_1.png | ||
:width: 100% | ||
:align: center | ||
:height: 600px | ||
:alt: Milestone_1 | ||
|
||
* Realization (estimated 20h). | ||
|
||
.. image:: project_milestone_time_kpi/static/description/milestone_2.png | ||
:width: 100% | ||
:align: center | ||
:height: 600px | ||
:alt: Milestone_2 | ||
|
||
2. From the project tasks, assigned to the respective milestones, add timelines. | ||
|
||
.. image:: project_milestone_time_kpi/static/description/milestone_spent_hours.png | ||
:width: 100% | ||
:align: center | ||
:height: 600px | ||
:alt: Milestone_spent_hours | ||
|
||
You can see that the estimated times of the milestones, and the times spent are well up in the list of milestones. | ||
|
||
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 @@ | ||
# © 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,17 @@ | ||
# © 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 Milestone Time KPI", | ||
"version": "1.0.0", | ||
"author": "Numigi", | ||
"maintainer": "Numigi", | ||
"website": "https://bit.ly/numigi-com", | ||
"license": "LGPL-3", | ||
"category": "Project", | ||
"summary": "Track budgeted hours on projects using milestone", | ||
"depends": ["project_milestone_estimated_hours", | ||
"project_milestone_spent_hours"], | ||
"data": ["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,42 @@ | ||
# Translation of Odoo Server. | ||
# This file contains the translation of the following modules: | ||
# * project_milestone_time_kpi | ||
# | ||
msgid "" | ||
msgstr "" | ||
"Project-Id-Version: Odoo Server 12.0\n" | ||
"Report-Msgid-Bugs-To: \n" | ||
"POT-Creation-Date: 2022-06-25 00:11+0000\n" | ||
"PO-Revision-Date: 2022-06-25 00:11+0000\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_milestone_time_kpi | ||
#: model:ir.model,name:project_milestone_time_kpi.model_project_project | ||
msgid "Project" | ||
msgstr "Projet" | ||
|
||
#. module: project_milestone_time_kpi | ||
#: model:ir.model.fields,field_description:project_milestone_time_kpi.field_project_project__remaining_estimated_hours | ||
msgid "Remaining Estimated Hours" | ||
msgstr "Heures estimées restantes" | ||
|
||
#. module: project_milestone_time_kpi | ||
#: model:ir.model.fields,field_description:project_milestone_time_kpi.field_project_project__total_estimated_hours | ||
msgid "Total Estimated Hours" | ||
msgstr "Total heures estimés" | ||
|
||
#. module: project_milestone_time_kpi | ||
#: model:ir.model.fields,field_description:project_milestone_time_kpi.field_project_project__total_remaining_hours | ||
msgid "Total Remaining Work" | ||
msgstr "Total restant à faire" | ||
|
||
#. module: project_milestone_time_kpi | ||
#: model:ir.model.fields,field_description:project_milestone_time_kpi.field_project_project__total_spent_hours | ||
msgid "Total Spent Hours" | ||
msgstr "Total heures passées" | ||
|
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 @@ | ||
# © 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_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,43 @@ | ||
# © 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 Project(models.Model): | ||
_inherit = "project.project" | ||
|
||
total_estimated_hours = fields.Float( | ||
"Total Estimated Hours", compute="_compute_total_hours", store=True | ||
) | ||
total_spent_hours = fields.Float( | ||
"Total Spent Hours", compute="_compute_total_hours", store=True | ||
) | ||
remaining_estimated_hours = fields.Float( | ||
"Remaining Estimated Hours", | ||
compute="_compute_remaining_estimated_hours", store=True | ||
) | ||
total_remaining_hours = fields.Float( | ||
"Total Remaining Work", compute="_compute_remaining_hours", store=True | ||
) | ||
|
||
@api.depends("milestone_ids", "milestone_ids.estimated_hours", | ||
"milestone_ids.total_hours") | ||
def _compute_total_hours(self): | ||
for project in self: | ||
project.total_estimated_hours = sum( | ||
project.milestone_ids.mapped("estimated_hours")) | ||
project.total_spent_hours = sum( | ||
project.milestone_ids.mapped("total_hours")) | ||
|
||
@api.depends("total_estimated_hours", "total_spent_hours") | ||
def _compute_remaining_estimated_hours(self): | ||
for project in self: | ||
project.remaining_estimated_hours = \ | ||
project.total_estimated_hours - project.total_spent_hours | ||
|
||
@api.depends("task_ids", "task_ids.remaining_hours") | ||
def _compute_remaining_hours(self): | ||
for project in self: | ||
project.total_remaining_hours = sum( | ||
project.task_ids.mapped("remaining_hours")) |
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.
Binary file added
BIN
+381 KB
project_milestone_time_kpi/static/description/milestone_spent_hours.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
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 @@ | ||
# © 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 test_project_milestone_time_kpi |
98 changes: 98 additions & 0 deletions
98
project_milestone_time_kpi/tests/test_project_milestone_time_kpi.py
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,98 @@ | ||
# © 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.tests.common import SavepointCase | ||
|
||
|
||
class TestProjectMilestoneTimeKPI(SavepointCase): | ||
|
||
@classmethod | ||
def setUpClass(cls): | ||
super(TestProjectMilestoneTimeKPI, cls).setUpClass() | ||
|
||
# create a generic Project with 2 milestones and 2 tasks associated to milestones | ||
cls.stage = cls.env["project.task.type"].create({ | ||
"name": "New" | ||
}) | ||
|
||
cls.project_a = cls.env["project.project"].create({ | ||
"name": "Project A", | ||
"use_milestones": True, | ||
}) | ||
|
||
cls.milestone_a = cls.env['project.milestone'].create({ | ||
"name": "Analysis", | ||
"estimated_hours": 8, | ||
"project_id": cls.project_a.id, | ||
}) | ||
cls.milestone_b = cls.env['project.milestone'].create({ | ||
"name": "Realization", | ||
"estimated_hours": 20, | ||
"project_id": cls.project_a.id, | ||
}) | ||
|
||
cls.task_a = cls.env["project.task"].create({ | ||
"name": "Task A", | ||
"project_id": cls.project_a.id, | ||
"milestone_id": cls.milestone_a.id, | ||
"planned_hours": 4, | ||
"stage_id": cls.stage.id, | ||
}) | ||
|
||
cls.task_b = cls.env["project.task"].create({ | ||
"name": "Task B", | ||
"project_id": cls.project_a.id, | ||
"milestone_id": cls.milestone_b.id, | ||
"planned_hours": 8, | ||
"stage_id": cls.stage.id, | ||
}) | ||
|
||
# Add timelines to created tasks | ||
cls.timesheet_a = cls.env["account.analytic.line"].create({ | ||
"name": "Analyse", | ||
"project_id": cls.project_a.id, | ||
"task_id": cls.task_a.id, | ||
"unit_amount": 3, | ||
"employee_id": 1, | ||
"date": "2022-06-25", | ||
}) | ||
|
||
cls.timesheet_b = cls.env["account.analytic.line"].create({ | ||
"name": "Conception", | ||
"project_id": cls.project_a.id, | ||
"task_id": cls.task_b.id, | ||
"unit_amount": 4, | ||
"employee_id": 1, | ||
"date": "2022-06-30", | ||
}) | ||
|
||
def test_timesheet_line_created(self): | ||
""" Test total_remaining_hours calculation after adding a new timeline to task """ | ||
self.env["account.analytic.line"].create({ | ||
"name": "Development", | ||
"project_id": self.project_a.id, | ||
"task_id": self.task_b.id, | ||
"unit_amount": 3, | ||
"employee_id": 1, | ||
"date": "2022-06-30", | ||
}) | ||
assert self.project_a.total_estimated_hours == 28 | ||
assert self.project_a.total_spent_hours == 10 | ||
assert self.project_a.total_remaining_hours == 2 | ||
|
||
def test_add_milestone(self): | ||
self.env['project.milestone'].create({ | ||
"name": "Test", | ||
"estimated_hours": 8, | ||
"project_id": self.project_a.id, | ||
}) | ||
assert self.project_a.total_estimated_hours == 36 | ||
|
||
def test_unlink_milestone(self): | ||
self.milestone_b.unlink() | ||
assert self.project_a.total_estimated_hours == 8 | ||
assert self.project_a.total_spent_hours == 3 | ||
|
||
def test_update_milestone(self): | ||
self.milestone_a.write({"estimated_hours": 10}) | ||
assert self.project_a.total_estimated_hours == 30 |
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,31 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<odoo> | ||
<record id="project_form" model="ir.ui.view"> | ||
<field name="name">Project: add milestone time kpi</field> | ||
<field name="model">project.project</field> | ||
<field name="inherit_id" ref="project_milestone.project_enhancement_milestone_view_inherit_form"/> | ||
<field name="arch" type="xml"> | ||
<xpath expr="//page[@name='milestone_ids']" position="inside"> | ||
<group name="milestone_time_kpi" class="oe_subtotal_footer oe_right" colspan="2"> | ||
<field name="total_estimated_hours" widget="float_time"/> | ||
<field name="total_spent_hours" widget="float_time"/> | ||
<field name="remaining_estimated_hours" widget="float_time"/> | ||
<field name="total_remaining_hours" widget="float_time"/> | ||
</group> | ||
</xpath> | ||
</field> | ||
</record> | ||
|
||
<record id="project_list" model="ir.ui.view"> | ||
<field name="name">Project: add milestone time kpi</field> | ||
<field name="model">project.project</field> | ||
<field name="inherit_id" ref="project_milestone.project_milestone_view_inherit_list"/> | ||
<field name="arch" type="xml"> | ||
<field name="milestone_ids" position="after"> | ||
<field name="total_estimated_hours" widget="float_time"/> | ||
<field name="total_spent_hours" widget="float_time"/> | ||
<field name="total_remaining_hours" widget="float_time"/> | ||
</field> | ||
</field> | ||
</record> | ||
</odoo> |