Skip to content

Commit

Permalink
[14.0][ADD] project_milestone_time_kpi
Browse files Browse the repository at this point in the history
  • Loading branch information
majouda committed Jun 7, 2024
1 parent a15aafa commit e6c2bf6
Show file tree
Hide file tree
Showing 13 changed files with 311 additions and 0 deletions.
68 changes: 68 additions & 0 deletions project_milestone_time_kpi/README.rst
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)
4 changes: 4 additions & 0 deletions project_milestone_time_kpi/__init__.py
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
17 changes: 17 additions & 0 deletions project_milestone_time_kpi/__manifest__.py
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,
}
42 changes: 42 additions & 0 deletions project_milestone_time_kpi/i18n/fr.po
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"

4 changes: 4 additions & 0 deletions project_milestone_time_kpi/models/__init__.py
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
43 changes: 43 additions & 0 deletions project_milestone_time_kpi/models/project_project.py
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.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions project_milestone_time_kpi/tests/__init__.py
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
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
31 changes: 31 additions & 0 deletions project_milestone_time_kpi/views/project_project.xml
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>

0 comments on commit e6c2bf6

Please sign in to comment.