Initial commit: Hr packages

This commit is contained in:
Ernad Husremovic 2025-08-29 15:20:50 +02:00
commit 62531cd146
2820 changed files with 1432848 additions and 0 deletions

View file

@ -0,0 +1,137 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="portal_layout" name="Portal layout: timesheet menu entry" inherit_id="portal.portal_breadcrumbs" priority="35">
<xpath expr="//ol[hasclass('o_portal_submenu')]" position="inside">
<li t-if="page_name == 'timesheet' or timesheet" t-attf-class="breadcrumb-item #{'active' if not timesheet else ''}">
<t>Timesheets</t>
</li>
</xpath>
</template>
<template id="portal_my_home_timesheet" name="Show Timesheets" customize_show="True" inherit_id="portal.portal_my_home" priority="45">
<xpath expr="//div[hasclass('o_portal_docs')]" position="inside">
<t t-call="portal.portal_docs_entry">
<t t-set="title">Timesheets</t>
<t t-set="url" t-value="'/my/timesheets'"/>
<t t-set="placeholder_count" t-value="'timesheet_count'"/>
</t>
</xpath>
</template>
<template id="portal_my_timesheets" name="My Timesheets">
<t t-call="portal.portal_layout">
<t t-set="breadcrumbs_searchbar" t-value="True"/>
<t t-call="portal.portal_searchbar">
<t t-set="title">Timesheets</t>
</t>
<t t-if="not grouped_timesheets">
<div class="alert alert-warning mt8" role="alert">
There are no timesheets.
</div>
</t>
<t t-if="grouped_timesheets">
<t t-call="portal.portal_table">
<thead>
<tr>
<th t-if="not groupby == 'date'">Date</th>
<th t-if="not groupby == 'employee'">Employee</th>
<th t-if="not groupby == 'project'">Project</th>
<th t-if="not groupby == 'task'">Task</th>
<th>Description</th>
<th t-if="is_uom_day" class="text-end" t-att-colspan="2 if groupby != 'none' else 0">Days Spent</th>
<th t-else="" class="text-end" t-att-colspan="2 if groupby != 'none' else 0">Hours Spent</th>
</tr>
</thead>
<t t-foreach="grouped_timesheets" t-as="timesheets_with_hours">
<t t-set="timesheets" t-value="timesheets_with_hours[0]"/>
<t t-set="hours_spent" t-value="timesheets_with_hours[1]"/>
<tbody style="font-size: 0.8rem">
<tr t-if="not groupby =='none'" class="table-light">
<t t-if="groupby == 'project'">
<th t-if="groupby == 'project'" colspan="5">
<span t-field="timesheets[0].project_id.name"/>
</th>
<th colspan="1" class="text-end text-muted">
<t t-if="is_uom_day">
Total: <span class="text-muted" t-esc="timesheets._convert_hours_to_days(hours_spent)" t-options='{"widget": "timesheet_uom"}'/>
</t>
<t t-else="">
Total: <span class="text-muted" t-esc="hours_spent" t-options='{"widget": "float_time"}'/>
</t>
</th>
</t>
<t t-elif="groupby == 'task'">
<th colspan="5">
<span t-field="timesheets[0].task_id.name"/>
</th>
<th colspan="1" class="text-end text-muted">
<t t-if="is_uom_day">
Total: <span t-esc="timesheets._convert_hours_to_days(hours_spent)" t-options='{"widget": "timesheet_uom"}'/>
</t>
<t t-else="">
Total: <span t-esc="hours_spent" t-options='{"widget": "float_time"}'/>
</t>
</th>
</t>
<t t-elif="groupby == 'date'">
<th colspan="5">
<span t-field="timesheets[0].date"/>
</th>
<th colspan="1" class="text-end text-muted">
<t t-if="is_uom_day">
Total: <span t-esc="timesheets._convert_hours_to_days(hours_spent)" t-options='{"widget": "timesheet_uom"}'/>
</t>
<t t-else="">
Total: <span t-esc="hours_spent" t-options='{"widget": "float_time"}'/>
</t>
</th>
</t>
<t t-elif="groupby == 'employee'">
<th colspan="5">
<span t-field="timesheets[0].employee_id.name"/>
</th>
<th colspan="1" class="text-end text-muted">
<t t-if="is_uom_day">
Total: <span t-esc="timesheets._convert_hours_to_days(hours_spent)" t-options='{"widget": "timesheet_uom"}'/>
</t>
<t t-else="">
Total: <span t-esc="hours_spent" t-options='{"widget": "float_time"}'/>
</t>
</th>
</t>
</tr>
<tr t-else="">
<div style="text-align: right;" class="me-2 mb-1 text-muted">
<t t-if="is_uom_day">
Total: <span t-esc="timesheets._convert_hours_to_days(hours_spent)" t-options='{"widget": "timesheet_uom"}'/>
</t>
<t t-else="">
Total: <span t-esc="hours_spent" t-options='{"widget": "float_time"}'/>
</t>
</div>
</tr>
</tbody>
<tbody style="font-size: 0.8rem">
<t t-foreach="timesheets" t-as="timesheet">
<tr>
<td t-if="not groupby == 'date'"><span t-field="timesheet.date" t-options='{"widget": "date"}'/></td>
<td t-if="not groupby == 'employee'"><span t-field="timesheet.employee_id" t-att-title="timesheet.employee_id.display_name" /></td>
<td t-if="not groupby == 'project'"><span t-field="timesheet.project_id" t-att-title="timesheet.project_id.display_name"/></td>
<td t-if="not groupby == 'task'"><span t-field="timesheet.task_id" t-att-title="timesheet.task_id.display_name"/></td>
<td><span t-esc="timesheet.name" t-att-title="timesheet.name"/></td>
<td class="text-end" t-att-colspan="2 if groupby != 'none' else 0">
<span t-if="is_uom_day" t-esc="timesheet._get_timesheet_time_day()" t-options='{"widget": "timesheet_uom"}'/>
<span t-else="" t-field="timesheet.unit_amount" t-options='{"widget": "float_time"}'/>
</td>
</tr>
</t>
</tbody>
</t>
</t>
</t>
</t>
</template>
</odoo>

View file

@ -0,0 +1,421 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<!-- Timesheet root menus -->
<menuitem id="timesheet_menu_root"
name="Timesheets"
sequence="75"
groups="group_hr_timesheet_user"
web_icon="hr_timesheet,static/description/icon_timesheet.png"/>
<menuitem id="menu_hr_time_tracking"
name="Timesheets"
parent="timesheet_menu_root"
groups = "group_hr_timesheet_approver"
sequence="5"/>
<!--
Timesheet line Views
-->
<record id="hr_timesheet_line_tree" model="ir.ui.view">
<field name="name">account.analytic.line.tree.hr_timesheet</field>
<field name="model">account.analytic.line</field>
<field name="arch" type="xml">
<tree editable="bottom" string="Timesheet Activities" sample="1">
<field name="date"/>
<field name="employee_id" invisible="1"/>
<field name="project_id" required="1" options="{'no_create_edit': True, 'no_open': 1}"/>
<field name="task_id" optional="show" options="{'no_create_edit': True, 'no_open': True}" widget="task_with_hours" context="{'default_project_id': project_id}"/>
<field name="name" optional="show" required="0"/>
<field name="unit_amount" optional="show" widget="timesheet_uom" sum="Total" decoration-danger="unit_amount &gt; 24 or unit_amount &lt; 0"/>
<field name="company_id" invisible="1"/>
<field name="user_id" invisible="1"/>
</tree>
</field>
</record>
<record id="timesheet_view_tree_user" model="ir.ui.view">
<field name="name">account.analytic.line.view.tree.with.user</field>
<field name="model">account.analytic.line</field>
<field name="inherit_id" ref="hr_timesheet_line_tree"/>
<field name="mode">primary</field>
<field name="priority">10</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='employee_id']" position="attributes">
<attribute name="invisible">0</attribute>
<attribute name="required">1</attribute>
<attribute name="widget">many2one_avatar_employee</attribute>
<attribute name="context">{'active_test': True}</attribute>
</xpath>
</field>
</record>
<record id="view_hr_timesheet_line_pivot" model="ir.ui.view">
<field name="name">account.analytic.line.pivot</field>
<field name="model">account.analytic.line</field>
<field name="arch" type="xml">
<pivot string="Timesheets" sample="1">
<field name="employee_id" type="row"/>
<field name="date" interval="month" type="col"/>
<field name="unit_amount" type="measure" widget="timesheet_uom"/>
<field name="amount" string="Timesheet Costs"/>
</pivot>
</field>
</record>
<record id="view_my_timesheet_line_pivot" model="ir.ui.view">
<field name="name">account.analytic.line.pivot</field>
<field name="model">account.analytic.line</field>
<field name="arch" type="xml">
<pivot string="Timesheet" sample="1">
<field name="date" interval="week" type="row"/>
<field name="unit_amount" type="measure" widget="timesheet_uom"/>
<field name="amount" string="Timesheet Costs"/>
</pivot>
</field>
</record>
<record id="view_hr_timesheet_line_graph" model="ir.ui.view">
<field name="name">account.analytic.line.graph</field>
<field name="model">account.analytic.line</field>
<field name="arch" type="xml">
<graph string="Timesheets" sample="1" js_class="hr_timesheet_graphview">
<field name="task_id"/>
<field name="project_id"/>
<field name="unit_amount" type="measure" widget="timesheet_uom"/>
<field name="amount" string="Timesheet Costs"/>
</graph>
</field>
</record>
<!-- For My Timesheet view, groups by week then project -->
<record id="view_hr_timesheet_line_graph_my" model="ir.ui.view">
<field name="name">account.analytic.line.graph</field>
<field name="model">account.analytic.line</field>
<field name="arch" type="xml">
<graph string="Timesheet" sample="1" js_class="hr_timesheet_graphview">
<field name="date" interval="week"/>
<field name="project_id"/>
<field name="amount" type="measure" string="Timesheet Costs"/>
<field name="unit_amount" type="measure" widget="timesheet_uom"/>
</graph>
</field>
</record>
<!-- For Other Timesheet view, groups by employee then project -->
<record id="view_hr_timesheet_line_graph_all" model="ir.ui.view">
<field name="name">account.analytic.line.graph</field>
<field name="model">account.analytic.line</field>
<field name="arch" type="xml">
<graph string="Timesheet" sample="1" js_class="hr_timesheet_graphview">
<field name="employee_id"/>
<field name="project_id"/>
<field name="amount" type="measure" string="Timesheet Costs"/>
<field name="unit_amount" type="measure" widget="timesheet_uom"/>
</graph>
</field>
</record>
<record id="view_hr_timesheet_line_graph_by_employee" model="ir.ui.view">
<field name="name">account.analytic.line.graph.by.employee</field>
<field name="model">account.analytic.line</field>
<field name="inherit_id" ref="hr_timesheet.view_hr_timesheet_line_graph_all"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<field name="project_id" position="replace"/>
</field>
</record>
<record id="hr_timesheet_line_form" model="ir.ui.view">
<field name="name">account.analytic.line.form</field>
<field name="model">account.analytic.line</field>
<field name="priority">1</field>
<field name="inherit_id" eval="False"/>
<field name="arch" type="xml">
<form string="Analytic Entry">
<sheet string="Analytic Entry">
<group>
<group>
<field name="project_id" required="1"/>
<field name="task_id" widget="task_with_hours" context="{'default_project_id': project_id}"/>
<field name="name"/>
<field name="company_id" groups="base.group_multi_company" invisible="1"/>
</group>
<group>
<field name="date"/>
<field name="amount" invisible="1"/>
<field name="unit_amount" widget="timesheet_uom" decoration-danger="unit_amount &gt; 24"/>
<field name="currency_id" invisible="1"/>
<field name="company_id" invisible="1"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
<record id="timesheet_view_form_user" model="ir.ui.view">
<field name="name">account.analytic.line.tree.with.user</field>
<field name="model">account.analytic.line</field>
<field name="inherit_id" ref="hr_timesheet.hr_timesheet_line_form"/>
<field name="mode">primary</field>
<field name="priority">10</field>
<field name="arch" type="xml">
<xpath expr="//field[@name='date']" position="before">
<field name="employee_id" required="1" context="{'active_test': True}"/>
<field name="user_id" invisible="1"/>
</xpath>
</field>
</record>
<record id="hr_timesheet_line_search" model="ir.ui.view">
<field name="name">account.analytic.line.search</field>
<field name="model">account.analytic.line</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>
</field>
</record>
<record id="hr_timesheet_line_my_timesheet_search" model="ir.ui.view">
<field name="name">view.search.my.timesheet.menu</field>
<field name="model">account.analytic.line</field>
<field name="inherit_id" ref="hr_timesheet_line_search"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<field name="employee_id" position="replace"/>
<field name="department_id" position="replace"/>
<field name="manager_id" position="replace"/>
<filter name="mine" position="replace"/>
<filter name="groupby_department" position="replace"/>
<filter name="groupby_manager" position="replace"/>
<filter name="groupby_employee" position="replace"/>
</field>
</record>
<record id="view_kanban_account_analytic_line" model="ir.ui.view">
<field name="name">account.analytic.line.kanban</field>
<field name="model">account.analytic.line</field>
<field name="arch" type="xml">
<kanban class="o_kanban_mobile" sample="1">
<field name="date"/>
<field name="employee_id"/>
<field name="user_id"/>
<field name="name"/>
<field name="project_id"/>
<field name="task_id" context="{'default_project_id': project_id}" domain="[('project_id', '=', project_id)]"/>
<field name="unit_amount" widget="timesheet_uom"/>
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_global_click o_kanban_record_has_image_fill px-0 pb-0">
<div class="oe_kanban_details d-flex flex-column">
<div class="o_kanban_record_top px-2 h-50">
<img t-att-src="kanban_image('hr.employee', 'avatar_128', record.employee_id.raw_value)" t-att-title="record.employee_id.value" t-att-alt="record.employee_id.value" class="o_image_40_cover float-start"/>
<div class="o_kanban_record_headings ps-4 pe-2">
<div class="text-truncate">
<strong class="o_kanban_record_title">
<span t-esc="record.project_id.value" t-att-title="record.project_id.value"/>
</strong>
</div>
<div class="text-truncate">
<i><span t-esc="record.task_id.value" t-att-title="record.task_id.value"/></i>
</div>
<div class="text-truncate">
<span t-esc="record.name.value" t-att-title="record.name.value"/>
</div>
</div>
</div>
<div class="o_kanban_record_bottom d-block mx-2">
<hr class="mt4 mb4"/>
<span>
<i class="fa fa-calendar me-1" role="img" aria-label="Date" title="Date"></i>
<t t-esc="record.date.value"/>
</span>
<span class="float-end" name="duration">
<strong>Duration: </strong><field name="unit_amount" widget="timesheet_uom" decoration-danger="unit_amount &gt; 24"/>
</span>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<!--
Menus and Actions
-->
<record id="act_hr_timesheet_line" model="ir.actions.act_window">
<field name="name">My Timesheets</field>
<field name="res_model">account.analytic.line</field>
<field name="view_mode">tree,form,kanban,pivot,graph</field>
<field name="domain">[('project_id', '!=', False), ('user_id', '=', uid)]</field>
<field name="context">{
"search_default_week":1,
"is_timesheet": 1,
}</field>
<field name="search_view_id" ref="hr_timesheet_line_my_timesheet_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No timesheets found. Let's create one!
</p>
<p>
Keep track of your working hours by project every day and bill your customers for that time.
</p>
</field>
</record>
<record id="act_hr_timesheet_line_view_tree" model="ir.actions.act_window.view">
<field name="view_mode">tree</field>
<field name="sequence" eval="4"/>
<field name="view_id" ref="hr_timesheet_line_tree"/>
<field name="act_window_id" ref="act_hr_timesheet_line"/>
</record>
<record id="act_hr_timesheet_line_view_form" model="ir.actions.act_window.view">
<field name="view_mode">form</field>
<field name="sequence" eval="5"/>
<field name="view_id" ref="hr_timesheet_line_form"/>
<field name="act_window_id" ref="act_hr_timesheet_line"/>
</record>
<record id="act_hr_timesheet_line_view_kanban" model="ir.actions.act_window.view">
<field name="view_mode">kanban</field>
<field name="sequence">6</field>
<field name="view_id" ref="hr_timesheet.view_kanban_account_analytic_line"/>
<field name="act_window_id" ref="act_hr_timesheet_line"/>
</record>
<record id="act_hr_timesheet_line_view_pivot" model="ir.actions.act_window.view">
<field name="view_mode">pivot</field>
<field name="sequence">7</field>
<field name="view_id" ref="hr_timesheet.view_my_timesheet_line_pivot"/>
<field name="act_window_id" ref="act_hr_timesheet_line"/>
</record>
<record id="act_hr_timesheet_line_view_graph" model="ir.actions.act_window.view">
<field name="view_mode">graph</field>
<field name="sequence">8</field>
<field name="view_id" ref="hr_timesheet.view_hr_timesheet_line_graph_my"/>
<field name="act_window_id" ref="act_hr_timesheet_line"/>
</record>
<menuitem id="timesheet_menu_activity_mine"
name="My Timesheets"
groups="group_hr_timesheet_approver"
parent="menu_hr_time_tracking"
action="act_hr_timesheet_line"/>
<menuitem id="timesheet_menu_activity_user"
name="My Timesheets"
groups="group_hr_timesheet_user"
parent="timesheet_menu_root"
action="act_hr_timesheet_line"/>
<record id="timesheet_action_task" model="ir.actions.act_window">
<field name="name">Task's Timesheets</field>
<field name="res_model">account.analytic.line</field>
<field name="context">{
'is_timesheet': 1,
}</field>
<field name="domain">[('task_id', 'in', active_ids)]</field>
<field name="view_mode">tree</field>
<field name="view_id" ref="timesheet_view_tree_user"/>
</record>
<record id="timesheet_action_project" model="ir.actions.act_window">
<field name="name">Project's Timesheets</field>
<field name="res_model">account.analytic.line</field>
<field name="context">{
'is_timesheet': 1,
}</field>
<field name="domain">[('project_id', 'in', active_ids)]</field>
<field name="view_mode">tree</field>
<field name="view_id" ref="timesheet_view_tree_user"/>
</record>
<record id="timesheet_action_all" model="ir.actions.act_window">
<field name="name">All Timesheets</field>
<field name="res_model">account.analytic.line</field>
<field name="view_mode">tree,form,kanban,pivot,graph</field>
<field name="search_view_id" ref="hr_timesheet_line_search"/>
<field name="domain">[('project_id', '!=', False)]</field>
<field name="context">{
'search_default_week':1,
'is_timesheet': 1,
}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No timesheets found. Let's create one!
</p>
<p>
Keep track of your working hours by project every day and bill your customers for that time.
</p>
</field>
</record>
<record id="timesheet_action_view_all_tree" model="ir.actions.act_window.view">
<field name="sequence" eval="4"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="timesheet_view_tree_user"/>
<field name="act_window_id" ref="timesheet_action_all"/>
</record>
<record id="timesheet_action_view_all_form" model="ir.actions.act_window.view">
<field name="sequence" eval="5"/>
<field name="view_mode">form</field>
<field name="view_id" ref="timesheet_view_form_user"/>
<field name="act_window_id" ref="timesheet_action_all"/>
</record>
<record id="timesheet_action_view_all_kanban" model="ir.actions.act_window.view">
<field name="view_mode">kanban</field>
<field name="sequence">6</field>
<field name="view_id" ref="hr_timesheet.view_kanban_account_analytic_line"/>
<field name="act_window_id" ref="timesheet_action_all"/>
</record>
<record id="timesheet_action_view_all_pivot" model="ir.actions.act_window.view">
<field name="sequence" eval="7"/>
<field name="view_mode">pivot</field>
<field name="view_id" ref="view_hr_timesheet_line_pivot"/>
<field name="act_window_id" ref="timesheet_action_all"/>
</record>
<record id="timesheet_action_view_all_graph" model="ir.actions.act_window.view">
<field name="sequence" eval="8"/>
<field name="view_mode">graph</field>
<field name="view_id" ref="view_hr_timesheet_line_graph_all"/>
<field name="act_window_id" ref="timesheet_action_all"/>
</record>
<menuitem id="timesheet_menu_activity_all"
name="All Timesheets"
parent="menu_hr_time_tracking"
action="timesheet_action_all"
groups="hr_timesheet.group_hr_timesheet_approver"/>
</data>
</odoo>

View file

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="timesheet_action_from_employee" model="ir.actions.act_window">
<field name="name">Timesheets</field>
<field name="res_model">account.analytic.line</field>
<field name="search_view_id" ref="hr_timesheet_line_search"/>
<field name="domain">[('project_id', '!=', False)]</field>
<field name="context">{
'search_default_month':1,
'search_default_employee_id': [active_id],
'default_employee_id': active_id,
"is_timesheet": 1,
}</field>
</record>
<record id="timesheet_action_view_from_employee_list" model="ir.actions.act_window.view">
<field name="sequence" eval="5"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="hr_timesheet_line_tree"/>
<field name="act_window_id" ref="timesheet_action_from_employee"/>
</record>
<record id="timesheet_action_view_from_employee_form" model="ir.actions.act_window.view">
<field name="sequence" eval="10"/>
<field name="view_mode">form</field>
<field name="view_id" ref="hr_timesheet_line_form"/>
<field name="act_window_id" ref="timesheet_action_from_employee"/>
</record>
<record id="hr_employee_view_form_inherit_timesheet" model="ir.ui.view">
<field name="name">hr.employee.form.timesheet</field>
<field name="model">hr.employee</field>
<field name="inherit_id" ref="hr_hourly_cost.view_employee_form"/>
<field name="priority" eval="40"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button class="oe_stat_button" type="action" name="%(timesheet_action_from_employee)d" icon="fa-calendar" groups="hr_timesheet.group_hr_timesheet_user">
<div class="o_stat_info">
<span class="o_stat_text">Timesheets</span>
</div>
</button>
</xpath>
</field>
</record>
<record id="hr_department_view_kanban" model="ir.ui.view">
<field name="name">hr.department.kanban.inherit</field>
<field name="model">hr.department</field>
<field name="inherit_id" ref="hr.hr_department_view_kanban"/>
<field name="arch" type="xml">
<data>
<xpath expr="//div[hasclass('o_kanban_manage_reports')]" position="inside">
<a name="%(act_hr_timesheet_report)d" type="action"
groups="hr_timesheet.group_timesheet_manager" class="dropdown-item"
context="{ 'search_default_department_id': [active_id], 'default_department_id': active_id}">
Timesheets
</a>
</xpath>
</data>
</field>
</record>
</odoo>

View file

@ -0,0 +1,114 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="portal_my_task" inherit_id="project.portal_my_task" name="Portal: My Task with Timesheets">
<xpath expr="//li[@id='task-nav']" position="before">
<li t-if="timesheets and allow_timesheets" class="list-group-item d-grid flex-grow-1" id='nav-report'>
<div class="o_download_pdf d-flex gap-1">
<a class="btn btn-secondary flex-fill o_download_btn" t-att-href="task.get_portal_url(report_type='pdf', download=True)" title="Download"><i class="fa fa-download"/> Download</a>
<a class="btn btn-secondary flex-fill o_print_btn o_project_timesheet_print" t-att-href="task.get_portal_url(report_type='pdf')" href="#" title="Print" target="_blank"><i class="fa fa-print"/> Print</a>
</div>
</li>
</xpath>
<xpath expr="//li[@id='nav-header']" position="after">
<li t-if="timesheets and allow_timesheets" class="nav-item">
<a class="nav-link ps-3" href="#task_timesheets">
Timesheets
</a>
</li>
</xpath>
<xpath expr="//div[@id='card_body']" position="inside">
<div class="container" t-if="timesheets and allow_timesheets">
<hr class="mt-4 mb-1"/>
<h5 id="task_timesheets" class="mt-2 mb-2" data-anchor="true">Timesheets</h5>
<t t-call="hr_timesheet.portal_timesheet_table"/>
</div>
</xpath>
<xpath expr="//div[@name='portal_my_task_planned_hours']" position="after">
<div t-if="task.planned_hours > 0"><strong>Progress:</strong> <span t-field="task.progress"/>%</div>
</xpath>
<xpath expr="//div[@name='portal_my_task_planned_hours']/t" position="replace">
<t t-call="hr_timesheet.portal_my_task_planned_hours_template"></t>
</xpath>
</template>
<template id="portal_my_task_planned_hours_template">
<t t-if="is_uom_day and timesheets._convert_hours_to_days(task.planned_hours) > 0">
<strong>Allocated Days:</strong> <span t-esc="timesheets._convert_hours_to_days(task.planned_hours)" t-options='{"widget": "timesheet_uom"}'/>
</t>
<t t-if="not is_uom_day and task.planned_hours > 0" t-call="project.portal_my_task_planned_hours_template"></t>
</template>
<template id="portal_tasks_list_inherit" inherit_id="project.portal_tasks_list" name="Portal: My Tasks with Timesheets">
<xpath expr="//t[@t-foreach='tasks']/tr" position="before">
<t t-set="timesheet_ids" t-value="task.sudo().timesheet_ids"/>
<t t-set="is_uom_day" t-value="timesheet_ids._is_timesheet_encode_uom_day()"/>
</xpath>
<xpath expr="//thead/tr/t[@t-set='number_of_header']" position="attributes">
<attribute name="t-value">9</attribute>
</xpath>
<xpath expr="//thead/tr/th[@name='project_portal_milestones']" position="after">
<th t-if="is_uom_day" class="text-end">Days Spent</th>
<th t-else="" class="text-end">Hours Spent</th>
</xpath>
<xpath expr="//tbody/t/tr/td[@name='project_portal_milestones']" position="after">
<td class="text-end">
<t t-if="is_uom_day">
<t t-out="timesheet_ids._convert_hours_to_days(task.effective_hours)"/>
<span t-if="task.planned_hours > 0"> / <t t-out="timesheet_ids._convert_hours_to_days(task.planned_hours)"/></span>
</t>
<t t-else="">
<span t-field="task.effective_hours" t-options='{"widget": "float_time"}'/>
<t t-if="task.planned_hours > 0">
/
<span t-field="task.planned_hours" t-options='{"widget": "float_time"}'/>
</t>
</t>
</td>
</xpath>
</template>
<template id="portal_timesheet_table" name="Portal Timesheet Table">
<table class="o_portal_my_doc_table table table-sm">
<thead>
<tr>
<th>Date</th>
<th>Employee</th>
<th>Description</th>
<th t-if="is_uom_day" class="text-end">Days Spent</th>
<th t-else="" class="text-end">Hours Spent</th>
</tr>
</thead>
<tr t-foreach="timesheets" t-as="timesheet">
<td><t t-esc="timesheet.date" t-options='{"widget": "date"}'/></td>
<td t-attf-title="#{timesheet.employee_id.name}"><t t-esc="timesheet.employee_id.name"/></td>
<td><t t-esc="timesheet.name"/></td>
<td class="text-end">
<span t-if="is_uom_day" t-esc="timesheet._get_timesheet_time_day()" t-options='{"widget": "timesheet_uom"}'/>
<span t-else="" t-field="timesheet.unit_amount" t-options='{"widget": "float_time"}'/>
</td>
</tr>
<tfoot>
<tr>
<th colspan="3"></th>
<th class="text-end">
<t t-set="timesheets_amount" t-value="round(sum(timesheets.mapped('unit_amount')), 2) or 0.0"></t>
<div t-if="is_uom_day"><strong>Days Spent:</strong> <span t-esc="timesheets._convert_hours_to_days(timesheets_amount)" t-options='{"widget": "timesheet_uom"}'/></div>
<div t-else=""><strong>Hours Spent:</strong> <span t-esc="timesheets_amount" t-options='{"widget": "float_time"}'/></div>
<t t-set="timesheets_by_subtask_amount" t-value="round(sum(sum(timesheet_by_subtask.mapped('unit_amount') or 0.0) for timesheet_by_subtask in timesheets_by_subtask.values()), 2) or 0.0"></t>
<div t-if="timesheets_by_subtask">
<div t-if="is_uom_day">Days recorded on sub-tasks: <span t-esc="timesheets._convert_hours_to_days(timesheets_by_subtask_amount)" t-options='{"widget": "timesheet_uom"}'/></div>
<div t-else="">Hours recorded on sub-tasks: <span t-esc="timesheets_by_subtask_amount" t-options='{"widget": "float_time"}'/></div>
</div>
<t t-set="planned_time" t-value="task.planned_hours"></t>
<div t-if="planned_time > 0" name="planned_time" t-attf-class="{{task.remaining_hours &lt; 0 and 'text-danger' or ''}}">
<div t-if="is_uom_day">Remaining Days: <span t-esc="timesheets._convert_hours_to_days(planned_time - timesheets_amount - timesheets_by_subtask_amount)" t-options='{"widget": "timesheet_uom"}'/></div>
<div t-else="">Remaining Hours: <span t-esc="planned_time - timesheets_amount - timesheets_by_subtask_amount" t-options='{"widget": "float_time"}'/></div>
</div>
</th>
</tr>
</tfoot>
</table>
</template>
</odoo>

View file

@ -0,0 +1,177 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="project_sharing_inherit_project_task_view_form" model="ir.ui.view">
<field name="name">project.sharing.project.task.view.form.inherit</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.project_sharing_project_task_view_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='child_ids']/tree/field[@name='portal_user_names']" position="after">
<field name="planned_hours" widget="timesheet_uom_no_toggle" sum="Initially Planned Hours" optional="hide"/>
<field name="effective_hours" widget="timesheet_uom" sum="Effective Hours" optional="hide"/>
<field name="subtask_effective_hours" string="Sub-tasks Hours Spent" widget="timesheet_uom" sum="Sub-tasks Hours Spent" optional="hide"/>
<field name="total_hours_spent" string="Total Hours" widget="timesheet_uom" sum="Total Hours" optional="hide"/>
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress &gt;= 100" decoration-warning="progress &gt;= 80 and progress &lt; 100"/>
<field name="progress" widget="progressbar" optional="hide"/>
</xpath>
<xpath expr="//notebook/page[@name='description_page']" position="after">
<field name="analytic_account_active" invisible="1"/>
<field name="allow_timesheets" invisible="1"/>
<field name="encode_uom_in_days" invisible="1"/>
<field name="subtask_count" invisible="1"/>
<page string="Timesheets" id="timesheets_tab" attrs="{'invisible': [('allow_timesheets', '=', False)]}">
<group>
<group>
<div colspan="2">
<label for="planned_hours" string="Allocated Hours" class="me-2" attrs="{'invisible': [('encode_uom_in_days', '=', True)]}"/>
<label for="planned_hours" string="Allocated Days" class="me-2" attrs="{'invisible': [('encode_uom_in_days', '=', False)]}"/>
<field name="planned_hours" class="o_field_float_time oe_inline ms-2" widget="timesheet_uom_no_toggle"/>
<span attrs="{'invisible': ['|', ('allow_subtasks', '=', False), ('subtask_count', '=', 0)]}">
(incl. <field name="subtask_planned_hours" nolabel="1" groups="project.group_subtask_project" widget="timesheet_uom_no_toggle" class="oe_inline"/> on
<span class="fw-bold text-dark"> Sub-tasks</span>)
</span>
</div>
</group>
<group>
<field name="progress" widget="progressbar"/>
</group>
</group>
<field name="timesheet_ids" mode="tree,kanban"
attrs="{'invisible': [('analytic_account_active', '=', False)]}">
<tree string="Timesheet Activities" default_order="date" no_open="1" create="false" delete="0">
<field name="date"/>
<field name="employee_id"/>
<field name="name"/>
<field name="unit_amount" widget="timesheet_uom" decoration-danger="unit_amount &gt; 24"/>
</tree>
<kanban class="o_kanban_mobile">
<field name="date"/>
<field name="employee_id"/>
<field name="name"/>
<field name="unit_amount" decoration-danger="unit_amount &gt; 24"/>
<field name="project_id"/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_card oe_kanban_global_click">
<div class="row">
<div class="col-6">
<strong><span><t t-esc="record.employee_id.value"/></span></strong>
</div>
<div class="col-6 float-end text-end">
<strong><t t-esc="record.date.value"/></strong>
</div>
</div>
<div class="row">
<div class="col-6 text-muted">
<span><t t-esc="record.name.value"/></span>
</div>
<div class="col-6">
<span class="float-end text-end">
<field name="unit_amount" widget="float_time"/>
</span>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
<group attrs="{'invisible': [('analytic_account_active', '=', False)]}">
<group class="oe_subtotal_footer oe_right" name="project_hours">
<span class="o_td_label float-start">
<label class="fw-bold" for="effective_hours" string="Hours Spent" attrs="{'invisible': [('encode_uom_in_days', '=', True)]}"/>
<label class="fw-bold" for="effective_hours" string="Days Spent" attrs="{'invisible': [('encode_uom_in_days', '=', False)]}"/>
</span>
<field name="effective_hours" widget="timesheet_uom" nolabel="1"/>
<!-- remove o_form_subtask_button class from master-->
<button name="action_view_subtask_timesheet" type="object" class="o_form_subtask_button ps-0 border-0 oe_inline oe_link mb-2 o_td_label float-start" attrs="{'invisible' : ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}">
<span class="text-nowrap" attrs="{'invisible' : [('encode_uom_in_days', '=', True)]}">Hours Spent on Sub-tasks:</span>
<span class="text-nowrap" attrs="{'invisible' : [('encode_uom_in_days', '=', False)]}">Days Spent on Sub-tasks:</span>
</button>
<field name="subtask_effective_hours" class="mt-2" widget="timesheet_uom"
attrs="{'invisible' : ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}" nolabel="1"/>
<span id="total_hours_spent_label" attrs="{'invisible': ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}" class="o_td_label float-start">
<label class="fw-bold" for="total_hours_spent" string="Total Hours"
attrs="{'invisible': [('encode_uom_in_days', '=', True)]}"/>
<label class="fw-bold" for="total_hours_spent" string="Total Days"
attrs="{'invisible': [('encode_uom_in_days', '=', False)]}"/>
</span>
<field name="total_hours_spent" widget="timesheet_uom" class="oe_subtotal_footer_separator" nolabel="1"
attrs="{'invisible' : ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}" />
<span class="o_td_label float-start">
<label class="fw-bold" for="remaining_hours" string="Remaining Hours"
attrs="{'invisible': ['|', '|', ('planned_hours', '=', 0.0), ('encode_uom_in_days', '=', True), ('remaining_hours', '&lt;', 0)]}"/>
<label class="fw-bold" for="remaining_hours" string="Remaining Days"
attrs="{'invisible': ['|', '|', ('planned_hours', '=', 0.0), ('encode_uom_in_days', '=', False), ('remaining_hours', '&lt;', 0)]}"/>
<label class="fw-bold text-danger" for="remaining_hours" string="Remaining Hours"
attrs="{'invisible': ['|', '|', ('planned_hours', '=', 0.0), ('encode_uom_in_days', '=', True), ('remaining_hours', '&gt;=', 0)]}"/>
<label class="fw-bold text-danger" for="remaining_hours" string="Remaining Days"
attrs="{'invisible': ['|', '|', ('planned_hours', '=', 0.0), ('encode_uom_in_days', '=', False), ('remaining_hours', '&gt;=', 0)]}"/>
</span>
<field name="remaining_hours" widget="timesheet_uom" class="oe_subtotal_footer_separator"
attrs="{'invisible' : [('planned_hours', '=', 0.0)]}" nolabel="1"/>
</group>
</group>
</page>
</xpath>
</field>
</record>
<record id="project_sharing_kanban_inherit_project_task_view_kanban" model="ir.ui.view">
<field name="name">project.sharing.project.task.timesheet.kanban.inherited</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.project_sharing_project_task_view_kanban"/>
<field name="arch" type="xml">
<templates position="before">
<field name="progress" />
<field name="remaining_hours" />
<field name="planned_hours" />
<field name="allow_timesheets"/>
<field name="encode_uom_in_days" invisible="1"/>
</templates>
<div class="oe_kanban_bottom_left" position="inside">
<t name="planned_hours" t-if="record.planned_hours.raw_value &gt; 0 and record.allow_timesheets.raw_value">
<t t-set="badge" t-value="'border border-success'"/>
<t t-set="badge" t-value="'border border-warning'" t-if="record.progress.raw_value &gt;= 80 and record.progress.raw_value &lt;= 100"/>
<t t-set="badge" t-value="'border border-danger'" t-if="record.remaining_hours.raw_value &lt; 0"/>
<t t-set="title" t-value="'Remaining days'" t-if="record.encode_uom_in_days.raw_value"/>
<t t-set="title" t-value="'Remaining hours'" t-else=""/>
<div t-attf-class="oe_kanban_align badge {{ badge }}" t-att-title="title">
<field name="remaining_hours" widget="timesheet_uom" />
</div>
</t>
</div>
</field>
</record>
<record id="project_sharing_inherit_project_task_view_tree" model="ir.ui.view">
<field name="name">project.task.tree.inherited</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.project_sharing_project_task_view_tree" />
<field name="arch" type="xml">
<field name="portal_user_names" position="after">
<field name="allow_subtasks" invisible="1"/>
<field name="allow_timesheets" invisible="1"/>
<field name="planned_hours" widget="timesheet_uom_no_toggle" sum="Initially Planned Hours" optional="hide" attrs="{'column_invisible': [('allow_timesheets', '=', False)]}"/>
<field name="effective_hours" widget="timesheet_uom" sum="Effective Hours" optional="hide" attrs="{'column_invisible': [('allow_timesheets', '=', False)]}"/>
<field name="subtask_effective_hours" widget="timesheet_uom" attrs="{'column_invisible' : ['|', ('allow_subtasks', '=', False), ('allow_timesheets', '=', False)]}" optional="hide"/>
<field name="total_hours_spent" widget="timesheet_uom" attrs="{'column_invisible' : ['|', ('allow_subtasks', '=', False), ('allow_timesheets', '=', False)]}" optional="hide"/>
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress &gt;= 100" decoration-warning="progress &gt;= 80 and progress &lt; 100" attrs="{'column_invisible': [('allow_timesheets', '=', False)]}"/>
<field name="progress" widget="progressbar" attrs="{'column_invisible': [('allow_timesheets', '=', False)]}" optional="hide"/>
</field>
</field>
</record>
<record id="project_sharing_project_task_view_search_inherit_timesheet" model="ir.ui.view">
<field name="name">project.sharing.project.task.view.search.inherit.timesheet</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.project_sharing_project_task_view_search"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='late']" position='after'>
<filter string="Tasks in Overtime" name="overtime" domain="[('overtime', '&gt;', 0)]"/>
<filter string="Tasks Soon in Overtime" name="remaining_hours_percentage" domain="[('remaining_hours_percentage', '&lt;=', 0.2)]"/>
</xpath>
</field>
</record>
</odoo>

View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="project_update_view_search_inherit" model="ir.ui.view">
<field name="name">project.update.view.search.inherit</field>
<field name="model">project.update</field>
<field name="inherit_id" ref="project.project_update_view_search"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='my_updates']" position='after'>
<filter string="My Team's Updates" name="my_team_updates" domain="[('user_id.employee_parent_id.user_id', '=', uid)]"/>
<filter string="My Department's Updates" name="my_department_updates" domain="[('user_id.employee_id.member_of_department', '=', True)]"/>
</xpath>
</field>
</record>
</data>
</odoo>

View file

@ -0,0 +1,439 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="act_hr_timesheet_line_by_project" model="ir.actions.act_window">
<field name="name">Timesheets</field>
<field name="res_model">account.analytic.line</field>
<field name="view_mode">tree,kanban,pivot,graph,form</field>
<field name="view_id" ref="timesheet_view_tree_user"/>
<field name="domain">[('project_id', '=', active_id)]</field>
<field name="context">{
"default_project_id": active_id,
"is_timesheet": 1,
}</field>
<field name="search_view_id" ref="hr_timesheet_line_search"/>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
Record a new activity
</p><p>
Track your working hours by projects every day and invoice this time to your customers.
</p>
</field>
</record>
<record id="act_hr_timesheet_line_by_project_view_tree" model="ir.actions.act_window.view">
<field name="view_mode">tree</field>
<field name="sequence" eval="1"/>
<field name="view_id" ref="timesheet_view_tree_user"/>
<field name="act_window_id" ref="act_hr_timesheet_line_by_project"/>
</record>
<record id="act_hr_timesheet_line_by_project_view_kanban" model="ir.actions.act_window.view">
<field name="view_mode">kanban</field>
<field name="sequence" eval="2"/>
<field name="view_id" ref="view_kanban_account_analytic_line"/>
<field name="act_window_id" ref="act_hr_timesheet_line_by_project"/>
</record>
<record id="act_hr_timesheet_line_by_project_view_pivot" model="ir.actions.act_window.view">
<field name="view_mode">pivot</field>
<field name="sequence" eval="3"/>
<field name="view_id" ref="view_hr_timesheet_line_pivot"/>
<field name="act_window_id" ref="act_hr_timesheet_line_by_project"/>
</record>
<record id="act_hr_timesheet_line_by_project_view_graph" model="ir.actions.act_window.view">
<field name="view_mode">graph</field>
<field name="sequence" eval="4"/>
<field name="view_id" ref="view_hr_timesheet_line_graph_all"/>
<field name="act_window_id" ref="act_hr_timesheet_line_by_project"/>
</record>
<record id="act_hr_timesheet_line_by_project_view_form" model="ir.actions.act_window.view">
<field name="view_mode">form</field>
<field name="sequence" eval="10"/>
<field name="view_id" ref="hr_timesheet_line_form"/>
<field name="act_window_id" ref="act_hr_timesheet_line_by_project"/>
</record>
<record id="project_project_view_form_simplified_inherit_timesheet" model="ir.ui.view">
<field name="name">project.project.view.form.simplified.inherit.timesheet</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.project_project_view_form_simplified"/>
<field name="priority">24</field>
<field name="arch" type="xml">
<xpath expr="//div[hasclass('o_settings_container')]" position="inside">
<div class="col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="allow_timesheets"/>
</div>
<div class="o_setting_right_pane">
<label for="allow_timesheets" string="Timesheets"/>
<div class="text-muted">
Log time on tasks
</div>
</div>
</div>
</xpath>
</field>
</record>
<record id="project_invoice_form" model="ir.ui.view">
<field name="name">Inherit project form : Invoicing Data</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.edit_project"/>
<field name="priority">24</field>
<field name="arch" type="xml">
<xpath expr="//div[@name='dates']" position="after">
<field name="allocated_hours" widget="timesheet_uom_no_toggle" attrs="{'invisible': [('allow_timesheets', '=', False)]}"/>
</xpath>
<xpath expr="//group[@name='group_time_managment']" position="attributes">
<attribute name="invisible">0</attribute>
</xpath>
<xpath expr="//group[@name='group_time_managment']" position="inside">
<div class="o_setting_box" id="timesheet_settings" colspan="2">
<div class="o_setting_left_pane">
<field name="allow_timesheets"/>
</div>
<div class="o_setting_right_pane">
<label for="allow_timesheets" string="Timesheets"/>
<div class="text-muted">
Log time on tasks
</div>
</div>
</div>
</xpath>
</field>
</record>
<record model="ir.ui.view" id="view_task_form2_inherited">
<field name="name">project.task.form.inherited</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_form2" />
<field name="arch" type="xml">
<xpath expr="//field[@name='child_ids']/tree/field[@name='company_id']" position="after">
<field name="planned_hours" widget="timesheet_uom_no_toggle" sum="Initially Planned Hours" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
<field name="effective_hours" widget="timesheet_uom" sum="Effective Hours" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
<field name="subtask_effective_hours" string="Sub-tasks Hours Spent" widget="timesheet_uom" sum="Sub-tasks Hours Spent" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
<field name="total_hours_spent" string="Total Hours" widget="timesheet_uom" sum="Total Hours" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress &gt;= 100" decoration-warning="progress &gt;= 80 and progress &lt; 100" groups="hr_timesheet.group_hr_timesheet_user"/>
<field name="progress" widget="progressbar" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
</xpath>
<xpath expr="//notebook/page[@name='description_page']" position="after">
<field name="allow_timesheets" invisible="1"/>
<field name="allow_subtasks" invisible="1"/>
<t groups="hr_timesheet.group_hr_timesheet_user">
<field name="analytic_account_active" invisible="1"/>
<field name="encode_uom_in_days" invisible="1"/>
<field name="subtask_count" invisible="1"/>
</t>
<page string="Timesheets" id="timesheets_tab" attrs="{'invisible': [('allow_timesheets', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user">
<group>
<group>
<label for="planned_hours" string="Allocated Hours" attrs="{'invisible': [('encode_uom_in_days', '=', True)]}"/>
<label for="planned_hours" string="Allocated Days" attrs="{'invisible': [('encode_uom_in_days', '=', False)]}"/>
<div class="o_row">
<field name="planned_hours" class="o_field_float_time oe_inline ms-2" widget="timesheet_uom_no_toggle"/>
<span attrs="{'invisible': ['|', ('allow_subtasks', '=', False), ('subtask_count', '=', 0)]}">
(incl. <field name="subtask_planned_hours" nolabel="1" groups="project.group_subtask_project" widget="timesheet_uom_no_toggle" class="oe_inline"/> on
<span class="fw-bold text-dark"> Sub-tasks</span>)
</span>
</div>
</group>
<group>
<field name="progress" widget="progressbar"/>
</group>
</group>
<group name="timesheet_error" attrs="{'invisible': [('analytic_account_active', '!=', False)]}">
<div class="alert alert-warning" role="alert" colspan="2">
You cannot log timesheets on this project since it is linked to an inactive analytic account. Please change this account, or reactivate the current one to timesheet on the project.
</div>
</group>
<field name="timesheet_ids" mode="tree,kanban" attrs="{'invisible': [('analytic_account_active', '=', False)]}" context="{'default_project_id': project_id, 'default_name':''}">
<tree editable="bottom" string="Timesheet Activities" default_order="date">
<field name="date"/>
<field name="user_id" invisible="1"/>
<field name="employee_id" required="1" widget="many2one_avatar_employee" context="{'active_test': True}" options="{'relation': 'hr.employee.public'}" groups="!hr.group_hr_user"/>
<field name="employee_id" required="1" widget="many2one_avatar_employee" context="{'active_test': True}" options="{'relation': 'hr.employee'}" groups="hr.group_hr_user"/>
<field name="name" required="0"/>
<field name="unit_amount" widget="timesheet_uom" decoration-danger="unit_amount &gt; 24 or unit_amount &lt; 0"/>
<field name="project_id" invisible="1"/>
<field name="task_id" invisible="1"/>
<field name="company_id" invisible="1"/>
</tree>
<kanban class="o_kanban_mobile">
<field name="date"/>
<field name="user_id"/>
<field name="name"/>
<field name="unit_amount" decoration-danger="unit_amount &gt; 24"/>
<field name="project_id"/>
<field name="task_id" invisible="1"/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_card oe_kanban_global_click">
<div class="row">
<div class="col-6">
<field name="employee_id" widget="many2one_avatar_employee" context="{'active_test': True}" options="{'relation': 'hr.employee.public'}" groups="!hr.group_hr_user"/>
<field name="employee_id" widget="many2one_avatar_employee" context="{'active_test': True}" options="{'relation': 'hr.employee'}" groups="hr.group_hr_user"/>
<strong><span><t t-esc="record.employee_id.value"/></span></strong>
</div>
<div class="col-6 float-end text-end">
<strong><t t-esc="record.date.value"/></strong>
</div>
</div>
<div class="row">
<div class="col-6 text-muted">
<span><t t-esc="record.name.value"/></span>
</div>
<div class="col-6">
<span class="float-end text-end">
<field name="unit_amount" widget="float_time"/>
</span>
</div>
</div>
</div>
</t>
</templates>
</kanban>
<form string="Timesheet Activities">
<sheet>
<group>
<field name="date"/>
<field name="user_id" invisible="1"/>
<field name="employee_id" required="1" widget="many2one_avatar_employee" context="{'active_test': True}" options="{'relation': 'hr.employee.public'}" groups="!hr.group_hr_user"/>
<field name="employee_id" required="1" widget="many2one_avatar_employee" context="{'active_test': True}" options="{'relation': 'hr.employee'}" groups="hr.group_hr_user"/>
<field name="name" required="0"/>
<field name="unit_amount" string="Duration" widget="float_time" decoration-danger="unit_amount &gt; 24"/>
<field name="project_id" invisible="1"/>
<field name="task_id" invisible="1"/>
<field name="company_id" invisible="1"/>
</group>
</sheet>
</form>
</field>
<group attrs="{'invisible': [('analytic_account_active', '=', False)]}">
<group class="oe_subtotal_footer oe_right" name="project_hours">
<span class="o_td_label float-start">
<label class="fw-bold" for="effective_hours" string="Hours Spent" attrs="{'invisible': [('encode_uom_in_days', '=', True)]}"/>
<label class="fw-bold" for="effective_hours" string="Days Spent" attrs="{'invisible': [('encode_uom_in_days', '=', False)]}"/>
</span>
<field name="effective_hours" widget="timesheet_uom" nolabel="1"/>
<!-- remove o_form_subtask_button class from master-->
<button name="action_view_subtask_timesheet" type="object" class="o_form_subtask_button ps-0 border-0 oe_inline oe_link mb-2 o_td_label float-start" attrs="{'invisible' : ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}">
<span class="text-nowrap" attrs="{'invisible' : [('encode_uom_in_days', '=', True)]}">Hours Spent on Sub-tasks:</span>
<span class="text-nowrap" attrs="{'invisible' : [('encode_uom_in_days', '=', False)]}">Days Spent on Sub-tasks:</span>
</button>
<field name="subtask_effective_hours" class="mt-2" widget="timesheet_uom"
attrs="{'invisible' : ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}" nolabel="1"/>
<span attrs="{'invisible': ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}" class="o_td_label float-start">
<label class="fw-bold" for="total_hours_spent" string="Total Hours"
attrs="{'invisible': ['|', '|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0), ('encode_uom_in_days', '=', True)]}"/>
<label class="fw-bold" for="total_hours_spent" string="Total Days"
attrs="{'invisible': ['|', '|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0), ('encode_uom_in_days', '=', False)]}"/>
</span>
<field name="total_hours_spent" widget="timesheet_uom" class="oe_subtotal_footer_separator" nolabel="1"
attrs="{'invisible' : ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}" />
<span class="o_td_label float-start" attrs="{'invisible': [('planned_hours', '=', 0.0)]}">
<label class="fw-bold" for="remaining_hours" string="Remaining Hours"
attrs="{'invisible': ['|', ('encode_uom_in_days', '=', True), ('remaining_hours', '&lt;', 0)]}"/>
<label class="fw-bold" for="remaining_hours" string="Remaining Days"
attrs="{'invisible': ['|', ('encode_uom_in_days', '=', False), ('remaining_hours', '&lt;', 0)]}"/>
<label class="fw-bold text-danger" for="remaining_hours" string="Remaining Hours"
attrs="{'invisible': ['|', ('encode_uom_in_days', '=', True), ('remaining_hours', '&gt;=', 0)]}"/>
<label class="fw-bold text-danger" for="remaining_hours" string="Remaining Days"
attrs="{'invisible': ['|', ('encode_uom_in_days', '=', False), ('remaining_hours', '&gt;=', 0)]}"/>
</span>
<field name="remaining_hours" widget="timesheet_uom" class="oe_subtotal_footer_separator"
attrs="{'invisible' : [('planned_hours', '=', 0.0)]}" nolabel="1"/>
</group>
</group>
</page>
</xpath>
<xpath expr="//field[@name='depend_on_ids']/tree//field[@name='company_id']" position="after">
<field name="allow_subtasks" invisible="1" groups="hr_timesheet.group_hr_timesheet_user"/>
<field name="progress" invisible="1" groups="hr_timesheet.group_hr_timesheet_user"/>
<field name="planned_hours" widget="timesheet_uom_no_toggle" sum="Initially Planned Hours" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
<field name="effective_hours" widget="timesheet_uom" sum="Effective Hours" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
<field name="subtask_effective_hours" widget="timesheet_uom" attrs="{'invisible' : [('allow_subtasks', '=', False)]}" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
<field name="total_hours_spent" widget="timesheet_uom" attrs="{'invisible' : [('allow_subtasks', '=', False)]}" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress &gt;= 100" decoration-warning="progress &gt;= 80 and progress &lt; 100" groups="hr_timesheet.group_hr_timesheet_user"/>
<field name="progress" widget="progressbar" optional="hide" groups="hr_timesheet.group_hr_timesheet_user"/>
</xpath>
</field>
</record>
<record id="project_project_view_tree_inherit_sale_project" model="ir.ui.view">
<field name="name">project.project.tree.inherit.sale.timesheet</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.view_project"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='date']" position="after">
<field name="allocated_hours" widget="timesheet_uom_no_toggle" optional="show" attrs="{'invisible' : [('allocated_hours', '=', 0)]}"/>
</xpath>
</field>
</record>
<record id="view_task_tree2_inherited" model="ir.ui.view">
<field name="name">project.task.tree.inherited</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_tree2" />
<field eval="2" name="priority"/>
<field name="arch" type="xml">
<field name="company_id" position="after">
<field name="allow_subtasks" invisible="1"/>
<field name="progress" invisible="1"/>
<field name="planned_hours" widget="timesheet_uom_no_toggle" sum="Initially Planned Hours" optional="hide"/>
<field name="effective_hours" widget="timesheet_uom" sum="Effective Hours" optional="hide"/>
<field name="subtask_effective_hours" widget="timesheet_uom" attrs="{'invisible' : [('allow_subtasks', '=', False)]}" optional="hide"/>
<field name="total_hours_spent" widget="timesheet_uom" attrs="{'invisible' : [('allow_subtasks', '=', False)]}" optional="hide"/>
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress &gt;= 100" decoration-warning="progress &gt;= 80 and progress &lt; 100"/>
<field name="progress" widget="progressbar" optional="hide" groups="hr_timesheet.group_hr_timesheet_user" attrs="{'invisible' : [('planned_hours', '=', 0)]}"/>
</field>
</field>
</record>
<record id="view_project_kanban_inherited" model="ir.ui.view">
<field name="name">project.project.timesheet.kanban.inherited</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.view_project_kanban"/>
<field name="priority">24</field>
<field name="arch" type="xml">
<field name="partner_id" position="after">
<field name="allow_timesheets" invisible="1"/>
<field name="timesheet_count" invisible="1"/>
<field name="remaining_hours" invisible="1"/>
<field name="encode_uom_in_days" invisible="1"/>
<field name="allocated_hours" invisible="1"/>
</field>
<xpath expr="//div[hasclass('o_kanban_manage_view')]" position="inside">
<div role="menuitem" t-if="record.allow_timesheets.raw_value and record.timesheet_count and record.timesheet_count.raw_value" groups="hr_timesheet.group_hr_timesheet_user">
<a name="action_project_timesheets" type="object">Timesheets</a>
</div>
</xpath>
<xpath expr="//div[hasclass('o_project_kanban_boxes')]" position="after">
<t t-set="badgeColor" t-value="'border-success'"/>
<t t-set="badgeColor" t-value="'border-danger'" t-if="record.remaining_hours.raw_value &lt; 0"/>
<t t-set="title" t-value="'Remaining days'" t-if="record.encode_uom_in_days.raw_value"/>
<t t-set="title" t-value="'Remaining hours'" t-else=""/>
<div t-if="record.allow_timesheets.raw_value and record.allocated_hours.raw_value &gt; 0"
t-attf-class="oe_kanban_align badge border {{ badgeColor }}" t-att-title="title" groups="hr_timesheet.group_hr_timesheet_user">
<field name="remaining_hours" widget="timesheet_uom"/>
</div>
</xpath>
</field>
</record>
<record id="view_task_kanban_inherited_progress" model="ir.ui.view">
<field name="name">project.task.timesheet.kanban.inherited.progress</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_kanban"/>
<field name="arch" type="xml">
<templates position="before">
<field name="progress" />
<field name="remaining_hours" />
<field name="planned_hours" />
<field name="allow_timesheets"/>
<field name="encode_uom_in_days" invisible="1"/>
</templates>
<div class="oe_kanban_bottom_left" position="inside">
<t name="planned_hours" t-if="record.planned_hours.raw_value &gt; 0 and record.allow_timesheets.raw_value" groups="hr_timesheet.group_hr_timesheet_user">
<t t-set="badge" t-value=""/>
<t t-set="badge" t-value="'border-warning'" t-if="record.progress.raw_value &gt;= 80 and record.progress.raw_value &lt;= 100"/>
<t t-set="badge" t-value="'border-danger'" t-if="record.remaining_hours.raw_value &lt; 0"/>
<t t-set="title" t-value="'Remaining days'" t-if="record.encode_uom_in_days.raw_value"/>
<t t-set="title" t-value="'Remaining hours'" t-else=""/>
<div t-attf-class="oe_kanban_align badge {{ badge }}" t-att-title="title">
<field name="remaining_hours" widget="timesheet_uom" />
</div>
</t>
</div>
</field>
</record>
<record id="project_task_view_search" model="ir.ui.view">
<field name="name">project.task.view.search.inherit.sale.timesheet.enterprise</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_search_form"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='followed_by_me']" position='after'>
<filter string="My Team's Tasks" name="my_team_tasks" domain="[('user_ids.employee_parent_id.user_id', '=', uid)]"/>
<filter string="My Department's Tasks" name="my_department" domain="[('user_ids.employee_id.member_of_department', '=', True)]"/>
</xpath>
<xpath expr="//filter[@name='tasks_due_today']" position='after'>
<filter string="Tasks in Overtime" name="overtime" domain="[('overtime', '&gt;', 0)]"/>
<filter string="Tasks Soon in Overtime" name="remaining_hours_percentage" domain="[('remaining_hours_percentage', '&lt;=', 0.2)]"/>
</xpath>
</field>
</record>
<record id="view_task_search_form_hr_extended" model="ir.ui.view">
<field name="name">project.task.view.search.inherit.hr.timesheet</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_task_search_form_extended"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='my_favorite_projects']" position='after'>
<filter string="My Team's Projects" name="my_teams_projects" domain="[('project_id.user_id.employee_parent_id.user_id', '=', uid)]"/>
<filter string="My Department's Projects" name="my_department" domain="[('manager_id.employee_id.member_of_department', '=', True)]"/>
</xpath>
</field>
</record>
<record id="view_project_project_filter_inherit_timesheet" model="ir.ui.view">
<field name="name">project.project.view.inherit.timesheet</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.view_project_project_filter"/>
<field name="arch" type="xml">
<filter name="followed_by_me" position='before'>
<filter string="My Team" name="my_team_projects" domain="[('user_id.employee_parent_id.user_id', '=', uid)]"/>
<filter string="My Department" name="my_department" domain="[('user_id.employee_id.member_of_department', '=', True)]"/>
</filter>
<filter name="late_milestones" position="before">
<filter string="Projects in Overtime" name="projects_in_overtime" domain="[('is_project_overtime', '=', True)]"/>
</filter>
</field>
</record>
<record id="project_task_view_graph" model="ir.ui.view">
<field name="name">project.task.view.graph.inherited</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_project_task_graph"/>
<field name="arch" type="xml">
<xpath expr="//graph" position="attributes">
<attribute name="js_class">hr_timesheet_graphview</attribute>
</xpath>
<xpath expr="//field[@name='stage_id']" position='after'>
<field name="planned_hours" widget="timesheet_uom"/>
<field name="remaining_hours" widget="timesheet_uom"/>
<field name="effective_hours" widget="timesheet_uom"/>
<field name="total_hours_spent" widget="timesheet_uom"/>
<field name="overtime" widget="timesheet_uom"/>
<field name="subtask_effective_hours" widget="timesheet_uom"/>
</xpath>
</field>
</record>
<record id="project_task_view_pivot" model="ir.ui.view">
<field name="name">project.task.view.pivot.inherited</field>
<field name="model">project.task</field>
<field name="inherit_id" ref="project.view_project_task_pivot"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='stage_id']" position='after'>
<field name="planned_hours" widget="timesheet_uom"/>
<field name="remaining_hours" widget="timesheet_uom"/>
<field name="effective_hours" widget="timesheet_uom"/>
<field name="total_hours_spent" widget="timesheet_uom"/>
<field name="overtime" widget="timesheet_uom"/>
<field name="subtask_effective_hours" widget="timesheet_uom"/>
</xpath>
</field>
</record>
<record id="project.open_view_project_all" model="ir.actions.act_window">
<field name="domain">[('is_internal_project', '=', False)]</field>
</record>
<record id="project.open_view_project_all_group_stage" model="ir.actions.act_window">
<field name="domain">[('is_internal_project', '=', False)]</field>
</record>
</data>
</odoo>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="rating_rating_view_search_project_inherited" model="ir.ui.view">
<field name="name">rating.rating.search.project.inherited</field>
<field name="model">rating.rating</field>
<field name="inherit_id" ref="project.rating_rating_view_search_project"/>
<field name="arch" type="xml">
<xpath expr="//filter[@name='my_ratings']" position="after">
<filter string="My Team's Ratings" name="my_team_rating" domain="[('rated_partner_id.user_ids.employee_parent_id.user_id', '=', uid)]"/>
<filter string="My Department's Ratings" name="my_department_rating" domain="[('rated_partner_id.user_ids.employee_id.member_of_department', '=', True)]"/>
</xpath>
</field>
</record>
</odoo>

View file

@ -0,0 +1,129 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="res_config_settings_view_form" model="ir.ui.view">
<field name="name">res.config.settings.view.form.inherit.hr.timesheet</field>
<field name="model">res.config.settings</field>
<field name="priority" eval="55"/>
<field name="inherit_id" ref="base.res_config_settings_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[hasclass('settings')]" position="inside">
<div class="app_settings_block" data-string="Timesheets" string="Timesheets" data-key="hr_timesheet" groups="hr_timesheet.group_timesheet_manager" id="timesheets">
<h2>Time Encoding</h2>
<div class="row mt16 o_settings_container" name="time_encoding_setting_container">
<div class="col-12 col-lg-6 o_setting_box"
id="time_mode_setting"
attrs="{'invisible':[('project_time_mode_id', '!=', False)]}">
<div class="o_setting_right_pane">
<label for="project_time_mode_id"/>
<span class="fa fa-lg fa-building-o" title="Values set here are company-specific." groups="base.group_multi_company"/>
<div class="content-group">
<div class="mt16">
<field name="project_time_mode_id" options="{'no_create': True, 'no_open': True}"/>
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box" id="time_unit_timesheets_setting">
<div class="o_setting_right_pane">
<label for="timesheet_encode_uom_id"/>
<span class="fa fa-lg fa-building-o" title="Values set here are company-specific." groups="base.group_multi_company"/>
<div class="row">
<div class="text-muted col-md-12">
Time unit used to record your timesheets
</div>
</div>
<div class="content-group">
<div class="mt16">
<field name="timesheet_encode_uom_id" options="{'no_create': True, 'no_open': True}" required="1" class="col-lg-5 ps-0"/>
<field name="is_encode_uom_days" invisible="1"/>
</div>
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box" id="synchronize_web_mobile_setting" invisible="1">
<div class="o_setting_left_pane">
<field name="module_project_timesheet_synchro" widget="upgrade_boolean"/>
</div>
<div class="o_setting_right_pane">
<label for="module_project_timesheet_synchro"/>
<div class="text-muted">
Track your time from anywhere, even offline, with our web/mobile apps
</div>
<div class="content-group">
<div class="row mt16 oe_center">
<div class="col-lg-3 pe-0 o_chrome_store_link d-none d-sm-inline-block">
<a href="http://www.odoo.com/app/timesheet?platform=chrome" class="align-middle" target="_blank">
<img alt="Google Chrome Store" class="img img-fluid align-middle mt-1" style="height: 85% !important;" src="project/static/src/img/chrome_store.png"/>
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<h2>Timesheets Control</h2>
<div class="row mt16 o_settings_container">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="reminder_user_allow" widget="upgrade_boolean"/>
</div>
<div class="o_setting_right_pane" id="reminder_user_allow">
<label for="reminder_user_allow"/>
<span class="fa fa-lg fa-building-o " title="Values set here are company-specific." groups="base.group_multi_company"/>
<div class="text-muted">
Send a periodical email reminder to timesheets users<br/>
that still have timesheets to encode
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="reminder_manager_allow" widget="upgrade_boolean"/>
</div>
<div class="o_setting_right_pane" id="reminder_manager_allow">
<label for="reminder_manager_allow"/>
<span class="fa fa-lg fa-building-o " title="Values set here are company-specific." groups="base.group_multi_company"/>
<div class="text-muted">
Send a periodical email reminder to timesheets managers<br/>
that still have timesheets to validate
</div>
</div>
</div>
</div>
<div name="section_leaves">
<h2>Time Off</h2>
<div class="row mt16 o_settings_container" name="timesheet_control">
<div class="col-12 col-lg-6 o_setting_box" id="timesheet_off_validation_setting">
<div class="o_setting_left_pane">
<field name="module_project_timesheet_holidays"/>
</div>
<div class="o_setting_right_pane">
<label for="module_project_timesheet_holidays"/>
<span class="fa fa-lg fa-building-o" title="Values set here are company-specific." groups="base.group_multi_company"/>
<div class="text-muted">
Generate timesheets upon time off validation
</div>
<div class="content-group">
<div id="module_project_timesheet_holidays"/>
</div>
</div>
</div>
</div>
</div>
</div>
</xpath>
</field>
</record>
<record id="hr_timesheet_config_settings_action" model="ir.actions.act_window">
<field name="name">Settings</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">res.config.settings</field>
<field name="view_mode">form</field>
<field name="target">inline</field>
<field name="context">{'module' : 'hr_timesheet', 'bin_size': False}</field>
</record>
<menuitem id="hr_timesheet_menu_configuration" name="Configuration" parent="timesheet_menu_root"
action="hr_timesheet_config_settings_action" groups="base.group_system" sequence="100"/>
</odoo>