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 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