diff --git a/hr_payroll_overtime/__manifest__.py b/hr_payroll_overtime/__manifest__.py index 49e653f0..99e48601 100644 --- a/hr_payroll_overtime/__manifest__.py +++ b/hr_payroll_overtime/__manifest__.py @@ -1,7 +1,7 @@ { 'name': 'Payroll Overtime', 'description': 'Provide mechanisms to calculate overtime.', - 'version': '13.0.1.0.0', + 'version': '13.0.1.0.1', 'website': 'https://hibou.io/', 'author': 'Hibou Corp. ', 'license': 'AGPL-3', diff --git a/hr_payroll_overtime/models/hr_payslip.py b/hr_payroll_overtime/models/hr_payslip.py index 13667cc9..3b50cd7a 100644 --- a/hr_payroll_overtime/models/hr_payslip.py +++ b/hr_payroll_overtime/models/hr_payslip.py @@ -32,75 +32,56 @@ class HRPayslip(models.Model): iso_days = set() for iso_date, entries in work_data: iso_date = _adjust_week(iso_date) - week = iso_date[1] for work_type, hours, _ in entries: - if work_type.overtime_work_type_id and work_type.overtime_type_id: - ot_h_w = work_type.overtime_type_id.hours_per_week - ot_h_d = work_type.overtime_type_id.hours_per_day - if ot_h_d and (day_hours[iso_date] + hours) > ot_h_d: - if day_hours[iso_date] >= ot_h_d: - # no time is regular time - if iso_date not in iso_days: - iso_days.add(iso_date) - result[work_type.overtime_work_type_id][0] += 1.0 - result[work_type.overtime_work_type_id][1] += hours - result[work_type.overtime_work_type_id][2] = work_type.overtime_type_id.multiplier - else: - remaining_regular_hours = ot_h_d - day_hours[iso_date] - if remaining_regular_hours - hours < 0.0: - # some time is regular time - regular_hours = remaining_regular_hours - overtime_hours = hours - remaining_regular_hours - if iso_date not in iso_days: - iso_days.add(iso_date) - result[work_type][0] += 1.0 - result[work_type][1] += regular_hours - result[work_type.overtime_work_type_id][1] += overtime_hours - result[work_type.overtime_work_type_id][2] = work_type.overtime_type_id.multiplier - else: - # all time is regular time - if iso_date not in iso_days: - iso_days.add(iso_date) - result[work_type][0] += 1.0 - result[work_type][1] += hours - elif ot_h_w: - if week_hours[week] > ot_h_w: - # no time is regular time - if iso_date not in iso_days: - iso_days.add(iso_date) - result[work_type.overtime_work_type_id][0] += 1.0 - result[work_type.overtime_work_type_id][1] += hours - result[work_type.overtime_work_type_id][2] = work_type.overtime_type_id.multiplier - else: - remaining_regular_hours = ot_h_w - week_hours[week] - if remaining_regular_hours - hours < 0.0: - # some time is regular time - regular_hours = remaining_regular_hours - overtime_hours = hours - remaining_regular_hours - if iso_date not in iso_days: - iso_days.add(iso_date) - result[work_type][0] += 1.0 - result[work_type][1] += regular_hours - result[work_type.overtime_work_type_id][1] += overtime_hours - result[work_type.overtime_work_type_id][2] = work_type.overtime_type_id.multiplier - else: - # all time is regular time - if iso_date not in iso_days: - iso_days.add(iso_date) - result[work_type][0] += 1.0 - result[work_type][1] += hours - else: - # all time is regular time - if iso_date not in iso_days: - iso_days.add(iso_date) - result[work_type][0] += 1.0 - result[work_type][1] += hours - else: - if iso_date not in iso_days: - iso_days.add(iso_date) - result[work_type][0] += 1.0 - result[work_type][1] += hours - # Always - day_hours[iso_date] += hours - week_hours[week] += hours + self._aggregate_overtime_add_work_type_hours(work_type, hours, iso_date, result, iso_days, day_hours, week_hours) + return result + + def _aggregate_overtime_add_work_type_hours(self, work_type, hours, iso_date, working_aggregation, iso_days, day_hours, week_hours): + """ + :param work_type: work type of hours being added + :param hours: hours being added + :param iso_date: date hours were worked + :param working_aggregation: dict of work type hours as they are processed + :param iso_days: set of iso days already seen + :param day_hours: hours worked on iso dates already processed + :param week_hours: hours worked on iso week already processed + :return: + """ + week = iso_date[1] + if work_type.overtime_work_type_id and work_type.overtime_type_id: + ot_h_w = work_type.overtime_type_id.hours_per_week + ot_h_d = work_type.overtime_type_id.hours_per_day + + regular_hours = hours + # adjust the hours based on overtime conditions + if ot_h_d and (day_hours[iso_date] + hours) > ot_h_d: + # daily overtime in effect + remaining_hours = max(ot_h_d - day_hours[iso_date], 0.0) + regular_hours = min(remaining_hours, hours) + elif ot_h_w: + # not daily, but weekly limits.... + remaining_hours = max(ot_h_w - week_hours[week], 0.0) + regular_hours = min(remaining_hours, hours) + ot_hours = hours - regular_hours + if regular_hours: + if iso_date not in iso_days: + iso_days.add(iso_date) + working_aggregation[work_type][0] += 1.0 + working_aggregation[work_type][1] += regular_hours + day_hours[iso_date] += regular_hours + week_hours[week] += regular_hours + if ot_hours: + # 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 + 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: + # No overtime, just needs added to set + if iso_date not in iso_days: + iso_days.add(iso_date) + working_aggregation[work_type][0] += 1.0 + working_aggregation[work_type][1] += hours + day_hours[iso_date] += hours + week_hours[week] += hours diff --git a/hr_payroll_overtime/tests/test_overtime.py b/hr_payroll_overtime/tests/test_overtime.py index 6d6f0804..808c073e 100644 --- a/hr_payroll_overtime/tests/test_overtime.py +++ b/hr_payroll_overtime/tests/test_overtime.py @@ -269,3 +269,55 @@ class TestOvertime(common.TransactionCase): def test_11_overtime_aggregation_daily_week_start(self): self.employee.resource_calendar_id.day_week_start = '7' self.test_10_overtime_aggregation_daily() + + def test_12_recursive_daily(self): + # recursive will use a second overtime + self.work_type_overtime2 = self.env['hr.work.entry.type'].create({ + 'name': 'Test Overtime 2', + 'code': 'TEST_OT2' + }) + self.overtime_rules2 = self.env['hr.work.entry.overtime.type'].create({ + 'name': 'Test2', + 'hours_per_week': 999.0, + 'hours_per_day': 12.0, + 'multiplier': 2.0, + }) + 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_rules2 + self.work_type_overtime.overtime_work_type_id = self.work_type_overtime2 + + work_data = [ + ((2020, 24, 1), [ + # regular day + (self.work_type, 4.0, None), + (self.work_type, 4.0, None), + ]), + ((2020, 24, 2), [ + # 2hr overtime + (self.work_type, 4.0, None), + (self.work_type, 6.0, None), + ]), + ((2020, 24, 3), [ + # 4hr overtime + (self.work_type, 6.0, None), + (self.work_type, 6.0, None), + ]), + ((2020, 24, 4), [ + # 4hr overtime + # 2hr overtime2 + (self.work_type, 6.0, None), + (self.work_type, 8.0, None), + ]), + ] + + 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) + self.assertTrue(self.work_type_overtime in result_data) + self.assertEqual(result_data[self.work_type_overtime][0], 0) + self.assertEqual(result_data[self.work_type_overtime][1], 10.0) + 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)