From f61bb6e15cbfed028fa330e2dcd592b9c18541ef Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Wed, 9 Dec 2020 15:21:29 -0800 Subject: [PATCH] [IMP] hr_payroll_overtime: refactor to abstract override class and use on Work Types themselves E.g. It is now possible to support "Sunday Pay" where before it was only possible to give "Sunday Overtime Pay" as an override to overtime itself. --- hr_payroll_overtime/models/hr_payslip.py | 13 ++++ hr_payroll_overtime/models/hr_work_entry.py | 64 ++++++++++++------- .../security/ir.model.access.csv | 4 +- hr_payroll_overtime/tests/test_overtime.py | 37 +++++++++++ .../views/hr_work_entry_views.xml | 10 +++ 5 files changed, 104 insertions(+), 24 deletions(-) diff --git a/hr_payroll_overtime/models/hr_payslip.py b/hr_payroll_overtime/models/hr_payslip.py index 9031b3a4..538d9b9f 100644 --- a/hr_payroll_overtime/models/hr_payslip.py +++ b/hr_payroll_overtime/models/hr_payslip.py @@ -91,6 +91,19 @@ class HRPayslip(models.Model): :param week_hours: hours worked on iso week already processed :return: """ + override = work_type.override_for_iso_date(iso_date) + if override: + new_work_type = override.work_type_id + multiplier = override.multiplier + if work_type == new_work_type: + # trivial infinite recursion from override + raise UserError('Work type "%s" (id %s) must not have itself as its override work type. ' + 'This occurred due to an override line "%s".' % ( + work_type.name, work_type.id, override.name)) + # update the work_type on this step. + work_type = new_work_type + working_aggregation[work_type][2] = multiplier + 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 diff --git a/hr_payroll_overtime/models/hr_work_entry.py b/hr_payroll_overtime/models/hr_work_entry.py index 3225da5b..f607fd32 100644 --- a/hr_payroll_overtime/models/hr_work_entry.py +++ b/hr_payroll_overtime/models/hr_work_entry.py @@ -4,11 +4,51 @@ from odoo.exceptions import ValidationError from .resource_calendar import WEEKDAY_SELECTION +class WorkEntryOverride(models.AbstractModel): + _name = 'hr.work.entry.type.override.abstract' + _order = 'date desc, day_of_week' + + name = fields.Char(string='Description') + work_type_id = fields.Many2one('hr.work.entry.type', string='Override Work Type', required=True, + help='Distinct Work Type for when this applies.') + multiplier = fields.Float(string='Multiplier', + help='Rate for override. E.g. maybe you have "Sunday Pay" at 2.0x') + day_of_week = fields.Selection(WEEKDAY_SELECTION, string='Day of Week') + date = fields.Date(string='Date') + + @api.constrains('day_of_week', 'date') + def _constrain_days(self): + for override in self: + if override.day_of_week and override.date: + raise ValidationError('An override should only have a Date OR Day of Week.') + + def iso_date_applies(self, iso_date): + for override in self: + if override.date and override.date.isocalendar() == iso_date: + return override + if int(override.day_of_week) == iso_date[2]: + return override + + class HRWorkEntryType(models.Model): _inherit = 'hr.work.entry.type' overtime_work_type_id = fields.Many2one('hr.work.entry.type', string='Overtime Work Type') overtime_type_id = fields.Many2one('hr.work.entry.overtime.type', string='Overtime Rules') + override_ids = fields.One2many('hr.work.entry.type.override', 'original_type_id', string='Overrides', + help='Override work entry type on payslip.') + + def override_for_iso_date(self, iso_date): + return self.override_ids.iso_date_applies(iso_date) + + +class HRWorkEntryTypeOverride(models.Model): + _name = 'hr.work.entry.type.override' + _inherit = 'hr.work.entry.type.override.abstract' + _description = 'Work Type Override' + + original_type_id = fields.Many2one('hr.work.entry.type', + string='Work Entry Type') class HRWorkEntryOvertime(models.Model): @@ -32,30 +72,8 @@ class HRWorkEntryOvertime(models.Model): class HRWorkEntryOvertimeOverride(models.Model): _name = 'hr.work.entry.overtime.type.override' + _inherit = 'hr.work.entry.type.override.abstract' _description = 'Overtime Rule Override' - _order = 'date desc, day_of_week' - name = fields.Char(string='Description') overtime_type_id = fields.Many2one('hr.work.entry.overtime.type', string='Overtime Rules') - work_type_id = fields.Many2one('hr.work.entry.type', string='Overtime Work Type', required=True, - help='Distinct Work Type for this. Given the different rate, it should ' - ' be different from other Overtime Work Types (because payslips ' - 'should only have one line/rate per work type).') - multiplier = fields.Float(string='Multiplier', - help='Rate for when overtime is reached.') - day_of_week = fields.Selection(WEEKDAY_SELECTION, string='Day of Week') - date = fields.Date(string='Date') - - @api.constrains('day_of_week', 'date') - def _constrain_days(self): - for override in self: - if override.day_of_week and override.date: - raise ValidationError('An override should only have a Date OR Day of Week.') - - def iso_date_applies(self, iso_date): - for override in self: - if override.date and override.date.isocalendar() == iso_date: - return override - if int(override.day_of_week) == iso_date[2]: - return override diff --git a/hr_payroll_overtime/security/ir.model.access.csv b/hr_payroll_overtime/security/ir.model.access.csv index 03aadd5f..0b0223eb 100644 --- a/hr_payroll_overtime/security/ir.model.access.csv +++ b/hr_payroll_overtime/security/ir.model.access.csv @@ -2,4 +2,6 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_hr_work_entry_overtime_type,access_hr_work_entry_overtime_type,model_hr_work_entry_overtime_type,base.group_user,1,0,0,0 manage_hr_work_entry_overtime_type,manage_hr_work_entry_overtime_type,model_hr_work_entry_overtime_type,hr_payroll.group_hr_payroll_manager,1,1,1,1 access_hr_work_entry_overtime_type_override,access_hr_work_entry_overtime_type_override,model_hr_work_entry_overtime_type_override,base.group_user,1,0,0,0 -manage_hr_work_entry_overtime_type_override,manage_hr_work_entry_overtime_type_override,model_hr_work_entry_overtime_type_override,hr_payroll.group_hr_payroll_manager,1,1,1,1 \ No newline at end of file +manage_hr_work_entry_overtime_type_override,manage_hr_work_entry_overtime_type_override,model_hr_work_entry_overtime_type_override,hr_payroll.group_hr_payroll_manager,1,1,1,1 +access_hr_work_entry_type_override,access_hr_work_entry_type_override,model_hr_work_entry_type_override,base.group_user,1,0,0,0 +manage_hr_work_entry_type_override,manage_hr_work_entry_type_override,model_hr_work_entry_type_override,hr_payroll.group_hr_payroll_manager,1,1,1,1 diff --git a/hr_payroll_overtime/tests/test_overtime.py b/hr_payroll_overtime/tests/test_overtime.py index 1c4023e0..fc7e7eaf 100644 --- a/hr_payroll_overtime/tests/test_overtime.py +++ b/hr_payroll_overtime/tests/test_overtime.py @@ -453,3 +453,40 @@ class TestOvertime(common.TransactionCase): 'work_type_id': self.work_type_overtime.id, # Note that this wouldn't be good in practice })] }) + + def test_18_override_day_of_week_on_work_type(self): + iso_date = (2020, 24, 1) + iso_date2 = (2020, 24, 2) + + work_data = [ + (iso_date, [ + (self.work_type, 4.0, None), + ]), + (iso_date2, [ + (self.work_type, 4.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], 2) + self.assertEqual(result_data[self.work_type][1], 8.0) + + # Now lets make an override line + test_multiplier = 3.0 + self.work_type.write({ + 'override_ids': [(0, 0, { + 'name': 'Day 2 Override', + 'multiplier': test_multiplier, + 'day_of_week': str(iso_date[2]), + '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) + 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], 4.0) + self.assertTrue(self.work_type_overtime in result_data) + self.assertEqual(result_data[self.work_type_overtime][0], 1) + self.assertEqual(result_data[self.work_type_overtime][1], 4.0) + self.assertEqual(result_data[self.work_type_overtime][2], test_multiplier) diff --git a/hr_payroll_overtime/views/hr_work_entry_views.xml b/hr_payroll_overtime/views/hr_work_entry_views.xml index c60f693a..774d61ed 100644 --- a/hr_payroll_overtime/views/hr_work_entry_views.xml +++ b/hr_payroll_overtime/views/hr_work_entry_views.xml @@ -15,6 +15,16 @@ + + + + + + + + + +