Report revenue updated
This commit is contained in:
parent
8b537e13c9
commit
1a3a7e27bd
|
@ -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
|
||||
|
|
@ -0,0 +1,105 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="project_revenue_custom_report3_view_pivot" model="ir.ui.view">
|
||||
<field name="name">project.revenue.custom.report3.pivot</field>
|
||||
<field name="model">project.revenue.custom.report3</field>
|
||||
<field name="arch" type="xml">
|
||||
<pivot string="Revenue Analysis" disable_linking="True" sample="1">
|
||||
<field name="project_id" type="row"/>
|
||||
<field name="overall_budgeted_revenue" type="measure"/>
|
||||
<field name="budgeted_revenue" type="measure"/>
|
||||
<field name="actual_revenue" type="measure"/>
|
||||
<field name="actual_cost" type="measure"/>
|
||||
<field name="budgeted_hours" type="measure"/>
|
||||
<field name="overall_hourly_rate" type="measure"/>
|
||||
<field name="pro_hourly_rate" type="measure"/>
|
||||
<field name="expenses_amt" type="measure"/>
|
||||
<field name="profit_amt" type="measure"/>
|
||||
<field name="profit_per" type="measure"/>
|
||||
<field name="unit_amount" widget="float_time" type="measure"/>
|
||||
<field name="timesheet_cost" type="measure"/>
|
||||
</pivot>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_revenue_custom_report3_view_graph" model="ir.ui.view">
|
||||
<field name="name">project.revenue.custom.report3.graph</field>
|
||||
<field name="model">project.revenue.custom.report3</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Revenue Analysis" type="bar" stacked="True" sample="1" disable_linking="1">
|
||||
<field name="project_id" type="col"/>
|
||||
<field name="budgeted_revenue" type="row"/>
|
||||
<field name="actual_revenue" type="row"/>
|
||||
<field name="actual_cost" type="row"/>
|
||||
</graph>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_revenue_custom_report3_view_tree" model="ir.ui.view">
|
||||
<field name="name">project.revenue.custom.report3.tree</field>
|
||||
<field name="model">project.revenue.custom.report3</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Revenue Analysis" create="false" edit="false" delete="false">
|
||||
<field name="project_id"/>
|
||||
<field name="employee_id"/>
|
||||
<field name="budgeted_revenue"/>
|
||||
<field name="actual_revenue"/>
|
||||
<field name="actual_cost"/>
|
||||
<field name="budgeted_hours"/>
|
||||
<field name="pro_hourly_rate"/>
|
||||
<field name="profit_amt"/>
|
||||
<field name="profit_per"/>
|
||||
<field name="unit_amount" widget="float_time"/>
|
||||
<field name="timesheet_cost"/>
|
||||
<field name="start_date"/>
|
||||
<field name="end_date"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="project_revenue_custom_report3_view_search" model="ir.ui.view">
|
||||
<field name="name">project.revenue.custom.report3.search</field>
|
||||
<field name="model">project.revenue.custom.report3</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Revenue Analysis">
|
||||
<field name="project_id"/>
|
||||
<field name="employee_id"/>
|
||||
<filter string="Fixed rate" name="fixed" domain="[('pricing_type','=','fixed_rate')]"/>
|
||||
<filter string="Hours are budgeted according to a consultant" name="cons" domain="[('pricing_type','=','employee_rate'),('project_type','=','hours_in_consultant')]"/>
|
||||
<filter string="Total hours are budgeted without division to consultant" name="limit" domain="[('pricing_type','=','employee_rate'),('project_type','=','hours_no_limit')]"/>
|
||||
<filter string="Start Date" name="filter_start_date" date="start_date"/>
|
||||
<filter string="End Date" name="filter_end_date" date="end_date"/>
|
||||
<filter string="Active" name="active_project" domain="[('project_active','=',True)]"/>
|
||||
<filter string="Archived" name="active_project" domain="[('project_active','=',False)]"/>
|
||||
<group expand="1" string="Group By">
|
||||
<filter string="Project" name="group_project" context="{'group_by':'project_id'}"/>
|
||||
<filter string="Consultant" name="group_employee" context="{'group_by':'employee_id'}"/>
|
||||
<filter string="End Date" name="group_enddate" domain="[]" context="{'group_by':'end_date:month'}"/>
|
||||
<!--<filter string="Budgeted Hours" name="budgethrs"
|
||||
domain="[('timesheet_sdatetime','=',False)]"/>
|
||||
<filter string="Current Year" name="currentyear"
|
||||
domain="[('timesheet_sdatetime','<=', time.strftime('%%Y-12-31')),('timesheet_sdatetime','>=',time.strftime('%%Y-01-01'))]"/>
|
||||
<filter string="Last Year" name="lyear" domain="[('timesheet_sdatetime','>=',(context_today()-relativedelta(years=1)).strftime('%%Y-01-01')),('timesheet_sdatetime','<', time.strftime('%%Y-01-01'))]"/>-->
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_revenue_custom_report3_action" model="ir.actions.act_window">
|
||||
<field name="name">Projects Revenue (updated)</field>
|
||||
<field name="res_model">project.revenue.custom.report3</field>
|
||||
<field name="view_mode">pivot,tree,graph</field>
|
||||
<field name="search_view_id" ref="project_revenue_custom_report3_view_search"/>
|
||||
<field name="context">{}</field>
|
||||
</record>
|
||||
|
||||
|
||||
<menuitem id="menu_project_revenue_custom_report3"
|
||||
parent="project.menu_project_report"
|
||||
action="project_revenue_custom_report3_action"
|
||||
name="Projects Revenue (upd)"
|
||||
sequence="50"/>
|
||||
|
||||
</odoo>
|
Loading…
Reference in New Issue