[IMP] base, hr_timesheet: add option digital in qweb field duration
The option `digital` has been added. It allows to display "01:00" instead of "1 hour". This behaviour was implemented in a report (timesheet) but the logic was done in the XML, which is not adequate. This commit also allows to have a negative value for this widget.
This commit is contained in:
@@ -43,7 +43,7 @@
|
||||
<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': 'float_time'}"/>
|
||||
<span t-field="l.unit_amount" t-options="{'widget': 'duration', 'digital': True, 'unit': 'hour', 'round': 'minute'}"/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -51,9 +51,7 @@
|
||||
<td />
|
||||
<td t-if="show_task"/>
|
||||
<td class="text-right"><strong>Total</strong></td>
|
||||
<t t-set="total_value" t-value="sum(docs.mapped('unit_amount'))" />
|
||||
<t t-set="sign" t-value="'-' if total_value < 0 else ''"/>
|
||||
<td class="text-right"><strong t-esc="sign + '%s:%02.0f' % tuple(int(round(x)) for x in divmod(abs(total_value)*60,60))"/></td>
|
||||
<td class="text-right"><strong t-esc="sum(docs.mapped('unit_amount'))" t-options="{'widget': 'duration', 'digital': True, 'unit': 'hour', 'round': 'minute'}"/></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -471,9 +471,13 @@ class DurationConverter(models.AbstractModel):
|
||||
|
||||
Can be used on any numerical field.
|
||||
|
||||
Has a mandatory option ``unit`` which can be one of ``second``, ``minute``,
|
||||
Has an option ``unit`` which can be one of ``second``, ``minute``,
|
||||
``hour``, ``day``, ``week`` or ``year``, used to interpret the numerical
|
||||
field value before converting it.
|
||||
field value before converting it. By default use ``second``.
|
||||
|
||||
Has an option ``round``. By default use ``second``.
|
||||
|
||||
Has an option ``digital`` to display 01:00 instead of 1 hour
|
||||
|
||||
Sub-second values will be ignored.
|
||||
"""
|
||||
@@ -485,8 +489,9 @@ class DurationConverter(models.AbstractModel):
|
||||
options = super(DurationConverter, self).get_available_options()
|
||||
unit = [[u[0], _(u[0])] for u in TIMEDELTA_UNITS]
|
||||
options.update(
|
||||
unit=dict(type="selection", params=unit, string=_('Date unit'), description=_('Date unit used for comparison and formatting'), default_value='hour'),
|
||||
round=dict(type="selection", params=unit, string=_('Rounding unit'), description=_('Date unit used for the rounding. If the value is given, this must be smaller than the unit'), default_value='Same unit than "unit" option'),
|
||||
digital=dict(type="boolean", string=_('Digital formating')),
|
||||
unit=dict(type="selection", params=unit, string=_('Date unit'), description=_('Date unit used for comparison and formatting'), default_value='second', required=True),
|
||||
round=dict(type="selection", params=unit, string=_('Rounding unit'), description=_("Date unit used for the rounding. The value must be smaller than 'hour' if you use the digital formating."), default_value='second'),
|
||||
)
|
||||
return options
|
||||
|
||||
@@ -494,22 +499,32 @@ class DurationConverter(models.AbstractModel):
|
||||
def value_to_html(self, value, options):
|
||||
units = dict(TIMEDELTA_UNITS)
|
||||
|
||||
if value < 0:
|
||||
raise ValueError(_("Durations can't be negative"))
|
||||
|
||||
if not options or options.get('unit', 'hour') not in units:
|
||||
raise ValueError(_("A unit must be provided to duration widgets"))
|
||||
|
||||
locale = babel.Locale.parse(self.user_lang().code)
|
||||
factor = units[options.get('unit', 'hour')]
|
||||
factor = units[options.get('unit', 'second')]
|
||||
round_to = units[options.get('round', 'second')]
|
||||
|
||||
if options.get('digital') and round_to > 3600:
|
||||
round_to = 3600
|
||||
|
||||
r = round((value * factor) / round_to) * round_to
|
||||
|
||||
sections = []
|
||||
|
||||
r = value * factor
|
||||
if options.get('round') in units:
|
||||
round_to = units[options['round']]
|
||||
r = round(r / round_to) * round_to
|
||||
if options.get('digital'):
|
||||
for unit, secs_per_unit in TIMEDELTA_UNITS:
|
||||
if secs_per_unit > 3600:
|
||||
continue
|
||||
v, r = divmod(r, secs_per_unit)
|
||||
if not v and (secs_per_unit > factor or secs_per_unit < round_to):
|
||||
continue
|
||||
if len(sections):
|
||||
sections.append(u':')
|
||||
sections.append(u"%02.0f" % int(round(v)))
|
||||
return u''.join(sections)
|
||||
|
||||
if value < 0:
|
||||
r = -r
|
||||
sections.append(u'-')
|
||||
for unit, secs_per_unit in TIMEDELTA_UNITS:
|
||||
v, r = divmod(r, secs_per_unit)
|
||||
if not v:
|
||||
|
||||
@@ -297,17 +297,18 @@ class TestDurationExport(TestBasicExport):
|
||||
# needs to have lang installed otherwise falls back on en_US
|
||||
self.env['res.lang'].load_lang('fr_FR')
|
||||
|
||||
def test_default_unit(self):
|
||||
converter = self.get_converter('float', 'duration')
|
||||
self.assertEqual(converter(4), u'4 seconds')
|
||||
|
||||
def test_negative(self):
|
||||
converter = self.get_converter('float', 'duration')
|
||||
self.assertEqual(converter(-4), u'- 4 seconds')
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
converter(-4)
|
||||
|
||||
def test_missing_unit(self):
|
||||
def test_negative_with_round(self):
|
||||
converter = self.get_converter('float', 'duration')
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
converter(4)
|
||||
result = converter(-4.678, {'unit': 'year', 'round': 'hour'}, {'lang': 'fr_FR'})
|
||||
self.assertEqual(result, u'- 4 ans 8 mois 1 semaine 11 heures')
|
||||
|
||||
def test_basic(self):
|
||||
converter = self.get_converter('float', 'duration')
|
||||
|
||||
Reference in New Issue
Block a user