Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[14.0][ADD] project_milestone_time_kpi #2

Open
wants to merge 1 commit into
base: 14.0
Choose a base branch
from
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
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.

- 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

- 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
16 changes: 16 additions & 0 deletions project_milestone_time_kpi/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# © 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": "14.0.1.0.0",
"author": "Numigi, Odoo Community Association (OCA)",
"maintainer": "Numigi",
"website": "https://github.com/OCA/project",
"license": "AGPL-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
47 changes: 47 additions & 0 deletions project_milestone_time_kpi/models/project_project.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# © 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 api, fields, models


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
113 changes: 113 additions & 0 deletions project_milestone_time_kpi/tests/test_project_milestone_time_kpi.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# © 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
41 changes: 41 additions & 0 deletions project_milestone_time_kpi/views/project_project.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?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>
6 changes: 6 additions & 0 deletions setup/project_milestone_time_kpi/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import setuptools

setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)
Loading