mirror of
https://gitlab.com/hibou-io/hibou-odoo/suite.git
synced 2025-01-20 12:37:31 +02:00
[IMP] hr_payroll_overtime: refactor/improve API (for hr_payroll_attendance)
This commit is contained in:
@@ -13,7 +13,7 @@
|
|||||||
'views/resource_calendar_views.xml',
|
'views/resource_calendar_views.xml',
|
||||||
],
|
],
|
||||||
'depends': [
|
'depends': [
|
||||||
'hr_payroll',
|
'hr_payroll_hibou',
|
||||||
'hr_work_entry',
|
'hr_work_entry',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,54 @@
|
|||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from odoo import models
|
from odoo import api, fields, models
|
||||||
from odoo.exceptions import UserError
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
|
||||||
class HRPayslip(models.Model):
|
class HRPayslip(models.Model):
|
||||||
_inherit = 'hr.payslip'
|
_inherit = 'hr.payslip'
|
||||||
|
|
||||||
def aggregate_overtime(self, work_data, day_week_start=None):
|
def _get_worked_day_lines_values(self, domain=None):
|
||||||
|
worked_day_lines_values = super()._get_worked_day_lines_values(domain=domain)
|
||||||
|
return self._process_worked_day_lines_values(worked_day_lines_values, domaian=domain)
|
||||||
|
|
||||||
|
def _process_worked_day_lines_values(self, worked_day_lines_values, domaian=None):
|
||||||
|
if not self.state in ('draft', 'verify'):
|
||||||
|
return worked_day_lines_values
|
||||||
|
|
||||||
|
worked_day_lines_values = self._filter_worked_day_lines_values(worked_day_lines_values)
|
||||||
|
work_data = self._pre_aggregate_work_data()
|
||||||
|
work_data = self._post_aggregate_work_data(work_data)
|
||||||
|
processed_data = self._aggregate_overtime(work_data)
|
||||||
|
worked_day_lines_values += [{
|
||||||
|
'number_of_days': data[0],
|
||||||
|
'number_of_hours': data[1],
|
||||||
|
'rate': data[2],
|
||||||
|
'contract_id': self.contract_id.id,
|
||||||
|
'work_entry_type_id': work_type.id,
|
||||||
|
} for work_type, data in processed_data.items()]
|
||||||
|
return worked_day_lines_values
|
||||||
|
|
||||||
|
def _filter_worked_day_lines_values(self, worked_day_lines_values):
|
||||||
|
# e.g. maybe you want to remove the stock 'WORK100' lines
|
||||||
|
# returns new worked_day_lines_values
|
||||||
|
return worked_day_lines_values
|
||||||
|
|
||||||
|
def _pre_aggregate_work_data(self):
|
||||||
|
# returns dict(iso_date: list(tuple(hr.work.entry.type(), hours, original_record))
|
||||||
|
return defaultdict(list)
|
||||||
|
|
||||||
|
def _post_aggregate_work_data(self, work_data):
|
||||||
|
# takes pre_aggregate data format and converts it.
|
||||||
|
# this is to simplify algorithm and guarantee ordered by iso_date semantics
|
||||||
|
# work_data: dict(iso_date: list(tuple(hr.work.entry.type(), hours, original_record))
|
||||||
|
# returns: list(tuple(iso_date, list(tuple(hr.work.entry.type(), hours, original_record))
|
||||||
|
return [(iso_date, work_data[iso_date]) for iso_date in sorted(work_data.keys())]
|
||||||
|
|
||||||
|
def _aggregate_overtime(self, work_data, day_week_start=None):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
:param work_data: list(tuple(iso_date, list(tuple(hr.work.entry.type(), hours, original_record))
|
:param work_data: list(tuple(iso_date, list(tuple(hr.work.entry.type(), hours, original_record))
|
||||||
:param day_week_start: day of the week to start (otherwise employee's resource calendar start day of week)
|
:param day_week_start: day of the week to start (otherwise employee's resource calendar start day of week)
|
||||||
:return: dict(hr.work.entry.type(): list(days_worked, hours_worked, ))
|
:return: dict(hr.work.entry.type(): list(days_worked, hours_worked, rate))
|
||||||
"""
|
"""
|
||||||
if not day_week_start:
|
if not day_week_start:
|
||||||
if self.employee_id.resource_calendar_id.day_week_start:
|
if self.employee_id.resource_calendar_id.day_week_start:
|
||||||
@@ -105,3 +142,20 @@ class HRPayslip(models.Model):
|
|||||||
working_aggregation[work_type][1] += hours
|
working_aggregation[work_type][1] += hours
|
||||||
day_hours[iso_date] += hours
|
day_hours[iso_date] += hours
|
||||||
week_hours[week] += hours
|
week_hours[week] += hours
|
||||||
|
|
||||||
|
|
||||||
|
class HrPayslipWorkedDays(models.Model):
|
||||||
|
_inherit = 'hr.payslip.worked_days'
|
||||||
|
|
||||||
|
rate = fields.Float(string='Rate', default=1.0)
|
||||||
|
|
||||||
|
@api.depends('is_paid', 'number_of_hours', 'payslip_id', 'payslip_id.normal_wage', 'payslip_id.sum_worked_hours', 'rate')
|
||||||
|
def _compute_amount(self):
|
||||||
|
for worked_days in self:
|
||||||
|
if not worked_days.contract_id:
|
||||||
|
worked_days.amount = 0
|
||||||
|
continue
|
||||||
|
if worked_days.payslip_id.wage_type == "hourly":
|
||||||
|
worked_days.amount = worked_days.payslip_id.contract_id._get_contract_wage(work_type=worked_days.work_entry_type_id) * worked_days.number_of_hours * worked_days.rate if worked_days.is_paid else 0
|
||||||
|
else:
|
||||||
|
worked_days.amount = worked_days.payslip_id.normal_wage * worked_days.number_of_hours / (worked_days.payslip_id.sum_worked_hours or 1) if worked_days.is_paid else 0
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
(self.work_type, 6.0, None),
|
(self.work_type, 6.0, None),
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertTrue(self.work_type_overtime not in result_data)
|
self.assertTrue(self.work_type_overtime not in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 4)
|
self.assertEqual(result_data[self.work_type][0], 4)
|
||||||
@@ -63,7 +63,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
(self.work_type, 10.0, None),
|
(self.work_type, 10.0, None),
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 5)
|
self.assertEqual(result_data[self.work_type][0], 5)
|
||||||
self.assertEqual(result_data[self.work_type][1], 40.0)
|
self.assertEqual(result_data[self.work_type][1], 40.0)
|
||||||
@@ -77,7 +77,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
(self.work_type, 4.0, None),
|
(self.work_type, 4.0, None),
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 5)
|
self.assertEqual(result_data[self.work_type][0], 5)
|
||||||
self.assertEqual(result_data[self.work_type][1], 40.0)
|
self.assertEqual(result_data[self.work_type][1], 40.0)
|
||||||
@@ -110,7 +110,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
work_data.append(data)
|
work_data.append(data)
|
||||||
work_data.append(((data[0][0], data[0][1]+1, data[0][2]), data[1]))
|
work_data.append(((data[0][0], data[0][1]+1, data[0][2]), data[1]))
|
||||||
|
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertTrue(self.work_type_overtime not in result_data)
|
self.assertTrue(self.work_type_overtime not in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 8)
|
self.assertEqual(result_data[self.work_type][0], 8)
|
||||||
@@ -123,7 +123,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
(self.work_type, 6.0, None),
|
(self.work_type, 6.0, None),
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 9)
|
self.assertEqual(result_data[self.work_type][0], 9)
|
||||||
self.assertEqual(result_data[self.work_type][1], 78.0)
|
self.assertEqual(result_data[self.work_type][1], 78.0)
|
||||||
@@ -137,7 +137,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
(self.work_type, 4.0, None),
|
(self.work_type, 4.0, None),
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 9)
|
self.assertEqual(result_data[self.work_type][0], 9)
|
||||||
self.assertEqual(result_data[self.work_type][1], 78.0)
|
self.assertEqual(result_data[self.work_type][1], 78.0)
|
||||||
@@ -171,7 +171,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
(self.work_type, 6.0, None),
|
(self.work_type, 6.0, None),
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 4)
|
self.assertEqual(result_data[self.work_type][0], 4)
|
||||||
self.assertEqual(result_data[self.work_type][1], 32.0)
|
self.assertEqual(result_data[self.work_type][1], 32.0)
|
||||||
@@ -185,7 +185,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
(self.work_type, 10.0, None),
|
(self.work_type, 10.0, None),
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 5)
|
self.assertEqual(result_data[self.work_type][0], 5)
|
||||||
self.assertEqual(result_data[self.work_type][1], 40.0)
|
self.assertEqual(result_data[self.work_type][1], 40.0)
|
||||||
@@ -199,7 +199,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
(self.work_type, 4.0, None),
|
(self.work_type, 4.0, None),
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 5)
|
self.assertEqual(result_data[self.work_type][0], 5)
|
||||||
self.assertEqual(result_data[self.work_type][1], 40.0)
|
self.assertEqual(result_data[self.work_type][1], 40.0)
|
||||||
@@ -232,7 +232,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
work_data.append(data)
|
work_data.append(data)
|
||||||
work_data.append(((data[0][0], data[0][1]+1, data[0][2]), data[1]))
|
work_data.append(((data[0][0], data[0][1]+1, data[0][2]), data[1]))
|
||||||
|
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertTrue(self.work_type_overtime in result_data)
|
self.assertTrue(self.work_type_overtime in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 8)
|
self.assertEqual(result_data[self.work_type][0], 8)
|
||||||
@@ -247,7 +247,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
(self.work_type, 6.0, None),
|
(self.work_type, 6.0, None),
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 9)
|
self.assertEqual(result_data[self.work_type][0], 9)
|
||||||
self.assertEqual(result_data[self.work_type][1], 70.0)
|
self.assertEqual(result_data[self.work_type][1], 70.0)
|
||||||
@@ -261,7 +261,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
(self.work_type, 4.0, None),
|
(self.work_type, 4.0, None),
|
||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 9)
|
self.assertEqual(result_data[self.work_type][0], 9)
|
||||||
self.assertEqual(result_data[self.work_type][1], 70.0)
|
self.assertEqual(result_data[self.work_type][1], 70.0)
|
||||||
@@ -314,7 +314,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
|
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 4)
|
self.assertEqual(result_data[self.work_type][0], 4)
|
||||||
self.assertEqual(result_data[self.work_type][1], 32.0)
|
self.assertEqual(result_data[self.work_type][1], 32.0)
|
||||||
@@ -342,7 +342,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
]
|
]
|
||||||
|
|
||||||
with self.assertRaises(UserError):
|
with self.assertRaises(UserError):
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
|
|
||||||
def test_14_recursive_infinite_loop(self):
|
def test_14_recursive_infinite_loop(self):
|
||||||
# recursive will use a second overtime, but not this time!
|
# recursive will use a second overtime, but not this time!
|
||||||
@@ -361,7 +361,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
]
|
]
|
||||||
|
|
||||||
with self.assertRaises(UserError):
|
with self.assertRaises(UserError):
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
|
|
||||||
def test_15_override_day_of_week(self):
|
def test_15_override_day_of_week(self):
|
||||||
iso_date = (2020, 24, 1)
|
iso_date = (2020, 24, 1)
|
||||||
@@ -374,7 +374,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
|
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 1)
|
self.assertEqual(result_data[self.work_type][0], 1)
|
||||||
self.assertEqual(result_data[self.work_type][1], 8.0)
|
self.assertEqual(result_data[self.work_type][1], 8.0)
|
||||||
@@ -393,7 +393,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
'work_type_id': self.work_type_overtime.id, # Note that this wouldn't be good in practice
|
'work_type_id': self.work_type_overtime.id, # Note that this wouldn't be good in practice
|
||||||
})]
|
})]
|
||||||
})
|
})
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 1)
|
self.assertEqual(result_data[self.work_type][0], 1)
|
||||||
self.assertEqual(result_data[self.work_type][1], 8.0)
|
self.assertEqual(result_data[self.work_type][1], 8.0)
|
||||||
@@ -413,7 +413,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
]),
|
]),
|
||||||
]
|
]
|
||||||
|
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 1)
|
self.assertEqual(result_data[self.work_type][0], 1)
|
||||||
self.assertEqual(result_data[self.work_type][1], 8.0)
|
self.assertEqual(result_data[self.work_type][1], 8.0)
|
||||||
@@ -432,7 +432,7 @@ class TestOvertime(common.TransactionCase):
|
|||||||
})]
|
})]
|
||||||
})
|
})
|
||||||
self.overtime_rules.flush()
|
self.overtime_rules.flush()
|
||||||
result_data = self.payslip.aggregate_overtime(work_data)
|
result_data = self.payslip._aggregate_overtime(work_data)
|
||||||
self.assertTrue(self.work_type in result_data)
|
self.assertTrue(self.work_type in result_data)
|
||||||
self.assertEqual(result_data[self.work_type][0], 1)
|
self.assertEqual(result_data[self.work_type][0], 1)
|
||||||
self.assertEqual(result_data[self.work_type][1], 8.0)
|
self.assertEqual(result_data[self.work_type][1], 8.0)
|
||||||
|
|||||||
Reference in New Issue
Block a user