diff --git a/project_report/report/project_revenue_custom_report3.py b/project_report/report/project_revenue_custom_report3.py new file mode 100644 index 0000000..0d964ff --- /dev/null +++ b/project_report/report/project_revenue_custom_report3.py @@ -0,0 +1,154 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models, tools + +class ProjectRevenueCustomReport3(models.Model): + + _name = "project.revenue.custom.report3" + _description = "Project Revenue Custom Analysis report3" + #_order = 'project_id' + _auto = False + + start_date = fields.Datetime(string='Start Date', readonly=True) + end_date = fields.Datetime(string='End Date', readonly=True) + project_id = fields.Many2one('project.project', string='Project', readonly=True) + parent_project = fields.Many2one('project.project', string='Parent Project', readonly=True) + partner_id = fields.Many2one('res.partner', string='Client', readonly=True) + #timesheet_id = fields.Integer(string='Timesheet ID', readonly=True) + 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) + employee_id = fields.Many2one('hr.employee', string='Consultant', readonly=True) + timesheet_sdatetime = fields.Datetime(string='Timesheet Start Time', readonly=True) + unit_amount = fields.Float('Timesheet Hours', digits=(16, 2)) + timesheet_cost = fields.Float('Hourly Cost', digits=(16, 2)) + profit_per = fields.Float(string='Profit (%)', digits=(16, 2)) + profit_amt = fields.Float(string='Profit Amount', digits=(16, 2)) + expenses_amt = fields.Float(string='Expenses Amount', digits=(16, 2)) + pro_hourly_rate = fields.Float("Hourly Revenue", digits=(16, 2), group_operator="max") + budgeted_hours = fields.Float("Budgeted Hours", digits=(16, 2), readonly=True, group_operator="sum") + budgeted_revenue = fields.Float("Budgeted Revenue", digits=(16, 2), readonly=True, group_operator="sum") + actual_revenue = fields.Float("Actual Revenue", digits=(16, 2), readonly=True, group_operator="sum") + actual_cost = fields.Float("Actual Cost", digits=(16, 2), readonly=True, group_operator="sum") + overall_budgeted_revenue = fields.Float("Overall Budgeted Rev.", digits=(16, 2), readonly=True, group_operator="sum") + overall_hourly_rate = fields.Float("Overall Hourly Rate", digits=(16, 2), group_operator="sum") + project_active = fields.Boolean('Active') + + + def init(self): + '''Create the view''' + tools.drop_view_if_exists(self._cr, self._table) + self._cr.execute(""" + CREATE OR REPLACE VIEW %s AS ( +with data1 as (SELECT + date_trunc('month', min(AAL.start_datetime::date)) AS min, + date_trunc('month', max(AAL.end_datetime::date)) AS max, + AAL.start_datetime::date as t_startdate, + AAL.end_datetime::date as t_enddate, + AAL.project_id as project_id, + PRO.active as project_active, + (select project_id from project_subproject_rel as PAR where PRO.id=PAR.id limit 1) as parent_project, + PRO.partner_id AS partner_id, + PRO.pricing_type, PRO.project_type, AAL.employee_id, + AAL.unit_amount as unit_amount, + AAL.amount * -1 as actual_cost, + (AAL.unit_amount * PRO.hourly_rate) as actual_revenue, + PRO.expenses_amt + 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 + GROUP BY PRO.id, PRO.pricing_type, PRO.project_type, PRO.hourly_rate, PRO.expenses_amt, AAL.project_id, PRO.pricing_type, PRO.project_type, + AAL.employee_id, AAL.start_datetime::date, AAL.end_datetime::date, AAL.unit_amount, AAL.amount + ), +input_data as (SELECT generate_series(min, max,'1 month'):: date AS start_date, + (generate_series(min, max, '1 month'):: date + '1 month' :: interval - '1 day' :: interval):: date AS end_date, + project_id, employee_id + from data1), +data3 as ( +select PRO_EMP.start_date as cons_start_date, PRO_EMP.end_date as cons_end_date, + sum(PRO_EMP.price_unit) as price_unit, PRO_EMP.project_id, PRO_EMP.employee_id, + ((AAL.amount * -1)/NULLIF(AAL.unit_amount, 0)) as timesheet_cost, + CASE WHEN PRO_EMP.employee_price is null then ((AAL.amount * -1)/NULLIF(AAL.unit_amount, 0)) else (AAL.unit_amount * PRO_EMP.employee_price) end as cons_timesheet_cost, + CASE WHEN PRO_EMP.employee_price is null then (AAL.amount * -1) else (AAL.unit_amount * PRO_EMP.employee_price) end as cons_actual_cost, + (AAL.unit_amount * COALESCE(PRO_EMP.price_unit, 0)) AS cons_actual_revenue + from project_sale_line_employee_map PRO_EMP + LEFT JOIN account_analytic_line AAL ON AAL.project_id = PRO_EMP.project_id AND AAL.employee_id = PRO_EMP.employee_id + GROUP BY PRO_EMP.start_date, PRO_EMP.end_date, PRO_EMP.project_id, PRO_EMP.employee_id, PRO_EMP.employee_price, PRO_EMP.price_unit, + AAL.amount, AAL.unit_amount +) +SELECT ROW_NUMBER() OVER() as id, start_date, end_date, +input_data.project_id, data1.project_active, data1.parent_project, data1.partner_id, input_data.employee_id, +data1.pricing_type, data1.project_type, 0 as overall_budgeted_revenue, +0 as budgeted_revenue, 0 as budgeted_hours, 0 as pro_hourly_rate, 0 as overall_hourly_rate, +data1.unit_amount, +CASE WHEN data1.pricing_type = 'fixed_rate' THEN data3.timesheet_cost + WHEN data1.pricing_type = 'employee_rate' and data1.project_type='hours_no_limit' THEN data3.timesheet_cost + WHEN data1.pricing_type = 'employee_rate' and data1.project_type='hours_in_consultant' THEN data3.cons_timesheet_cost +END as timesheet_cost, +CASE WHEN data1.pricing_type = 'fixed_rate' THEN data1.actual_cost + WHEN data1.pricing_type = 'employee_rate' and data1.project_type='hours_no_limit' THEN data1.actual_cost + WHEN data1.pricing_type = 'employee_rate' and data1.project_type='hours_in_consultant' THEN data3.cons_actual_cost +END as actual_cost, +CASE WHEN data1.pricing_type = 'fixed_rate' THEN 0 + WHEN data1.pricing_type = 'employee_rate' and data1.project_type='hours_no_limit' THEN data1.actual_revenue + WHEN data1.pricing_type = 'employee_rate' and data1.project_type='hours_in_consultant' THEN data3.cons_actual_revenue +END as actual_revenue, +0 as expenses_amt, +CASE WHEN pricing_type = 'fixed_rate' THEN 0.0 - data1.actual_cost + WHEN pricing_type = 'employee_rate' and project_type='hours_no_limit' THEN data1.actual_revenue - data1.actual_cost + WHEN pricing_type = 'employee_rate' and project_type='hours_in_consultant' THEN data3.cons_actual_revenue - data3.cons_actual_cost +END as profit_amt, +CASE WHEN pricing_type = 'fixed_rate' THEN 0.0 + WHEN pricing_type = 'employee_rate' and project_type='hours_no_limit' THEN (data1.actual_revenue - data1.actual_cost)/NULLIF(data1.actual_revenue,0) + WHEN pricing_type = 'employee_rate' and project_type='hours_in_consultant' THEN (data3.cons_actual_revenue - data3.cons_actual_cost)/NULLIF(data3.cons_actual_revenue,0) * 100 +END as profit_per +from input_data +left join data1 on data1.project_id = input_data.project_id and data1.employee_id = input_data.employee_id +and data1.t_startdate >= input_data.start_date and data1.t_enddate <= input_data.end_date +left join data3 on data3.project_id = input_data.project_id and data3.employee_id = input_data.employee_id +and data3.cons_start_date >= input_data.start_date and data3.cons_end_date <= input_data.end_date +where data1.project_id=45 +group by start_date, end_date, input_data.project_id, data1.project_active, data1.parent_project, data1.partner_id, +data1.pricing_type, data1.project_type, +t_startdate, t_enddate, input_data.employee_id, data1.unit_amount, +data3.price_unit, actual_cost, data3.cons_actual_cost, actual_revenue, cons_actual_revenue, data3.timesheet_cost, data3.cons_timesheet_cost +order by employee_id, start_date + )""" % (self._table,)) + + @api.model + def read_group(self, domain, fields, groupby, offset=0, limit=None, orderby=False, lazy=True): + res = super(ProjectRevenueCustomReport3, self).read_group(domain, fields, groupby, offset=offset, limit=limit, orderby=orderby, lazy=lazy) + pro_hourly_rate = 0 + actual_cost = 0 + for line in res: + try: + if 'actual_cost' in line: + actual_cost = line['actual_cost'] + if 'overall_budgeted_revenue' in line and 'budgeted_hours' in line: + if line['budgeted_hours'] > 0: + line['overall_hourly_rate'] = line['overall_budgeted_revenue'] / line['budgeted_hours'] + if 'pro_hourly_rate' in line and 'budgeted_hours' in line: + if line['budgeted_hours'] > 0: + line['pro_hourly_rate'] = line['budgeted_revenue'] / line['budgeted_hours'] + if 'actual_revenue' in line and 'actual_cost' in line and 'expenses_amt' in line: + line['profit_amt'] = line['actual_revenue'] - line['actual_cost'] - line['expenses_amt'] + if 'profit_amt' in line and 'actual_revenue' in line: + try: + line['profit_per'] = (line['profit_amt'] / line['actual_revenue']) * 100 + except ZeroDivisionError: + pass + if 'unit_amount' in line: + try: + line['timesheet_cost'] = actual_cost / line['unit_amount'] + except ZeroDivisionError: + pass + except Exception: + pass + return res + diff --git a/project_report/report/project_revenue_custom_report3_views.xml b/project_report/report/project_revenue_custom_report3_views.xml new file mode 100644 index 0000000..7a36d01 --- /dev/null +++ b/project_report/report/project_revenue_custom_report3_views.xml @@ -0,0 +1,105 @@ + + + + + project.revenue.custom.report3.pivot + project.revenue.custom.report3 + + + + + + + + + + + + + + + + + + + + + project.revenue.custom.report3.graph + project.revenue.custom.report3 + + + + + + + + + + + + project.revenue.custom.report3.tree + project.revenue.custom.report3 + + + + + + + + + + + + + + + + + + + + + + project.revenue.custom.report3.search + project.revenue.custom.report3 + + + + + + + + + + + + + + + + + + + + + + + Projects Revenue (updated) + project.revenue.custom.report3 + pivot,tree,graph + + {} + + + + + +