[IMP] hr_payroll_overtime: detect recursion and prevent the simplest type in the form view

This commit is contained in:
Jared Kipe
2020-10-06 07:56:04 -07:00
parent af837721f5
commit 5c5de9d9c1
3 changed files with 58 additions and 6 deletions

View File

@@ -1,5 +1,6 @@
from collections import defaultdict
from odoo import models
from odoo.exceptions import UserError
class HRPayslip(models.Model):
@@ -30,10 +31,15 @@ class HRPayslip(models.Model):
day_hours = defaultdict(float)
week_hours = defaultdict(float)
iso_days = set()
for iso_date, entries in work_data:
iso_date = _adjust_week(iso_date)
for work_type, hours, _ in entries:
self._aggregate_overtime_add_work_type_hours(work_type, hours, iso_date, result, iso_days, day_hours, week_hours)
try:
for iso_date, entries in work_data:
iso_date = _adjust_week(iso_date)
for work_type, hours, _ in entries:
self._aggregate_overtime_add_work_type_hours(work_type, hours, iso_date, result, iso_days, day_hours, week_hours)
except RecursionError:
raise UserError('RecursionError raised. Ensure you have not overtime loops, you should have an '
'end work type that does not have any "overtime" version, and would be considered '
'the "highest overtime" work type and rate.')
return result
@@ -75,6 +81,9 @@ class HRPayslip(models.Model):
# we need to save this because it won't be set once it reenter, we won't know what the original
# overtime multiplier was
working_aggregation[work_type.overtime_work_type_id][2] = work_type.overtime_type_id.multiplier
if work_type == work_type.overtime_work_type_id:
# trivial infinite recursion
raise UserError('Work type %s (id %s) must not have itself as its next overtime type.' % (work_type.name, work_type.id))
self._aggregate_overtime_add_work_type_hours(work_type.overtime_work_type_id, ot_hours, iso_date,
working_aggregation, iso_days, day_hours, week_hours)
else:

View File

@@ -1,4 +1,5 @@
from odoo.tests import common
from odoo.exceptions import UserError
class TestOvertime(common.TransactionCase):
@@ -321,3 +322,41 @@ class TestOvertime(common.TransactionCase):
self.assertTrue(self.work_type_overtime2 in result_data)
self.assertEqual(result_data[self.work_type_overtime2][0], 0)
self.assertEqual(result_data[self.work_type_overtime2][1], 2.0)
def test_13_recursive_infinite_trivial(self):
# recursive should will use a second overtime, but not this time!
self.overtime_rules.hours_per_day = 8.0
self.overtime_rules.multiplier_per_day = 1.5
self.work_type.overtime_type_id = self.overtime_rules
# overtime goes to itself
self.work_type.overtime_work_type_id = self.work_type
work_data = [
((2020, 24, 2), [
# 2hr overtime
(self.work_type, 4.0, None),
(self.work_type, 6.0, None),
]),
]
with self.assertRaises(UserError):
result_data = self.payslip.aggregate_overtime(work_data)
def test_14_recursive_infinite_loop(self):
# recursive will use a second overtime, but not this time!
self.overtime_rules.hours_per_day = 8.0
self.overtime_rules.multiplier_per_day = 1.5
self.work_type_overtime.overtime_type_id = self.overtime_rules
# overtime goes back to worktype
self.work_type_overtime.overtime_work_type_id = self.work_type
work_data = [
((2020, 24, 2), [
# 2hr overtime
(self.work_type, 4.0, None),
(self.work_type, 6.0, None),
]),
]
with self.assertRaises(UserError):
result_data = self.payslip.aggregate_overtime(work_data)

View File

@@ -9,8 +9,12 @@
<field name="arch" type="xml">
<xpath expr="//group[@name='main_group']" position="after">
<group name="overtime_group">
<field name="overtime_work_type_id" attrs="{'required': [('overtime_type_id', '!=', False)]}"/>
<field name="overtime_type_id" attrs="{'required': [('overtime_work_type_id', '!=', False)]}"/>
<field name="overtime_work_type_id"
domain="[('id', '!=', id)]"
attrs="{'required': [('overtime_type_id', '!=', False)]}" />
<field name="overtime_type_id"
domain="[('id', '!=', id)]"
attrs="{'required': [('overtime_work_type_id', '!=', False)]}" />
</group>
</xpath>
</field>