From ab52aa7ff449f77cb195ddb433a78f885ab56965 Mon Sep 17 00:00:00 2001 From: projectsodoo Date: Sat, 27 Feb 2021 12:24:14 +0530 Subject: [PATCH] New Project timeline report added --- project_report/__manifest__.py | 1 + project_report/report/__init__.py | 1 + .../report/project_timeline_report.py | 129 ++++++++++++++++++ .../report/project_timeline_report_views.xml | 88 ++++++++++++ project_report/security/ir.model.access.csv | 2 + .../static/src/js/graph_renderer.js | 10 +- 6 files changed, 230 insertions(+), 1 deletion(-) create mode 100755 project_report/report/project_timeline_report.py create mode 100755 project_report/report/project_timeline_report_views.xml diff --git a/project_report/__manifest__.py b/project_report/__manifest__.py index 8d26622..549e4b3 100755 --- a/project_report/__manifest__.py +++ b/project_report/__manifest__.py @@ -19,6 +19,7 @@ #'views/project_view.xml', 'report/project_budget_hrs_analysis_views.xml', 'report/project_budget_amt_analysis_views.xml', + 'report/project_timeline_report_views.xml', ], 'qweb': [ "static/src/xml/base.xml", diff --git a/project_report/report/__init__.py b/project_report/report/__init__.py index bda40bb..643d4d2 100755 --- a/project_report/report/__init__.py +++ b/project_report/report/__init__.py @@ -3,3 +3,4 @@ from . import project_budget_hrs_analysis from . import project_budget_amt_analysis +from . import project_timeline_report diff --git a/project_report/report/project_timeline_report.py b/project_report/report/project_timeline_report.py new file mode 100755 index 0000000..616208b --- /dev/null +++ b/project_report/report/project_timeline_report.py @@ -0,0 +1,129 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import fields, models, tools, api + + +class ProjectTimelineReport(models.Model): + + _name = "project.timeline.report" + _description = "Project Timeline Report" + #_order = 'project_id' + _auto = False + + #analytic_account_id = fields.Many2one('account.analytic.account', string='Analytic Account', readonly=True) + project_id = fields.Many2one('project.project', string='Project', readonly=True) + parent_project = fields.Many2one('project.project', string='Parent Project', readonly=True) + #is_sub_project = fields.Boolean("Is Sub Project", readonly=True) + #sub_project = fields.Many2one('project.project', string='Sub Project', readonly=True) + start_date = fields.Date(string='Start Date', readonly=True) + end_date = fields.Date(string='End Date', readonly=True) + partner_id = fields.Many2one('res.partner', string='Client', readonly=True) + employee_id = fields.Many2one('hr.employee', string='Consultant', readonly=True) + timeline_type = fields.Char(string="Timeline Type", readonly=True) + timeline = fields.Float("Timeline", digits=(16, 2), readonly=True, group_operator="sum") + pricing_type = fields.Selection([ + ('fixed_rate', 'Fixed rate'), + ('employee_rate', 'Consultant rate') + ], string="Pricing", readonly=True) + project_type = fields.Selection([ + ('hours_in_consultant', 'Hours are budgeted according to a consultant'), + ('hours_no_limit', 'Total hours are budgeted without division to consultant'), + ], string="Project Type", readonly=True) + + def init(self): + '''Create the view''' + tools.drop_view_if_exists(self._cr, self._table) + self._cr.execute(""" + CREATE OR REPLACE VIEW %s AS ( + SELECT + ROW_NUMBER() OVER() as id, + project_id, + parentproject as parent_project, + startdate as start_date, + enddate as end_date, + pricing_type as pricing_type, + project_type as project_type, + partner_id, + employee_id, + timeline_type, + timeline from ( + SELECT + pro.id AS project_id, + (select project_id from project_subproject_rel as par where pro.id=par.id limit 1) as parentproject, + pro_emp.employee_id AS employee_id, + date_start AS startdate, + date AS enddate, + 'Budgeted' as timeline_type, + --DATE_PART('day', AGE(date, date_start)) AS timeline, + pro_emp.budgeted_qty as timeline, + pro.* + FROM + project_project PRO + Left JOIN project_sale_line_employee_map pro_emp ON pro_emp.project_id = pro.id + Where + pro.active = 't' + and PRO.pricing_type = 'employee_rate' and PRO.project_type = 'hours_in_consultant' + Union all + SELECT + pro.id AS project_id, + (select project_id from project_subproject_rel as par where pro.id=par.id limit 1) as parentproject, + null::int AS employee_id, + date_start AS startdate, + date AS enddate, + 'Budgeted' as timeline_type, + --DATE_PART('day', AGE(date, date_start)) AS timeline, + pro.budgeted_hours2 as timeline, + pro.* + FROM + project_project PRO + Where + pro.active = 't' + and PRO.pricing_type = 'employee_rate' and PRO.project_type = 'hours_no_limit' + Union all + SELECT + pro.id AS project_id, + (select project_id from project_subproject_rel as par where pro.id=par.id limit 1) as parentproject, + AAL.employee_id AS employee_id, + start_datetime::DATE AS startdate, + end_datetime::DATE AS enddate, + 'Actual' as timeline_type, + --DATE_PART('day', AGE(end_datetime, start_datetime)) AS timeline, + unit_amount as timeline, + pro.* + FROM project_project PRO + Left JOIN project_sale_line_employee_map pro_emp ON pro_emp.project_id = pro.id + LEFT JOIN account_analytic_account AA ON PRO.analytic_account_id = AA.id + LEFT JOIN account_analytic_line AAL ON AAL.account_id = AA.id and AAL.project_id = PRO.id and AAL.employee_id = pro_emp.employee_id + Where + PRO.active = 't' and PRO.pricing_type = 'employee_rate' + Union all + SELECT + pro.id AS project_id, + (select project_id from project_subproject_rel as par where pro.id=par.id limit 1) as parentproject, + AAL.employee_id AS employee_id, + start_datetime::DATE AS startdate, + end_datetime::DATE AS enddate, + 'Actual' as timeline_type, + --DATE_PART('day', AGE(end_datetime, start_datetime)) AS timeline, + unit_amount as timeline, + pro.* + FROM project_project PRO + LEFT JOIN account_analytic_account AA ON PRO.analytic_account_id = AA.id + LEFT JOIN account_analytic_line AAL ON AAL.account_id = AA.id and AAL.project_id = PRO.id + --and AAL.employee_id = pro_emp.employee_id + Where + PRO.active = 't' + and PRO.pricing_type = 'fixed_rate' + ) as res + order by + project_id desc, + --start_date, + --end_date desc, + --pricing_type, + --project_type, + timeline_type desc + --employee_id + )""" % (self._table,)) + + diff --git a/project_report/report/project_timeline_report_views.xml b/project_report/report/project_timeline_report_views.xml new file mode 100755 index 0000000..34e81c4 --- /dev/null +++ b/project_report/report/project_timeline_report_views.xml @@ -0,0 +1,88 @@ + + + + + project.timeline.report.pivot + project.timeline.report + + + + + + + + + + + project.timeline.report.graph + project.timeline.report + + + + + + + + + + + project.timeline.report.tree + project.timeline.report + + + + + + + + + + + + + + + + + project.timeline.report.search + project.timeline.report + + + + + + + + + + + + + + + + + + + + + + + + + + + Projects Timeline Acutal Vs Budget + project.timeline.report + graph,tree,pivot + + {'search_default_group_by_project': 1,'search_default_sdate': 1,'search_default_edate': 1,'search_default_group_by_timeline_type': 1, 'default_res_model':'project.timeline.report'} + + + + + diff --git a/project_report/security/ir.model.access.csv b/project_report/security/ir.model.access.csv index a8965bb..f653d5a 100755 --- a/project_report/security/ir.model.access.csv +++ b/project_report/security/ir.model.access.csv @@ -5,3 +5,5 @@ access_project_create_expense_manager,access_project_create_expense_project_mana access_project_budget_hrs_report_user,project.budget.hrs.report.user,model_project_budget_hrs_report,project.group_project_user,1,0,0,0 access_project_budget_amt_report_user,project.budget.amt.report.user,model_project_budget_amt_report,project.group_project_user,1,0,0,0 access_project_create_expense_user,access_project_create_expense_project_user,model_project_create_expense,project.group_project_user,1,0,0,0 +access_project_timeline_report_manager,project.timeline.report,model_project_timeline_report,project.group_project_manager,1,1,1,1 +access_project_timeline_report_user,project.timeline.report,model_project_timeline_report,project.group_project_user,1,0,0,0 \ No newline at end of file diff --git a/project_report/static/src/js/graph_renderer.js b/project_report/static/src/js/graph_renderer.js index 8dde8d8..493f860 100755 --- a/project_report/static/src/js/graph_renderer.js +++ b/project_report/static/src/js/graph_renderer.js @@ -348,7 +348,6 @@ var MAX_LEGEND_LENGTH = 25 * (1 + config.device.size_class); _getTooltipItems: function (tooltipModel) { var self = this; var data = this.chart.config.data; - var orderedItems = tooltipModel.dataPoints.sort(function (dPt1, dPt2) { return dPt2.yLabel - dPt1.yLabel; }); @@ -578,6 +577,7 @@ var MAX_LEGEND_LENGTH = 25 * (1 + config.device.size_class); var self = this; // prepare data + //console.log("datappppppppppp", dataPoints); var data = this._prepareData(dataPoints); // this.title = 'Time Line'; @@ -658,6 +658,9 @@ var MAX_LEGEND_LENGTH = 25 * (1 + config.device.size_class); // used when stacked dataset.stack = self.state.stacked ? self.state.origins[dataset.originIndex] : undefined; // set dataset color + if (self.state.stacked && dataset.label.includes("Actual")) { + dataset.stack = 1; + } if (self.resModel == 'project.budget.hrs.report') { //if (dataset.label.indexOf("Actual Hours") === -1 && dataset.label.toLowerCase().indexOf("Budgeted Hours") === -1) { if (dataset.label.indexOf("Actual") === -1 && dataset.label.toLowerCase().indexOf("Budgeted") === -1) { @@ -715,6 +718,8 @@ var MAX_LEGEND_LENGTH = 25 * (1 + config.device.size_class); // prepare data var data = this._prepareData(dataPoints); + //console.log("datappppppppppp", dataPoints); + if (self.resModel == 'project.budget.hrs.report') { //var groupedData = _.groupBy(data.datasets, x => x.label.replace('Actual Hours', '').replace('Budgeted Hours', '').split("/")[0]); var groupedData = _.groupBy(data.datasets, x => x.label.replace('Actual', '').replace('Budgeted', '').split("/")[0]); @@ -785,6 +790,9 @@ var MAX_LEGEND_LENGTH = 25 * (1 + config.device.size_class); // used when stacked dataset.stack = self.state.stacked ? self.state.origins[dataset.originIndex] : undefined; // set dataset color + if (self.state.stacked && dataset.label.includes("Actual")) { + dataset.stack = 1; + } if (self.resModel == 'project.budget.hrs.report') { if (dataset.label.indexOf("Actual") === -1 && dataset.label.toLowerCase().indexOf("Budgeted") === -1) { var color = self._getColor(index);