diff --git a/addons/hr_holidays_attendance/__init__.py b/addons/hr_holidays_attendance/__init__.py
index e69de29bb2d..90b17558ffc 100644
--- a/addons/hr_holidays_attendance/__init__.py
+++ b/addons/hr_holidays_attendance/__init__.py
@@ -0,0 +1,5 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import models
+from . import tests
diff --git a/addons/hr_holidays_attendance/__manifest__.py b/addons/hr_holidays_attendance/__manifest__.py
index 322a6081147..c3533fad036 100644
--- a/addons/hr_holidays_attendance/__manifest__.py
+++ b/addons/hr_holidays_attendance/__manifest__.py
@@ -1,15 +1,30 @@
# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': "HR Attendance Holidays",
- 'summary': """""",
+ 'summary': """Attendance Holidays""",
'category': 'Human Resources',
'description': """
-Hides the attendance presence button when an employee is on leave.
+Convert employee's extra hours to leave allocations.
""",
'version': '1.0',
'depends': ['hr_attendance', 'hr_holidays'],
'auto_install': True,
'data': [
+ 'security/hr_holidays_attendance_security.xml',
+ 'views/hr_leave_allocation_views.xml',
+ 'views/hr_leave_type_views.xml',
+ 'views/hr_leave_views.xml',
'views/hr_employee_views.xml',
+ 'views/res_users_views.xml',
+ 'data/hr_holidays_attendance_data.xml',
],
+ 'demo': [
+ 'data/hr_holidays_attendance_demo.xml',
+ ],
+ 'assets': {
+ 'web.assets_qweb': [
+ 'hr_holidays_attendance/static/src/xml/time_off_calendar.xml',
+ ],
+ },
}
diff --git a/addons/hr_holidays_attendance/data/hr_holidays_attendance_data.xml b/addons/hr_holidays_attendance/data/hr_holidays_attendance_data.xml
new file mode 100644
index 00000000000..963a07053fa
--- /dev/null
+++ b/addons/hr_holidays_attendance/data/hr_holidays_attendance_data.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/addons/hr_holidays_attendance/data/hr_holidays_attendance_demo.xml b/addons/hr_holidays_attendance/data/hr_holidays_attendance_demo.xml
new file mode 100644
index 00000000000..6d6ffa73ee4
--- /dev/null
+++ b/addons/hr_holidays_attendance/data/hr_holidays_attendance_demo.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/addons/hr_holidays_attendance/models/__init__.py b/addons/hr_holidays_attendance/models/__init__.py
new file mode 100644
index 00000000000..a55416cd161
--- /dev/null
+++ b/addons/hr_holidays_attendance/models/__init__.py
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import hr_leave_allocation
+from . import hr_leave_type
+from . import hr_leave
+from . import res_company
+from . import res_users
diff --git a/addons/hr_holidays_attendance/models/hr_leave.py b/addons/hr_holidays_attendance/models/hr_leave.py
new file mode 100644
index 00000000000..7c34d74caf7
--- /dev/null
+++ b/addons/hr_holidays_attendance/models/hr_leave.py
@@ -0,0 +1,81 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models, _
+from odoo.exceptions import ValidationError
+from odoo.tools import float_round
+
+
+class HRLeave(models.Model):
+ _inherit = 'hr.leave'
+
+ overtime_id = fields.Many2one('hr.attendance.overtime', string='Extra Hours', groups='hr_holidays.group_hr_holidays_user')
+ employee_overtime = fields.Float(related='employee_id.total_overtime')
+ overtime_deductible = fields.Boolean(compute='_compute_overtime_deductible')
+
+ @api.depends('holiday_status_id')
+ def _compute_overtime_deductible(self):
+ for leave in self:
+ leave.overtime_deductible = leave.holiday_status_id.overtime_deductible and leave.holiday_status_id.allocation_type == 'no'
+
+ @api.model_create_multi
+ def create(self, vals_list):
+ res = super().create(vals_list)
+ today = fields.Date.today()
+ for leave in res:
+ if not leave.overtime_deductible:
+ continue
+ employee = leave.employee_id.sudo()
+ duration = leave.number_of_hours_display
+ if duration > employee.total_overtime:
+ if employee.user_id == self.env.user:
+ raise ValidationError(_('You do not have enough extra hours to request this leave'))
+ raise ValidationError(_('The employee does not have enough extra hours to request this leave.'))
+ if not leave.overtime_id:
+ leave.sudo().overtime_id = self.env['hr.attendance.overtime'].sudo().create({
+ 'employee_id': employee.id,
+ 'date': today,
+ 'adjustment': True,
+ 'duration': -1 * duration,
+ })
+ return res
+
+ def write(self, vals):
+ res = super().write(vals)
+ fields_to_check = {'number_of_days', 'date_from', 'date_to', 'state', 'employee_id'}
+ if not any(field for field in fields_to_check if field in vals):
+ return res
+ #User may not have access to overtime_id field
+ for leave in self.sudo().filtered('overtime_id'):
+ employee = leave.employee_id
+ duration = leave.number_of_hours_display
+ overtime_duration = leave.overtime_id.sudo().duration
+ if overtime_duration != duration:
+ if duration > employee.total_overtime - overtime_duration:
+ raise ValidationError(_('The employee does not have enough extra hours to extend this leave.'))
+ leave.overtime_id.sudo().duration = -1 * duration
+ return res
+
+ def action_draft(self):
+ overtime_leaves = self.filtered('overtime_deductible')
+ if any([l.employee_overtime < float_round(l.number_of_hours_display, 2) for l in overtime_leaves]):
+ if self.employee_id.user_id.id == self.env.user.id:
+ raise ValidationError(_('You do not have enough extra hours to request this leave'))
+ raise ValidationError(_('The employee does not have enough extra hours to request this leave.'))
+
+ res = super().action_draft()
+ overtime_leaves.overtime_id.sudo().unlink()
+ for leave in overtime_leaves:
+ overtime = self.env['hr.attendance.overtime'].sudo().create({
+ 'employee_id': leave.employee_id.id,
+ 'date': fields.Date.today(),
+ 'adjustment': True,
+ 'duration': -1 * leave.number_of_hours_display
+ })
+ leave.sudo().overtime_id = overtime.id
+ return res
+
+ def action_refuse(self):
+ res = super().action_refuse()
+ self.overtime_id.sudo().unlink()
+ return res
diff --git a/addons/hr_holidays_attendance/models/hr_leave_allocation.py b/addons/hr_holidays_attendance/models/hr_leave_allocation.py
new file mode 100644
index 00000000000..7a87d3bb868
--- /dev/null
+++ b/addons/hr_holidays_attendance/models/hr_leave_allocation.py
@@ -0,0 +1,83 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models, _
+from odoo.exceptions import ValidationError
+from odoo.tools import float_round
+
+
+class HolidaysAllocation(models.Model):
+ _inherit = 'hr.leave.allocation'
+
+ def default_get(self, fields):
+ res = super().default_get(fields)
+ if 'holiday_status_id' in fields and self.env.context.get('deduct_extra_hours'):
+ leave_type = self.env['hr.leave.type'].search([
+ ('valid', '=', True),
+ ('overtime_deductible', '=', True),
+ ('allocation_type', '!=', 'no')], limit=1)
+ res['holiday_status_id'] = leave_type.id
+ return res
+
+ overtime_deductible = fields.Boolean(compute='_compute_overtime_deductible')
+ overtime_id = fields.Many2one('hr.attendance.overtime', string='Extra Hours', groups='hr_holidays.group_hr_holidays_user')
+ employee_overtime = fields.Float(related='employee_id.total_overtime')
+ hr_attendance_overtime = fields.Boolean(related='employee_company_id.hr_attendance_overtime')
+
+ @api.depends('holiday_status_id')
+ def _compute_overtime_deductible(self):
+ for allocation in self:
+ allocation.overtime_deductible = allocation.holiday_status_id.company_id.hr_attendance_overtime and allocation.holiday_status_id.overtime_deductible
+
+ @api.model_create_multi
+ def create(self, vals_list):
+ res = super().create(vals_list)
+ for allocation in res:
+ if allocation.overtime_deductible and allocation.holiday_type == 'employee':
+ duration = allocation.number_of_hours_display
+ if duration > allocation.employee_id.total_overtime:
+ raise ValidationError(_('The employee does not have enough overtime hours to request this leave.'))
+ if not allocation.overtime_id:
+ allocation.sudo().overtime_id = self.env['hr.attendance.overtime'].sudo().create({
+ 'employee_id': allocation.employee_id.id,
+ 'date': fields.Date.today(),
+ 'adjustment': True,
+ 'duration': -1 * duration,
+ })
+ return res
+
+ def write(self, vals):
+ res = super().write(vals)
+ if 'number_of_days' not in vals:
+ return res
+ for allocation in self.filtered('overtime_id'):
+ employee = allocation.employee_id
+ duration = allocation.number_of_hours_display
+ overtime_duration = allocation.overtime_id.sudo().duration
+ if overtime_duration != duration:
+ if duration > employee.total_overtime - overtime_duration:
+ raise ValidationError(_('The employee does not have enough extra hours to extend this allocation.'))
+ allocation.overtime_id.sudo().duration = -1 * duration
+ return res
+
+ def action_draft(self):
+ overtime_allocations = self.filtered('overtime_deductible')
+ if any([a.employee_overtime < float_round(a.number_of_hours_display, 2) for a in overtime_allocations]):
+ raise ValidationError(_('The employee does not have enough extra hours to request this allocation.'))
+ res = super().action_draft()
+
+ overtime_allocations.overtime_id.sudo().unlink()
+ for allocation in overtime_allocations:
+ overtime = self.env['hr.attendance.overtime'].sudo().create({
+ 'employee_id': allocation.employee_id.id,
+ 'date': fields.Date.today(),
+ 'adjustment': True,
+ 'duration': -1 * allocation.number_of_hours_display
+ })
+ allocation.sudo().overtime_id = overtime.id
+ return res
+
+ def action_refuse(self):
+ res = super().action_refuse()
+ self.overtime_id.sudo().unlink()
+ return res
diff --git a/addons/hr_holidays_attendance/models/hr_leave_type.py b/addons/hr_holidays_attendance/models/hr_leave_type.py
new file mode 100644
index 00000000000..ea6b5accac5
--- /dev/null
+++ b/addons/hr_holidays_attendance/models/hr_leave_type.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import fields, models
+
+
+class HRLeaveType(models.Model):
+ _inherit = 'hr.leave.type'
+
+ hr_attendance_overtime = fields.Boolean(related='company_id.hr_attendance_overtime')
+ overtime_deductible = fields.Boolean(
+ "Deduct Extra Hours", default=False,
+ help="Once a time off of this type is approved, extra hours in attendances will be deducted.")
+
+ def get_employees_days(self, employee_ids):
+ res = super().get_employees_days(employee_ids)
+ deductible_time_off_type_ids = self.env['hr.leave.type'].search([
+ ('overtime_deductible', '=', True),
+ ('allocation_type', '=', 'no')]).ids
+ for employee_id, allocations in res.items():
+ for allocation_id in allocations:
+ if allocation_id in deductible_time_off_type_ids:
+ res[employee_id][allocation_id]['virtual_remaining_leaves'] = self.env['hr.employee'].sudo().browse(employee_id).total_overtime
+ res[employee_id][allocation_id]['overtime_deductible'] = True
+ else:
+ res[employee_id][allocation_id]['overtime_deductible'] = False
+ return res
+
+ def _get_days_request(self):
+ res = super()._get_days_request()
+ res[1]['overtime_deductible'] = self.overtime_deductible
+ return res
diff --git a/addons/hr_holidays_attendance/models/res_company.py b/addons/hr_holidays_attendance/models/res_company.py
new file mode 100644
index 00000000000..1e13e416b15
--- /dev/null
+++ b/addons/hr_holidays_attendance/models/res_company.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models
+
+
+class ResCompany(models.Model):
+ _inherit = 'res.company'
+
+ def write(self, vals):
+ res = super().write(vals)
+ if 'hr_attendance_overtime' not in vals:
+ return res
+ extra_hours_time_off_type = self.env.ref('hr_holidays_attendance.holiday_status_extra_hours', raise_if_not_found=False)
+ if not extra_hours_time_off_type:
+ return res
+ all_companies = self.env['res.company'].sudo().search([])
+ # Unarchive time of type if the feature is enabled
+ if any(company.hr_attendance_overtime and not extra_hours_time_off_type.active for company in all_companies):
+ extra_hours_time_off_type.toggle_active()
+ # Archive time of type if the feature is disabled for all the company
+ if all(not company.hr_attendance_overtime and extra_hours_time_off_type.active for company in all_companies):
+ extra_hours_time_off_type.toggle_active()
+ return res
diff --git a/addons/hr_holidays_attendance/models/res_users.py b/addons/hr_holidays_attendance/models/res_users.py
new file mode 100644
index 00000000000..9267827d3a9
--- /dev/null
+++ b/addons/hr_holidays_attendance/models/res_users.py
@@ -0,0 +1,32 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+
+
+class ResUsers(models.Model):
+ _inherit = 'res.users'
+
+ request_overtime = fields.Boolean(compute='_compute_request_overtime')
+
+ @property
+ def SELF_READABLE_FIELDS(self):
+ return super().SELF_READABLE_FIELDS + ['request_overtime']
+
+ @api.depends_context('uid')
+ @api.depends('total_overtime')
+ def _compute_request_overtime(self):
+ is_holiday_user = self.env.user.has_group('hr_holidays.group_hr_holidays_user')
+ time_off_types = self.env['hr.leave.type'].search_count([
+ ('valid', '=', True),
+ ('allocation_type', '=', 'fixed_allocation'),
+ ('overtime_deductible', '=', True)
+ ])
+ for user in self:
+ if user.total_overtime >= 1:
+ if is_holiday_user:
+ user.request_overtime = True
+ else:
+ user.request_overtime = time_off_types
+ else:
+ user.request_overtime = False
diff --git a/addons/hr_holidays_attendance/security/hr_holidays_attendance_security.xml b/addons/hr_holidays_attendance/security/hr_holidays_attendance_security.xml
new file mode 100644
index 00000000000..f96b3771553
--- /dev/null
+++ b/addons/hr_holidays_attendance/security/hr_holidays_attendance_security.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/addons/hr_holidays_attendance/static/src/xml/time_off_calendar.xml b/addons/hr_holidays_attendance/static/src/xml/time_off_calendar.xml
new file mode 100644
index 00000000000..dc9bfb8f81d
--- /dev/null
+++ b/addons/hr_holidays_attendance/static/src/xml/time_off_calendar.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+ Hours Available
+
+
+
+
+
+ need_allocation
+
+
+
+
+
+
+ Hours Available
+
+
+
+
+
+ need_allocation
+
+
+
diff --git a/addons/hr_holidays_attendance/tests/__init__.py b/addons/hr_holidays_attendance/tests/__init__.py
new file mode 100644
index 00000000000..0bb87260a2c
--- /dev/null
+++ b/addons/hr_holidays_attendance/tests/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import test_holidays_overtime
diff --git a/addons/hr_holidays_attendance/tests/test_holidays_overtime.py b/addons/hr_holidays_attendance/tests/test_holidays_overtime.py
new file mode 100644
index 00000000000..448bd1b3392
--- /dev/null
+++ b/addons/hr_holidays_attendance/tests/test_holidays_overtime.py
@@ -0,0 +1,216 @@
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from datetime import datetime
+
+from odoo.tests import new_test_user
+from odoo.tests.common import TransactionCase, tagged
+
+from odoo.exceptions import AccessError, ValidationError
+
+@tagged('post_install', '-at_install', 'holidays_attendance')
+class TestHolidaysOvertime(TransactionCase):
+
+ def setUp(self):
+ super().setUp()
+ self.company = self.env['res.company'].create({
+ 'name': 'SweatChipChop Inc.',
+ 'hr_attendance_overtime': True,
+ 'overtime_start_date': datetime(2021, 1, 1)
+ })
+ self.env.company = self.company
+ self.user = new_test_user(self.env, login='user', groups='base.group_user,hr_attendance.group_hr_attendance', company_id=self.company.id)
+ self.user_manager = new_test_user(self.env, login='manager', groups='base.group_user,hr_holidays.group_hr_holidays_user', company_id=self.company.id)
+
+ self.manager = self.env['hr.employee'].create({
+ 'name': 'Dominique',
+ 'user_id': self.user_manager.id,
+ 'company_id': self.company.id,
+ })
+ self.employee = self.env['hr.employee'].create({
+ 'name': 'Barnabé',
+ 'user_id': self.user.id,
+ 'parent_id': self.manager.id,
+ 'company_id': self.company.id,
+ })
+
+ self.leave_type_no_alloc = self.env['hr.leave.type'].create({
+ 'name': 'Overtime Compensation No Allocation',
+ 'company_id': self.company.id,
+ 'allocation_type': 'no',
+ 'overtime_deductible': True,
+ })
+ self.leave_type_employee_allocation = self.env['hr.leave.type'].create({
+ 'name': 'Overtime Compensation Employee Allocation',
+ 'company_id': self.company.id,
+ 'allocation_type': 'fixed_allocation',
+ 'overtime_deductible': True,
+ })
+ self.leave_type_manager_alloc = self.env['hr.leave.type'].create({
+ 'name': 'Overtime Compensation Manager Allocation',
+ 'company_id': self.company.id,
+ 'allocation_type': 'fixed',
+ 'overtime_deductible': True,
+ })
+
+ def new_attendance(self, check_in, check_out=False):
+ return self.env['hr.attendance'].create({
+ 'employee_id': self.employee.id,
+ 'check_in': check_in,
+ 'check_out': check_out,
+ })
+
+ def test_deduct_button_visibility(self):
+ with self.with_user('user'):
+ self.assertFalse(self.user.request_overtime, 'Button should not be visible')
+
+ self.new_attendance(check_in=datetime(2021, 1, 2, 8), check_out=datetime(2021, 1, 2, 18))
+ self.assertEqual(self.user.total_overtime, 10, 'Should have 10 hours of overtime')
+ self.assertTrue(self.user.request_overtime, 'Button should be visible')
+
+ def test_check_overtime(self):
+ with self.with_user('user'):
+ self.assertEqual(self.user.total_overtime, 0, 'No overtime')
+
+ with self.assertRaises(ValidationError), self.cr.savepoint():
+ self.env['hr.leave'].create({
+ 'name': 'no overtime',
+ 'employee_id': self.employee.id,
+ 'holiday_status_id': self.leave_type_no_alloc.id,
+ 'number_of_days': 1,
+ 'date_from': datetime(2021, 1, 4),
+ 'date_to': datetime(2021, 1, 5),
+ 'state': 'draft',
+ })
+
+ self.new_attendance(check_in=datetime(2021, 1, 2, 8), check_out=datetime(2021, 1, 2, 16))
+ self.assertEqual(self.employee.total_overtime, 8, 'Should have 8 hours of overtime')
+ leave = self.env['hr.leave'].create({
+ 'name': 'no overtime',
+ 'employee_id': self.employee.id,
+ 'holiday_status_id': self.leave_type_no_alloc.id,
+ 'number_of_days': 1,
+ 'date_from': datetime(2021, 1, 4),
+ 'date_to': datetime(2021, 1, 5),
+ })
+
+ # The employee doesn't have the right to read the overtime from the leave
+ overtime = leave.sudo().overtime_id.with_user(self.user)
+
+ # An employee cannot delete an overtime adjustment
+ with self.assertRaises(AccessError), self.cr.savepoint():
+ overtime.unlink()
+
+ # ... nor change its duration
+ with self.assertRaises(AccessError), self.cr.savepoint():
+ overtime.duration = 8
+
+ def test_leave_adjust_overtime(self):
+ self.new_attendance(check_in=datetime(2021, 1, 2, 8), check_out=datetime(2021, 1, 2, 16))
+ self.assertEqual(self.employee.total_overtime, 8, 'Should have 8 hours of overtime')
+
+ leave = self.env['hr.leave'].create({
+ 'name': 'no overtime',
+ 'employee_id': self.employee.id,
+ 'holiday_status_id': self.leave_type_no_alloc.id,
+ 'number_of_days': 1,
+ 'date_from': datetime(2021, 1, 4),
+ 'date_to': datetime(2021, 1, 5),
+ })
+
+ self.assertTrue(leave.overtime_id.adjustment, "An adjustment overtime should be created")
+ self.assertEqual(leave.overtime_id.duration, -8)
+
+ self.assertEqual(self.employee.total_overtime, 0)
+
+ leave.action_refuse()
+ self.assertFalse(leave.overtime_id.exists(), "Overtime should be deleted")
+ self.assertEqual(self.employee.total_overtime, 8)
+
+ leave.action_draft()
+ self.assertTrue(leave.overtime_id.exists(), "Overtime should be created")
+ self.assertEqual(self.employee.total_overtime, 0)
+
+ def test_leave_check_overtime_write(self):
+ self.new_attendance(check_in=datetime(2021, 1, 2, 8), check_out=datetime(2021, 1, 2, 16))
+ self.new_attendance(check_in=datetime(2021, 1, 3, 8), check_out=datetime(2021, 1, 3, 16))
+ self.assertEqual(self.employee.total_overtime, 16)
+
+ leave = self.env['hr.leave'].create({
+ 'name': 'no overtime',
+ 'employee_id': self.employee.id,
+ 'holiday_status_id': self.leave_type_no_alloc.id,
+ 'number_of_days': 1,
+ 'date_from': datetime(2021, 1, 4),
+ 'date_to': datetime(2021, 1, 5),
+ })
+ self.assertEqual(self.employee.total_overtime, 8)
+
+ leave.date_to = datetime(2021, 1, 6)
+ self.assertEqual(self.employee.total_overtime, 0)
+ with self.assertRaises(ValidationError), self.cr.savepoint():
+ leave.date_to = datetime(2021, 1, 7)
+
+ leave.date_to = datetime(2021, 1, 5)
+ self.assertEqual(self.employee.total_overtime, 8)
+
+ def test_employee_create_allocation(self):
+ with self.with_user('user'):
+ self.assertEqual(self.employee.total_overtime, 0)
+ with self.assertRaises(ValidationError), self.cr.savepoint():
+ self.env['hr.leave.allocation'].create({
+ 'name': 'test allocation',
+ 'holiday_status_id': self.leave_type_employee_allocation.id,
+ 'employee_id': self.employee.id,
+ 'number_of_days': 1,
+ 'state': 'draft',
+ })
+
+ self.new_attendance(check_in=datetime(2021, 1, 2, 8), check_out=datetime(2021, 1, 2, 16))
+ self.assertAlmostEqual(self.employee.total_overtime, 8, 'Should have 8 hours of overtime')
+
+ allocation = self.env['hr.leave.allocation'].create({
+ 'name': 'test allocation',
+ 'holiday_status_id': self.leave_type_employee_allocation.id,
+ 'employee_id': self.employee.id,
+ 'number_of_days': 1,
+ 'state': 'draft',
+ })
+ allocation.action_confirm()
+ self.assertEqual(self.employee.total_overtime, 0)
+
+ leave_type = self.env['hr.leave.type'].sudo().create({
+ 'name': 'Overtime Compensation Employee Allocation',
+ 'company_id': self.company.id,
+ 'allocation_type': 'fixed_allocation',
+ 'overtime_deductible': False,
+ })
+
+ # User can request another allocation even without overtime
+ allocation2 = self.env['hr.leave.allocation'].create({
+ 'name': 'test allocation',
+ 'holiday_status_id': leave_type.id,
+ 'employee_id': self.employee.id,
+ 'number_of_days': 1,
+ 'state': 'draft',
+ })
+ allocation2.action_confirm()
+
+ def test_allocation_check_overtime_write(self):
+ self.new_attendance(check_in=datetime(2021, 1, 2, 8), check_out=datetime(2021, 1, 2, 16))
+ self.new_attendance(check_in=datetime(2021, 1, 3, 8), check_out=datetime(2021, 1, 3, 16))
+ self.assertEqual(self.employee.total_overtime, 16, 'Should have 16 hours of overtime')
+
+ alloc = self.env['hr.leave.allocation'].create({
+ 'name': 'test allocation',
+ 'holiday_status_id': self.leave_type_employee_allocation.id,
+ 'employee_id': self.employee.id,
+ 'number_of_days': 1,
+ 'state': 'draft',
+ })
+ self.assertEqual(self.employee.total_overtime, 8)
+
+ with self.assertRaises(ValidationError), self.cr.savepoint():
+ alloc.number_of_days = 3
+
+ alloc.number_of_days = 2
+ self.assertEqual(self.employee.total_overtime, 0)
diff --git a/addons/hr_holidays_attendance/views/hr_employee_views.xml b/addons/hr_holidays_attendance/views/hr_employee_views.xml
index 53cc6d53e90..8d7af3d44d5 100644
--- a/addons/hr_holidays_attendance/views/hr_employee_views.xml
+++ b/addons/hr_holidays_attendance/views/hr_employee_views.xml
@@ -31,6 +31,23 @@
+
+ hr.holidays.attendance.employee.view.form.inherit
+ hr.employee
+
+
+
+
+
+
+
+
hr.employee.holidays.attendance.inherit
hr.employee
diff --git a/addons/hr_holidays_attendance/views/hr_leave_allocation_views.xml b/addons/hr_holidays_attendance/views/hr_leave_allocation_views.xml
new file mode 100644
index 00000000000..18456a9ed99
--- /dev/null
+++ b/addons/hr_holidays_attendance/views/hr_leave_allocation_views.xml
@@ -0,0 +1,73 @@
+
+
+
+ hr.leave.allocation
+
+
+
+
+
+
+
+ Extra Hours Available
+
+
+
+
+
+
+ hr.leave.allocation
+
+ primary
+ 60
+
+
+ 1
+
+
+
+ [('valid', '=', True), ('overtime_deductible', '=', True), ('allocation_type', '=', 'fixed_allocation')]
+ {'no_create': True, 'no_open': True}
+
+
+
+
+
+
+
+
+ hr.leave.allocation
+
+ primary
+ 70
+
+
+ [('valid', '=', True), ('overtime_deductible', '=', True), ('allocation_type', '!=', 'no')]
+
+
+
+
+
+ New Allocation Request
+ hr.leave.allocation
+ form
+
+ new
+
+
+
+ New Allocation Request
+ hr.leave.allocation
+ form
+
+ new
+
+
diff --git a/addons/hr_holidays_attendance/views/hr_leave_type_views.xml b/addons/hr_holidays_attendance/views/hr_leave_type_views.xml
new file mode 100644
index 00000000000..99f5b635c36
--- /dev/null
+++ b/addons/hr_holidays_attendance/views/hr_leave_type_views.xml
@@ -0,0 +1,14 @@
+
+
+
+ hr.leave.type.view.form.inherit
+ hr.leave.type
+
+
+
+
+
+
+
+
+
diff --git a/addons/hr_holidays_attendance/views/hr_leave_views.xml b/addons/hr_holidays_attendance/views/hr_leave_views.xml
new file mode 100644
index 00000000000..e02b5c88c30
--- /dev/null
+++ b/addons/hr_holidays_attendance/views/hr_leave_views.xml
@@ -0,0 +1,16 @@
+
+
+
+ hr.leave
+
+
+
+
+
+
+ Extra Hours Available
+
+
+
+
+
diff --git a/addons/hr_holidays_attendance/views/res_users_views.xml b/addons/hr_holidays_attendance/views/res_users_views.xml
new file mode 100644
index 00000000000..8b6d149baf9
--- /dev/null
+++ b/addons/hr_holidays_attendance/views/res_users_views.xml
@@ -0,0 +1,19 @@
+
+
+
+ res.users
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/hr_work_entry_holidays/tests/test_performance.py b/addons/hr_work_entry_holidays/tests/test_performance.py
index 615b0faf33c..f83e76487eb 100644
--- a/addons/hr_work_entry_holidays/tests/test_performance.py
+++ b/addons/hr_work_entry_holidays/tests/test_performance.py
@@ -47,7 +47,7 @@ class TestWorkEntryHolidaysPerformance(TestWorkEntryHolidaysBase):
@users('__system__', 'admin')
@warmup
def test_performance_leave_create(self):
- with self.assertQueryCount(__system__=22, admin=23):
+ with self.assertQueryCount(__system__=24, admin=25):
leave = self.create_leave(datetime(2018, 1, 1, 7, 0), datetime(2018, 1, 1, 18, 0))
leave.action_refuse()
@@ -56,7 +56,7 @@ class TestWorkEntryHolidaysPerformance(TestWorkEntryHolidaysBase):
def test_performance_leave_confirm(self):
leave = self.create_leave(datetime(2018, 1, 1, 7, 0), datetime(2018, 1, 1, 18, 0))
leave.action_draft()
- with self.assertQueryCount(__system__=20, admin=21):
+ with self.assertQueryCount(__system__=22, admin=23):
leave.action_confirm()
leave.state = 'cancel'