[IMP] hr_timesheet: hide the "Round timesheets" feature in the settings of Timesheets if encoding unit is Days
[IMP] hr_timesheet: hide the "Timesheet Timer" feature in the project form if encoding unit is not Hours [IMP] hr_timesheet: clean display of Days for timesheets A computed field has to be added to retrieve the encoding uom of the current company and be used in the views. FieldTimesheetFactor field has to be registered to "float_factor" widget for the "planned_hours" field to work correctly with Days (for the conversion from hours to days to be computed and to be able to set a greater value than 1 day) If encoding unit for timesheets is set to Days: - Display Days instead of Hours in Portal > Timesheets - Display Days instead of Hours in Portal > Tasks - Display Days instead of Hours in Portal > Invoices & Bills - Display Days instead of Hours in Portal > Tickets [IMP] hr_timesheet: display clean duration in Timesheet Entries Display Days instead of Hours in Timesheet Entries (print action) if encoding unit for timesheets is set to Days [IMP] hr_timesheet: correctly display timesheets in Days if configured to - correctly display remaining days in project.task kanban and list view - hide timer start button if timesheet is configured in Days - update several column names [IMP] hr_timesheet: clean code [IMP] hr_timesheet: small improvements when displaying timesheets in days - Rename some labels - Fixes style - Prevent fields displayed with widget "timesheet_uom" to change when timer is on - Display correctly "Initially Planned Days" field in task creation form
This commit is contained in:
@@ -141,5 +141,6 @@ class TimesheetCustomerPortal(CustomerPortal):
|
||||
'searchbar_groupby': searchbar_groupby,
|
||||
'searchbar_filters': OrderedDict(sorted(searchbar_filters.items())),
|
||||
'filterby': filterby,
|
||||
'is_uom_day': request.env['account.analytic.line']._is_timesheet_encode_uom_day(),
|
||||
})
|
||||
return request.render("hr_timesheet.portal_my_timesheets", values)
|
||||
|
||||
@@ -22,4 +22,5 @@ class ProjectCustomerPortal(CustomerPortal):
|
||||
timesheets_by_subtask[timesheet.task_id] |= timesheet
|
||||
values['timesheets'] = timesheets
|
||||
values['timesheets_by_subtask'] = timesheets_by_subtask
|
||||
values['is_uom_day'] = request.env['account.analytic.line']._is_timesheet_encode_uom_day()
|
||||
return values
|
||||
|
||||
@@ -189,3 +189,15 @@ class AccountAnalyticLine(models.Model):
|
||||
'amount': amount_converted,
|
||||
})
|
||||
return result
|
||||
|
||||
def _is_timesheet_encode_uom_day(self):
|
||||
company_uom = self.env.company.timesheet_encode_uom_id
|
||||
return company_uom == self.env.ref('uom.product_uom_day')
|
||||
|
||||
def _convert_hours_to_days(self, time):
|
||||
uom_hour = self.env.ref('uom.product_uom_hour')
|
||||
uom_day = self.env.ref('uom.product_uom_day')
|
||||
return round(uom_hour._compute_quantity(time, uom_day, raise_if_failure=False), 2)
|
||||
|
||||
def _get_timesheet_time_day(self):
|
||||
return self._convert_hours_to_days(self.unit_amount)
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from lxml import etree
|
||||
|
||||
from odoo import models, fields, api, _
|
||||
from odoo.exceptions import UserError, ValidationError, RedirectWarning
|
||||
|
||||
@@ -24,6 +26,10 @@ class Project(models.Model):
|
||||
total_timesheet_time = fields.Integer(
|
||||
compute='_compute_total_timesheet_time',
|
||||
help="Total number of time (in the proper UoM) recorded in the project, rounded to the unit.")
|
||||
encode_uom_in_days = fields.Boolean(compute='_compute_encode_uom_in_days')
|
||||
|
||||
def _compute_encode_uom_in_days(self):
|
||||
self.encode_uom_in_days = self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_day')
|
||||
|
||||
@api.depends('analytic_account_id')
|
||||
def _compute_allow_timesheets(self):
|
||||
@@ -106,6 +112,13 @@ class Task(models.Model):
|
||||
overtime = fields.Float(compute='_compute_progress_hours', store=True)
|
||||
subtask_effective_hours = fields.Float("Sub-tasks Hours Spent", compute='_compute_subtask_effective_hours', store=True, help="Time spent on the sub-tasks (and their own sub-tasks) of this task.")
|
||||
timesheet_ids = fields.One2many('account.analytic.line', 'task_id', 'Timesheets')
|
||||
encode_uom_in_days = fields.Boolean(compute='_compute_encode_uom_in_days', default=lambda self: self._uom_in_days())
|
||||
|
||||
def _uom_in_days(self):
|
||||
return self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_day')
|
||||
|
||||
def _compute_encode_uom_in_days(self):
|
||||
self.encode_uom_in_days = self._uom_in_days()
|
||||
|
||||
@api.depends('project_id.analytic_account_id.active')
|
||||
def _compute_analytic_account_active(self):
|
||||
@@ -180,7 +193,10 @@ class Task(models.Model):
|
||||
if self.env.context.get('hr_timesheet_display_remaining_hours'):
|
||||
name_mapping = dict(super().name_get())
|
||||
for task in self:
|
||||
if task.allow_timesheets and task.planned_hours > 0:
|
||||
if task.allow_timesheets and task.planned_hours > 0 and task.encode_uom_in_days:
|
||||
days_left = _("(%s days remaining)") % task._convert_hours_to_days(task.remaining_hours)
|
||||
name_mapping[task.id] = name_mapping.get(task.id, '') + " ‒ " + days_left
|
||||
elif task.allow_timesheets and task.planned_hours > 0:
|
||||
hours, mins = (str(int(duration)).rjust(2, '0') for duration in divmod(abs(task.remaining_hours) * 60, 60))
|
||||
hours_left = _(
|
||||
"(%(sign)s%(hours)s:%(minutes)s remaining)",
|
||||
@@ -197,8 +213,24 @@ class Task(models.Model):
|
||||
""" Set the correct label for `unit_amount`, depending on company UoM """
|
||||
result = super(Task, self)._fields_view_get(view_id=view_id, view_type=view_type, toolbar=toolbar, submenu=submenu)
|
||||
result['arch'] = self.env['account.analytic.line']._apply_timesheet_label(result['arch'])
|
||||
|
||||
if view_type == 'tree' and self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_day'):
|
||||
result['arch'] = self._apply_time_label(result['arch'])
|
||||
return result
|
||||
|
||||
@api.model
|
||||
def _apply_time_label(self, view_arch):
|
||||
doc = etree.XML(view_arch)
|
||||
encoding_uom = self.env.company.timesheet_encode_uom_id
|
||||
|
||||
for node in doc.xpath("//field[@name='planned_hours'][@widget='timesheet_uom'][not(@string)]"):
|
||||
node.set('string', _('Planned %s') % encoding_uom.name or '')
|
||||
for node in doc.xpath("//field[@name='effective_hours'][@widget='timesheet_uom'][not(@string)]"):
|
||||
node.set('string', _('%s Spent') % encoding_uom.name or '')
|
||||
for node in doc.xpath("//field[@name='remaining_hours'][@widget='timesheet_uom'][not(@string)]"):
|
||||
node.set('string', _('Remaining %s') % encoding_uom.name or '')
|
||||
return etree.tostring(doc, encoding='unicode')
|
||||
|
||||
def unlink(self):
|
||||
"""
|
||||
If some tasks to unlink have some timesheets entries, these
|
||||
@@ -216,3 +248,8 @@ class Task(models.Model):
|
||||
warning_msg, self.env.ref('hr_timesheet.timesheet_action_task').id,
|
||||
_('See timesheet entries'), {'active_ids': tasks_with_timesheets.ids})
|
||||
return super(Task, self).unlink()
|
||||
|
||||
def _convert_hours_to_days(self, time):
|
||||
uom_hour = self.env.ref('uom.product_uom_hour')
|
||||
uom_day = self.env.ref('uom.product_uom_day')
|
||||
return round(uom_hour._compute_quantity(time, uom_day, raise_if_failure=False), 2)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import fields, models
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class ResConfigSettings(models.TransientModel):
|
||||
@@ -20,3 +20,10 @@ class ResConfigSettings(models.TransientModel):
|
||||
and widgets to help the encoding. All reporting will still be expressed in hours (default value).""")
|
||||
timesheet_min_duration = fields.Integer('Minimal duration', default=15, config_parameter='hr_timesheet.timesheet_min_duration')
|
||||
timesheet_rounding = fields.Integer('Rounding up', default=15, config_parameter='hr_timesheet.timesheet_rounding')
|
||||
is_encode_uom_days = fields.Boolean(compute='_compute_is_encode_uom_days')
|
||||
|
||||
@api.depends('timesheet_encode_uom_id')
|
||||
def _compute_is_encode_uom_days(self):
|
||||
product_uom_day = self.env.ref('uom.product_uom_day')
|
||||
for settings in self:
|
||||
settings.is_encode_uom_days = settings.timesheet_encode_uom_id == product_uom_day
|
||||
|
||||
@@ -20,15 +20,19 @@
|
||||
|
||||
<div class="row" style="margin-top:10px;">
|
||||
<div class="col-lg-12">
|
||||
<t t-set='is_uom_day' t-value='docs._is_timesheet_encode_uom_day()'/>
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><span>Date</span></th>
|
||||
<th><span>Responsible</span></th>
|
||||
<th><span>Description</span></th>
|
||||
<th t-if="show_project"><span>Project</span></th>
|
||||
<th t-if="show_task"><span>Task</span></th>
|
||||
<th class="text-right"><span>Time</span></th>
|
||||
<th class="align-middle"><span>Date</span></th>
|
||||
<th class="align-middle"><span>Responsible</span></th>
|
||||
<th class="align-middle"><span>Description</span></th>
|
||||
<th class="align-middle" t-if="show_project"><span>Project</span></th>
|
||||
<th class="align-middle" t-if="show_task"><span>Task</span></th>
|
||||
<th class="text-right">
|
||||
<span t-if="is_uom_day">Time Spent (Days)</span>
|
||||
<span t-else="">Time Spent (Hours)</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -49,16 +53,24 @@
|
||||
<t t-if="l.task_id"><span t-field="l.task_id.name"/></t>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<span t-field="l.unit_amount" t-options="{'widget': 'duration', 'digital': True, 'unit': 'hour', 'round': 'minute'}"/>
|
||||
<span t-if="is_uom_day" t-esc="l._get_timesheet_time_day()" t-options="{'widget': 'timesheet_uom'}"/>
|
||||
<span t-else="" t-field="l.unit_amount" t-options="{'widget': 'duration', 'digital': True, 'unit': 'hour', 'round': 'minute'}"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td />
|
||||
<td />
|
||||
<td t-if="show_project"/>
|
||||
<td t-if="show_task"/>
|
||||
<td class="text-right"><strong>Total</strong></td>
|
||||
<td class="text-right"><strong t-esc="sum(docs.mapped('unit_amount'))" t-options="{'widget': 'duration', 'digital': True, 'unit': 'hour', 'round': 'minute'}"/></td>
|
||||
<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-right" t-attf-colspan="{{nbCols}}">
|
||||
<strong t-if="is_uom_day">
|
||||
<span style="margin-right: 15px;">Total (Days)</span>
|
||||
<t t-esc="docs._convert_hours_to_days(sum(docs.mapped('unit_amount')))" t-options="{'widget': 'timesheet_uom'}"/>
|
||||
</strong>
|
||||
<strong t-else="">
|
||||
<span style="margin-right: 15px;">Total (Hours)</span>
|
||||
<t t-esc="sum(docs.mapped('unit_amount'))" t-options="{'widget': 'duration', 'digital': True, 'unit': 'hour', 'round': 'minute'}"/>
|
||||
</strong>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
odoo.define('hr_timesheet.timesheet_factor', function (require) {
|
||||
'use strict';
|
||||
|
||||
const timesheetUomFields = require('hr_timesheet.timesheet_uom');
|
||||
const fieldUtils = require('web.field_utils');
|
||||
const fieldRegistry = require('web.field_registry');
|
||||
|
||||
fieldRegistry.add('timesheet_factor', timesheetUomFields.FieldTimesheetFactor);
|
||||
|
||||
fieldUtils.format.timesheet_factor = function(value, field, options) {
|
||||
const formatter = fieldUtils.format[timesheetUomFields.FieldTimesheetFactor.prototype.formatType];
|
||||
return formatter(value, field, options);
|
||||
};
|
||||
|
||||
fieldUtils.parse.timesheet_factor = function(value, field, options) {
|
||||
const parser = fieldUtils.parse[timesheetUomFields.FieldTimesheetFactor.prototype.formatType];
|
||||
return parser(value, field, options);
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
.o_form_subtask_button {
|
||||
border: none;
|
||||
padding-left: 0px;
|
||||
}
|
||||
@@ -2,8 +2,10 @@
|
||||
<odoo>
|
||||
<template id="assets_backend" name="timesheet assets" inherit_id="web.assets_backend">
|
||||
<xpath expr="." position="inside">
|
||||
<link type="text/scss" href="/hr_timesheet/static/src/scss/timesheets_task_form.scss" rel="stylesheet"/>
|
||||
<script type="text/javascript" src="/hr_timesheet/static/src/js/task_with_hours.js"></script>
|
||||
<script type="text/javascript" src="/hr_timesheet/static/src/js/timesheet_uom.js"/>
|
||||
<script type="text/javascript" src="/hr_timesheet/static/src/js/timesheet_factor.js"/>
|
||||
</xpath>
|
||||
</template>
|
||||
</odoo>
|
||||
|
||||
@@ -61,7 +61,8 @@
|
||||
<th t-if="not groupby == 'project'">Project</th>
|
||||
<th t-if="not groupby == 'task'">Task</th>
|
||||
<th>Description</th>
|
||||
<th class="text-right">Hours Spent <small class="text-muted">(Total: <span t-esc="hours_spent" t-options='{"widget": "float_time"}'/>)</small></th>
|
||||
<th t-if="is_uom_day" class="text-right">Days Spent <small class="text-muted">(Total: <span t-esc="timesheets[0]._convert_hours_to_days(hours_spent)" t-options='{"widget": "timesheet_uom"}'/>)</small></th>
|
||||
<th t-else="" class="text-right">Hours Spent <small class="text-muted">(Total: <span t-esc="hours_spent" t-options='{"widget": "float_time"}'/>)</small></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -72,7 +73,10 @@
|
||||
<td t-if="not groupby == 'project'"><span t-field="timesheet.project_id"/></td>
|
||||
<td t-if="not groupby == 'task'"><span t-field="timesheet.task_id"/></td>
|
||||
<td><span t-esc="timesheet.name"/></td>
|
||||
<td class="text-right"><span t-field="timesheet.unit_amount" t-options='{"widget": "float_time"}'/></td>
|
||||
<td class="text-right">
|
||||
<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>
|
||||
|
||||
@@ -21,14 +21,18 @@
|
||||
<th>Date</th>
|
||||
<th>Employee</th>
|
||||
<th>Description</th>
|
||||
<th class="text-right">Duration</th>
|
||||
<th t-if="is_uom_day" class="text-right">Duration (Days)</th>
|
||||
<th t-else="" class="text-right">Duration (Hours)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tr t-foreach="timesheets" t-as="timesheet">
|
||||
<td><t t-esc="timesheet.date" t-options='{"widget": "date"}'/></td>
|
||||
<td><t t-esc="timesheet.employee_id.name"/></td>
|
||||
<td><t t-esc="timesheet.name"/></td>
|
||||
<td class="text-right"><span t-field="timesheet.unit_amount" t-options='{"widget": "float_time"}'/></td>
|
||||
<td class="text-right">
|
||||
<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>
|
||||
|
||||
@@ -79,11 +79,26 @@
|
||||
<field name="analytic_account_active" invisible="1"/>
|
||||
<field name="allow_timesheets" invisible="1"/>
|
||||
<field name="allow_subtasks" invisible="1"/>
|
||||
<field name="encode_uom_in_days" invisible="1"/>
|
||||
<page string="Timesheets" id="timesheets_tab" attrs="{'invisible': [('allow_timesheets', '=', False)]}">
|
||||
<group>
|
||||
<group>
|
||||
<field name="planned_hours" widget="float_time"/>
|
||||
<field name="subtask_planned_hours" widget="float_time" groups="project.group_subtask_project" attrs="{'invisible': ['|', ('allow_subtasks', '=', False), ('subtask_count', '=', 0)]}"/>
|
||||
<label for="planned_hours" string="Initially Planned Hours" attrs="{'invisible': [('encode_uom_in_days', '=', True)]}"/>
|
||||
<field name="planned_hours" widget="float_time" attrs="{'invisible': [('encode_uom_in_days', '=', True)]}" nolabel="1" />
|
||||
<label for="planned_hours" string="Initially Planned Days" attrs="{'invisible': [('encode_uom_in_days', '=', False)]}"/>
|
||||
<field name="planned_hours" widget="timesheet_factor" attrs="{'invisible': [('encode_uom_in_days', '=', False)]}" nolabel="1" />
|
||||
<label for="subtask_planned_hours" groups="project.group_subtask_project" string="Sub-tasks Planned Hours"
|
||||
attrs="{'invisible': ['|', '|', ('allow_subtasks', '=', False), ('subtask_count', '=', 0), ('encode_uom_in_days', '=', True)]}"/>
|
||||
<span class="o_row" groups="project.group_subtask_project"
|
||||
attrs="{'invisible': ['|', '|', ('allow_subtasks', '=', False), ('subtask_count', '=', 0), ('encode_uom_in_days', '=', True)]}">
|
||||
<field name="subtask_planned_hours" widget="float_time"/>
|
||||
</span>
|
||||
<label for="subtask_planned_hours" groups="project.group_subtask_project" string="Sub-tasks Planned Days"
|
||||
attrs="{'invisible': ['|', '|', ('allow_subtasks', '=', False), ('subtask_count', '=', 0), ('encode_uom_in_days', '=', False)]}"/>
|
||||
<div class="o_row" groups="project.group_subtask_project"
|
||||
attrs="{'invisible': ['|', '|', ('allow_subtasks', '=', False), ('subtask_count', '=', 0), ('encode_uom_in_days', '=', False)]}">
|
||||
<field name="subtask_planned_hours" widget="timesheet_uom"/>
|
||||
</div>
|
||||
</group>
|
||||
<group>
|
||||
<field name="progress" widget="progressbar"/>
|
||||
@@ -155,11 +170,34 @@
|
||||
</field>
|
||||
<group attrs="{'invisible': [('analytic_account_active', '=', False)]}">
|
||||
<group class="oe_subtotal_footer oe_right" name="project_hours">
|
||||
<field name="effective_hours" widget="float_time" />
|
||||
<button name="action_view_subtask_timesheet" string="Sub-tasks Hours Spent" type="object" class="o_td_label o_form_label oe_inline oe_link mr-0" attrs="{'invisible' : ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}"/>
|
||||
<field name="subtask_effective_hours" class="mt-2" widget="float_time" attrs="{'invisible' : ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}" nolabel="1"/>
|
||||
<field name="total_hours_spent" widget="float_time" class="oe_subtotal_footer_separator" attrs="{'invisible' : ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}" />
|
||||
<field name="remaining_hours" widget="float_time" class="oe_subtotal_footer_separator" attrs="{'invisible' : [('planned_hours', '=', 0.0)]}"/>
|
||||
<span>
|
||||
<label class="font-weight-bold" for="effective_hours" string="Hours Spent" attrs="{'invisible': [('encode_uom_in_days', '=', True)]}"/>
|
||||
<label class="font-weight-bold" for="effective_hours" string="Days Spent" attrs="{'invisible': [('encode_uom_in_days', '=', False)]}"/>
|
||||
</span>
|
||||
<field name="effective_hours" widget="timesheet_uom" nolabel="1"/>
|
||||
|
||||
<button name="action_view_subtask_timesheet" type="object" class="o_td_label o_form_label o_form_subtask_button oe_inline oe_link mr-0" attrs="{'invisible' : ['|', ('allow_subtasks', '=', False), ('subtask_effective_hours', '=', 0.0)]}">
|
||||
<span class="text-nowrap" attrs="{'invisible' : [('encode_uom_in_days', '=', True)]}">Sub-tasks Hours Spent</span>
|
||||
<span class="text-nowrap" attrs="{'invisible' : [('encode_uom_in_days', '=', False)]}">Sub-tasks Days Spent</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>
|
||||
<label class="font-weight-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="font-weight-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>
|
||||
<label class="font-weight-bold" for="remaining_hours" string="Remaining Hours"
|
||||
attrs="{'invisible': ['|', ('planned_hours', '=', 0.0), ('encode_uom_in_days', '=', True)]}"/>
|
||||
<label class="font-weight-bold" for="remaining_hours" string="Remaining Days"
|
||||
attrs="{'invisible': ['|', ('planned_hours', '=', 0.0), ('encode_uom_in_days', '=', False)]}"/>
|
||||
</span>
|
||||
<field name="remaining_hours" widget="timesheet_uom" class="oe_subtotal_footer_separator"
|
||||
attrs="{'invisible' : [('planned_hours', '=', 0.0)]}" nolabel="1"/>
|
||||
</group>
|
||||
</group>
|
||||
</page>
|
||||
@@ -175,11 +213,11 @@
|
||||
<field name="arch" type="xml">
|
||||
<field name="company_id" position="after">
|
||||
<field name="allow_subtasks" invisible="1"/>
|
||||
<field name="planned_hours" widget="float_time" sum="Initially Planned Hours" optional="hide"/>
|
||||
<field name="effective_hours" widget="float_time" sum="Effective Hours" optional="show"/>
|
||||
<field name="remaining_hours" widget="float_time" sum="Remaining Hours" optional="hide" decoration-danger="progress >= 100" decoration-warning="progress >= 80 and progress < 100"/>
|
||||
<field name="subtask_effective_hours" widget="float_time" attrs="{'invisible' : [('allow_subtasks', '=', False)]}" optional="hide"/>
|
||||
<field name="total_hours_spent" widget="float_time" attrs="{'invisible' : [('allow_subtasks', '=', False)]}" optional="hide"/>
|
||||
<field name="planned_hours" widget="timesheet_uom" sum="Initially Planned Hours" optional="hide"/>
|
||||
<field name="effective_hours" widget="timesheet_uom" sum="Effective Hours" optional="show"/>
|
||||
<field name="remaining_hours" widget="timesheet_uom" sum="Remaining Hours" optional="hide" decoration-danger="progress >= 100" decoration-warning="progress >= 80 and progress < 100"/>
|
||||
<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="progress" widget="progressbar" optional="show" groups="hr_timesheet.group_hr_timesheet_user"/>
|
||||
</field>
|
||||
</field>
|
||||
@@ -217,15 +255,18 @@
|
||||
<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 > 0 and record.allow_timesheets.raw_value">
|
||||
<t t-set="badge" t-value=""/>
|
||||
<t t-set="badge" t-value="'badge-warning'" t-if="record.progress.raw_value >= 80 and record.progress.raw_value <= 100"/>
|
||||
<t t-set="badge" t-value="'badge-danger'" t-if="record.remaining_hours.raw_value < 0"/>
|
||||
<div t-attf-class="oe_kanban_align badge {{ badge }}" title="Remaining hours">
|
||||
<field name="remaining_hours" widget="float_time" />
|
||||
</div>
|
||||
<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>
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<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"/>
|
||||
<field name="is_encode_uom_days" invisible="1"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -72,7 +73,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6 o_setting_box">
|
||||
<div class="col-12 col-lg-6 o_setting_box" attrs="{'invisible': [('module_project_timesheet_synchro', '=', False), ('is_encode_uom_days', '=', True)]}">
|
||||
<div class="o_setting_right_pane">
|
||||
<strong>Round Timesheets</strong>
|
||||
<div class="o_row w-30">
|
||||
|
||||
Reference in New Issue
Block a user