diff --git a/hr_payroll_overtime/__manifest__.py b/hr_payroll_overtime/__manifest__.py index 0895cf20..fc685a77 100644 --- a/hr_payroll_overtime/__manifest__.py +++ b/hr_payroll_overtime/__manifest__.py @@ -13,7 +13,7 @@ 'views/resource_calendar_views.xml', ], 'depends': [ - 'hr_payroll', + 'hr_payroll_hibou', 'hr_work_entry', ], } diff --git a/hr_payroll_overtime/models/hr_payslip.py b/hr_payroll_overtime/models/hr_payslip.py index 0ff4d2a0..9031b3a4 100644 --- a/hr_payroll_overtime/models/hr_payslip.py +++ b/hr_payroll_overtime/models/hr_payslip.py @@ -1,17 +1,54 @@ from collections import defaultdict -from odoo import models +from odoo import api, fields, models from odoo.exceptions import UserError class HRPayslip(models.Model): _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 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 self.employee_id.resource_calendar_id.day_week_start: @@ -105,3 +142,20 @@ class HRPayslip(models.Model): working_aggregation[work_type][1] += hours day_hours[iso_date] += 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 diff --git a/hr_payroll_overtime/tests/test_overtime.py b/hr_payroll_overtime/tests/test_overtime.py index 6160f1b2..1c4023e0 100644 --- a/hr_payroll_overtime/tests/test_overtime.py +++ b/hr_payroll_overtime/tests/test_overtime.py @@ -51,7 +51,7 @@ class TestOvertime(common.TransactionCase): (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_overtime not in result_data) self.assertEqual(result_data[self.work_type][0], 4) @@ -63,7 +63,7 @@ class TestOvertime(common.TransactionCase): (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.assertEqual(result_data[self.work_type][0], 5) self.assertEqual(result_data[self.work_type][1], 40.0) @@ -77,7 +77,7 @@ class TestOvertime(common.TransactionCase): (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.assertEqual(result_data[self.work_type][0], 5) 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[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_overtime not in result_data) self.assertEqual(result_data[self.work_type][0], 8) @@ -123,7 +123,7 @@ class TestOvertime(common.TransactionCase): (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.assertEqual(result_data[self.work_type][0], 9) self.assertEqual(result_data[self.work_type][1], 78.0) @@ -137,7 +137,7 @@ class TestOvertime(common.TransactionCase): (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.assertEqual(result_data[self.work_type][0], 9) self.assertEqual(result_data[self.work_type][1], 78.0) @@ -171,7 +171,7 @@ class TestOvertime(common.TransactionCase): (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.assertEqual(result_data[self.work_type][0], 4) self.assertEqual(result_data[self.work_type][1], 32.0) @@ -185,7 +185,7 @@ class TestOvertime(common.TransactionCase): (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.assertEqual(result_data[self.work_type][0], 5) self.assertEqual(result_data[self.work_type][1], 40.0) @@ -199,7 +199,7 @@ class TestOvertime(common.TransactionCase): (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.assertEqual(result_data[self.work_type][0], 5) 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[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_overtime in result_data) self.assertEqual(result_data[self.work_type][0], 8) @@ -247,7 +247,7 @@ class TestOvertime(common.TransactionCase): (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.assertEqual(result_data[self.work_type][0], 9) self.assertEqual(result_data[self.work_type][1], 70.0) @@ -261,7 +261,7 @@ class TestOvertime(common.TransactionCase): (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.assertEqual(result_data[self.work_type][0], 9) 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.assertEqual(result_data[self.work_type][0], 4) self.assertEqual(result_data[self.work_type][1], 32.0) @@ -342,7 +342,7 @@ class TestOvertime(common.TransactionCase): ] 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): # recursive will use a second overtime, but not this time! @@ -361,7 +361,7 @@ class TestOvertime(common.TransactionCase): ] 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): 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.assertEqual(result_data[self.work_type][0], 1) 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 })] }) - 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.assertEqual(result_data[self.work_type][0], 1) 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.assertEqual(result_data[self.work_type][0], 1) self.assertEqual(result_data[self.work_type][1], 8.0) @@ -432,7 +432,7 @@ class TestOvertime(common.TransactionCase): })] }) 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.assertEqual(result_data[self.work_type][0], 1) self.assertEqual(result_data[self.work_type][1], 8.0)