From b6edc9120747faec62a8731dc5c930e59c94b221 Mon Sep 17 00:00:00 2001 From: Kristen Marie Kulha Date: Thu, 12 Jul 2018 16:04:20 -0700 Subject: [PATCH 1/6] Initial commit of `hr_holidays_accrual` and `hr_holidays_accrual_payroll` --- hr_holidays_accrual/README.rst | 30 +++++++++++ hr_holidays_accrual/__init__.py | 1 + hr_holidays_accrual/__manifest__.py | 18 +++++++ hr_holidays_accrual/models/__init__.py | 1 + hr_holidays_accrual/models/hr_holidays.py | 54 +++++++++++++++++++ hr_holidays_accrual/tests/__init__.py | 1 + hr_holidays_accrual/tests/test_leaves.py | 43 +++++++++++++++ .../views/hr_holidays_views.xml | 13 +++++ 8 files changed, 161 insertions(+) create mode 100644 hr_holidays_accrual/README.rst create mode 100755 hr_holidays_accrual/__init__.py create mode 100755 hr_holidays_accrual/__manifest__.py create mode 100644 hr_holidays_accrual/models/__init__.py create mode 100644 hr_holidays_accrual/models/hr_holidays.py create mode 100755 hr_holidays_accrual/tests/__init__.py create mode 100644 hr_holidays_accrual/tests/test_leaves.py create mode 100644 hr_holidays_accrual/views/hr_holidays_views.xml diff --git a/hr_holidays_accrual/README.rst b/hr_holidays_accrual/README.rst new file mode 100644 index 00000000..bd4d2af7 --- /dev/null +++ b/hr_holidays_accrual/README.rst @@ -0,0 +1,30 @@ +*************************** +Hibou - HR Holidays Accrual +*************************** + +Use Employee Tags to grant Leave Allocations. + +For more information and add-ons, visit `Hibou.io `_. + +============= +Main Features +============= + +* Adds new boolean field `grant_by_tag` to Leave Allocations. +* Allows user to create a Leave Allocation by employee tag, then use that tag to grant the newly created leave to as many employees as desired. + +.. image:: https://user-images.githubusercontent.com/15882954/42062226-cf3ce23e-7ae1-11e8-96dc-43268c7b904c.png + :alt: 'Equipment Detail' + :width: 988 + :align: left + + + + +======= +License +======= + +Please see `LICENSE `_. + +Copyright Hibou Corp. 2018 \ No newline at end of file diff --git a/hr_holidays_accrual/__init__.py b/hr_holidays_accrual/__init__.py new file mode 100755 index 00000000..0650744f --- /dev/null +++ b/hr_holidays_accrual/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/hr_holidays_accrual/__manifest__.py b/hr_holidays_accrual/__manifest__.py new file mode 100755 index 00000000..022546ee --- /dev/null +++ b/hr_holidays_accrual/__manifest__.py @@ -0,0 +1,18 @@ +{ + 'name': 'HR Holidays Accrual', + 'author': 'Hibou Corp. ', + 'version': '11.0.0.0.0', + 'category': 'Human Resources', + 'sequence': 95, + 'summary': 'Grant leave allocations with tags', + 'description': """ +Create leave allocations by tag, then use tags to grant leaves to employees. + """, + 'website': 'https://hibou.io/', + 'depends': ['hr_holidays'], + 'data': [ + 'views/hr_holidays_views.xml', + ], + 'installable': True, + 'application': False, +} diff --git a/hr_holidays_accrual/models/__init__.py b/hr_holidays_accrual/models/__init__.py new file mode 100644 index 00000000..070ade81 --- /dev/null +++ b/hr_holidays_accrual/models/__init__.py @@ -0,0 +1 @@ +from . import hr_holidays diff --git a/hr_holidays_accrual/models/hr_holidays.py b/hr_holidays_accrual/models/hr_holidays.py new file mode 100644 index 00000000..cbb7e3ac --- /dev/null +++ b/hr_holidays_accrual/models/hr_holidays.py @@ -0,0 +1,54 @@ +from odoo import api, fields, models + + +class HRHolidays(models.Model): + _inherit = 'hr.holidays' + + grant_by_tag = fields.Boolean(string="Grant by Tag") + + def _accrue_for_employee_values(self, employee): + return { + 'holiday_status_id': self.holiday_status_id.id, + 'number_of_days_temp': self.number_of_days_temp, + 'holiday_type': 'employee', + 'employee_id': employee.id, + 'type': 'add', + 'state': 'confirm', + 'double_validation': self.double_validation, + 'grant_by_tag': self.grant_by_tag, + } + + def accrue_for_employee(self, employee): + holidays = self.env['hr.holidays'].sudo() + for leave_to_create in self: + values = leave_to_create._accrue_for_employee_values(employee) + if values: + leave = holidays.create(values) + leave.action_approve() + + +class HREmployee(models.Model): + _inherit = 'hr.employee' + + @api.multi + def write(self, values): + holidays = self.env['hr.holidays'].sudo() + for emp in self: + if values.get('category_ids'): + categ_ids_command_list = values.get('category_ids') + for categ_ids_command in categ_ids_command_list: + ids = None + if categ_ids_command[0] == 6: + ids = set(categ_ids_command[2]) + ids -= set(emp.category_ids.ids) + if categ_ids_command[0] == 4: + id = categ_ids_command[1] + if id not in emp.category_ids.ids: + ids = [id] + if ids: + # new category ids + leaves = holidays.search([('category_id', 'in', list(ids)), + ('grant_by_tag', '=', True)]) + leaves.accrue_for_employee(emp) + + return super(HREmployee, self).write(values) diff --git a/hr_holidays_accrual/tests/__init__.py b/hr_holidays_accrual/tests/__init__.py new file mode 100755 index 00000000..76829b5b --- /dev/null +++ b/hr_holidays_accrual/tests/__init__.py @@ -0,0 +1 @@ +from . import test_leaves diff --git a/hr_holidays_accrual/tests/test_leaves.py b/hr_holidays_accrual/tests/test_leaves.py new file mode 100644 index 00000000..1fc9de93 --- /dev/null +++ b/hr_holidays_accrual/tests/test_leaves.py @@ -0,0 +1,43 @@ +from odoo.addons.hr_holidays.tests.common import TestHrHolidaysBase + + +class TestLeaves(TestHrHolidaysBase): + + def setUp(self): + super(TestLeaves, self).setUp() + + self.categ = self.env['hr.employee.category'].create({'name': 'Test Category'}) + department = self.env['hr.department'].create({'name': 'Test Department'}) + self.employee = self.env['hr.employee'].create({'name': 'Mark', 'department_id': department.id}) + self.leave_type = self.env['hr.holidays.status'].create({ + 'name': 'Test Status', + 'color_name': 'red', + }) + self.test_leave = self.env['hr.holidays'].create({ + 'holiday_status_id': self.leave_type.id, + 'number_of_days_temp': 5, + 'holiday_type': 'category', + 'category_id': self.categ.id, + 'type': 'add', + 'state': 'draft', + 'grant_by_tag': True, + }) + + def test_tag_assignment(self): + self.test_leave.action_confirm() + self.test_leave.action_approve() + self.assertEqual(self.employee.leaves_count, 0.0) + self.employee.write({'category_ids': [(6, False, [self.categ.id])]}) + self.assertEqual(self.employee.leaves_count, 5.0) + leave = self.env['hr.holidays'].search([('employee_id', '=', self.employee.id)]) + self.assertEqual(leave.holiday_status_id.id, self.leave_type.id) + + def test_double_validation(self): + self.test_leave.write({'double_validation': True}) + self.test_leave.action_confirm() + self.test_leave.action_approve() + self.test_leave.action_validate() + self.employee.write({'category_ids': [(6, False, [self.categ.id])]}) + leave = self.env['hr.holidays'].search([('employee_id', '=', self.employee.id)]) + self.assertEqual(leave.state, 'validate1') + self.assertEqual(leave.first_approver_id.id, self.env.uid) diff --git a/hr_holidays_accrual/views/hr_holidays_views.xml b/hr_holidays_accrual/views/hr_holidays_views.xml new file mode 100644 index 00000000..7ca7bad6 --- /dev/null +++ b/hr_holidays_accrual/views/hr_holidays_views.xml @@ -0,0 +1,13 @@ + + + + hr.holidays.edit.holiday.new.inherit + hr.holidays + + + + + + + + \ No newline at end of file From 973fd3cd030e91d00a00208f7330594fe0d8eba8 Mon Sep 17 00:00:00 2001 From: Kristen Marie Kulha Date: Thu, 9 Aug 2018 11:24:13 -0700 Subject: [PATCH 2/6] Added new `accrue_max` field to `hr_holidays_accrual` and `hr_holidays_accrual_payroll`. --- hr_holidays_accrual/views/hr_holidays_views.xml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hr_holidays_accrual/views/hr_holidays_views.xml b/hr_holidays_accrual/views/hr_holidays_views.xml index 7ca7bad6..a2cbed87 100644 --- a/hr_holidays_accrual/views/hr_holidays_views.xml +++ b/hr_holidays_accrual/views/hr_holidays_views.xml @@ -5,8 +5,10 @@ hr.holidays - - + + + + From 15ca4c0e3e5d6b2291f9d6b06934fabc3fa4446b Mon Sep 17 00:00:00 2001 From: Kristen Marie Kulha Date: Thu, 12 Jul 2018 16:04:20 -0700 Subject: [PATCH 3/6] Initial commit of `hr_holidays_accrual` and `hr_holidays_accrual_payroll` --- hr_holidays_accrual_payroll/README.rst | 30 +++++++++++++ hr_holidays_accrual_payroll/__init__.py | 1 + hr_holidays_accrual_payroll/__manifest__.py | 21 +++++++++ .../models/__init__.py | 1 + .../models/hr_payslip.py | 32 ++++++++++++++ hr_holidays_accrual_payroll/tests/__init__.py | 1 + .../tests/test_accrual.py | 43 +++++++++++++++++++ .../views/hr_holidays_views.xml | 14 ++++++ 8 files changed, 143 insertions(+) create mode 100644 hr_holidays_accrual_payroll/README.rst create mode 100755 hr_holidays_accrual_payroll/__init__.py create mode 100755 hr_holidays_accrual_payroll/__manifest__.py create mode 100644 hr_holidays_accrual_payroll/models/__init__.py create mode 100644 hr_holidays_accrual_payroll/models/hr_payslip.py create mode 100755 hr_holidays_accrual_payroll/tests/__init__.py create mode 100644 hr_holidays_accrual_payroll/tests/test_accrual.py create mode 100644 hr_holidays_accrual_payroll/views/hr_holidays_views.xml diff --git a/hr_holidays_accrual_payroll/README.rst b/hr_holidays_accrual_payroll/README.rst new file mode 100644 index 00000000..2873ee26 --- /dev/null +++ b/hr_holidays_accrual_payroll/README.rst @@ -0,0 +1,30 @@ +************************************* +Hibou - HR Holidays Accrual - Payroll +************************************* + +Accrue employee leave allocations every pay period. + +For more information and add-ons, visit `Hibou.io `_. + +============= +Main Features +============= + +* New Fields `accrue_by_pay_period` and `allocation_per_pay_period` on Leave allocations. +* Can set up an accrual by individual employee, or make an Allocation by Employee Tag for multiple employees. + + +.. image:: https://user-images.githubusercontent.com/15882954/42062853-f4175416-7ae3-11e8-8432-f54e26fe6094.png + :alt: 'Equipment Detail' + :width: 988 + :align: left + + + +======= +License +======= + +Please see `LICENSE `_. + +Copyright Hibou Corp. 2018 \ No newline at end of file diff --git a/hr_holidays_accrual_payroll/__init__.py b/hr_holidays_accrual_payroll/__init__.py new file mode 100755 index 00000000..0650744f --- /dev/null +++ b/hr_holidays_accrual_payroll/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/hr_holidays_accrual_payroll/__manifest__.py b/hr_holidays_accrual_payroll/__manifest__.py new file mode 100755 index 00000000..061f4429 --- /dev/null +++ b/hr_holidays_accrual_payroll/__manifest__.py @@ -0,0 +1,21 @@ +{ + 'name': 'HR Holidays Accrual - Payroll', + 'author': 'Hibou Corp. ', + 'version': '11.0.0.0.0', + 'category': 'Human Resources', + 'sequence': 95, + 'summary': 'Grant leave allocations per payperiod', + 'description': """ +Automates leave allocations. + """, + 'website': 'https://hibou.io/', + 'depends': [ + 'hr_holidays_accrual', + 'hr_payroll' + ], + 'data': [ + 'views/hr_holidays_views.xml', + ], + 'installable': True, + 'application': False, +} diff --git a/hr_holidays_accrual_payroll/models/__init__.py b/hr_holidays_accrual_payroll/models/__init__.py new file mode 100644 index 00000000..5d988fa2 --- /dev/null +++ b/hr_holidays_accrual_payroll/models/__init__.py @@ -0,0 +1 @@ +from . import hr_payslip diff --git a/hr_holidays_accrual_payroll/models/hr_payslip.py b/hr_holidays_accrual_payroll/models/hr_payslip.py new file mode 100644 index 00000000..893be350 --- /dev/null +++ b/hr_holidays_accrual_payroll/models/hr_payslip.py @@ -0,0 +1,32 @@ +from odoo import api, fields, models + + +class HRPayslip(models.Model): + _inherit = 'hr.payslip' + + @api.multi + def action_payslip_done(self): + res = super(HRPayslip, self).action_payslip_done() + if res and isinstance(res, (int, bool)): + holidays = self.env['hr.holidays'].sudo() + leaves_to_update = holidays.search([('employee_id', '=', self.employee_id.id), + ('accrue_by_pay_period', '=', True)]) + for leave_to_update in leaves_to_update: + new_allocation = leave_to_update.number_of_days_temp + leave_to_update.allocation_per_period + leave_to_update.write({'number_of_days_temp': new_allocation}) + + return res + + +class HRHolidays(models.Model): + _inherit = 'hr.holidays' + + accrue_by_pay_period = fields.Boolean(string="Accrue by Pay Period") + allocation_per_period = fields.Float(string="Allocation Per Pay Period", digits=(12, 4)) + + def _accrue_for_employee_values(self, employee): + values = super(HRHolidays, self)._accrue_for_employee_values(employee) + if values: + values['accrue_by_pay_period'] = self.accrue_by_pay_period + values['allocation_per_period'] = self.allocation_per_period + return values diff --git a/hr_holidays_accrual_payroll/tests/__init__.py b/hr_holidays_accrual_payroll/tests/__init__.py new file mode 100755 index 00000000..6e022122 --- /dev/null +++ b/hr_holidays_accrual_payroll/tests/__init__.py @@ -0,0 +1 @@ +from . import test_accrual diff --git a/hr_holidays_accrual_payroll/tests/test_accrual.py b/hr_holidays_accrual_payroll/tests/test_accrual.py new file mode 100644 index 00000000..43f325de --- /dev/null +++ b/hr_holidays_accrual_payroll/tests/test_accrual.py @@ -0,0 +1,43 @@ +from odoo.addons.hr_holidays.tests.common import TestHrHolidaysBase + + +class TestLeaves(TestHrHolidaysBase): + + def setUp(self): + super(TestLeaves, self).setUp() + + self.categ = self.env['hr.employee.category'].create({'name': 'Test Category'}) + department = self.env['hr.department'].create({'name': 'Test Department'}) + self.employee = self.env['hr.employee'].create({'name': 'Mark', 'department_id': department.id}) + self.leave_type = self.env['hr.holidays.status'].create({ + 'name': 'Test Status', + 'color_name': 'red', + }) + self.test_leave = self.env['hr.holidays'].create({ + 'holiday_status_id': self.leave_type.id, + 'number_of_days_temp': 0, + 'holiday_type': 'category', + 'category_id': self.categ.id, + 'type': 'add', + 'state': 'draft', + 'grant_by_tag': True, + }) + + def test_payslip_accrual(self): + self.test_leave.write({ + 'accrue_by_pay_period': True, + 'allocation_per_period': 1 + }) + self.test_leave.action_confirm() + self.test_leave.action_approve() + + self.employee.write({'category_ids': [(6, False, [self.categ.id])]}) + self.assertEqual(self.employee.leaves_count, 0.0) + + payslip = self.env['hr.payslip'].create({ + 'employee_id': self.employee.id, + 'date_from': '2018-01-01', + 'date_to': '2018-01-31' + }) + payslip.action_payslip_done() + self.assertEqual(self.employee.leaves_count, 1.0) diff --git a/hr_holidays_accrual_payroll/views/hr_holidays_views.xml b/hr_holidays_accrual_payroll/views/hr_holidays_views.xml new file mode 100644 index 00000000..8c15f42c --- /dev/null +++ b/hr_holidays_accrual_payroll/views/hr_holidays_views.xml @@ -0,0 +1,14 @@ + + + + hr.holidays.edit.holiday.new.inherit.pay + hr.holidays + + + + + + + + + \ No newline at end of file From a24b2fcb3a22eb7af49f577f61ec8271e8457250 Mon Sep 17 00:00:00 2001 From: Kristen Marie Kulha Date: Thu, 9 Aug 2018 11:24:13 -0700 Subject: [PATCH 4/6] Added new `accrue_max` field to `hr_holidays_accrual` and `hr_holidays_accrual_payroll`. --- hr_holidays_accrual_payroll/__manifest__.py | 2 +- .../models/hr_payslip.py | 17 ++++++++++++++++- .../views/hr_holidays_views.xml | 3 ++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/hr_holidays_accrual_payroll/__manifest__.py b/hr_holidays_accrual_payroll/__manifest__.py index 061f4429..96893f7f 100755 --- a/hr_holidays_accrual_payroll/__manifest__.py +++ b/hr_holidays_accrual_payroll/__manifest__.py @@ -1,7 +1,7 @@ { 'name': 'HR Holidays Accrual - Payroll', 'author': 'Hibou Corp. ', - 'version': '11.0.0.0.0', + 'version': '11.0.1.0.0', 'category': 'Human Resources', 'sequence': 95, 'summary': 'Grant leave allocations per payperiod', diff --git a/hr_holidays_accrual_payroll/models/hr_payslip.py b/hr_holidays_accrual_payroll/models/hr_payslip.py index 893be350..1c47b6a4 100644 --- a/hr_holidays_accrual_payroll/models/hr_payslip.py +++ b/hr_holidays_accrual_payroll/models/hr_payslip.py @@ -13,7 +13,20 @@ class HRPayslip(models.Model): ('accrue_by_pay_period', '=', True)]) for leave_to_update in leaves_to_update: new_allocation = leave_to_update.number_of_days_temp + leave_to_update.allocation_per_period - leave_to_update.write({'number_of_days_temp': new_allocation}) + q = """SELECT SUM(number_of_days) + FROM hr_holidays + WHERE employee_id = %d AND holiday_status_id = %d;""" % (leave_to_update.employee_id.id, leave_to_update.holiday_status_id.id) + self.env.cr.execute(q) + total_days = self.env.cr.fetchall() + total_days = total_days[0][0] + new_total_days = total_days + leave_to_update.allocation_per_period + if leave_to_update.accrue_max and total_days > leave_to_update.accrue_max: + new_allocation = leave_to_update.number_of_days_temp + elif leave_to_update.accrue_max and new_total_days > leave_to_update.accrue_max: + difference = leave_to_update.accrue_max - total_days + new_allocation = leave_to_update.number_of_days_temp + difference + if leave_to_update.number_of_days_temp != new_allocation: + leave_to_update.write({'number_of_days_temp': new_allocation}) return res @@ -23,10 +36,12 @@ class HRHolidays(models.Model): accrue_by_pay_period = fields.Boolean(string="Accrue by Pay Period") allocation_per_period = fields.Float(string="Allocation Per Pay Period", digits=(12, 4)) + accrue_max = fields.Float(string="Maximum Accrual") def _accrue_for_employee_values(self, employee): values = super(HRHolidays, self)._accrue_for_employee_values(employee) if values: values['accrue_by_pay_period'] = self.accrue_by_pay_period values['allocation_per_period'] = self.allocation_per_period + values['accrue_max'] = self.accrue_max return values diff --git a/hr_holidays_accrual_payroll/views/hr_holidays_views.xml b/hr_holidays_accrual_payroll/views/hr_holidays_views.xml index 8c15f42c..a10a06fe 100644 --- a/hr_holidays_accrual_payroll/views/hr_holidays_views.xml +++ b/hr_holidays_accrual_payroll/views/hr_holidays_views.xml @@ -5,9 +5,10 @@ hr.holidays - + + From dcde3ff5ce55364f33c7cbb604f4af7aece1f355 Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Mon, 20 May 2019 15:50:01 -0700 Subject: [PATCH 5/6] WIP Initial migration to 12.0 for `hr_holidays_accrual`, known to not work. --- hr_holidays_accrual/models/hr_holidays.py | 6 +++--- hr_holidays_accrual/tests/test_leaves.py | 8 ++++---- hr_holidays_accrual/views/hr_holidays_views.xml | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/hr_holidays_accrual/models/hr_holidays.py b/hr_holidays_accrual/models/hr_holidays.py index cbb7e3ac..606eda93 100644 --- a/hr_holidays_accrual/models/hr_holidays.py +++ b/hr_holidays_accrual/models/hr_holidays.py @@ -2,7 +2,7 @@ from odoo import api, fields, models class HRHolidays(models.Model): - _inherit = 'hr.holidays' + _inherit = 'hr.leave.type' grant_by_tag = fields.Boolean(string="Grant by Tag") @@ -19,7 +19,7 @@ class HRHolidays(models.Model): } def accrue_for_employee(self, employee): - holidays = self.env['hr.holidays'].sudo() + holidays = self.env['hr.leave'].sudo() for leave_to_create in self: values = leave_to_create._accrue_for_employee_values(employee) if values: @@ -32,7 +32,7 @@ class HREmployee(models.Model): @api.multi def write(self, values): - holidays = self.env['hr.holidays'].sudo() + holidays = self.env['hr.leave'].sudo() for emp in self: if values.get('category_ids'): categ_ids_command_list = values.get('category_ids') diff --git a/hr_holidays_accrual/tests/test_leaves.py b/hr_holidays_accrual/tests/test_leaves.py index 1fc9de93..44e44e4c 100644 --- a/hr_holidays_accrual/tests/test_leaves.py +++ b/hr_holidays_accrual/tests/test_leaves.py @@ -9,11 +9,11 @@ class TestLeaves(TestHrHolidaysBase): self.categ = self.env['hr.employee.category'].create({'name': 'Test Category'}) department = self.env['hr.department'].create({'name': 'Test Department'}) self.employee = self.env['hr.employee'].create({'name': 'Mark', 'department_id': department.id}) - self.leave_type = self.env['hr.holidays.status'].create({ + self.leave_type = self.env['hr.leave.type'].create({ 'name': 'Test Status', 'color_name': 'red', }) - self.test_leave = self.env['hr.holidays'].create({ + self.test_leave = self.env['hr.leave'].create({ 'holiday_status_id': self.leave_type.id, 'number_of_days_temp': 5, 'holiday_type': 'category', @@ -29,7 +29,7 @@ class TestLeaves(TestHrHolidaysBase): self.assertEqual(self.employee.leaves_count, 0.0) self.employee.write({'category_ids': [(6, False, [self.categ.id])]}) self.assertEqual(self.employee.leaves_count, 5.0) - leave = self.env['hr.holidays'].search([('employee_id', '=', self.employee.id)]) + leave = self.env['hr.leave'].search([('employee_id', '=', self.employee.id)]) self.assertEqual(leave.holiday_status_id.id, self.leave_type.id) def test_double_validation(self): @@ -38,6 +38,6 @@ class TestLeaves(TestHrHolidaysBase): self.test_leave.action_approve() self.test_leave.action_validate() self.employee.write({'category_ids': [(6, False, [self.categ.id])]}) - leave = self.env['hr.holidays'].search([('employee_id', '=', self.employee.id)]) + leave = self.env['hr.leave'].search([('employee_id', '=', self.employee.id)]) self.assertEqual(leave.state, 'validate1') self.assertEqual(leave.first_approver_id.id, self.env.uid) diff --git a/hr_holidays_accrual/views/hr_holidays_views.xml b/hr_holidays_accrual/views/hr_holidays_views.xml index a2cbed87..ec1b50e2 100644 --- a/hr_holidays_accrual/views/hr_holidays_views.xml +++ b/hr_holidays_accrual/views/hr_holidays_views.xml @@ -2,8 +2,8 @@ hr.holidays.edit.holiday.new.inherit - hr.holidays - + hr.leave.type + From 123292efde38b87c139a4223fa4a203dc5ac1486 Mon Sep 17 00:00:00 2001 From: Jared Kipe Date: Tue, 23 Jul 2019 13:53:04 -0700 Subject: [PATCH 6/6] MIG `hr_holidays_accrual_payroll` Rewrite to extend built in behavior in 12.0 Deprecate `hr_holidays_accrual` --- hr_holidays_accrual/README.rst | 11 +-- hr_holidays_accrual/__manifest__.py | 4 +- hr_holidays_accrual_payroll/README.rst | 13 ++- hr_holidays_accrual_payroll/__manifest__.py | 4 +- .../models/__init__.py | 1 + .../models/hr_leave_allocation.py | 98 +++++++++++++++++++ .../models/hr_payslip.py | 37 +------ .../tests/test_accrual.py | 40 ++++---- .../views/hr_holidays_views.xml | 23 +++-- 9 files changed, 151 insertions(+), 80 deletions(-) create mode 100644 hr_holidays_accrual_payroll/models/hr_leave_allocation.py diff --git a/hr_holidays_accrual/README.rst b/hr_holidays_accrual/README.rst index bd4d2af7..9077cef1 100644 --- a/hr_holidays_accrual/README.rst +++ b/hr_holidays_accrual/README.rst @@ -2,13 +2,12 @@ Hibou - HR Holidays Accrual *************************** -Use Employee Tags to grant Leave Allocations. +**This Module is now Deprecated in 12.0** -For more information and add-ons, visit `Hibou.io `_. +If it comes back, it would be a new module. -============= -Main Features -============= + +**Old** * Adds new boolean field `grant_by_tag` to Leave Allocations. * Allows user to create a Leave Allocation by employee tag, then use that tag to grant the newly created leave to as many employees as desired. @@ -27,4 +26,4 @@ License Please see `LICENSE `_. -Copyright Hibou Corp. 2018 \ No newline at end of file +Copyright Hibou Corp. 2019 \ No newline at end of file diff --git a/hr_holidays_accrual/__manifest__.py b/hr_holidays_accrual/__manifest__.py index 022546ee..bac2ab3c 100755 --- a/hr_holidays_accrual/__manifest__.py +++ b/hr_holidays_accrual/__manifest__.py @@ -1,7 +1,7 @@ { 'name': 'HR Holidays Accrual', 'author': 'Hibou Corp. ', - 'version': '11.0.0.0.0', + 'version': '12.0.1.0.0', 'category': 'Human Resources', 'sequence': 95, 'summary': 'Grant leave allocations with tags', @@ -13,6 +13,6 @@ Create leave allocations by tag, then use tags to grant leaves to employees. 'data': [ 'views/hr_holidays_views.xml', ], - 'installable': True, + 'installable': False, 'application': False, } diff --git a/hr_holidays_accrual_payroll/README.rst b/hr_holidays_accrual_payroll/README.rst index 2873ee26..29cd57c4 100644 --- a/hr_holidays_accrual_payroll/README.rst +++ b/hr_holidays_accrual_payroll/README.rst @@ -10,14 +10,13 @@ For more information and add-ons, visit `Hibou.io `_. Main Features ============= -* New Fields `accrue_by_pay_period` and `allocation_per_pay_period` on Leave allocations. -* Can set up an accrual by individual employee, or make an Allocation by Employee Tag for multiple employees. +*New in 12.0* +Odoo implemented their own accruals, which we now extend. The old base module `hr_holidays_accrual` is now deprecated. -.. image:: https://user-images.githubusercontent.com/15882954/42062853-f4175416-7ae3-11e8-8432-f54e26fe6094.png - :alt: 'Equipment Detail' - :width: 988 - :align: left +* Adds 'Payslip' to the interval dropdown +* When Payslip for employee is confirmed, the accrual will be made. +* Additionally exposes the `accrual_limit` field "Balance limit" (Why Odoo doesn't...) @@ -27,4 +26,4 @@ License Please see `LICENSE `_. -Copyright Hibou Corp. 2018 \ No newline at end of file +Copyright Hibou Corp. 2019 diff --git a/hr_holidays_accrual_payroll/__manifest__.py b/hr_holidays_accrual_payroll/__manifest__.py index 96893f7f..c434a958 100755 --- a/hr_holidays_accrual_payroll/__manifest__.py +++ b/hr_holidays_accrual_payroll/__manifest__.py @@ -1,7 +1,7 @@ { 'name': 'HR Holidays Accrual - Payroll', 'author': 'Hibou Corp. ', - 'version': '11.0.1.0.0', + 'version': '12.0.1.0.0', 'category': 'Human Resources', 'sequence': 95, 'summary': 'Grant leave allocations per payperiod', @@ -10,7 +10,7 @@ Automates leave allocations. """, 'website': 'https://hibou.io/', 'depends': [ - 'hr_holidays_accrual', + 'hr_holidays', 'hr_payroll' ], 'data': [ diff --git a/hr_holidays_accrual_payroll/models/__init__.py b/hr_holidays_accrual_payroll/models/__init__.py index 5d988fa2..8b3405a2 100644 --- a/hr_holidays_accrual_payroll/models/__init__.py +++ b/hr_holidays_accrual_payroll/models/__init__.py @@ -1 +1,2 @@ +from . import hr_leave_allocation from . import hr_payslip diff --git a/hr_holidays_accrual_payroll/models/hr_leave_allocation.py b/hr_holidays_accrual_payroll/models/hr_leave_allocation.py new file mode 100644 index 00000000..e05c10c8 --- /dev/null +++ b/hr_holidays_accrual_payroll/models/hr_leave_allocation.py @@ -0,0 +1,98 @@ +from datetime import datetime, time +from dateutil.relativedelta import relativedelta + +from odoo import api, fields, models +from odoo.addons.resource.models.resource import HOURS_PER_DAY + + +class HRLeaveAllocation(models.Model): + _inherit = 'hr.leave.allocation' + + interval_unit = fields.Selection(selection_add=[('payslip', 'Payslip')]) + + @api.model + def payslip_update_accrual(self, payslips): + employees = payslips.mapped('employee_id') + holidays = self.env['hr.leave.allocation'].search([ + ('employee_id', 'in', employees.ids), + ('accrual', '=', True), + ('state', '=', 'validate'), + ('holiday_type', '=', 'employee'), + '|', ('date_to', '=', False), + ('date_to', '>', fields.Datetime.now()), + ]) + for holiday in holidays: + days_to_give = holiday.number_per_interval + if holiday.unit_per_interval == 'hours': + # As we encode everything in days in the database we need to convert + # the number of hours into days for this we use the + # mean number of hours set on the employee's calendar + days_to_give = days_to_give / (holiday.employee_id.resource_calendar_id.hours_per_day or HOURS_PER_DAY) + + new_number_of_days = holiday.number_of_days + days_to_give + if holiday.accrual_limit > 0: + new_number_of_days = min(new_number_of_days, holiday.accrual_limit) + holiday.number_of_days = new_number_of_days + + + + + # This is a 'patch' because I cannot influence the domain/search here to filter out the ones by payslip. + @api.model + def _update_accrual(self): + """ + Method called by the cron task in order to increment the number_of_days when + necessary. + """ + today = fields.Date.from_string(fields.Date.today()) + + holidays = self.search([('interval_unit', '!=', 'payslip'), # new domain filter + ('accrual', '=', True), ('state', '=', 'validate'), ('holiday_type', '=', 'employee'), + '|', ('date_to', '=', False), ('date_to', '>', fields.Datetime.now()), + '|', ('nextcall', '=', False), ('nextcall', '<=', today)]) + + for holiday in holidays: + values = {} + + delta = relativedelta(days=0) + + if holiday.interval_unit == 'weeks': + delta = relativedelta(weeks=holiday.interval_number) + if holiday.interval_unit == 'months': + delta = relativedelta(months=holiday.interval_number) + if holiday.interval_unit == 'years': + delta = relativedelta(years=holiday.interval_number) + + values['nextcall'] = (holiday.nextcall if holiday.nextcall else today) + delta + + period_start = datetime.combine(today, time(0, 0, 0)) - delta + period_end = datetime.combine(today, time(0, 0, 0)) + + # We have to check when the employee has been created + # in order to not allocate him/her too much leaves + start_date = holiday.employee_id._get_date_start_work() + # If employee is created after the period, we cancel the computation + if period_end <= start_date: + holiday.write(values) + continue + + # If employee created during the period, taking the date at which he has been created + if period_start <= start_date: + period_start = start_date + + worked = holiday.employee_id.get_work_days_data(period_start, period_end, domain=[('holiday_id.holiday_status_id.unpaid', '=', True), ('time_type', '=', 'leave')])['days'] + left = holiday.employee_id.get_leave_days_data(period_start, period_end, domain=[('holiday_id.holiday_status_id.unpaid', '=', True), ('time_type', '=', 'leave')])['days'] + prorata = worked / (left + worked) if worked else 0 + + days_to_give = holiday.number_per_interval + if holiday.unit_per_interval == 'hours': + # As we encode everything in days in the database we need to convert + # the number of hours into days for this we use the + # mean number of hours set on the employee's calendar + days_to_give = days_to_give / (holiday.employee_id.resource_calendar_id.hours_per_day or HOURS_PER_DAY) + + values['number_of_days'] = holiday.number_of_days + days_to_give * prorata + if holiday.accrual_limit > 0: + values['number_of_days'] = min(values['number_of_days'], holiday.accrual_limit) + + holiday.write(values) diff --git a/hr_holidays_accrual_payroll/models/hr_payslip.py b/hr_holidays_accrual_payroll/models/hr_payslip.py index 1c47b6a4..c046cf9f 100644 --- a/hr_holidays_accrual_payroll/models/hr_payslip.py +++ b/hr_holidays_accrual_payroll/models/hr_payslip.py @@ -8,40 +8,5 @@ class HRPayslip(models.Model): def action_payslip_done(self): res = super(HRPayslip, self).action_payslip_done() if res and isinstance(res, (int, bool)): - holidays = self.env['hr.holidays'].sudo() - leaves_to_update = holidays.search([('employee_id', '=', self.employee_id.id), - ('accrue_by_pay_period', '=', True)]) - for leave_to_update in leaves_to_update: - new_allocation = leave_to_update.number_of_days_temp + leave_to_update.allocation_per_period - q = """SELECT SUM(number_of_days) - FROM hr_holidays - WHERE employee_id = %d AND holiday_status_id = %d;""" % (leave_to_update.employee_id.id, leave_to_update.holiday_status_id.id) - self.env.cr.execute(q) - total_days = self.env.cr.fetchall() - total_days = total_days[0][0] - new_total_days = total_days + leave_to_update.allocation_per_period - if leave_to_update.accrue_max and total_days > leave_to_update.accrue_max: - new_allocation = leave_to_update.number_of_days_temp - elif leave_to_update.accrue_max and new_total_days > leave_to_update.accrue_max: - difference = leave_to_update.accrue_max - total_days - new_allocation = leave_to_update.number_of_days_temp + difference - if leave_to_update.number_of_days_temp != new_allocation: - leave_to_update.write({'number_of_days_temp': new_allocation}) - + self.env['hr.leave.allocation'].payslip_update_accrual(self) return res - - -class HRHolidays(models.Model): - _inherit = 'hr.holidays' - - accrue_by_pay_period = fields.Boolean(string="Accrue by Pay Period") - allocation_per_period = fields.Float(string="Allocation Per Pay Period", digits=(12, 4)) - accrue_max = fields.Float(string="Maximum Accrual") - - def _accrue_for_employee_values(self, employee): - values = super(HRHolidays, self)._accrue_for_employee_values(employee) - if values: - values['accrue_by_pay_period'] = self.accrue_by_pay_period - values['allocation_per_period'] = self.allocation_per_period - values['accrue_max'] = self.accrue_max - return values diff --git a/hr_holidays_accrual_payroll/tests/test_accrual.py b/hr_holidays_accrual_payroll/tests/test_accrual.py index 43f325de..2a329ae6 100644 --- a/hr_holidays_accrual_payroll/tests/test_accrual.py +++ b/hr_holidays_accrual_payroll/tests/test_accrual.py @@ -9,35 +9,37 @@ class TestLeaves(TestHrHolidaysBase): self.categ = self.env['hr.employee.category'].create({'name': 'Test Category'}) department = self.env['hr.department'].create({'name': 'Test Department'}) self.employee = self.env['hr.employee'].create({'name': 'Mark', 'department_id': department.id}) - self.leave_type = self.env['hr.holidays.status'].create({ + self.leave_type = self.env['hr.leave.type'].create({ 'name': 'Test Status', 'color_name': 'red', }) - self.test_leave = self.env['hr.holidays'].create({ + self.allocation = self.env['hr.leave.allocation'].create({ + 'employee_id': self.employee.id, 'holiday_status_id': self.leave_type.id, - 'number_of_days_temp': 0, - 'holiday_type': 'category', - 'category_id': self.categ.id, - 'type': 'add', - 'state': 'draft', - 'grant_by_tag': True, + 'number_of_days': 0.0, + 'state': 'validate', + 'accrual': True, + 'holiday_type': 'employee', + 'number_per_interval': 0.75, + 'unit_per_interval': 'days', + 'interval_unit': 'payslip', + 'accrual_limit': 1, }) def test_payslip_accrual(self): - self.test_leave.write({ - 'accrue_by_pay_period': True, - 'allocation_per_period': 1 - }) - self.test_leave.action_confirm() - self.test_leave.action_approve() - - self.employee.write({'category_ids': [(6, False, [self.categ.id])]}) - self.assertEqual(self.employee.leaves_count, 0.0) - payslip = self.env['hr.payslip'].create({ 'employee_id': self.employee.id, 'date_from': '2018-01-01', 'date_to': '2018-01-31' }) payslip.action_payslip_done() - self.assertEqual(self.employee.leaves_count, 1.0) + self.assertEqual(self.allocation.number_of_days, 0.75) + + # Should be capped at 1 day + payslip = self.env['hr.payslip'].create({ + 'employee_id': self.employee.id, + 'date_from': '2018-02-01', + 'date_to': '2018-02-28' + }) + payslip.action_payslip_done() + self.assertEqual(self.allocation.number_of_days, 1.0) diff --git a/hr_holidays_accrual_payroll/views/hr_holidays_views.xml b/hr_holidays_accrual_payroll/views/hr_holidays_views.xml index a10a06fe..045d1e99 100644 --- a/hr_holidays_accrual_payroll/views/hr_holidays_views.xml +++ b/hr_holidays_accrual_payroll/views/hr_holidays_views.xml @@ -1,15 +1,22 @@ - - hr.holidays.edit.holiday.new.inherit.pay - hr.holidays - + + + hr.leave.allocation.view.form.manager.payslip + hr.leave.allocation + - - - - + + {'required': [('accrual', '=', True)], 'invisible': [('interval_unit', '=', 'payslip')]} + + + + \ No newline at end of file