19.0 vanilla

This commit is contained in:
Ernad Husremovic 2026-03-09 09:31:00 +01:00
parent a1137a1456
commit e1d89e11e3
2789 changed files with 1093187 additions and 605897 deletions

View file

@ -1,11 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="timesheets_analysis_report_list" model="ir.ui.view">
<field name="name">timesheets.analysis.report.list</field>
<field name="model">timesheets.analysis.report</field>
<field name="arch" type="xml">
<list>
<field name="date"/>
<field name="employee_id"/>
<field name="project_id"/>
<field name="task_id"/>
<field name="currency_id" column_invisible="1"/>
<field name="amount" string="Timesheet Costs" optional="hide" sum="Total"/>
<field name="unit_amount" optional="show" widget="timesheet_uom" sum="Total"
decoration-danger="unit_amount &gt; 24 or unit_amount &lt; 0"
decoration-muted="unit_amount == 0"
/>
</list>
</field>
</record>
<record id="timesheets_analysis_report_form" model="ir.ui.view">
<field name="name">timesheets.analysis.report.form</field>
<field name="model">timesheets.analysis.report</field>
<field name="arch" type="xml">
<form string="Timesheets Analysis">
<sheet>
<group>
<group>
<field name="project_id"/>
<field name="task_id"/>
</group>
<group>
<field name="employee_id"/>
<field name="date"/>
<field name="amount" invisible="1"/>
<field name="unit_amount" string="Time Spent" widget="timesheet_uom" decoration-danger="unit_amount &gt; 24"
decoration-muted="unit_amount == 0"/>
</group>
</group>
<field name="name" widget="text" nolabel="1"/>
</sheet>
</form>
</field>
</record>
<record id="timesheets_analysis_report_pivot_employee" model="ir.ui.view">
<field name="name">timesheets.analysis.report.pivot</field>
<field name="model">timesheets.analysis.report</field>
<field name="arch" type="xml">
<pivot string="Timesheets Analysis" sample="1" disable_linking="True">
<pivot string="Timesheets Analysis" sample="1">
<field name="employee_id" type="row"/>
<field name="date" interval="month" type="col"/>
<field name="amount" string="Timesheet Costs"/>
@ -18,7 +62,7 @@
<field name="name">timesheets.analysis.report.graph</field>
<field name="model">timesheets.analysis.report</field>
<field name="arch" type="xml">
<graph string="Timesheets" sample="1" js_class="hr_timesheet_graphview" disable_linking="True">
<graph string="Timesheets" sample="1" js_class="hr_timesheet_graphview">
<field name="employee_id" type="row"/>
<field name="amount" string="Timesheet Costs"/>
<field name="unit_amount" type="measure" widget="timesheet_uom"/>
@ -30,7 +74,7 @@
<field name="name">timesheets.analysis.report.pivot</field>
<field name="model">timesheets.analysis.report</field>
<field name="arch" type="xml">
<pivot string="Timesheets Analysis" sample="1" disable_linking="True">
<pivot string="Timesheets Analysis" sample="1">
<field name="project_id" type="row"/>
<field name="date" interval="month" type="col"/>
<field name="amount" string="Timesheet Costs"/>
@ -43,7 +87,7 @@
<field name="name">timesheets.analysis.report.graph</field>
<field name="model">timesheets.analysis.report</field>
<field name="arch" type="xml">
<graph string="Timesheets" sample="1" js_class="hr_timesheet_graphview" disable_linking="True">
<graph string="Timesheets" sample="1" js_class="hr_timesheet_graphview">
<field name="project_id" type="row"/>
<field name="amount" string="Timesheet Costs"/>
<field name="unit_amount" type="measure" widget="timesheet_uom"/>
@ -55,7 +99,7 @@
<field name="name">timesheets.analysis.report.pivot</field>
<field name="model">timesheets.analysis.report</field>
<field name="arch" type="xml">
<pivot string="Timesheets Analysis" sample="1" disable_linking="True">
<pivot string="Timesheets Analysis" sample="1">
<field name="amount" string="Timesheet Costs"/>
<field name="project_id" type="row"/>
<field name="task_id" type="row"/>
@ -69,7 +113,7 @@
<field name="name">timesheets.analysis.report.graph</field>
<field name="model">timesheets.analysis.report</field>
<field name="arch" type="xml">
<graph string="Timesheets" sample="1" js_class="hr_timesheet_graphview" disable_linking="True">
<graph string="Timesheets" sample="1" js_class="hr_timesheet_graphview">
<field name="project_id" type="row"/>
<field name="task_id" type="row"/>
<field name="amount" string="Timesheet Costs"/>
@ -81,28 +125,11 @@
<record id="hr_timesheet_report_search" model="ir.ui.view">
<field name="name">timesheets.analysis.report.search</field>
<field name="model">timesheets.analysis.report</field>
<field name="inherit_id" ref="hr_timesheet.hr_timesheet_line_search"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<search string="Timesheet">
<field name="date"/>
<field name="employee_id"/>
<field name="project_id"/>
<field name="ancestor_task_id" groups="project.group_subtask_project"/>
<field name="task_id"/>
<field name="name"/>
<field name="department_id"/>
<field name="manager_id"/>
<filter name="mine" string="My Timesheets" domain="[('user_id', '=', uid)]"/>
<separator/>
<filter name="month" string="Date" date="date"/>
<group expand="0" string="Group By">
<filter string="Project" name="groupby_project" domain="[]" context="{'group_by': 'project_id'}"/>
<filter string="Ancestor Task" name="groupby_parent_task" domain="[]" context="{'group_by': 'ancestor_task_id'}" groups="project.group_subtask_project"/>
<filter string="Task" name="groupby_task" domain="[]" context="{'group_by': 'task_id'}"/>
<filter string="Date" name="groupby_date" domain="[]" context="{'group_by': 'date'}" help="Timesheet by Date"/>
<filter string="Department" name="groupby_department" domain="[]" context="{'group_by': 'department_id'}"/>
<filter string="Manager" name="groupby_manager" domain="[]" context="{'group_by': 'manager_id'}"/>
<filter string="Employee" name="groupby_employee" domain="[]" context="{'group_by': 'employee_id'}"/>
</group>
<search position="attributes">
<attribute name="string">Timesheet Report</attribute>
</search>
</field>
</record>
@ -111,6 +138,7 @@
<record id="act_hr_timesheet_report" model="ir.actions.act_window">
<field name="name">Timesheets by Employee</field>
<field name="res_model">timesheets.analysis.report</field>
<field name="path">timesheets-by-employee</field>
<field name="domain">[('project_id', '!=', False)]</field>
<field name="context">{}</field>
<field name="search_view_id" ref="hr_timesheet_report_search"/>
@ -139,10 +167,18 @@
<field name="act_window_id" ref="act_hr_timesheet_report"/>
</record>
<record id="timesheet_action_view_report_by_employee_list" model="ir.actions.act_window.view">
<field name="sequence" eval="10"/>
<field name="view_mode">list</field>
<field name="view_id" ref="hr_timesheet.timesheets_analysis_report_list"/>
<field name="act_window_id" ref="act_hr_timesheet_report"/>
</record>
<!-- Group by project-->
<record id="timesheet_action_report_by_project" model="ir.actions.act_window">
<field name="name">Timesheets by Project</field>
<field name="res_model">timesheets.analysis.report</field>
<field name="path">timesheets-by-project</field>
<field name="domain">[('project_id', '!=', False)]</field>
<field name="context">{}</field>
<field name="search_view_id" ref="hr_timesheet_report_search"/>
@ -171,10 +207,18 @@
<field name="act_window_id" ref="timesheet_action_report_by_project"/>
</record>
<record id="timesheet_action_view_report_by_project_list" model="ir.actions.act_window.view">
<field name="sequence" eval="10"/>
<field name="view_mode">list</field>
<field name="view_id" ref="hr_timesheet.timesheets_analysis_report_list"/>
<field name="act_window_id" ref="timesheet_action_report_by_project"/>
</record>
<!-- Group by task -->
<record id="timesheet_action_report_by_task" model="ir.actions.act_window">
<field name="name">Timesheets by Task</field>
<field name="res_model">timesheets.analysis.report</field>
<field name="path">timesheets-by-task</field>
<field name="domain">[('project_id', '!=', False)]</field>
<field name="context">{}</field>
<field name="search_view_id" ref="hr_timesheet_report_search"/>
@ -203,34 +247,11 @@
<field name="act_window_id" ref="timesheet_action_report_by_task"/>
</record>
<menuitem id="menu_timesheets_reports"
name="Reporting"
parent="timesheet_menu_root"
sequence="99"/>
<menuitem id="menu_timesheets_reports_timesheet"
name="Timesheets"
parent="menu_timesheets_reports"
sequence="10"/>
<menuitem id="menu_hr_activity_analysis"
parent="menu_timesheets_reports_timesheet"
action="act_hr_timesheet_report"
groups="hr_timesheet.group_hr_timesheet_approver"
name="By Employee"
sequence="10"/>
<menuitem id="timesheet_menu_report_timesheet_by_project"
parent="menu_timesheets_reports_timesheet"
action="timesheet_action_report_by_project"
name="By Project"
sequence="15"/>
<menuitem id="timesheet_menu_report_timesheet_by_task"
parent="menu_timesheets_reports_timesheet"
action="timesheet_action_report_by_task"
name="By Task"
sequence="20"/>
<record id="timesheet_action_view_report_by_task_list" model="ir.actions.act_window.view">
<field name="sequence" eval="10"/>
<field name="view_mode">list</field>
<field name="view_id" ref="hr_timesheet.timesheets_analysis_report_list"/>
<field name="act_window_id" ref="timesheet_action_report_by_task"/>
</record>
</data>
</odoo>

View file

@ -1,47 +1,31 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models, api
from odoo import fields, models
class ReportProjectTaskUser(models.Model):
_inherit = "report.project.task.user"
hours_planned = fields.Float('Planned Hours', readonly=True)
hours_effective = fields.Float('Effective Hours', readonly=True)
remaining_hours = fields.Float('Remaining Hours', readonly=True)
progress = fields.Float('Progress', group_operator='avg', readonly=True)
overtime = fields.Float(readonly=True)
allocated_hours = fields.Float('Allocated Time', readonly=True, groups="hr_timesheet.group_hr_timesheet_user")
effective_hours = fields.Float('Time Spent', readonly=True, groups="hr_timesheet.group_hr_timesheet_user")
remaining_hours = fields.Float('Time Remaining', readonly=True, groups="hr_timesheet.group_hr_timesheet_user")
remaining_hours_percentage = fields.Float('Time Remaining Percentage', readonly=True, groups="hr_timesheet.group_hr_timesheet_user")
progress = fields.Float('Progress', aggregator='avg', readonly=True, groups="hr_timesheet.group_hr_timesheet_user")
overtime = fields.Float(readonly=True, groups="hr_timesheet.group_hr_timesheet_user")
def _select(self):
select_to_append = """,
(t.effective_hours * 100) / NULLIF(t.planned_hours, 0) as progress,
t.effective_hours as hours_effective,
t.planned_hours - t.effective_hours - t.subtask_effective_hours as remaining_hours,
NULLIF(t.planned_hours, 0) as hours_planned,
t.overtime as overtime
return super()._select() + """,
CASE WHEN COALESCE(t.allocated_hours, 0) = 0 THEN NULL ELSE t.effective_hours * 100 / t.allocated_hours END as progress,
NULLIF(t.effective_hours, 0) as effective_hours,
CASE WHEN COALESCE(t.allocated_hours, 0) = 0 THEN NULL ELSE t.allocated_hours - t.effective_hours END as remaining_hours,
CASE WHEN t.allocated_hours > 0 THEN t.remaining_hours / t.allocated_hours ELSE 0 END as remaining_hours_percentage,
NULLIF(t.allocated_hours, 0) as allocated_hours,
NULLIF(t.overtime, 0) as overtime
"""
return super(ReportProjectTaskUser, self)._select() + select_to_append
def _group_by(self):
group_by_append = """,
return super()._group_by() + """,
t.effective_hours,
t.subtask_effective_hours,
t.planned_hours,
t.allocated_hours,
t.overtime
"""
return super(ReportProjectTaskUser, self)._group_by() + group_by_append
@api.model
def _get_view_cache_key(self, view_id=None, view_type='form', **options):
"""The override of _get_view changing the time field labels according to the company timesheet encoding UOM
makes the view cache dependent on the company timesheet encoding uom"""
key = super()._get_view_cache_key(view_id, view_type, **options)
return key + (self.env.company.timesheet_encode_uom_id,)
@api.model
def _get_view(self, view_id=None, view_type='form', **options):
arch, view = super()._get_view(view_id, view_type, **options)
if view_type in ['pivot', 'graph'] and self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_day'):
arch = self.env['account.analytic.line']._apply_time_label(arch, related_model=self._name)
return arch, view

View file

@ -6,13 +6,12 @@
<field name="model">report.project.task.user</field>
<field name="inherit_id" ref="project.view_task_project_user_graph" />
<field name="arch" type="xml">
<xpath expr="//graph" position="attributes">
<attribute name="js_class">hr_timesheet_graphview</attribute>
</xpath>
<xpath expr="//field[@name='project_id']" position='after'>
<field name="hours_planned" widget="timesheet_uom" type="measure"/>
<field name="hours_effective" widget="timesheet_uom" type="measure"/>
<field name="allocated_hours" widget="timesheet_uom" type="measure"/>
<field name="effective_hours" widget="timesheet_uom" type="measure"/>
<field name="overtime" widget="timesheet_uom"/>
<field name="remaining_hours" widget="timesheet_uom" type="measure"/>
<field name="remaining_hours_percentage" invisible="1"/>
</xpath>
</field>
</record>
@ -22,43 +21,14 @@
<field name="model">report.project.task.user</field>
<field name="inherit_id" ref="project.view_task_project_user_pivot"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='project_id']" position='after'>
<field name="hours_planned" widget="timesheet_uom" type="measure"/>
<field name="hours_effective" widget="timesheet_uom" type="measure"/>
<pivot position="inside">
<field name="allocated_hours" widget="timesheet_uom" type="measure"/>
<field name="effective_hours" widget="timesheet_uom" type="measure"/>
<field name="remaining_hours" widget="timesheet_uom" type="measure"/>
</xpath>
<field name="overtime" widget="timesheet_uom" type="measure"/>
<field name="remaining_hours_percentage" invisible="1"/>
</pivot>
</field>
</record>
<record id="report_project_task_user_view_search" model="ir.ui.view">
<field name="name">report.project.task.user.view.search.inherit.hr.timesheet</field>
<field name="model">report.project.task.user</field>
<field name="inherit_id" ref="project.view_task_project_user_search" />
<field name="arch" type="xml">
<xpath expr="//filter[@name='late']" position="after">
<filter string="Tasks in Overtime" name="overtime" domain="[('overtime', '&gt;', 0)]"/>
</xpath>
<xpath expr="//filter[@name='my_favorite_projects']" position="after">
<filter string="My Team's Projects" name="my_team_projects" domain="[('project_id.user_id.employee_id.parent_id.user_id', '=', uid), ('project_id.user_id', '!=', uid), ('user_ids', '!=', uid), ('user_ids', '!=', False)]"/>
<filter string="My Department's Projects" name="my_department" domain="[('project_id.user_id.employee_id.member_of_department', '=', True)]"/>
</xpath>
<xpath expr="//filter[@name='followed_by_me']" position="after">
<filter string="My Team's Tasks" name="my_team_tasks" domain="[('user_ids.employee_id.parent_id.user_id', '=', uid)]" />
<filter string="My Department's Tasks" name="my_department" domain="[('user_ids.employee_id.member_of_department', '=', True)]"/>
</xpath>
</field>
</record>
<record id="report_project_task_user_view_tree" model="ir.ui.view">
<field name="name">report.project.task.user.view.tree.inherit.hr.timesheet</field>
<field name="model">report.project.task.user</field>
<field name="inherit_id" ref="project.report_project_task_user_view_tree"/>
<field name="arch" type="xml">
<field name="user_ids" position="after">
<field name="hours_effective" optional="hide" widget="float_time"/>
<field name="progress" optional="hide" widget="progressbar"/>
</field>
</field>
</record>
</data>
</odoo>

View file

@ -7,52 +7,57 @@
<table class="table table-sm">
<thead style="display: table-row-group">
<tr>
<t t-set="timesheet_record_label" t-value="False"/>
<t t-if="show_task">
<t t-set="timesheet_record_label">Task</t>
</t>
<t t-elif="show_project">
<t t-set="timesheet_record_label">Project</t>
</t>
<th class="text-start align-middle"><span>Date</span></th>
<th class="text-start align-middle"><span>Employee</span></th>
<th class="text-start align-middle" t-if="show_project"><span>Project</span></th>
<th class="text-start align-middle" t-if="show_task"><span>Task</span></th>
<th class="text-start align-middle" t-if="timesheet_record_label"><span t-out="timesheet_record_label"/></th>
<th class="text-start align-middle"><span>Description</span></th>
<th class="text-end">
<span t-if="is_uom_day">Days Spent</span>
<span t-else="">Hours Spent</span>
<span>Time Spent</span>
</th>
</tr>
</thead>
<tbody>
<tr t-foreach="lines" t-as="line" t-att-style="'background-color: #F1F1F1;' if line_index % 2 == 0 else ''">
<td class="align-middle">
<span t-field="line.date"/>
<span t-field="line.date">2021-09-01</span>
</td>
<td class="align-middle">
<span t-field="line.user_id.partner_id.name"/>
<span t-if="not line.user_id.partner_id.name" t-field="line.employee_id"/>
<span t-if="line.user_id.partner_id.name" t-field="line.user_id.partner_id.name">Audrey Peterson</span>
<span t-else="" t-field="line.employee_id">Audrey Peterson</span>
</td>
<td t-if="show_project" class="align-middle">
<span t-field="line.project_id.sudo().name"/>
</td>
<td t-if="show_task" class="align-middle">
<span t-if="line.task_id" t-field="line.task_id.sudo().name"/>
<t t-set="timesheet_record_info" t-value="False"/>
<t t-if="show_project">
<t t-set="timesheet_record_info" t-value="line.project_id.sudo().name"/>
<t t-if="show_task and line.task_id" t-set="timesheet_record_info" t-value="'%s / %s' % (timesheet_record_info, line.task_id.sudo().name)"/>
</t>
<t t-elif="show_task" t-set="timesheet_record_info" t-value="line.task_id.sudo().name"/>
<td t-if="show_task or show_project" class="align-middle">
<span t-if="timesheet_record_info" t-out="timesheet_record_info">Research and Development/New Portal System</span>
</td>
<td class="align-middle">
<span t-field="line.name" t-options="{'widget': 'text'}"/>
<span t-field="line.name" t-options="{'widget': 'text'}">Call client and discuss project</span>
</td>
<td class="text-end align-middle">
<span t-if="is_uom_day" t-esc="line._get_timesheet_time_day()" t-options="{'widget': 'timesheet_uom'}"/>
<span t-else="" t-field="line.unit_amount" t-options="{'widget': 'duration', 'digital': True, 'unit': 'hour', 'round': 'minute'}"/>
<span t-if="not is_uom_day" t-field="line.unit_amount" t-options="{'widget': 'duration', 'digital': True, 'unit': 'hour', 'round': 'minute'}">2 hours</span>
<span t-else="" t-esc="line._get_timesheet_time_day()" t-options="{'widget': 'timesheet_uom'}">1 day</span>
</td>
</tr>
<tr>
<t t-set="nbCols" t-value="4"/>
<t t-if="show_project" t-set="nbCols" t-value="nbCols + 1"/>
<t t-if="show_task" t-set="nbCols" t-value="nbCols + 1"/>
<td class="text-end" t-attf-colspan="{{nbCols}}">
<strong t-if="is_uom_day">
<span style="margin-right: 15px;">Total (Days)</span>
<t t-esc="lines._convert_hours_to_days(sum(lines.mapped('unit_amount')))" t-options="{'widget': 'timesheet_uom'}"/>
<td class="text-end" colspan="100">
<strong t-if="not is_uom_day">
<span style="margin-right: 15px;">Total (Hours)</span>
<t t-esc="sum(lines.mapped('unit_amount'))" t-options="{'widget': 'duration', 'digital': True, 'unit': 'hour', 'round': 'minute'}">2 hours</t>
</strong>
<strong t-else="">
<span style="margin-right: 15px;">Total (Hours)</span>
<t t-esc="sum(lines.mapped('unit_amount'))" t-options="{'widget': 'duration', 'digital': True, 'unit': 'hour', 'round': 'minute'}"/>
<span style="margin-right: 15px;">Total (Days)</span>
<t t-esc="lines._convert_hours_to_days(sum(lines.mapped('unit_amount')))" t-options="{'widget': 'timesheet_uom'}">1 day</t>
</strong>
</td>
</tr>
@ -66,32 +71,28 @@
<t t-set="show_record" t-value="len(docs.ids) == 1"/>
<t t-set="title" t-value="docs._description"/>
<t t-set="company" t-value="docs.company_id if len(docs) == 1 else docs.env.company"/>
<t t-set="show_task" t-value="True"/>
<t t-if="not from_project">
<t t-set="show_task" t-value="False"/>
<t t-set="timesheet_report_data" t-value="docs._get_timesheet_report_data()"/>
<t t-set="timesheets_per_task" t-value="timesheet_report_data['timesheets_per_task']"/>
<t t-set="subtask_ids_per_task_id" t-value="timesheet_report_data['subtask_ids_per_task_id']"/>
</t>
<t t-call="web.html_container">
<t t-call="web.external_layout">
<t t-call="web.internal_layout">
<div class="page">
<t t-foreach="docs" t-as="doc">
<t t-if="from_project" t-set="show_task"
t-value="bool(doc.timesheet_ids.task_id)"/>
<div class="oe_structure"/>
<div class="row mt8">
<div class="col-12">
<t t-if="doc.allow_timesheets and doc.timesheet_ids">
<h1 class="my-4">
<t t-if="not show_record">
<t t-out="title"/>: <span t-field="doc.name"/>
</t>
</h1>
<h2>
<span>Timesheets
<t t-if="show_record">
for the <t t-out="doc.name"/> <t t-out="title"/>
</t>
</span>
</h2>
<t t-set='lines' t-value='doc.timesheet_ids'/>
<t t-call="hr_timesheet.timesheet_table"/>
</t>
</div>
<div class="mt8">
<t t-set="lines" t-value="doc.timesheet_ids if from_project else timesheets_per_task.get(doc, doc.env['account.analytic.line'])"/>
<h1 t-if="lines">
<t t-out="title"/>: <t t-out="doc.name"/>
</h1>
<t t-if="lines" t-call="hr_timesheet.timesheet_table"/>
<t t-if="not from_project">
<t t-set="task_id" t-value="doc.id"/>
<t t-call="hr_timesheet.timesheet_report_subtask"/>
</t>
</div>
</t>
</div>
@ -99,10 +100,25 @@
</t>
</template>
<template id="timesheet_report_subtask">
<t t-set="subtasks" t-value="doc.browse(subtask_ids_per_task_id.get(task_id, []))"/>
<t t-foreach="subtasks.sorted('sequence')" t-as="subtask">
<t t-if="subtask in timesheets_per_task">
<h2 class="my-4">
Sub-Task of '<t t-out="doc.name"/>': <t t-out="subtask.name"/>
</h2>
<t t-set="lines" t-value="timesheets_per_task[subtask]"/>
<t t-call="hr_timesheet.timesheet_table"/>
</t>
<t t-set="task_id" t-value="subtask.id"/>
<t t-call="hr_timesheet.timesheet_report_subtask"/>
</t>
</template>
<template id="report_timesheet">
<t t-call="web.html_container">
<t t-call="web.external_layout">
<t t-set="company" t-value="docs.project_id.company_id if len(docs.project_id) == 1 else docs.env.company"/>
<t t-call="web.internal_layout">
<t t-set="company" t-value="docs.project_id.sudo().company_id if len(docs.project_id) == 1 else docs.env.company"/>
<t t-set="show_task" t-value="bool(docs.task_id)"/>
<t t-set="show_project" t-value="len(docs.project_id) > 1"/>
<div class="page">
@ -112,7 +128,7 @@
<h2>
<span>Timesheets
<t t-if="len(docs.project_id) == 1">
for the <t t-out="docs.project_id.name"/> Project
for the <t t-out="docs.project_id.sudo().name">Research and Development</t> Project
</t>
</span>
</h2>
@ -129,7 +145,7 @@
<!-- Project Task Timesheet Report for given timesheets -->
<template id="report_timesheet_task">
<t t-call="web.html_container">
<t t-call="web.external_layout">
<t t-call="web.internal_layout">
<t t-set="company" t-value="docs.project_id.company_id if len(docs.project_id) == 1 else docs.env.company"/>
<t t-set="show_task" t-value="len(docs.task_id) > 1"/>
<t t-set="show_project" t-value="False"/>
@ -172,6 +188,7 @@
<record id="timesheet_report_task" model="ir.actions.report">
<field name="name">Timesheets</field>
<field name="model">project.task</field>
<field name="group_ids" eval="[(4,ref('hr_timesheet.group_hr_timesheet_user'))]"/>
<field name="report_type">qweb-pdf</field>
<field name="report_name">hr_timesheet.report_project_task_timesheet</field>
<field name="report_file">report_timesheet_task</field>
@ -188,6 +205,7 @@
<record id="timesheet_report_project" model="ir.actions.report">
<field name="name">Timesheets</field>
<field name="model">project.project</field>
<field name="group_ids" eval="[(4,ref('hr_timesheet.group_hr_timesheet_user'))]"/>
<field name="report_type">qweb-pdf</field>
<field name="report_name">hr_timesheet.report_timesheet_project</field>
<field name="report_file">report_timesheet_project</field>

View file

@ -1,12 +1,13 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from psycopg2 import sql
from odoo import api, tools, fields, models
from odoo import api, fields, models
from odoo.tools.sql import drop_view_if_exists, SQL
class TimesheetsAnalysisReport(models.Model):
_name = "timesheets.analysis.report"
_name = 'timesheets.analysis.report'
_inherit = ["hr.manager.department.report"]
_description = "Timesheets Analysis Report"
_auto = False
@ -14,15 +15,26 @@ class TimesheetsAnalysisReport(models.Model):
user_id = fields.Many2one("res.users", string="User", readonly=True)
project_id = fields.Many2one("project.project", string="Project", readonly=True)
task_id = fields.Many2one("project.task", string="Task", readonly=True)
ancestor_task_id = fields.Many2one("project.task", string="Ancestor Task", readonly=True)
employee_id = fields.Many2one("hr.employee", string="Employee", readonly=True)
parent_task_id = fields.Many2one("project.task", string="Parent Task", readonly=True)
manager_id = fields.Many2one("hr.employee", "Manager", readonly=True)
company_id = fields.Many2one("res.company", string="Company", readonly=True)
department_id = fields.Many2one("hr.department", string="Department", readonly=True)
currency_id = fields.Many2one('res.currency', string="Currency", readonly=True)
date = fields.Date("Date", readonly=True)
amount = fields.Monetary("Amount", readonly=True)
unit_amount = fields.Float("Hours Spent", readonly=True)
amount = fields.Monetary("Amount", currency_field="currency_id", readonly=True)
unit_amount = fields.Float("Time Spent", readonly=True)
partner_id = fields.Many2one('res.partner', string="Partner", readonly=True)
milestone_id = fields.Many2one('project.milestone', related='task_id.milestone_id')
message_partner_ids = fields.Many2many('res.partner', compute='_compute_message_partner_ids',
search='_search_message_partner_ids', readonly=True)
@api.depends('project_id.message_partner_ids', 'task_id.message_partner_ids')
def _compute_message_partner_ids(self):
for line in self:
line.message_partner_ids = line.task_id.message_partner_ids | line.project_id.message_partner_ids
def _search_message_partner_ids(self, operator, value):
return self.env['account.analytic.line']._search_message_partner_ids(operator, value)
@property
def _table_query(self):
@ -37,7 +49,7 @@ class TimesheetsAnalysisReport(models.Model):
A.user_id AS user_id,
A.project_id AS project_id,
A.task_id AS task_id,
A.ancestor_task_id AS ancestor_task_id,
A.parent_task_id AS parent_task_id,
A.employee_id AS employee_id,
A.manager_id AS manager_id,
A.company_id AS company_id,
@ -45,7 +57,8 @@ class TimesheetsAnalysisReport(models.Model):
A.currency_id AS currency_id,
A.date AS date,
A.amount AS amount,
A.unit_amount AS unit_amount
A.unit_amount AS unit_amount,
A.partner_id AS partner_id
"""
@api.model
@ -56,25 +69,6 @@ class TimesheetsAnalysisReport(models.Model):
def _where(self):
return "WHERE A.project_id IS NOT NULL"
@api.model
def _get_view_cache_key(self, view_id=None, view_type='form', **options):
"""The override of _get_view changing the time field labels according to the company timesheet encoding UOM
makes the view cache dependent on the company timesheet encoding uom"""
key = super()._get_view_cache_key(view_id, view_type, **options)
return key + (self.env.company.timesheet_encode_uom_id,)
@api.model
def _get_view(self, view_id=None, view_type='form', **options):
arch, view = super()._get_view(view_id, view_type, **options)
if view_type in ["pivot", "graph"] and self.env.company.timesheet_encode_uom_id == self.env.ref("uom.product_uom_day"):
arch = self.env["account.analytic.line"]._apply_time_label(arch, related_model=self._name)
return arch, view
def init(self):
tools.drop_view_if_exists(self.env.cr, self._table)
self.env.cr.execute(
sql.SQL("CREATE or REPLACE VIEW {} as ({})").format(
sql.Identifier(self._table),
sql.SQL(self._table_query)
)
)
drop_view_if_exists(self.env.cr, self._table)
self.env.cr.execute(SQL("""CREATE or REPLACE VIEW %s as (%s)""", SQL.identifier(self._table), SQL(self._table_query)))