Skip to content

Commit

Permalink
[8.0][ADD] Added Earned Value Management Report (OCA#107)
Browse files Browse the repository at this point in the history
  • Loading branch information
sudhir-serpentcs authored and Ruter Lv committed Mar 22, 2019
1 parent 0894252 commit 08f21c9
Show file tree
Hide file tree
Showing 7 changed files with 411 additions and 0 deletions.
93 changes: 93 additions & 0 deletions business_requirement_earned_value/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: https://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3


===============================
Earned Value Management Report
===============================

Introduction
============

This module is part of a set of modules (`Business Requirements <https://github.com/OCA/business-requirement/blob/8.0/README.md>`_).


This module introduces **Earned Value Management report** based on the information
from the Business Requirements Resources and Deliverable lines. You can check the
following resources for more information about EVM:

* `[RFC] Earned Value Management Report <https://github.com/OCA/business-requirement/issues/81>`_
* `Earned Value Management introduction <https://www.humphreys-assoc.com/evms/basic-concepts-earned-value-management-evm-ta-a-74.html>`_
* `Wikipedia Entry <https://en.wikipedia.org/wiki/Earned_value_management>`_

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

To fully be able to use the report, the following information must be properly maintained:

* Cost price in the products associated to resources
* Resources lines should contain valid cost and total cost.
* Employees should contain a valid resource product (comparable to the one in the Resource lines)
* The time spent on Timesheets should be properly recorded

Usage
=====

#. In the Business requirement, you add Deliverable Lines as necessary,with the
corresponding Resources lines.
#. When the BR is validated the project should be created so that the users can
manage their tasks.
#. When the users spend time on a given task they should input their timesheets
accordingly in the task.

.. note::
When creating new tasks, the related BR should be set up properly in the task in
order to be properly accounted

.. figure:: static/img/bus_req_category.png
:width: 600 px
:alt: Inputing the deliverables and resources lines


.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/222/8.0

Known issues / Roadmap
======================

* Needs timesheets on the tasks related to BR. In the future we might think of a way
to input TS on project directly


Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/OCA/business-requirement/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback.


Credits
=======

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

* Eric Caudal <[email protected]>

Maintainer
----------

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

This module is maintained by the OCA.

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.

To contribute to this module, please visit https://odoo-community.org.
4 changes: 4 additions & 0 deletions business_requirement_earned_value/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# © 2017 Elico Corp (https://www.elico-corp.com).
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import report
23 changes: 23 additions & 0 deletions business_requirement_earned_value/__openerp__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
# © 2017 Elico Corp (https://www.elico-corp.com).
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'Earned Value Management',
'category': 'Business Requirements Management',
'summary': 'Manage the Earned Value for your customers',
'version': '8.0.1.0.0',
'website': 'https://www.elico-corp.com/',
'author': 'Elico Corp, Odoo Community Association (OCA)',
'depends': [
'hr_timesheet',
'project_timesheet',
'business_requirement_deliverable_cost',
'business_requirement_deliverable_project',
],
'data': [
'security/ir.model.access.csv',
'report/br_earned_value_report_view.xml',
],
'license': 'AGPL-3',
'installable': True,
}
5 changes: 5 additions & 0 deletions business_requirement_earned_value/report/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 2017 Elico Corp (www.elico-corp.com).
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from . import br_earned_value_report
243 changes: 243 additions & 0 deletions business_requirement_earned_value/report/br_earned_value_report.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
# -*- coding: utf-8 -*-
# © 2017 Elico Corp (https://www.elico-corp.com).
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import tools
from openerp import fields, models


class BusinessRequirementEarnedValueReport(models.Model):
_name = "business.requirement.earned.value.report"
_description = "Earned Value Report"
_auto = False

name = fields.Char('Name', readonly=True)
partner_id = fields.Many2one('res.partner', 'Customer',
readonly=True)
project_id = fields.Many2one('project.project', 'Master Project',
readonly=True)
res_product = fields.Many2one('product.product', 'Res Product',
readonly=True)
hr_timesheet_product = fields.Many2one('product.product',
'HR Timesheet Product',
readonly=True)
planned_time_in_rl = fields.Float('Planned Time', readonly=True)
product_cost_from_rl = fields.Float('Unit Cost', readonly=True)
planned_value = fields.Float('Planned Value', readonly=True)
actual_time_in_timesheet = fields.Float('Actual Time',
readonly=True)
product_cost_from_timesheet_product =\
fields.Float('Act. Unit Cost',
readonly=True)
actual_cost = fields.Float('Actual Cost', readonly=True)
variance = fields.Float('Variance', readonly=True)
per_variances = fields.Float('% Variance', readonly=True)
remaining_hours = fields.Float('Remaining time', readonly=True)
total_expected_time = fields.Float('Total Exp. time', readonly=True)
project_completion = fields.Float('% Completion', readonly=True)
earned_value = fields.Float('Earned Value', readonly=True)

def _select(self):
select_str = """
SELECT
br.id,
CONCAT(br.name,'[',br.description,']') as name,
br.partner_id AS partner_id,
br.project_id AS project_id,
ptm.id AS hr_timesheet_product,
SUM(res.qty) AS planned_time_in_rl,
SUM(res.unit_price) AS product_cost_from_rl,
(SUM(res.qty) * SUM(res.unit_price)) AS planned_value,
(SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id)
AS actual_time_in_timesheet,
(ptm.list_price) AS product_cost_from_timesheet_product,
((SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id) * ptm.list_price)
AS actual_cost,
CASE
WHEN (SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id) > 0
THEN
(((SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id
) * ptm.list_price) - (SUM(res.qty
) * SUM(res.unit_price)))
ElSE 0.0 END AS variance,
CASE
WHEN
(SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id) > 0
THEN
(abs(((SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id
) * ptm.list_price) - (SUM(res.qty
) * SUM(res.unit_price))) /
SUM(res.unit_price))
ElSE 0.0 END AS per_variances,
CASE
WHEN
(SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id) > 0
THEN
(SELECT
SUM(pt.remaining_hours)
FROM
project_task pt
WHERE pt.business_requirement_id = br.id)
ElSE 0.0 END AS remaining_hours,
CASE
WHEN
(SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id) > 0
THEN
((SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id
) + (SELECT
SUM(pt.remaining_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id))
ElSE 0.0 END AS total_expected_time,
CASE
WHEN ((SELECT
SUM(pt.remaining_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id)) > 0
THEN
CASE
WHEN
(SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id) > 0
THEN
((SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id
) / ((SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id
) + (SELECT
SUM(pt.remaining_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id)
) * 100) ELSE 0.0 END
ElSE 0.0 END AS project_completion,
CASE
WHEN
(SELECT
SUM(pt.remaining_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id) > 0
THEN
CASE
WHEN
(SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id) > 0
THEN
((SUM(res.qty) * SUM(res.unit_price)
) * ((SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id
) / ((SELECT
SUM(pt.effective_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id
) + (SELECT
SUM(pt.remaining_hours)
FROM
project_task pt
WHERE
pt.business_requirement_id = br.id))))
ElSE 0.0 END
ElSE 0.0 END AS earned_value
"""
return select_str

def _from(self):
from_str = """
business_requirement br
LEFT JOIN business_requirement_deliverable dlv
ON dlv.business_requirement_id = br.id
LEFT JOIN business_requirement_resource res
ON res.business_requirement_deliverable_id = dlv.id
JOIN product_template as ptm
ON ptm.id = res.product_id
"""
return from_str

def _group_by(self):
group_by_str = """
GROUP BY
br.id,ptm.id
"""
return group_by_str

def init(self, cr):
tools.drop_view_if_exists(cr, self._table)
cr.execute("""CREATE or REPLACE VIEW %s as (
%s
FROM ( %s )
%s
)""" % (self._table, self._select(), self._from(),
self._group_by()))
Loading

0 comments on commit 08f21c9

Please sign in to comment.