summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorRomainLibert <rli@odoo.com>2018-01-12 14:58:57 +0100
committerRomainLibert <rli@odoo.com>2018-01-15 10:38:31 +0100
commita0f4f6548bdb9a26151fc8a292d152fc67d1a13c (patch)
tree1ae2cdbae627c18ace26653042b0db5005c02971 /addons
parentf61687a4cdb0e22df19568c9b4ac3dbb579ca062 (diff)
[REF] hr_holidays: Split/Rename models/views according to guidelines
Purpose ======= Follow the coding guidelines (https://www.odoo.com/documentation/11.0/reference/guidelines.html) Specification ============= * each model has now its own .py file * view files have better names * reordered the methods for readability * reimplemented email subtypes
Diffstat (limited to 'addons')
-rw-r--r--addons/hr_holidays/__manifest__.py6
-rw-r--r--addons/hr_holidays/data/hr_holidays_data.xml95
-rw-r--r--addons/hr_holidays/models/__init__.py4
-rw-r--r--addons/hr_holidays/models/hr_holidays.py904
-rw-r--r--addons/hr_holidays/models/hr_leave.py458
-rw-r--r--addons/hr_holidays/models/hr_leave_allocation.py311
-rw-r--r--addons/hr_holidays/models/hr_leave_type.py147
-rw-r--r--addons/hr_holidays/report/__init__.py2
-rw-r--r--addons/hr_holidays/report/holidays_summary_report.py1
-rw-r--r--addons/hr_holidays/report/hr_holidays_views.xml215
-rw-r--r--addons/hr_holidays/report/hr_leave_report.py (renamed from addons/hr_holidays/report/report_all_leaves.py)0
-rw-r--r--addons/hr_holidays/report/hr_leave_reports.xml164
-rw-r--r--addons/hr_holidays/views/hr_holidays_views.xml815
-rw-r--r--addons/hr_holidays/views/hr_leave_allocation_views.xml215
-rw-r--r--addons/hr_holidays/views/hr_leave_type_views.xml113
-rw-r--r--addons/hr_holidays/views/hr_leave_views.xml437
-rw-r--r--addons/hr_holidays/views/hr_views.xml54
17 files changed, 1986 insertions, 1955 deletions
diff --git a/addons/hr_holidays/__manifest__.py b/addons/hr_holidays/__manifest__.py
index 7361860c627..ed14ca78a0c 100644
--- a/addons/hr_holidays/__manifest__.py
+++ b/addons/hr_holidays/__manifest__.py
@@ -33,7 +33,9 @@ A synchronization with an internal agenda (Meetings of the CRM module) is also p
'security/ir.model.access.csv',
'views/resource_views.xml',
- 'views/hr_holidays_views.xml',
+ 'views/hr_leave_type_views.xml',
+ 'views/hr_leave_allocation_views.xml',
+ 'views/hr_leave_views.xml',
'views/hr_views.xml',
'wizard/hr_holidays_summary_department_views.xml',
@@ -41,7 +43,7 @@ A synchronization with an internal agenda (Meetings of the CRM module) is also p
'report/hr_holidays_templates.xml',
'report/hr_holidays_reports.xml',
- 'report/hr_holidays_views.xml',
+ 'report/hr_leave_reports.xml',
],
'demo': [
'data/hr_holidays_demo.xml',
diff --git a/addons/hr_holidays/data/hr_holidays_data.xml b/addons/hr_holidays/data/hr_holidays_data.xml
index c6c155a385f..235bd6e036d 100644
--- a/addons/hr_holidays/data/hr_holidays_data.xml
+++ b/addons/hr_holidays/data/hr_holidays_data.xml
@@ -31,65 +31,128 @@
</record>
<!-- Holidays-related subtypes for messaging / Chatter -->
- <record id="mt_holidays_confirmed" model="mail.message.subtype">
+ <record id="mt_leave_confirmed" model="mail.message.subtype">
<field name="name">Confirmed</field>
<field name="res_model">hr.leave</field>
<field name="description">Request created and waiting confirmation</field>
</record>
- <record id="mt_holidays_first_validated" model="mail.message.subtype">
+ <record id="mt_leave_first_validated" model="mail.message.subtype">
<field name="name">Waiting Second Validation</field>
<field name="res_model">hr.leave</field>
<field name="default" eval="False"/>
<field name="description">Request validated, waiting second validation</field>
</record>
- <record id="mt_holidays_approved" model="mail.message.subtype">
+ <record id="mt_leave_approved" model="mail.message.subtype">
<field name="name">Approved</field>
<field name="res_model">hr.leave</field>
<field name="description">Request approved</field>
</record>
- <record id="mt_holidays_refused" model="mail.message.subtype">
+ <record id="mt_leave_refused" model="mail.message.subtype">
<field name="name">Refused</field>
<field name="res_model">hr.leave</field>
<field name="default" eval="False"/>
<field name="description">Request refused</field>
</record>
+ <!-- Allocation-related subtypes for messaging / Chatter -->
+ <record id="mt_leave_allocation_confirmed" model="mail.message.subtype">
+ <field name="name">Confirmed</field>
+ <field name="res_model">hr.leave.allocation</field>
+ <field name="description">Request created and waiting confirmation</field>
+ </record>
+
+ <record id="mt_leave_allocation_first_validated" model="mail.message.subtype">
+ <field name="name">Waiting Second Validation</field>
+ <field name="res_model">hr.leave.allocation</field>
+ <field name="default" eval="False"/>
+ <field name="description">Request validated, waiting second validation</field>
+ </record>
+
+ <record id="mt_leave_allocation_approved" model="mail.message.subtype">
+ <field name="name">Approved</field>
+ <field name="res_model">hr.leave.allocation</field>
+ <field name="description">Request approved</field>
+ </record>
+
+ <record id="mt_leave_allocation_refused" model="mail.message.subtype">
+ <field name="name">Refused</field>
+ <field name="res_model">hr.leave.allocation</field>
+ <field name="default" eval="False"/>
+ <field name="description">Request refused</field>
+ </record>
+
<!-- Department related subtypes for messaging / Chatter -->
- <record id="mt_department_holidays_confirmed" model="mail.message.subtype">
- <field name="name">Leaves/Allocations Confirmed</field>
+ <record id="mt_department_leave_confirmed" model="mail.message.subtype">
+ <field name="name">Leaves Confirmed</field>
+ <field name="res_model">hr.department</field>
+ <field name="default" eval="True"/>
+ <field name="parent_id" eval="ref('mt_leave_confirmed')"/>
+ <field name="relation_field">department_id</field>
+ <field name="sequence" eval="6"/>
+ </record>
+
+ <record id="mt_department_leave_first_validated" model="mail.message.subtype">
+ <field name="name">Leaves First Approval</field>
+ <field name="res_model">hr.department</field>
+ <field name="default" eval="False"/>
+ <field name="parent_id" eval="ref('mt_leave_first_validated')"/>
+ <field name="relation_field">department_id</field>
+ <field name="sequence" eval="7"/>
+ </record>
+
+ <record id="mt_department_leave_approved" model="mail.message.subtype">
+ <field name="name">Leaves Approved</field>
+ <field name="res_model">hr.department</field>
+ <field name="default" eval="False"/>
+ <field name="parent_id" eval="ref('mt_leave_approved')"/>
+ <field name="relation_field">department_id</field>
+ <field name="sequence" eval="8"/>
+ </record>
+
+ <record id="mt_department_leave_refused" model="mail.message.subtype">
+ <field name="name">Leaves Refused</field>
+ <field name="res_model">hr.department</field>
+ <field name="default" eval="False"/>
+ <field name="parent_id" eval="ref('mt_leave_refused')"/>
+ <field name="relation_field">department_id</field>
+ <field name="sequence" eval="9"/>
+ </record>
+
+ <record id="mt_department_leave_allocation_confirmed" model="mail.message.subtype">
+ <field name="name">Allocations Confirmed</field>
<field name="res_model">hr.department</field>
<field name="default" eval="True"/>
- <field name="parent_id" eval="ref('mt_holidays_confirmed')"/>
+ <field name="parent_id" eval="ref('mt_leave_allocation_confirmed')"/>
<field name="relation_field">department_id</field>
<field name="sequence" eval="6"/>
</record>
- <record id="mt_department_holidays_first_validated" model="mail.message.subtype">
- <field name="name">Leaves/Allocations First Approval</field>
+ <record id="mt_department_leave_allocation_first_validated" model="mail.message.subtype">
+ <field name="name">Allocations First Approval</field>
<field name="res_model">hr.department</field>
<field name="default" eval="False"/>
- <field name="parent_id" eval="ref('mt_holidays_first_validated')"/>
+ <field name="parent_id" eval="ref('mt_leave_allocation_first_validated')"/>
<field name="relation_field">department_id</field>
<field name="sequence" eval="7"/>
</record>
- <record id="mt_department_holidays_approved" model="mail.message.subtype">
- <field name="name">Leaves/Allocation Approved</field>
+ <record id="mt_department_leave_allocation_approved" model="mail.message.subtype">
+ <field name="name">Allocation Approved</field>
<field name="res_model">hr.department</field>
<field name="default" eval="False"/>
- <field name="parent_id" eval="ref('mt_holidays_approved')"/>
+ <field name="parent_id" eval="ref('mt_leave_allocation_approved')"/>
<field name="relation_field">department_id</field>
<field name="sequence" eval="8"/>
</record>
- <record id="mt_department_holidays_refused" model="mail.message.subtype">
- <field name="name">Leaves/Allocation Refused</field>
+ <record id="mt_department_leave_allocation_refused" model="mail.message.subtype">
+ <field name="name">Allocation Refused</field>
<field name="res_model">hr.department</field>
<field name="default" eval="False"/>
- <field name="parent_id" eval="ref('mt_holidays_refused')"/>
+ <field name="parent_id" eval="ref('mt_leave_allocation_refused')"/>
<field name="relation_field">department_id</field>
<field name="sequence" eval="9"/>
</record>
diff --git a/addons/hr_holidays/models/__init__.py b/addons/hr_holidays/models/__init__.py
index 334982c4a1d..621aeccd08b 100644
--- a/addons/hr_holidays/models/__init__.py
+++ b/addons/hr_holidays/models/__init__.py
@@ -3,4 +3,6 @@
from . import resource
from . import hr
-from . import hr_holidays
+from . import hr_leave
+from . import hr_leave_allocation
+from . import hr_leave_type
diff --git a/addons/hr_holidays/models/hr_holidays.py b/addons/hr_holidays/models/hr_holidays.py
deleted file mode 100644
index a21be760c68..00000000000
--- a/addons/hr_holidays/models/hr_holidays.py
+++ /dev/null
@@ -1,904 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-# Copyright (c) 2005-2006 Axelor SARL. (http://www.axelor.com)
-
-import logging
-import math
-from datetime import timedelta
-
-from odoo import api, fields, models
-from odoo.exceptions import UserError, AccessError, ValidationError
-from odoo.tools import float_compare
-from odoo.tools.translate import _
-
-_logger = logging.getLogger(__name__)
-
-
-HOURS_PER_DAY = 8
-
-
-class HolidaysType(models.Model):
- _name = "hr.leave.type"
- _description = "Leave Type"
-
- name = fields.Char('Leave Type', required=True, translate=True)
- categ_id = fields.Many2one('calendar.event.type', string='Meeting Type',
- help='Once a leave is validated, Odoo will create a corresponding meeting of this type in the calendar.')
- color_name = fields.Selection([
- ('red', 'Red'),
- ('blue', 'Blue'),
- ('lightgreen', 'Light Green'),
- ('lightblue', 'Light Blue'),
- ('lightyellow', 'Light Yellow'),
- ('magenta', 'Magenta'),
- ('lightcyan', 'Light Cyan'),
- ('black', 'Black'),
- ('lightpink', 'Light Pink'),
- ('brown', 'Brown'),
- ('violet', 'Violet'),
- ('lightcoral', 'Light Coral'),
- ('lightsalmon', 'Light Salmon'),
- ('lavender', 'Lavender'),
- ('wheat', 'Wheat'),
- ('ivory', 'Ivory')], string='Color in Report', required=True, default='red',
- help='This color will be used in the leaves summary located in Reporting > Leaves by Department.')
- limit = fields.Boolean('Allow to Override Limit',
- help='If you select this check box, the system allows the employees to take more leaves '
- 'than the available ones for this type and will not take them into account for the '
- '"Remaining Legal Leaves" defined on the employee form.')
- active = fields.Boolean('Active', default=True,
- help="If the active field is set to false, it will allow you to hide the leave type without removing it.")
-
- max_leaves = fields.Float(compute='_compute_leaves', string='Maximum Allowed',
- help='This value is given by the sum of all leaves requests with a positive value.')
- leaves_taken = fields.Float(compute='_compute_leaves', string='Leaves Already Taken',
- help='This value is given by the sum of all leaves requests with a negative value.')
- remaining_leaves = fields.Float(compute='_compute_leaves', string='Remaining Leaves',
- help='Maximum Leaves Allowed - Leaves Already Taken')
- virtual_remaining_leaves = fields.Float(compute='_compute_leaves', string='Virtual Remaining Leaves',
- help='Maximum Leaves Allowed - Leaves Already Taken - Leaves Waiting Approval')
-
- double_validation = fields.Boolean(string='Apply Double Validation',
- help="When selected, the Allocation/Leave Requests for this type require a second validation to be approved.")
- company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.user.company_id)
-
- @api.multi
- def get_days(self, employee_id):
- # need to use `dict` constructor to create a dict per id
- result = dict((id, dict(max_leaves=0, leaves_taken=0, remaining_leaves=0, virtual_remaining_leaves=0)) for id in self.ids)
-
- requests = self.env['hr.leave'].search([
- ('employee_id', '=', employee_id),
- ('state', 'in', ['confirm', 'validate1', 'validate']),
- ('holiday_status_id', 'in', self.ids)
- ])
-
- allocations = self.env['hr.leave.allocation'].search([
- ('employee_id', '=', employee_id),
- ('state', 'in', ['confirm', 'validate1', 'validate']),
- ('holiday_status_id', 'in', self.ids)
- ])
-
- for request in requests:
- status_dict = result[request.holiday_status_id.id]
- status_dict['virtual_remaining_leaves'] -= request.number_of_days_temp
- if request.state == 'validate':
- status_dict['leaves_taken'] += request.number_of_days_temp
- status_dict['remaining_leaves'] -= request.number_of_days_temp
-
- for allocation in allocations:
- status_dict = result[allocation.holiday_status_id.id]
- if allocation.state == 'validate':
- # note: add only validated allocation even for the virtual
- # count; otherwise pending then refused allocation allow
- # the employee to create more leaves than possible
- status_dict['virtual_remaining_leaves'] += allocation.number_of_days_temp
- status_dict['max_leaves'] += allocation.number_of_days_temp
- status_dict['remaining_leaves'] += allocation.number_of_days_temp
-
- return result
-
- @api.multi
- def _compute_leaves(self):
- data_days = {}
- if 'employee_id' in self._context:
- employee_id = self._context['employee_id']
- else:
- employee_id = self.env['hr.employee'].search([('user_id', '=', self.env.user.id)], limit=1).id
-
- if employee_id:
- data_days = self.get_days(employee_id)
-
- for holiday_status in self:
- result = data_days.get(holiday_status.id, {})
- holiday_status.max_leaves = result.get('max_leaves', 0)
- holiday_status.leaves_taken = result.get('leaves_taken', 0)
- holiday_status.remaining_leaves = result.get('remaining_leaves', 0)
- holiday_status.virtual_remaining_leaves = result.get('virtual_remaining_leaves', 0)
-
- @api.multi
- def name_get(self):
- if not self._context.get('employee_id'):
- # leave counts is based on employee_id, would be inaccurate if not based on correct employee
- return super(HolidaysType, self).name_get()
- res = []
- for record in self:
- name = record.name
- if not record.limit:
- name = "%(name)s (%(count)s)" % {
- 'name': name,
- 'count': _('%g remaining out of %g') % (record.virtual_remaining_leaves or 0.0, record.max_leaves or 0.0)
- }
- res.append((record.id, name))
- return res
-
- @api.model
- def _search(self, args, offset=0, limit=None, order=None, count=False, access_rights_uid=None):
- """ Override _search to order the results, according to some employee.
- The order is the following
-
- - limit (limited leaves first, such as Legal Leaves)
- - virtual remaining leaves (higher the better, so using reverse on sorted)
-
- This override is necessary because those fields are not stored and depends
- on an employee_id given in context. This sort will be done when there
- is an employee_id in context and that no other order has been given
- to the method.
- """
- leave_ids = super(HolidaysType, self)._search(args, offset=offset, limit=limit, order=order, count=count, access_rights_uid=access_rights_uid)
- if not count and not order and self._context.get('employee_id'):
- leaves = self.browse(leave_ids)
- sort_key = lambda l: (not l.limit, l.virtual_remaining_leaves)
- return leaves.sorted(key=sort_key, reverse=True).ids
- return leave_ids
-
-
-class HolidaysRequest(models.Model):
- _name = "hr.leave"
- _description = "Leave"
- _order = "date_from desc"
- _inherit = ['mail.thread']
-
- def _default_employee(self):
- return self.env.context.get('default_employee_id') or self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
-
- name = fields.Char('Description')
- state = fields.Selection([
- ('draft', 'To Submit'),
- ('cancel', 'Cancelled'),
- ('confirm', 'To Approve'),
- ('refuse', 'Refused'),
- ('validate1', 'Second Approval'),
- ('validate', 'Approved')
- ], string='Status', readonly=True, track_visibility='onchange', copy=False, default='confirm',
- help="The status is set to 'To Submit', when a leave request is created." +
- "\nThe status is 'To Approve', when leave request is confirmed by user." +
- "\nThe status is 'Refused', when leave request is refused by manager." +
- "\nThe status is 'Approved', when leave request is approved by manager.")
- payslip_status = fields.Boolean('Reported in last payslips',
- help='Green this button when the leave has been taken into account in the payslip.')
- report_note = fields.Text('HR Comments')
- user_id = fields.Many2one('res.users', string='User', related='employee_id.user_id', related_sudo=True, store=True, default=lambda self: self.env.uid, readonly=True)
- date_from = fields.Datetime('Start Date', readonly=True, index=True, copy=False, required=True,
- states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, track_visibility='onchange')
- date_to = fields.Datetime('End Date', readonly=True, copy=False, required=True,
- states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, track_visibility='onchange')
- holiday_status_id = fields.Many2one("hr.leave.type", string="Leave Type", required=True, readonly=True,
- states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
- employee_id = fields.Many2one('hr.employee', string='Employee', index=True, readonly=True,
- states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, default=_default_employee, track_visibility='onchange')
- manager_id = fields.Many2one('hr.employee', related='employee_id.parent_id', string='Manager', readonly=True, store=True)
- notes = fields.Text('Reasons', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
- number_of_days_temp = fields.Float(
- 'Allocation', copy=False, readonly=True,
- states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]},
- help='Number of days of the leave request according to your working schedule.')
- number_of_days = fields.Float('Number of Days', compute='_compute_number_of_days', store=True, track_visibility='onchange')
- meeting_id = fields.Many2one('calendar.event', string='Meeting')
-
- parent_id = fields.Many2one('hr.leave', string='Parent')
- linked_request_ids = fields.One2many('hr.leave', 'parent_id', string='Linked Requests')
- department_id = fields.Many2one('hr.department', string='Department', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
- category_id = fields.Many2one('hr.employee.category', string='Employee Tag', readonly=True,
- states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, help='Category of Employee')
- holiday_type = fields.Selection([
- ('employee', 'By Employee'),
- ('department', 'By Department'),
- ('category', 'By Employee Tag')
- ], string='Allocation Mode', readonly=True, required=True, default='employee',
- states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]},
- help='By Employee: Allocation/Request for individual Employee, By Employee Tag: Allocation/Request for group of employees in category')
- first_approver_id = fields.Many2one('hr.employee', string='First Approval', readonly=True, copy=False,
- help='This area is automatically filled by the user who validate the leave', oldname='manager_id')
- second_approver_id = fields.Many2one('hr.employee', string='Second Approval', readonly=True, copy=False, oldname='manager_id2',
- help='This area is automaticly filled by the user who validate the leave with second level (If Leave type need second validation)')
- double_validation = fields.Boolean('Apply Double Validation', related='holiday_status_id.double_validation')
- can_reset = fields.Boolean('Can reset', compute='_compute_can_reset')
-
- @api.multi
- @api.depends('number_of_days_temp')
- def _compute_number_of_days(self):
- for holiday in self:
- holiday.number_of_days = -holiday.number_of_days_temp
-
- @api.multi
- def _compute_can_reset(self):
- """ User can reset a leave request if it is its own leave request
- or if he is an Hr Manager.
- """
- user = self.env.user
- group_hr_manager = self.env.ref('hr_holidays.group_hr_holidays_manager')
- for holiday in self:
- if group_hr_manager in user.groups_id or holiday.employee_id and holiday.employee_id.user_id == user:
- holiday.can_reset = True
-
- @api.constrains('date_from', 'date_to')
- def _check_date(self):
- for holiday in self:
- domain = [
- ('date_from', '<=', holiday.date_to),
- ('date_to', '>=', holiday.date_from),
- ('employee_id', '=', holiday.employee_id.id),
- ('id', '!=', holiday.id),
- ('state', 'not in', ['cancel', 'refuse']),
- ]
- nholidays = self.search_count(domain)
- if nholidays:
- raise ValidationError(_('You can not have 2 leaves that overlaps on same day!'))
-
- @api.constrains('state', 'number_of_days_temp')
- def _check_holidays(self):
- for holiday in self:
- if holiday.holiday_type != 'employee' or not holiday.employee_id or holiday.holiday_status_id.limit:
- continue
- leave_days = holiday.holiday_status_id.get_days(holiday.employee_id.id)[holiday.holiday_status_id.id]
- if float_compare(leave_days['remaining_leaves'], 0, precision_digits=2) == -1 or \
- float_compare(leave_days['virtual_remaining_leaves'], 0, precision_digits=2) == -1:
- raise ValidationError(_('The number of remaining leaves is not sufficient for this leave type.\n'
- 'Please verify also the leaves waiting for validation.'))
-
- _sql_constraints = [
- ('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL) or (holiday_type='department' AND department_id IS NOT NULL) )",
- "The employee, department or employee category of this request is missing. Please make sure that your user login is linked to an employee."),
- ('date_check2', "CHECK ((date_from <= date_to))", "The start date must be anterior to the end date."),
- ('date_check', "CHECK ( number_of_days_temp >= 0 )", "The number of days must be greater than 0."),
- ]
-
- @api.onchange('holiday_type')
- def _onchange_type(self):
- if self.holiday_type == 'employee' and not self.employee_id:
- if self.env.user.employee_ids:
- self.employee_id = self.env.user.employee_ids[0]
- elif self.holiday_type == 'department':
- if self.env.user.employee_ids:
- self.department_id = self.department_id or self.env.user.employee_ids[0].department_id
- self.employee_id = None
- elif self.holiday_type == 'category':
- self.employee_id = None
- self.department_id = None
-
- @api.onchange('employee_id')
- def _onchange_employee(self):
- if self.holiday_type == 'employee':
- self.department_id = self.employee_id.department_id
-
- def _get_number_of_days(self, date_from, date_to, employee_id):
- """ Returns a float equals to the timedelta between two dates given as string."""
- from_dt = fields.Datetime.from_string(date_from)
- to_dt = fields.Datetime.from_string(date_to)
-
- if employee_id:
- employee = self.env['hr.employee'].browse(employee_id)
- return employee.get_work_days_count(from_dt, to_dt)
-
- time_delta = to_dt - from_dt
- return math.ceil(time_delta.days + float(time_delta.seconds) / 86400)
-
- @api.onchange('date_from')
- def _onchange_date_from(self):
- """ If there are no date set for date_to, automatically set one 8 hours later than
- the date_from. Also update the number_of_days.
- """
- date_from = self.date_from
- date_to = self.date_to
-
- # No date_to set so far: automatically compute one 8 hours later
- if date_from and not date_to:
- date_to_with_delta = fields.Datetime.from_string(date_from) + timedelta(hours=HOURS_PER_DAY)
- self.date_to = str(date_to_with_delta)
-
- # Compute and update the number of days
- if (date_to and date_from) and (date_from <= date_to):
- self.number_of_days_temp = self._get_number_of_days(date_from, date_to, self.employee_id.id)
- else:
- self.number_of_days_temp = 0
-
- @api.onchange('date_to')
- def _onchange_date_to(self):
- """ Update the number_of_days. """
- date_from = self.date_from
- date_to = self.date_to
-
- # Compute and update the number of days
- if (date_to and date_from) and (date_from <= date_to):
- self.number_of_days_temp = self._get_number_of_days(date_from, date_to, self.employee_id.id)
- else:
- self.number_of_days_temp = 0
-
- ####################################################
- # ORM Overrides methods
- ####################################################
-
- @api.multi
- def name_get(self):
- res = []
- for leave in self:
- if self.env.context.get('short_name'):
- res.append((leave.id, _("%s : %.2f day(s)") % (leave.name or leave.holiday_status_id.name, leave.number_of_days_temp)))
- else:
- res.append((leave.id, _("%s on %s : %.2f day(s)") % (leave.employee_id.name or leave.category_id.name, leave.holiday_status_id.name, leave.number_of_days_temp)))
- return res
-
- @api.multi
- def add_follower(self, employee_id):
- employee = self.env['hr.employee'].browse(employee_id)
- if employee.user_id:
- self.message_subscribe_users(user_ids=employee.user_id.ids)
-
- @api.model
- def create(self, values):
- """ Override to avoid automatic logging of creation """
- employee_id = values.get('employee_id', False)
- if not values.get('department_id'):
- values.update({'department_id': self.env['hr.employee'].browse(employee_id).department_id.id})
- holiday = super(HolidaysRequest, self.with_context(mail_create_nolog=True, mail_create_nosubscribe=True)).create(values)
- holiday.add_follower(employee_id)
- return holiday
-
- @api.multi
- def write(self, values):
- employee_id = values.get('employee_id', False)
- result = super(HolidaysRequest, self).write(values)
- self.add_follower(employee_id)
- return result
-
- @api.multi
- def unlink(self):
- for holiday in self.filtered(lambda holiday: holiday.state not in ['draft', 'cancel', 'confirm']):
- raise UserError(_('You cannot delete a leave which is in %s state.') % (holiday.state,))
- return super(HolidaysRequest, self).unlink()
-
- @api.multi
- def copy_data(self, default=None):
- raise UserError(_('A leave cannot be duplicated.'))
-
- ####################################################
- # Business methods
- ####################################################
-
- @api.multi
- def _create_resource_leave(self):
- """ This method will create entry in resource calendar leave object at the time of holidays validated """
- for leave in self:
- self.env['resource.calendar.leaves'].create({
- 'name': leave.name,
- 'date_from': leave.date_from,
- 'holiday_id': leave.id,
- 'date_to': leave.date_to,
- 'resource_id': leave.employee_id.resource_id.id,
- 'calendar_id': leave.employee_id.resource_calendar_id.id
- })
- return True
-
- @api.multi
- def _remove_resource_leave(self):
- """ This method will create entry in resource calendar leave object at the time of holidays cancel/removed """
- return self.env['resource.calendar.leaves'].search([('holiday_id', 'in', self.ids)]).unlink()
-
- @api.multi
- def action_draft(self):
- for holiday in self:
- if not holiday.can_reset:
- raise UserError(_('Only an HR Manager or the concerned employee can reset to draft.'))
- if holiday.state not in ['confirm', 'refuse']:
- raise UserError(_('Leave request state must be "Refused" or "To Approve" in order to reset to Draft.'))
- holiday.write({
- 'state': 'draft',
- 'first_approver_id': False,
- 'second_approver_id': False,
- })
- linked_requests = holiday.mapped('linked_request_ids')
- for linked_request in linked_requests:
- linked_request.action_draft()
- linked_requests.unlink()
- return True
-
- @api.multi
- def action_confirm(self):
- if self.filtered(lambda holiday: holiday.state != 'draft'):
- raise UserError(_('Leave request must be in Draft state ("To Submit") in order to confirm it.'))
- return self.write({'state': 'confirm'})
-
- # TODO: Do we have to remove this method?
- @api.multi
- def _check_security_action_approve(self):
- if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
- raise UserError(_('Only an HR Officer or Manager can approve leave requests.'))
-
- @api.multi
- def action_approve(self):
- # if double_validation: this method is the first approval approval
- # if not double_validation: this method calls action_validate() below
- self._check_security_action_approve()
-
- current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
- if any(holiday.state != 'confirm' for holiday in self):
- raise UserError(_('Leave request must be confirmed ("To Approve") in order to approve it.'))
-
- self.filtered(lambda hol: hol.double_validation).write({'state': 'validate1', 'first_approver_id': current_employee.id})
- self.filtered(lambda hol: not hol.double_validation).action_validate()
- return True
-
- @api.multi
- def _prepare_holiday_values(self, employee):
- self.ensure_one()
- values = {
- 'name': self.name,
- 'holiday_type': 'employee',
- 'holiday_status_id': self.holiday_status_id.id,
- 'date_from': self.date_from,
- 'date_to': self.date_to,
- 'notes': self.notes,
- 'number_of_days_temp': self.number_of_days_temp,
- 'parent_id': self.id,
- 'employee_id': employee.id
- }
- return values
-
- @api.multi
- def _check_security_action_validate(self):
- if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
- raise UserError(_('Only an HR Officer or Manager can approve leave requests.'))
-
- @api.multi
- def action_validate(self):
- self._check_security_action_validate()
-
- current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
- for holiday in self:
- if holiday.state not in ['confirm', 'validate1']:
- raise UserError(_('Leave request must be confirmed in order to approve it.'))
- if holiday.state == 'validate1' and not holiday.env.user.has_group('hr_holidays.group_hr_holidays_manager'):
- raise UserError(_('Only an HR Manager can apply the second approval on leave requests.'))
-
- holiday.write({'state': 'validate'})
- if holiday.double_validation:
- holiday.write({'second_approver_id': current_employee.id})
- else:
- holiday.write({'first_approver_id': current_employee.id})
- if holiday.holiday_type == 'employee':
- holiday._validate_leave_request()
- elif holiday.holiday_type in ['category', 'department']:
- leaves = self.env['hr.leave']
- employees = holiday.category_id.employee_ids if holiday.holiday_type == 'category' else holiday.department_id.member_ids
- for employee in employees:
- values = holiday._prepare_holiday_values(employee)
- leaves += self.with_context(mail_notify_force_send=False).create(values)
- # TODO is it necessary to interleave the calls?
- leaves.action_approve()
- if leaves and leaves[0].double_validation:
- leaves.action_validate()
- return True
-
- def _validate_leave_request(self):
- """ Validate leave requests (holiday_type='employee')
- by creating a calendar event and a resource leaves. """
- for holiday in self.filtered(lambda request: request.holiday_type == 'employee'):
- meeting_values = holiday._prepare_holidays_meeting_values()
- meeting = self.env['calendar.event'].with_context(no_mail_to_attendees=True).create(meeting_values)
- holiday.write({'meeting_id': meeting.id})
- holiday._create_resource_leave()
-
- @api.multi
- def _prepare_holidays_meeting_values(self):
- self.ensure_one()
- meeting_values = {
- 'name': self.display_name,
- 'categ_ids': [(6, 0, [
- self.holiday_status_id.categ_id.id])] if self.holiday_status_id.categ_id else [],
- 'duration': self.number_of_days_temp * HOURS_PER_DAY,
- 'description': self.notes,
- 'user_id': self.user_id.id,
- 'start': self.date_from,
- 'stop': self.date_to,
- 'allday': False,
- 'state': 'open', # to block that meeting date in the calendar
- 'privacy': 'confidential'
- }
- # Add the partner_id (if exist) as an attendee
- if self.user_id and self.user_id.partner_id:
- meeting_values['partner_ids'] = [
- (4, self.user_id.partner_id.id)]
- return meeting_values
-
- @api.multi
- def action_refuse(self):
- self._check_security_action_refuse()
-
- current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
- for holiday in self:
- if holiday.state not in ['confirm', 'validate', 'validate1']:
- raise UserError(_('Leave request must be confirmed or validated in order to refuse it.'))
-
- if holiday.state == 'validate1':
- holiday.write({'state': 'refuse', 'first_approver_id': current_employee.id})
- else:
- holiday.write({'state': 'refuse', 'second_approver_id': current_employee.id})
- # Delete the meeting
- if holiday.meeting_id:
- holiday.meeting_id.unlink()
- # If a category that created several holidays, cancel all related
- holiday.linked_request_ids.action_refuse()
- self._remove_resource_leave()
- return True
-
- @api.multi
- def _check_security_action_refuse(self):
- if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
- raise UserError(_('Only an HR Officer or Manager can refuse leave requests.'))
-
- ####################################################
- # Messaging methods
- ####################################################
-
- @api.multi
- def _track_subtype(self, init_values):
- if 'state' in init_values and self.state == 'validate':
- return 'hr_holidays.mt_holidays_approved'
- elif 'state' in init_values and self.state == 'validate1':
- return 'hr_holidays.mt_holidays_first_validated'
- elif 'state' in init_values and self.state == 'confirm':
- return 'hr_holidays.mt_holidays_confirmed'
- elif 'state' in init_values and self.state == 'refuse':
- return 'hr_holidays.mt_holidays_refused'
- return super(HolidaysRequest, self)._track_subtype(init_values)
-
- @api.multi
- def _notification_recipients(self, message, groups):
- """ Handle HR users and officers recipients that can validate or refuse holidays
- directly from email. """
- groups = super(HolidaysRequest, self)._notification_recipients(message, groups)
-
- self.ensure_one()
- hr_actions = []
- if self.state == 'confirm':
- app_action = self._notification_link_helper('controller', controller='/hr_holidays_request/validate')
- hr_actions += [{'url': app_action, 'title': _('Approve')}]
- if self.state in ['confirm', 'validate', 'validate1']:
- ref_action = self._notification_link_helper('controller', controller='/hr_holidays_request/refuse')
- hr_actions += [{'url': ref_action, 'title': _('Refuse')}]
-
- new_group = (
- 'group_hr_holidays_user', lambda partner: bool(partner.user_ids) and any(user.has_group('hr_holidays.group_hr_holidays_user') for user in partner.user_ids), {
- 'actions': hr_actions,
- })
-
- return [new_group] + groups
-
- @api.multi
- def _message_notification_recipients(self, message, recipients):
- result = super(HolidaysRequest, self)._message_notification_recipients(message, recipients)
- title = _("See Leave")
- for res in result:
- if result[res].get('button_access'):
- result[res]['button_access']['title'] = title
- return result
-
-
-class HolidaysAllocation(models.Model):
- _name = "hr.leave.allocation"
- _description = "Allocation"
- _inherit = ['mail.thread']
-
- def _default_employee(self):
- return self.env.context.get('default_employee_id') or self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
-
- name = fields.Char('Description')
- state = fields.Selection([
- ('draft', 'To Submit'),
- ('cancel', 'Cancelled'),
- ('confirm', 'To Approve'),
- ('refuse', 'Refused'),
- ('validate1', 'Second Approval'),
- ('validate', 'Approved')
- ], string='Status', readonly=True, track_visibility='onchange', copy=False, default='confirm',
- help="The status is set to 'To Submit', when a leave request is created." +
- "\nThe status is 'To Approve', when leave request is confirmed by user." +
- "\nThe status is 'Refused', when leave request is refused by manager." +
- "\nThe status is 'Approved', when leave request is approved by manager.")
- holiday_status_id = fields.Many2one("hr.leave.type", string="Leave Type", required=True, readonly=True,
- states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
- employee_id = fields.Many2one('hr.employee', string='Employee', index=True, readonly=True,
- states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, default=_default_employee, track_visibility='onchange')
- notes = fields.Text('Reasons', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
- number_of_days_temp = fields.Float(
- 'Allocation', copy=False, readonly=True,
- states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]},
- help='Number of days of the leave request according to your working schedule.')
- number_of_days = fields.Float('Number of Days', compute='_compute_number_of_days', store=True, track_visibility='onchange')
- parent_id = fields.Many2one('hr.leave.allocation', string='Parent')
- linked_request_ids = fields.One2many('hr.leave.allocation', 'parent_id', string='Linked Requests')
- department_id = fields.Many2one('hr.department', string='Department', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
- category_id = fields.Many2one('hr.employee.category', string='Employee Tag', readonly=True,
- states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, help='Category of Employee')
- holiday_type = fields.Selection([
- ('employee', 'By Employee'),
- ('department', 'By Department'),
- ('category', 'By Employee Tag')
- ], string='Allocation Mode', readonly=True, required=True, default='employee',
- states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]},
- help='By Employee: Allocation for individual Employee, By Employee Tag: Allocation for group of employees in category')
- first_approver_id = fields.Many2one('hr.employee', string='First Approval', readonly=True, copy=False,
- help='This area is automatically filled by the user who validate the leave', oldname='manager_id')
- second_approver_id = fields.Many2one('hr.employee', string='Second Approval', readonly=True, copy=False, oldname='manager_id2',
- help='This area is automaticly filled by the user who validate the leave with second level (If Leave type need second validation)')
- double_validation = fields.Boolean('Apply Double Validation', related='holiday_status_id.double_validation')
- can_reset = fields.Boolean('Can reset', compute='_compute_can_reset')
-
- @api.multi
- @api.depends('number_of_days_temp')
- def _compute_number_of_days(self):
- for holiday in self:
- holiday.number_of_days = holiday.number_of_days_temp
-
- @api.multi
- def _compute_can_reset(self):
- """ User can reset a leave request if it is its own leave request
- or if he is an Hr Manager.
- """
- user = self.env.user
- group_hr_manager = self.env.ref('hr_holidays.group_hr_holidays_manager')
- for holiday in self:
- if group_hr_manager in user.groups_id or holiday.employee_id and holiday.employee_id.user_id == user:
- holiday.can_reset = True
-
- _sql_constraints = [
- ('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL) or (holiday_type='department' AND department_id IS NOT NULL) )",
- "The employee, department or employee category of this request is missing. Please make sure that your user login is linked to an employee."),
- ('date_check', "CHECK ( number_of_days_temp >= 0 )", "The number of days must be greater than 0."),
- ]
-
- @api.onchange('holiday_type')
- def _onchange_type(self):
- if self.holiday_type == 'employee' and not self.employee_id:
- if self.env.user.employee_ids:
- self.employee_id = self.env.user.employee_ids[0]
- elif self.holiday_type == 'department':
- if self.env.user.employee_ids:
- self.department_id = self.department_id or self.env.user.employee_ids[0].department_id
- self.employee_id = None
- elif self.holiday_type == 'category':
- self.employee_id = None
- self.department_id = None
-
- @api.onchange('employee_id')
- def _onchange_employee(self):
- if self.holiday_type == 'employee':
- self.department_id = self.employee_id.department_id
-
- ####################################################
- # ORM Overrides methods
- ####################################################
-
- @api.multi
- def name_get(self):
- res = []
- for leave in self:
- res.append((leave.id, _("Allocation of %s : %.2f day(s) To %s") % (leave.holiday_status_id.name, leave.number_of_days_temp, leave.employee_id.name)))
- return res
-
- def _check_state_access_right(self, vals):
- if vals.get('state') and vals['state'] not in ['draft', 'confirm', 'cancel'] and not self.env['res.users'].has_group('hr_holidays.group_hr_holidays_user'):
- return False
- return True
-
- @api.multi
- def add_follower(self, employee_id):
- employee = self.env['hr.employee'].browse(employee_id)
- if employee.user_id:
- self.message_subscribe_users(user_ids=employee.user_id.ids)
-
- @api.model
- def create(self, values):
- """ Override to avoid automatic logging of creation """
- employee_id = values.get('employee_id', False)
- if not self._check_state_access_right(values):
- raise AccessError(_('You cannot set a leave request as \'%s\'. Contact a human resource manager.') % values.get('state'))
- if not values.get('department_id'):
- values.update({'department_id': self.env['hr.employee'].browse(employee_id).department_id.id})
- holiday = super(HolidaysAllocation, self.with_context(mail_create_nolog=True, mail_create_nosubscribe=True)).create(values)
- holiday.add_follower(employee_id)
- return holiday
-
- @api.multi
- def write(self, values):
- employee_id = values.get('employee_id', False)
- if not self._check_state_access_right(values):
- raise AccessError(_('You cannot set a leave request as \'%s\'. Contact a human resource manager.') % values.get('state'))
- result = super(HolidaysAllocation, self).write(values)
- self.add_follower(employee_id)
- return result
-
- @api.multi
- def unlink(self):
- for holiday in self.filtered(lambda holiday: holiday.state not in ['draft', 'cancel', 'confirm']):
- raise UserError(_('You cannot delete a leave which is in %s state.') % (holiday.state,))
- return super(HolidaysAllocation, self).unlink()
-
- @api.multi
- def copy_data(self, default=None):
- raise UserError(_('A leave cannot be duplicated.'))
-
- ####################################################
- # Business methods
- ####################################################
-
- @api.multi
- def action_draft(self):
- for holiday in self:
- if not holiday.can_reset:
- raise UserError(_('Only an HR Manager or the concerned employee can reset to draft.'))
- if holiday.state not in ['confirm', 'refuse']:
- raise UserError(_('Leave request state must be "Refused" or "To Approve" in order to reset to Draft.'))
- holiday.write({
- 'state': 'draft',
- 'first_approver_id': False,
- 'second_approver_id': False,
- })
- linked_requests = holiday.mapped('linked_request_ids')
- for linked_request in linked_requests:
- linked_request.action_draft()
- linked_requests.unlink()
- return True
-
- @api.multi
- def action_confirm(self):
- if self.filtered(lambda holiday: holiday.state != 'draft'):
- raise UserError(_('Leave request must be in Draft state ("To Submit") in order to confirm it.'))
- return self.write({'state': 'confirm'})
-
- @api.multi
- def _check_security_action_approve(self):
- if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
- raise UserError(_('Only an HR Officer or Manager can approve leave requests.'))
-
- @api.multi
- def action_approve(self):
- # if double_validation: this method is the first approval approval
- # if not double_validation: this method calls action_validate() below
- self._check_security_action_approve()
-
- current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
- if any(holiday.state != 'confirm' for holiday in self):
- raise UserError(_('Leave request must be confirmed ("To Approve") in order to approve it.'))
-
- self.filtered(lambda hol: hol.double_validation).write({'state': 'validate1', 'first_approver_id': current_employee.id})
- self.filtered(lambda hol: not hol.double_validation).action_validate()
- return True
-
- @api.multi
- def _prepare_holiday_values(self, employee):
- self.ensure_one()
- values = {
- 'name': self.name,
- 'holiday_type': 'employee',
- 'holiday_status_id': self.holiday_status_id.id,
- 'notes': self.notes,
- 'number_of_days_temp': self.number_of_days_temp,
- 'parent_id': self.id,
- 'employee_id': employee.id
- }
- return values
-
- @api.multi
- def _check_security_action_validate(self):
- if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
- raise UserError(_('Only an HR Officer or Manager can approve leave requests.'))
-
- @api.multi
- def action_validate(self):
- self._check_security_action_validate()
-
- current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
- for holiday in self:
- if holiday.state not in ['confirm', 'validate1']:
- raise UserError(_('Leave request must be confirmed in order to approve it.'))
- if holiday.state == 'validate1' and not holiday.env.user.has_group('hr_holidays.group_hr_holidays_manager'):
- raise UserError(_('Only an HR Manager can apply the second approval on leave requests.'))
-
- holiday.write({'state': 'validate'})
- if holiday.double_validation:
- holiday.write({'second_approver_id': current_employee.id})
- else:
- holiday.write({'first_approver_id': current_employee.id})
- if holiday.holiday_type in ['category', 'department']:
- leaves = self.env['hr.leave.allocation']
- employees = holiday.category_id.employee_ids if holiday.holiday_type == 'category' else holiday.department_id.member_ids
- for employee in employees:
- values = holiday._prepare_holiday_values(employee)
- leaves += self.with_context(mail_notify_force_send=False).create(values)
- # TODO is it necessary to interleave the calls?
- leaves.action_approve()
- if leaves and leaves[0].double_validation:
- leaves.action_validate()
- return True
-
- @api.multi
- def action_refuse(self):
- self._check_security_action_refuse()
-
- current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
- for holiday in self:
- if holiday.state not in ['confirm', 'validate', 'validate1']:
- raise UserError(_('Leave request must be confirmed or validated in order to refuse it.'))
-
- if holiday.state == 'validate1':
- holiday.write({'state': 'refuse', 'first_approver_id': current_employee.id})
- else:
- holiday.write({'state': 'refuse', 'second_approver_id': current_employee.id})
- # If a category that created several holidays, cancel all related
- holiday.linked_request_ids.action_refuse()
- return True
-
- @api.multi
- def _check_security_action_refuse(self):
- if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
- raise UserError(_('Only an HR Officer or Manager can refuse leave requests.'))
-
- ####################################################
- # Messaging methods
- ####################################################
-
- @api.multi
- def _track_subtype(self, init_values):
- if 'state' in init_values and self.state == 'validate':
- return 'hr_holidays.mt_holidays_approved'
- elif 'state' in init_values and self.state == 'validate1':
- return 'hr_holidays.mt_holidays_first_validated'
- elif 'state' in init_values and self.state == 'confirm':
- return 'hr_holidays.mt_holidays_confirmed'
- elif 'state' in init_values and self.state == 'refuse':
- return 'hr_holidays.mt_holidays_refused'
- return super(HolidaysAllocation, self)._track_subtype(init_values)
-
- @api.multi
- def _notification_recipients(self, message, groups):
- """ Handle HR users and officers recipients that can validate or refuse holidays
- directly from email. """
- groups = super(HolidaysAllocation, self)._notification_recipients(message, groups)
-
- self.ensure_one()
- hr_actions = []
- if self.state == 'confirm':
- app_action = self._notification_link_helper('controller', controller='/hr_holidays_allocation/validate')
- hr_actions += [{'url': app_action, 'title': _('Approve')}]
- if self.state in ['confirm', 'validate', 'validate1']:
- ref_action = self._notification_link_helper('controller', controller='/hr_holidays_allocation/refuse')
- hr_actions += [{'url': ref_action, 'title': _('Refuse')}]
-
- new_group = (
- 'group_hr_holidays_user', lambda partner: bool(partner.user_ids) and any(user.has_group('hr_holidays.group_hr_holidays_user') for user in partner.user_ids), {
- 'actions': hr_actions,
- })
-
- return [new_group] + groups
-
- @api.multi
- def _message_notification_recipients(self, message, recipients):
- result = super(HolidaysAllocation, self)._message_notification_recipients(message, recipients)
- title = _("See Allocation")
- for res in result:
- if result[res].get('button_access'):
- result[res]['button_access']['title'] = title
- return result
diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py
new file mode 100644
index 00000000000..66f209b40f2
--- /dev/null
+++ b/addons/hr_holidays/models/hr_leave.py
@@ -0,0 +1,458 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+# Copyright (c) 2005-2006 Axelor SARL. (http://www.axelor.com)
+
+import logging
+import math
+from datetime import timedelta
+
+from odoo import api, fields, models
+from odoo.exceptions import UserError, AccessError, ValidationError
+from odoo.tools import float_compare
+from odoo.tools.translate import _
+
+_logger = logging.getLogger(__name__)
+
+
+HOURS_PER_DAY = 8
+
+class HolidaysRequest(models.Model):
+ _name = "hr.leave"
+ _description = "Leave"
+ _order = "date_from desc"
+ _inherit = ['mail.thread']
+
+ def _default_employee(self):
+ return self.env.context.get('default_employee_id') or self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
+
+ name = fields.Char('Description')
+ state = fields.Selection([
+ ('draft', 'To Submit'),
+ ('cancel', 'Cancelled'),
+ ('confirm', 'To Approve'),
+ ('refuse', 'Refused'),
+ ('validate1', 'Second Approval'),
+ ('validate', 'Approved')
+ ], string='Status', readonly=True, track_visibility='onchange', copy=False, default='confirm',
+ help="The status is set to 'To Submit', when a leave request is created." +
+ "\nThe status is 'To Approve', when leave request is confirmed by user." +
+ "\nThe status is 'Refused', when leave request is refused by manager." +
+ "\nThe status is 'Approved', when leave request is approved by manager.")
+ payslip_status = fields.Boolean('Reported in last payslips',
+ help='Green this button when the leave has been taken into account in the payslip.')
+ report_note = fields.Text('HR Comments')
+ user_id = fields.Many2one('res.users', string='User', related='employee_id.user_id', related_sudo=True, store=True, default=lambda self: self.env.uid, readonly=True)
+ date_from = fields.Datetime('Start Date', readonly=True, index=True, copy=False, required=True,
+ states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, track_visibility='onchange')
+ date_to = fields.Datetime('End Date', readonly=True, copy=False, required=True,
+ states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, track_visibility='onchange')
+ holiday_status_id = fields.Many2one("hr.leave.type", string="Leave Type", required=True, readonly=True,
+ states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
+ employee_id = fields.Many2one('hr.employee', string='Employee', index=True, readonly=True,
+ states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, default=_default_employee, track_visibility='onchange')
+ manager_id = fields.Many2one('hr.employee', related='employee_id.parent_id', string='Manager', readonly=True, store=True)
+ notes = fields.Text('Reasons', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
+ number_of_days_temp = fields.Float(
+ 'Allocation', copy=False, readonly=True,
+ states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]},
+ help='Number of days of the leave request according to your working schedule.')
+ number_of_days = fields.Float('Number of Days', compute='_compute_number_of_days', store=True, track_visibility='onchange')
+ meeting_id = fields.Many2one('calendar.event', string='Meeting')
+
+ parent_id = fields.Many2one('hr.leave', string='Parent')
+ linked_request_ids = fields.One2many('hr.leave', 'parent_id', string='Linked Requests')
+ department_id = fields.Many2one('hr.department', string='Department', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
+ category_id = fields.Many2one('hr.employee.category', string='Employee Tag', readonly=True,
+ states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, help='Category of Employee')
+ holiday_type = fields.Selection([
+ ('employee', 'By Employee'),
+ ('department', 'By Department'),
+ ('category', 'By Employee Tag')
+ ], string='Allocation Mode', readonly=True, required=True, default='employee',
+ states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]},
+ help='By Employee: Allocation/Request for individual Employee, By Employee Tag: Allocation/Request for group of employees in category')
+ first_approver_id = fields.Many2one('hr.employee', string='First Approval', readonly=True, copy=False,
+ help='This area is automatically filled by the user who validate the leave', oldname='manager_id')
+ second_approver_id = fields.Many2one('hr.employee', string='Second Approval', readonly=True, copy=False, oldname='manager_id2',
+ help='This area is automaticly filled by the user who validate the leave with second level (If Leave type need second validation)')
+ double_validation = fields.Boolean('Apply Double Validation', related='holiday_status_id.double_validation')
+ can_reset = fields.Boolean('Can reset', compute='_compute_can_reset')
+
+ _sql_constraints = [
+ ('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL) or (holiday_type='department' AND department_id IS NOT NULL) )",
+ "The employee, department or employee category of this request is missing. Please make sure that your user login is linked to an employee."),
+ ('date_check2', "CHECK ((date_from <= date_to))", "The start date must be anterior to the end date."),
+ ('date_check', "CHECK ( number_of_days_temp >= 0 )", "The number of days must be greater than 0."),
+ ]
+
+ @api.multi
+ @api.depends('number_of_days_temp')
+ def _compute_number_of_days(self):
+ for holiday in self:
+ holiday.number_of_days = -holiday.number_of_days_temp
+
+ @api.multi
+ def _compute_can_reset(self):
+ """ User can reset a leave request if it is its own leave request
+ or if he is an Hr Manager.
+ """
+ user = self.env.user
+ group_hr_manager = self.env.ref('hr_holidays.group_hr_holidays_manager')
+ for holiday in self:
+ if group_hr_manager in user.groups_id or holiday.employee_id and holiday.employee_id.user_id == user:
+ holiday.can_reset = True
+
+ @api.onchange('holiday_type')
+ def _onchange_type(self):
+ if self.holiday_type == 'employee' and not self.employee_id:
+ if self.env.user.employee_ids:
+ self.employee_id = self.env.user.employee_ids[0]
+ elif self.holiday_type == 'department':
+ if self.env.user.employee_ids:
+ self.department_id = self.department_id or self.env.user.employee_ids[0].department_id
+ self.employee_id = None
+ elif self.holiday_type == 'category':
+ self.employee_id = None
+ self.department_id = None
+
+ @api.onchange('employee_id')
+ def _onchange_employee(self):
+ if self.holiday_type == 'employee':
+ self.department_id = self.employee_id.department_id
+
+ @api.onchange('date_from')
+ def _onchange_date_from(self):
+ """ If there are no date set for date_to, automatically set one 8 hours later than
+ the date_from. Also update the number_of_days.
+ """
+ date_from = self.date_from
+ date_to = self.date_to
+
+ # No date_to set so far: automatically compute one 8 hours later
+ if date_from and not date_to:
+ date_to_with_delta = fields.Datetime.from_string(date_from) + timedelta(hours=HOURS_PER_DAY)
+ self.date_to = str(date_to_with_delta)
+
+ # Compute and update the number of days
+ if (date_to and date_from) and (date_from <= date_to):
+ self.number_of_days_temp = self._get_number_of_days(date_from, date_to, self.employee_id.id)
+ else:
+ self.number_of_days_temp = 0
+
+ @api.onchange('date_to')
+ def _onchange_date_to(self):
+ """ Update the number_of_days. """
+ date_from = self.date_from
+ date_to = self.date_to
+
+ # Compute and update the number of days
+ if (date_to and date_from) and (date_from <= date_to):
+ self.number_of_days_temp = self._get_number_of_days(date_from, date_to, self.employee_id.id)
+ else:
+ self.number_of_days_temp = 0
+
+ @api.constrains('date_from', 'date_to')
+ def _check_date(self):
+ for holiday in self:
+ domain = [
+ ('date_from', '<=', holiday.date_to),
+ ('date_to', '>=', holiday.date_from),
+ ('employee_id', '=', holiday.employee_id.id),
+ ('id', '!=', holiday.id),
+ ('state', 'not in', ['cancel', 'refuse']),
+ ]
+ nholidays = self.search_count(domain)
+ if nholidays:
+ raise ValidationError(_('You can not have 2 leaves that overlaps on same day!'))
+
+ @api.constrains('state', 'number_of_days_temp')
+ def _check_holidays(self):
+ for holiday in self:
+ if holiday.holiday_type != 'employee' or not holiday.employee_id or holiday.holiday_status_id.limit:
+ continue
+ leave_days = holiday.holiday_status_id.get_days(holiday.employee_id.id)[holiday.holiday_status_id.id]
+ if float_compare(leave_days['remaining_leaves'], 0, precision_digits=2) == -1 or \
+ float_compare(leave_days['virtual_remaining_leaves'], 0, precision_digits=2) == -1:
+ raise ValidationError(_('The number of remaining leaves is not sufficient for this leave type.\n'
+ 'Please verify also the leaves waiting for validation.'))
+
+ def _get_number_of_days(self, date_from, date_to, employee_id):
+ """ Returns a float equals to the timedelta between two dates given as string."""
+ from_dt = fields.Datetime.from_string(date_from)
+ to_dt = fields.Datetime.from_string(date_to)
+
+ if employee_id:
+ employee = self.env['hr.employee'].browse(employee_id)
+ return employee.get_work_days_count(from_dt, to_dt)
+
+ time_delta = to_dt - from_dt
+ return math.ceil(time_delta.days + float(time_delta.seconds) / 86400)
+
+ ####################################################
+ # ORM Overrides methods
+ ####################################################
+
+ @api.multi
+ def name_get(self):
+ res = []
+ for leave in self:
+ if self.env.context.get('short_name'):
+ res.append((leave.id, _("%s : %.2f day(s)") % (leave.name or leave.holiday_status_id.name, leave.number_of_days_temp)))
+ else:
+ res.append((leave.id, _("%s on %s : %.2f day(s)") % (leave.employee_id.name or leave.category_id.name, leave.holiday_status_id.name, leave.number_of_days_temp)))
+ return res
+
+ @api.multi
+ def add_follower(self, employee_id):
+ employee = self.env['hr.employee'].browse(employee_id)
+ if employee.user_id:
+ self.message_subscribe_users(user_ids=employee.user_id.ids)
+
+ @api.model
+ def create(self, values):
+ """ Override to avoid automatic logging of creation """
+ employee_id = values.get('employee_id', False)
+ if not values.get('department_id'):
+ values.update({'department_id': self.env['hr.employee'].browse(employee_id).department_id.id})
+ holiday = super(HolidaysRequest, self.with_context(mail_create_nolog=True, mail_create_nosubscribe=True)).create(values)
+ holiday.add_follower(employee_id)
+ return holiday
+
+ @api.multi
+ def write(self, values):
+ employee_id = values.get('employee_id', False)
+ result = super(HolidaysRequest, self).write(values)
+ self.add_follower(employee_id)
+ return result
+
+ @api.multi
+ def unlink(self):
+ for holiday in self.filtered(lambda holiday: holiday.state not in ['draft', 'cancel', 'confirm']):
+ raise UserError(_('You cannot delete a leave which is in %s state.') % (holiday.state,))
+ return super(HolidaysRequest, self).unlink()
+
+ @api.multi
+ def copy_data(self, default=None):
+ raise UserError(_('A leave cannot be duplicated.'))
+
+ ####################################################
+ # Business methods
+ ####################################################
+
+ @api.multi
+ def _create_resource_leave(self):
+ """ This method will create entry in resource calendar leave object at the time of holidays validated """
+ for leave in self:
+ self.env['resource.calendar.leaves'].create({
+ 'name': leave.name,
+ 'date_from': leave.date_from,
+ 'holiday_id': leave.id,
+ 'date_to': leave.date_to,
+ 'resource_id': leave.employee_id.resource_id.id,
+ 'calendar_id': leave.employee_id.resource_calendar_id.id
+ })
+ return True
+
+ @api.multi
+ def _remove_resource_leave(self):
+ """ This method will create entry in resource calendar leave object at the time of holidays cancel/removed """
+ return self.env['resource.calendar.leaves'].search([('holiday_id', 'in', self.ids)]).unlink()
+
+ def _validate_leave_request(self):
+ """ Validate leave requests (holiday_type='employee')
+ by creating a calendar event and a resource leaves. """
+ for holiday in self.filtered(lambda request: request.holiday_type == 'employee'):
+ meeting_values = holiday._prepare_holidays_meeting_values()
+ meeting = self.env['calendar.event'].with_context(no_mail_to_attendees=True).create(meeting_values)
+ holiday.write({'meeting_id': meeting.id})
+ holiday._create_resource_leave()
+
+ @api.multi
+ def _prepare_holidays_meeting_values(self):
+ self.ensure_one()
+ meeting_values = {
+ 'name': self.display_name,
+ 'categ_ids': [(6, 0, [
+ self.holiday_status_id.categ_id.id])] if self.holiday_status_id.categ_id else [],
+ 'duration': self.number_of_days_temp * HOURS_PER_DAY,
+ 'description': self.notes,
+ 'user_id': self.user_id.id,
+ 'start': self.date_from,
+ 'stop': self.date_to,
+ 'allday': False,
+ 'state': 'open', # to block that meeting date in the calendar
+ 'privacy': 'confidential'
+ }
+ # Add the partner_id (if exist) as an attendee
+ if self.user_id and self.user_id.partner_id:
+ meeting_values['partner_ids'] = [
+ (4, self.user_id.partner_id.id)]
+ return meeting_values
+
+ @api.multi
+ def _prepare_holiday_values(self, employee):
+ self.ensure_one()
+ values = {
+ 'name': self.name,
+ 'holiday_type': 'employee',
+ 'holiday_status_id': self.holiday_status_id.id,
+ 'date_from': self.date_from,
+ 'date_to': self.date_to,
+ 'notes': self.notes,
+ 'number_of_days_temp': self.number_of_days_temp,
+ 'parent_id': self.id,
+ 'employee_id': employee.id
+ }
+ return values
+
+ @api.multi
+ def action_draft(self):
+ for holiday in self:
+ if not holiday.can_reset:
+ raise UserError(_('Only an HR Manager or the concerned employee can reset to draft.'))
+ if holiday.state not in ['confirm', 'refuse']:
+ raise UserError(_('Leave request state must be "Refused" or "To Approve" in order to reset to Draft.'))
+ holiday.write({
+ 'state': 'draft',
+ 'first_approver_id': False,
+ 'second_approver_id': False,
+ })
+ linked_requests = holiday.mapped('linked_request_ids')
+ for linked_request in linked_requests:
+ linked_request.action_draft()
+ linked_requests.unlink()
+ return True
+
+ @api.multi
+ def action_confirm(self):
+ if self.filtered(lambda holiday: holiday.state != 'draft'):
+ raise UserError(_('Leave request must be in Draft state ("To Submit") in order to confirm it.'))
+ return self.write({'state': 'confirm'})
+
+ @api.multi
+ def _check_security_action_approve(self):
+ if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
+ raise UserError(_('Only an HR Officer or Manager can approve leave requests.'))
+
+ @api.multi
+ def action_approve(self):
+ # if double_validation: this method is the first approval approval
+ # if not double_validation: this method calls action_validate() below
+ self._check_security_action_approve()
+
+ current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
+ if any(holiday.state != 'confirm' for holiday in self):
+ raise UserError(_('Leave request must be confirmed ("To Approve") in order to approve it.'))
+
+ self.filtered(lambda hol: hol.double_validation).write({'state': 'validate1', 'first_approver_id': current_employee.id})
+ self.filtered(lambda hol: not hol.double_validation).action_validate()
+ return True
+
+ @api.multi
+ def _check_security_action_validate(self):
+ if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
+ raise UserError(_('Only an HR Officer or Manager can approve leave requests.'))
+
+ @api.multi
+ def action_validate(self):
+ self._check_security_action_validate()
+
+ current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
+ for holiday in self:
+ if holiday.state not in ['confirm', 'validate1']:
+ raise UserError(_('Leave request must be confirmed in order to approve it.'))
+ if holiday.state == 'validate1' and not holiday.env.user.has_group('hr_holidays.group_hr_holidays_manager'):
+ raise UserError(_('Only an HR Manager can apply the second approval on leave requests.'))
+
+ holiday.write({'state': 'validate'})
+ if holiday.double_validation:
+ holiday.write({'second_approver_id': current_employee.id})
+ else:
+ holiday.write({'first_approver_id': current_employee.id})
+ if holiday.holiday_type == 'employee':
+ holiday._validate_leave_request()
+ elif holiday.holiday_type in ['category', 'department']:
+ leaves = self.env['hr.leave']
+ employees = holiday.category_id.employee_ids if holiday.holiday_type == 'category' else holiday.department_id.member_ids
+ for employee in employees:
+ values = holiday._prepare_holiday_values(employee)
+ leaves += self.with_context(mail_notify_force_send=False).create(values)
+ # TODO is it necessary to interleave the calls?
+ leaves.action_approve()
+ if leaves and leaves[0].double_validation:
+ leaves.action_validate()
+ return True
+
+ @api.multi
+ def action_refuse(self):
+ self._check_security_action_refuse()
+
+ current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
+ for holiday in self:
+ if holiday.state not in ['confirm', 'validate', 'validate1']:
+ raise UserError(_('Leave request must be confirmed or validated in order to refuse it.'))
+
+ if holiday.state == 'validate1':
+ holiday.write({'state': 'refuse', 'first_approver_id': current_employee.id})
+ else:
+ holiday.write({'state': 'refuse', 'second_approver_id': current_employee.id})
+ # Delete the meeting
+ if holiday.meeting_id:
+ holiday.meeting_id.unlink()
+ # If a category that created several holidays, cancel all related
+ holiday.linked_request_ids.action_refuse()
+ self._remove_resource_leave()
+ return True
+
+ @api.multi
+ def _check_security_action_refuse(self):
+ if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
+ raise UserError(_('Only an HR Officer or Manager can refuse leave requests.'))
+
+ ####################################################
+ # Messaging methods
+ ####################################################
+
+ @api.multi
+ def _track_subtype(self, init_values):
+ if 'state' in init_values and self.state == 'validate':
+ return 'hr_holidays.mt_leave_approved'
+ elif 'state' in init_values and self.state == 'validate1':
+ return 'hr_holidays.mt_leave_first_validated'
+ elif 'state' in init_values and self.state == 'confirm':
+ return 'hr_holidays.mt_leave_confirmed'
+ elif 'state' in init_values and self.state == 'refuse':
+ return 'hr_holidays.mt_leave_refused'
+ return super(HolidaysRequest, self)._track_subtype(init_values)
+
+ @api.multi
+ def _notification_recipients(self, message, groups):
+ """ Handle HR users and officers recipients that can validate or refuse holidays
+ directly from email. """
+ groups = super(HolidaysRequest, self)._notification_recipients(message, groups)
+
+ self.ensure_one()
+ hr_actions = []
+ if self.state == 'confirm':
+ app_action = self._notification_link_helper('controller', controller='/leave/validate')
+ hr_actions += [{'url': app_action, 'title': _('Approve')}]
+ if self.state in ['confirm', 'validate', 'validate1']:
+ ref_action = self._notification_link_helper('controller', controller='/leave/refuse')
+ hr_actions += [{'url': ref_action, 'title': _('Refuse')}]
+
+ new_group = (
+ 'group_hr_holidays_user', lambda partner: bool(partner.user_ids) and any(user.has_group('hr_holidays.group_hr_holidays_user') for user in partner.user_ids), {
+ 'actions': hr_actions,
+ })
+
+ return [new_group] + groups
+
+ @api.multi
+ def _message_notification_recipients(self, message, recipients):
+ result = super(HolidaysRequest, self)._message_notification_recipients(message, recipients)
+ title = _("See Leave")
+ for res in result:
+ if result[res].get('button_access'):
+ result[res]['button_access']['title'] = title
+ return result
diff --git a/addons/hr_holidays/models/hr_leave_allocation.py b/addons/hr_holidays/models/hr_leave_allocation.py
new file mode 100644
index 00000000000..da1260fffef
--- /dev/null
+++ b/addons/hr_holidays/models/hr_leave_allocation.py
@@ -0,0 +1,311 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+# Copyright (c) 2005-2006 Axelor SARL. (http://www.axelor.com)
+
+import logging
+
+from odoo import api, fields, models
+from odoo.exceptions import UserError, AccessError
+from odoo.tools.translate import _
+
+_logger = logging.getLogger(__name__)
+
+
+class HolidaysAllocation(models.Model):
+ _name = "hr.leave.allocation"
+ _description = "Allocation"
+ _inherit = ['mail.thread']
+
+ def _default_employee(self):
+ return self.env.context.get('default_employee_id') or self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
+
+ name = fields.Char('Description')
+ state = fields.Selection([
+ ('draft', 'To Submit'),
+ ('cancel', 'Cancelled'),
+ ('confirm', 'To Approve'),
+ ('refuse', 'Refused'),
+ ('validate1', 'Second Approval'),
+ ('validate', 'Approved')
+ ], string='Status', readonly=True, track_visibility='onchange', copy=False, default='confirm',
+ help="The status is set to 'To Submit', when a leave request is created." +
+ "\nThe status is 'To Approve', when leave request is confirmed by user." +
+ "\nThe status is 'Refused', when leave request is refused by manager." +
+ "\nThe status is 'Approved', when leave request is approved by manager.")
+ holiday_status_id = fields.Many2one("hr.leave.type", string="Leave Type", required=True, readonly=True,
+ states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
+ employee_id = fields.Many2one('hr.employee', string='Employee', index=True, readonly=True,
+ states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, default=_default_employee, track_visibility='onchange')
+ notes = fields.Text('Reasons', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
+ number_of_days_temp = fields.Float(
+ 'Allocation', copy=False, readonly=True,
+ states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]},
+ help='Number of days of the leave request according to your working schedule.')
+ number_of_days = fields.Float('Number of Days', compute='_compute_number_of_days', store=True, track_visibility='onchange')
+ parent_id = fields.Many2one('hr.leave.allocation', string='Parent')
+ linked_request_ids = fields.One2many('hr.leave.allocation', 'parent_id', string='Linked Requests')
+ department_id = fields.Many2one('hr.department', string='Department', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]})
+ category_id = fields.Many2one('hr.employee.category', string='Employee Tag', readonly=True,
+ states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, help='Category of Employee')
+ holiday_type = fields.Selection([
+ ('employee', 'By Employee'),
+ ('department', 'By Department'),
+ ('category', 'By Employee Tag')
+ ], string='Allocation Mode', readonly=True, required=True, default='employee',
+ states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]},
+ help='By Employee: Allocation for individual Employee, By Employee Tag: Allocation for group of employees in category')
+ first_approver_id = fields.Many2one('hr.employee', string='First Approval', readonly=True, copy=False,
+ help='This area is automatically filled by the user who validate the leave', oldname='manager_id')
+ second_approver_id = fields.Many2one('hr.employee', string='Second Approval', readonly=True, copy=False, oldname='manager_id2',
+ help='This area is automaticly filled by the user who validate the leave with second level (If Leave type need second validation)')
+ double_validation = fields.Boolean('Apply Double Validation', related='holiday_status_id.double_validation')
+ can_reset = fields.Boolean('Can reset', compute='_compute_can_reset')
+
+ _sql_constraints = [
+ ('type_value', "CHECK( (holiday_type='employee' AND employee_id IS NOT NULL) or (holiday_type='category' AND category_id IS NOT NULL) or (holiday_type='department' AND department_id IS NOT NULL) )",
+ "The employee, department or employee category of this request is missing. Please make sure that your user login is linked to an employee."),
+ ('date_check', "CHECK ( number_of_days_temp >= 0 )", "The number of days must be greater than 0."),
+ ]
+
+ @api.multi
+ @api.depends('number_of_days_temp')
+ def _compute_number_of_days(self):
+ for holiday in self:
+ holiday.number_of_days = holiday.number_of_days_temp
+
+ @api.multi
+ def _compute_can_reset(self):
+ """ User can reset a leave request if it is its own leave request
+ or if he is an Hr Manager.
+ """
+ user = self.env.user
+ group_hr_manager = self.env.ref('hr_holidays.group_hr_holidays_manager')
+ for holiday in self:
+ if group_hr_manager in user.groups_id or holiday.employee_id and holiday.employee_id.user_id == user:
+ holiday.can_reset = True
+
+ @api.onchange('holiday_type')
+ def _onchange_type(self):
+ if self.holiday_type == 'employee' and not self.employee_id:
+ if self.env.user.employee_ids:
+ self.employee_id = self.env.user.employee_ids[0]
+ elif self.holiday_type == 'department':
+ if self.env.user.employee_ids:
+ self.department_id = self.department_id or self.env.user.employee_ids[0].department_id
+ self.employee_id = None
+ elif self.holiday_type == 'category':
+ self.employee_id = None
+ self.department_id = None
+
+ @api.onchange('employee_id')
+ def _onchange_employee(self):
+ if self.holiday_type == 'employee':
+ self.department_id = self.employee_id.department_id
+
+ ####################################################
+ # ORM Overrides methods
+ ####################################################
+
+ @api.multi
+ def name_get(self):
+ res = []
+ for leave in self:
+ res.append((leave.id, _("Allocation of %s : %.2f day(s) To %s") % (leave.holiday_status_id.name, leave.number_of_days_temp, leave.employee_id.name)))
+ return res
+
+ @api.multi
+ def add_follower(self, employee_id):
+ employee = self.env['hr.employee'].browse(employee_id)
+ if employee.user_id:
+ self.message_subscribe_users(user_ids=employee.user_id.ids)
+
+ @api.model
+ def create(self, values):
+ """ Override to avoid automatic logging of creation """
+ employee_id = values.get('employee_id', False)
+ if not values.get('department_id'):
+ values.update({'department_id': self.env['hr.employee'].browse(employee_id).department_id.id})
+ holiday = super(HolidaysAllocation, self.with_context(mail_create_nolog=True, mail_create_nosubscribe=True)).create(values)
+ holiday.add_follower(employee_id)
+ return holiday
+
+ @api.multi
+ def write(self, values):
+ employee_id = values.get('employee_id', False)
+ result = super(HolidaysAllocation, self).write(values)
+ self.add_follower(employee_id)
+ return result
+
+ @api.multi
+ def unlink(self):
+ for holiday in self.filtered(lambda holiday: holiday.state not in ['draft', 'cancel', 'confirm']):
+ raise UserError(_('You cannot delete a leave which is in %s state.') % (holiday.state,))
+ return super(HolidaysAllocation, self).unlink()
+
+ @api.multi
+ def copy_data(self, default=None):
+ raise UserError(_('A leave cannot be duplicated.'))
+
+ ####################################################
+ # Business methods
+ ####################################################
+
+ @api.multi
+ def _prepare_holiday_values(self, employee):
+ self.ensure_one()
+ values = {
+ 'name': self.name,
+ 'holiday_type': 'employee',
+ 'holiday_status_id': self.holiday_status_id.id,
+ 'notes': self.notes,
+ 'number_of_days_temp': self.number_of_days_temp,
+ 'parent_id': self.id,
+ 'employee_id': employee.id
+ }
+ return values
+
+ @api.multi
+ def action_draft(self):
+ for holiday in self:
+ if not holiday.can_reset:
+ raise UserError(_('Only an HR Manager or the concerned employee can reset to draft.'))
+ if holiday.state not in ['confirm', 'refuse']:
+ raise UserError(_('Leave request state must be "Refused" or "To Approve" in order to reset to Draft.'))
+ holiday.write({
+ 'state': 'draft',
+ 'first_approver_id': False,
+ 'second_approver_id': False,
+ })
+ linked_requests = holiday.mapped('linked_request_ids')
+ for linked_request in linked_requests:
+ linked_request.action_draft()
+ linked_requests.unlink()
+ return True
+
+ @api.multi
+ def action_confirm(self):
+ if self.filtered(lambda holiday: holiday.state != 'draft'):
+ raise UserError(_('Leave request must be in Draft state ("To Submit") in order to confirm it.'))
+ return self.write({'state': 'confirm'})
+
+ @api.multi
+ def _check_security_action_approve(self):
+ if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
+ raise UserError(_('Only an HR Officer or Manager can approve leave requests.'))
+
+ @api.multi
+ def action_approve(self):
+ # if double_validation: this method is the first approval approval
+ # if not double_validation: this method calls action_validate() below
+ self._check_security_action_approve()
+
+ current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
+ if any(holiday.state != 'confirm' for holiday in self):
+ raise UserError(_('Leave request must be confirmed ("To Approve") in order to approve it.'))
+
+ self.filtered(lambda hol: hol.double_validation).write({'state': 'validate1', 'first_approver_id': current_employee.id})
+ self.filtered(lambda hol: not hol.double_validation).action_validate()
+ return True
+
+ @api.multi
+ def _check_security_action_validate(self):
+ if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
+ raise UserError(_('Only an HR Officer or Manager can approve leave requests.'))
+
+ @api.multi
+ def action_validate(self):
+ self._check_security_action_validate()
+
+ current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
+ for holiday in self:
+ if holiday.state not in ['confirm', 'validate1']:
+ raise UserError(_('Leave request must be confirmed in order to approve it.'))
+ if holiday.state == 'validate1' and not holiday.env.user.has_group('hr_holidays.group_hr_holidays_manager'):
+ raise UserError(_('Only an HR Manager can apply the second approval on leave requests.'))
+
+ holiday.write({'state': 'validate'})
+ if holiday.double_validation:
+ holiday.write({'second_approver_id': current_employee.id})
+ else:
+ holiday.write({'first_approver_id': current_employee.id})
+ if holiday.holiday_type in ['category', 'department']:
+ leaves = self.env['hr.leave.allocation']
+ employees = holiday.category_id.employee_ids if holiday.holiday_type == 'category' else holiday.department_id.member_ids
+ for employee in employees:
+ values = holiday._prepare_holiday_values(employee)
+ leaves += self.with_context(mail_notify_force_send=False).create(values)
+ # TODO is it necessary to interleave the calls?
+ leaves.action_approve()
+ if leaves and leaves[0].double_validation:
+ leaves.action_validate()
+ return True
+
+ @api.multi
+ def action_refuse(self):
+ self._check_security_action_refuse()
+
+ current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
+ for holiday in self:
+ if holiday.state not in ['confirm', 'validate', 'validate1']:
+ raise UserError(_('Leave request must be confirmed or validated in order to refuse it.'))
+
+ if holiday.state == 'validate1':
+ holiday.write({'state': 'refuse', 'first_approver_id': current_employee.id})
+ else:
+ holiday.write({'state': 'refuse', 'second_approver_id': current_employee.id})
+ # If a category that created several holidays, cancel all related
+ holiday.linked_request_ids.action_refuse()
+ return True
+
+ @api.multi
+ def _check_security_action_refuse(self):
+ if not self.env.user.has_group('hr_holidays.group_hr_holidays_user'):
+ raise UserError(_('Only an HR Officer or Manager can refuse leave requests.'))
+
+ ####################################################
+ # Messaging methods
+ ####################################################
+
+ @api.multi
+ def _track_subtype(self, init_values):
+ if 'state' in init_values and self.state == 'validate':
+ return 'hr_holidays.mt_leave_allocation_approved'
+ elif 'state' in init_values and self.state == 'validate1':
+ return 'hr_holidays.mt_leave_allocation_first_validated'
+ elif 'state' in init_values and self.state == 'confirm':
+ return 'hr_holidays.mt_leave_allocation_confirmed'
+ elif 'state' in init_values and self.state == 'refuse':
+ return 'hr_holidays.mt_leave_allocation_refused'
+ return super(HolidaysAllocation, self)._track_subtype(init_values)
+
+ @api.multi
+ def _notification_recipients(self, message, groups):
+ """ Handle HR users and officers recipients that can validate or refuse holidays
+ directly from email. """
+ groups = super(HolidaysAllocation, self)._notification_recipients(message, groups)
+
+ self.ensure_one()
+ hr_actions = []
+ if self.state == 'confirm':
+ app_action = self._notification_link_helper('controller', controller='/allocation/validate')
+ hr_actions += [{'url': app_action, 'title': _('Approve')}]
+ if self.state in ['confirm', 'validate', 'validate1']:
+ ref_action = self._notification_link_helper('controller', controller='/allocation/refuse')
+ hr_actions += [{'url': ref_action, 'title': _('Refuse')}]
+
+ new_group = (
+ 'group_hr_holidays_user', lambda partner: bool(partner.user_ids) and any(user.has_group('hr_holidays.group_hr_holidays_user') for user in partner.user_ids), {
+ 'actions': hr_actions,
+ })
+
+ return [new_group] + groups
+
+ @api.multi
+ def _message_notification_recipients(self, message, recipients):
+ result = super(HolidaysAllocation, self)._message_notification_recipients(message, recipients)
+ title = _("See Allocation")
+ for res in result:
+ if result[res].get('button_access'):
+ result[res]['button_access']['title'] = title
+ return result
diff --git a/addons/hr_holidays/models/hr_leave_type.py b/addons/hr_holidays/models/hr_leave_type.py
new file mode 100644
index 00000000000..f916000b10d
--- /dev/null
+++ b/addons/hr_holidays/models/hr_leave_type.py
@@ -0,0 +1,147 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+# Copyright (c) 2005-2006 Axelor SARL. (http://www.axelor.com)
+
+import logging
+
+from odoo import api, fields, models
+from odoo.tools.translate import _
+
+_logger = logging.getLogger(__name__)
+
+
+class HolidaysType(models.Model):
+ _name = "hr.leave.type"
+ _description = "Leave Type"
+
+ name = fields.Char('Leave Type', required=True, translate=True)
+ categ_id = fields.Many2one('calendar.event.type', string='Meeting Type',
+ help='Once a leave is validated, Odoo will create a corresponding meeting of this type in the calendar.')
+ color_name = fields.Selection([
+ ('red', 'Red'),
+ ('blue', 'Blue'),
+ ('lightgreen', 'Light Green'),
+ ('lightblue', 'Light Blue'),
+ ('lightyellow', 'Light Yellow'),
+ ('magenta', 'Magenta'),
+ ('lightcyan', 'Light Cyan'),
+ ('black', 'Black'),
+ ('lightpink', 'Light Pink'),
+ ('brown', 'Brown'),
+ ('violet', 'Violet'),
+ ('lightcoral', 'Light Coral'),
+ ('lightsalmon', 'Light Salmon'),
+ ('lavender', 'Lavender'),
+ ('wheat', 'Wheat'),
+ ('ivory', 'Ivory')], string='Color in Report', required=True, default='red',
+ help='This color will be used in the leaves summary located in Reporting > Leaves by Department.')
+ limit = fields.Boolean('Allow to Override Limit',
+ help='If you select this check box, the system allows the employees to take more leaves '
+ 'than the available ones for this type and will not take them into account for the '
+ '"Remaining Legal Leaves" defined on the employee form.')
+ active = fields.Boolean('Active', default=True,
+ help="If the active field is set to false, it will allow you to hide the leave type without removing it.")
+
+ max_leaves = fields.Float(compute='_compute_leaves', string='Maximum Allowed',
+ help='This value is given by the sum of all leaves requests with a positive value.')
+ leaves_taken = fields.Float(compute='_compute_leaves', string='Leaves Already Taken',
+ help='This value is given by the sum of all leaves requests with a negative value.')
+ remaining_leaves = fields.Float(compute='_compute_leaves', string='Remaining Leaves',
+ help='Maximum Leaves Allowed - Leaves Already Taken')
+ virtual_remaining_leaves = fields.Float(compute='_compute_leaves', string='Virtual Remaining Leaves',
+ help='Maximum Leaves Allowed - Leaves Already Taken - Leaves Waiting Approval')
+
+ double_validation = fields.Boolean(string='Apply Double Validation',
+ help="When selected, the Allocation/Leave Requests for this type require a second validation to be approved.")
+ company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.user.company_id)
+
+ @api.multi
+ def get_days(self, employee_id):
+ # need to use `dict` constructor to create a dict per id
+ result = dict((id, dict(max_leaves=0, leaves_taken=0, remaining_leaves=0, virtual_remaining_leaves=0)) for id in self.ids)
+
+ requests = self.env['hr.leave'].search([
+ ('employee_id', '=', employee_id),
+ ('state', 'in', ['confirm', 'validate1', 'validate']),
+ ('holiday_status_id', 'in', self.ids)
+ ])
+
+ allocations = self.env['hr.leave.allocation'].search([
+ ('employee_id', '=', employee_id),
+ ('state', 'in', ['confirm', 'validate1', 'validate']),
+ ('holiday_status_id', 'in', self.ids)
+ ])
+
+ for request in requests:
+ status_dict = result[request.holiday_status_id.id]
+ status_dict['virtual_remaining_leaves'] -= request.number_of_days_temp
+ if request.state == 'validate':
+ status_dict['leaves_taken'] += request.number_of_days_temp
+ status_dict['remaining_leaves'] -= request.number_of_days_temp
+
+ for allocation in allocations:
+ status_dict = result[allocation.holiday_status_id.id]
+ if allocation.state == 'validate':
+ # note: add only validated allocation even for the virtual
+ # count; otherwise pending then refused allocation allow
+ # the employee to create more leaves than possible
+ status_dict['virtual_remaining_leaves'] += allocation.number_of_days_temp
+ status_dict['max_leaves'] += allocation.number_of_days_temp
+ status_dict['remaining_leaves'] += allocation.number_of_days_temp
+
+ return result
+
+ @api.multi
+ def _compute_leaves(self):
+ data_days = {}
+ if 'employee_id' in self._context:
+ employee_id = self._context['employee_id']
+ else:
+ employee_id = self.env['hr.employee'].search([('user_id', '=', self.env.user.id)], limit=1).id
+
+ if employee_id:
+ data_days = self.get_days(employee_id)
+
+ for holiday_status in self:
+ result = data_days.get(holiday_status.id, {})
+ holiday_status.max_leaves = result.get('max_leaves', 0)
+ holiday_status.leaves_taken = result.get('leaves_taken', 0)
+ holiday_status.remaining_leaves = result.get('remaining_leaves', 0)
+ holiday_status.virtual_remaining_leaves = result.get('virtual_remaining_leaves', 0)
+
+ @api.multi
+ def name_get(self):
+ if not self._context.get('employee_id'):
+ # leave counts is based on employee_id, would be inaccurate if not based on correct employee
+ return super(HolidaysType, self).name_get()
+ res = []
+ for record in self:
+ name = record.name
+ if not record.limit:
+ name = "%(name)s (%(count)s)" % {
+ 'name': name,
+ 'count': _('%g remaining out of %g') % (record.virtual_remaining_leaves or 0.0, record.max_leaves or 0.0)
+ }
+ res.append((record.id, name))
+ return res
+
+ @api.model
+ def _search(self, args, offset=0, limit=None, order=None, count=False, access_rights_uid=None):
+ """ Override _search to order the results, according to some employee.
+ The order is the following
+
+ - limit (limited leaves first, such as Legal Leaves)
+ - virtual remaining leaves (higher the better, so using reverse on sorted)
+
+ This override is necessary because those fields are not stored and depends
+ on an employee_id given in context. This sort will be done when there
+ is an employee_id in context and that no other order has been given
+ to the method.
+ """
+ leave_ids = super(HolidaysType, self)._search(args, offset=offset, limit=limit, order=order, count=count, access_rights_uid=access_rights_uid)
+ if not count and not order and self._context.get('employee_id'):
+ leaves = self.browse(leave_ids)
+ sort_key = lambda l: (not l.limit, l.virtual_remaining_leaves)
+ return leaves.sorted(key=sort_key, reverse=True).ids
+ return leave_ids
diff --git a/addons/hr_holidays/report/__init__.py b/addons/hr_holidays/report/__init__.py
index 69b717e618b..4dcb9db9075 100644
--- a/addons/hr_holidays/report/__init__.py
+++ b/addons/hr_holidays/report/__init__.py
@@ -2,4 +2,4 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import holidays_summary_report
-from . import report_all_leaves
+from . import hr_leave_report
diff --git a/addons/hr_holidays/report/holidays_summary_report.py b/addons/hr_holidays/report/holidays_summary_report.py
index 1520bffb99f..1532f49e0f3 100644
--- a/addons/hr_holidays/report/holidays_summary_report.py
+++ b/addons/hr_holidays/report/holidays_summary_report.py
@@ -112,7 +112,6 @@ class HrHolidaySummaryReport(models.AbstractModel):
raise UserError(_("Form content is missing, this report cannot be printed."))
holidays_report = self.env['ir.actions.report']._get_report_from_name('hr_holidays.report_holidayssummary')
- # TODO: See if that is the correct type
holidays = self.env['hr.leave'].browse(self.ids)
return {
'doc_ids': self.ids,
diff --git a/addons/hr_holidays/report/hr_holidays_views.xml b/addons/hr_holidays/report/hr_holidays_views.xml
deleted file mode 100644
index d12f7e2bfba..00000000000
--- a/addons/hr_holidays/report/hr_holidays_views.xml
+++ /dev/null
@@ -1,215 +0,0 @@
-<?xml version="1.0"?>
-<odoo>
- <record id="view_holiday_pivot" model="ir.ui.view">
- <field name="name">hr.holidays.report_pivot</field>
- <field name="model">hr.leave.report</field>
- <field name="priority">20</field>
- <field name="arch" type="xml">
- <pivot string="Leaves Summary">
- <field name="employee_id" type="row"/>
- <field name="date_from" type="col"/>
- <field name="number_of_days" type="measure"/>
- </pivot>
- </field>
- </record>
-
- <record id="view_holiday_graph" model="ir.ui.view">
- <field name="name">hr.holidays.report_graph</field>
- <field name="model">hr.leave.report</field>
- <field name="priority">20</field>
- <field name="arch" type="xml">
- <graph string="Leaves Summary">
- <field name="employee_id"/>
- <field name="number_of_days" type="measure"/>
- </graph>
- </field>
- </record>
-
- <record id="view_holiday_calendar" model="ir.ui.view">
- <field name="name">hr.holidays.report.calendar</field>
- <field name="model">hr.leave.report</field>
- <field name="arch" type="xml">
- <calendar date_start="date_from" date_stop="date_to" mode="month" quick_add="False" color="employee_id">
- <field name="display_name"/>
- </calendar>
- </field>
- </record>
-
- <record id="action_hr_available_holidays_report" model="ir.actions.act_window">
- <field name="name">Leaves Analysis</field>
- <field name="res_model">hr.leave.report</field>
- <field name="view_type">form</field>
- <field name="view_mode">graph,pivot,calendar,form</field>
- <field name="view_id"></field> <!-- force empty -->
- <field name="context">{'search_default_leaves':1}</field>
- <field name="search_view_id" ref="view_hr_holidays_filter"/>
- </record>
-
- <record id="action_window_leave_pivot" model="ir.actions.act_window.view">
- <field name="sequence" eval="2"/>
- <field name="view_mode">pivot</field>
- <field name="view_id" ref="view_holiday_pivot"/>
- <field name="act_window_id" ref="action_hr_available_holidays_report"/>
- </record>
-
- <record id="action_window_leave_graph" model="ir.actions.act_window.view">
- <field name="sequence" eval="1"/>
- <field name="view_mode">graph</field>
- <field name="view_id" ref="view_holiday_graph"/>
- <field name="act_window_id" ref="action_hr_available_holidays_report"/>
- </record>
-
- <menuitem
- name="Leaves Analysis"
- id="menu_hr_available_holidays_report_tree"
- action="action_hr_available_holidays_report"
- parent="menu_hr_holidays_report" sequence="5"/>
-
- <record id="view_hr_holidays_filter_report" model="ir.ui.view">
- <field name="name">hr.holidays.filter</field>
- <field name="model">hr.leave.report</field>
- <field name="arch" type="xml">
- <search string="Search Leave">
- <field name="name"/>
- <filter domain="[('state','in',('confirm','validate1'))]" string="To Approve" name="approve"/>
- <filter string="Approved Requests" domain="[('state', '=', 'validate')]" name="validated"/>
- <separator/>
- <filter name="active_types" string="Active Types" domain="[('holiday_status_id.active', '=', True)]" help="Filters only on requests that belong to an leave type that is 'active' (active field is True)"/>
- <separator/>
- <filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
- <separator/>
- <filter string="My Department Leaves" name="department" domain="[('department_id.manager_id.user_id', '=', uid)]" help="My Department Leaves"/>
- <filter name="my_team_leaves" string="My Team Leaves" domain="[('employee_id.parent_id.user_id', '=', uid)]" groups="hr_holidays.group_hr_holidays_manager" help="Leaves of Your Team Member"/>
- <separator/>
- <filter string="To Report in Payslip" name="gray" domain="[('payslip_status', '=', False)]" groups="hr_holidays.group_hr_holidays_manager"/>
- <separator/>
- <filter string="To Do" name="todo" domain="[('payslip_status', '=', False), ('holiday_status_id.active', '=', True)]"/>
- <separator/>
- <filter name="year" string="Current Year"
- domain="[('holiday_status_id.active', '=', True)]" help="Active Leaves"/>
- <separator/>
- <filter string="My Requests" name="my_leaves" domain="[('employee_id.user_id', '=', uid)]"/>
- <separator/>
- <field name="employee_id"/>
- <field name="department_id"/>
- <field name="holiday_status_id"/>
- <group expand="0" string="Group By">
- <filter name="group_name" string="Description" context="{'group_by':'name'}"/>
- <filter name="group_employee" string="Employee" context="{'group_by':'employee_id'}"/>
- <filter name="group_category" string="Category" context="{'group_by':'category_id'}"/>
- <filter name="group_manager" string="Manager" context="{'group_by':'manager_id'}"/>
- <filter name="group_second_approver_id" string="Validator" context="{'group_by':'second_approver_id'}"/>
- <filter name="group_type" string="Type" context="{'group_by':'holiday_status_id'}"/>
- <separator/>
- <filter name="group_date_from" string="Start Month" context="{'group_by':'date_from'}"/>
- </group>
- </search>
- </field>
- </record>
-
- <record id="hr_leave_report_tree" model="ir.ui.view">
- <field name="name">report.hr.holidays.report.leave_all.tree</field>
- <field name="model">hr.leave.report</field>
- <field name="arch" type="xml">
- <tree create="0" edit="0" delete="0">
- <field name="type"/>
- <field name="name"/>
- <field name="number_of_days" string="Number of Days" sum="Remaining Days"/>
- <field name="date_from"/>
- <field name="date_to"/>
- <field name="state"/>
- </tree>
- </field>
- </record>
-
- <!-- TODO: See if we need to keep this -->
- <record id="hr_leave_report_kanban" model="ir.ui.view">
- <field name="name">report.hr.holidays.report.leave_all.kanban</field>
- <field name="model">hr.leave.report</field>
- <field name="arch" type="xml">
- <kanban class="o_kanban_mobile" create="0">
- <field name="employee_id"/>
- <field name="date_from"/>
- <field name="date_to"/>
- <field name="name"/>
- <field name="number_of_days"/>
- <templates>
- <t t-name="kanban-box">
- <div t-attf-class="oe_kanban_global_click">
- <div>
- <span>
- <img t-att-src="kanban_image('hr.employee', 'image_small', record.employee_id.raw_value)" t-att-title="record.employee_id.value" width="40" height="40" class="oe_kanban_avatar pull-left mr4"/>
- </span>
- <span>
- <div>
- <strong class="o_kanban_record_title"><t t-esc="record.employee_id.value"/></strong>
- <span class="pull-right">
- <field name="state" widget="kanban_label_selection" options="{'classes': {'draft': 'default', 'validate': 'success','confirm': 'default', 'cancel': 'danger'}}"/>
- </span>
- </div>
- <div class="text-muted o_kanban_record_subtitle">
- <t t-esc="record.name.value"/>
- </div>
- </span>
- </div>
- <hr class="mt4 mb8"/>
- <div class="o_kanban_record_bottom mt8 mb4">
- <div t-attf-class="oe_kanban_bottom_left #{record.date_from.value ? 'mt8 mb4': ''}">
- <table class="text-right" t-if="record.date_from.value">
- <tr>
- <td style="padding-bottom:4px"><small class="text-muted">from</small></td>
- <td style="padding:0 0 4px 4px"><t t-esc="record.date_from.value"/></td>
- </tr>
- <tr>
- <td><small class="text-muted">to</small></td>
- <td style="padding-left:4px"><t t-esc="record.date_to.value"/></td>
- </tr>
- </table>
- </div>
- <div t-attf-class="oe_kanban_bottom_right #{record.date_from.value ? 'mt8': ''}">
- <span class="badge"><t t-esc="record.number_of_days.value"/> days</span>
- </div>
- </div>
- </div>
- </t>
- </templates>
- </kanban>
- </field>
- </record>
-
- <record id="open_company_allocation" model="ir.actions.act_window">
- <field name="name">Leaves Summary</field>
- <field name="res_model">hr.leave.report</field>
- <field name="src_model">hr.employee</field>
- <field name="view_type">form</field>
- <field name="view_mode">tree,kanban</field>
- <field name="view_id" eval="hr_leave_report_tree"/>
- <field name="context">{
- 'search_default_group_type': 1,
- 'search_default_year': 1 ,
- 'search_default_my_leaves': 1,
- 'needaction_menu_ref':
- [
- 'hr_holidays.menu_open_ask_holidays_new',
- 'hr_holidays.menu_open_allocation_holidays',
- 'hr_holidays.menu_request_approve_allocation',
- 'hr_holidays.menu_open_employee_leave',
- ]
- }</field>
- <field name="help" type="html">
- <p class="oe_view_nocontent_create">
- Click here to create a new leave request.
- </p>
- </field>
- <field name="domain">[('holiday_type','=','employee')]</field>
- <field name="search_view_id" ref="view_hr_holidays_filter_report"/>
- </record>
-
- <menuitem
- id="menu_open_company_allocation"
- name="Leaves Summary"
- parent="menu_hr_holidays_my_leaves"
- action="open_company_allocation"
- sequence="21"/>
-
-</odoo>
diff --git a/addons/hr_holidays/report/report_all_leaves.py b/addons/hr_holidays/report/hr_leave_report.py
index acca821f58a..acca821f58a 100644
--- a/addons/hr_holidays/report/report_all_leaves.py
+++ b/addons/hr_holidays/report/hr_leave_report.py
diff --git a/addons/hr_holidays/report/hr_leave_reports.xml b/addons/hr_holidays/report/hr_leave_reports.xml
new file mode 100644
index 00000000000..8439ad12e7a
--- /dev/null
+++ b/addons/hr_holidays/report/hr_leave_reports.xml
@@ -0,0 +1,164 @@
+<?xml version="1.0"?>
+<odoo>
+
+ <record id="view_hr_holidays_filter_report" model="ir.ui.view">
+ <field name="name">hr.holidays.filter</field>
+ <field name="model">hr.leave.report</field>
+ <field name="arch" type="xml">
+ <search string="Search Leave">
+ <field name="name"/>
+ <filter domain="[('state','in',('confirm','validate1'))]" string="To Approve" name="approve"/>
+ <filter string="Approved Requests" domain="[('state', '=', 'validate')]" name="validated"/>
+ <separator/>
+ <filter name="active_types" string="Active Types" domain="[('holiday_status_id.active', '=', True)]" help="Filters only on requests that belong to an leave type that is 'active' (active field is True)"/>
+ <separator/>
+ <filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
+ <separator/>
+ <filter string="My Department Leaves" name="department" domain="[('department_id.manager_id.user_id', '=', uid)]" help="My Department Leaves"/>
+ <filter name="my_team_leaves" string="My Team Leaves" domain="[('employee_id.parent_id.user_id', '=', uid)]" groups="hr_holidays.group_hr_holidays_manager" help="Leaves of Your Team Member"/>
+ <separator/>
+ <filter string="To Report in Payslip" name="gray" domain="[('payslip_status', '=', False)]" groups="hr_holidays.group_hr_holidays_manager"/>
+ <separator/>
+ <filter string="To Do" name="todo" domain="[('payslip_status', '=', False), ('holiday_status_id.active', '=', True)]"/>
+ <separator/>
+ <filter name="year" string="Current Year"
+ domain="[('holiday_status_id.active', '=', True)]" help="Active Leaves"/>
+ <separator/>
+ <filter string="My Requests" name="my_leaves" domain="[('employee_id.user_id', '=', uid)]"/>
+ <separator/>
+ <field name="employee_id"/>
+ <field name="department_id"/>
+ <field name="holiday_status_id"/>
+ <group expand="0" string="Group By">
+ <filter name="group_name" string="Description" context="{'group_by':'name'}"/>
+ <filter name="group_employee" string="Employee" context="{'group_by':'employee_id'}"/>
+ <filter name="group_category" string="Category" context="{'group_by':'category_id'}"/>
+ <filter name="group_manager" string="Manager" context="{'group_by':'manager_id'}"/>
+ <filter name="group_second_approver_id" string="Validator" context="{'group_by':'second_approver_id'}"/>
+ <filter name="group_type" string="Type" context="{'group_by':'holiday_status_id'}"/>
+ <separator/>
+ <filter name="group_date_from" string="Start Month" context="{'group_by':'date_from'}"/>
+ </group>
+ </search>
+ </field>
+ </record>
+
+ <record id="hr_leave_report_tree" model="ir.ui.view">
+ <field name="name">report.hr.holidays.report.leave_all.tree</field>
+ <field name="model">hr.leave.report</field>
+ <field name="arch" type="xml">
+ <tree create="0" edit="0" delete="0">
+ <field name="type"/>
+ <field name="name"/>
+ <field name="number_of_days" string="Number of Days" sum="Remaining Days"/>
+ <field name="date_from"/>
+ <field name="date_to"/>
+ <field name="state"/>
+ </tree>
+ </field>
+ </record>
+
+ <!-- TODO: See if we need to keep this -->
+ <record id="hr_leave_report_kanban" model="ir.ui.view">
+ <field name="name">report.hr.holidays.report.leave_all.kanban</field>
+ <field name="model">hr.leave.report</field>
+ <field name="arch" type="xml">
+ <kanban class="o_kanban_mobile" create="0">
+ <field name="employee_id"/>
+ <field name="date_from"/>
+ <field name="date_to"/>
+ <field name="name"/>
+ <field name="number_of_days"/>
+ <templates>
+ <t t-name="kanban-box">
+ <div t-attf-class="oe_kanban_global_click">
+ <div>
+ <span>
+ <img t-att-src="kanban_image('hr.employee', 'image_small', record.employee_id.raw_value)" t-att-title="record.employee_id.value" width="40" height="40" class="oe_kanban_avatar pull-left mr4"/>
+ </span>
+ <span>
+ <div>
+ <strong class="o_kanban_record_title"><t t-esc="record.employee_id.value"/></strong>
+ <span class="pull-right">
+ <field name="state" widget="kanban_label_selection" options="{'classes': {'draft': 'default', 'validate': 'success','confirm': 'default', 'cancel': 'danger'}}"/>
+ </span>
+ </div>
+ <div class="text-muted o_kanban_record_subtitle">
+ <t t-esc="record.name.value"/>
+ </div>
+ </span>
+ </div>
+ <hr class="mt4 mb8"/>
+ <div class="o_kanban_record_bottom mt8 mb4">
+ <div t-attf-class="oe_kanban_bottom_left #{record.date_from.value ? 'mt8 mb4': ''}">
+ <table class="text-right" t-if="record.date_from.value">
+ <tr>
+ <td style="padding-bottom:4px"><small class="text-muted">from</small></td>
+ <td style="padding:0 0 4px 4px"><t t-esc="record.date_from.value"/></td>
+ </tr>
+ <tr>
+ <td><small class="text-muted">to</small></td>
+ <td style="padding-left:4px"><t t-esc="record.date_to.value"/></td>
+ </tr>
+ </table>
+ </div>
+ <div t-attf-class="oe_kanban_bottom_right #{record.date_from.value ? 'mt8': ''}">
+ <span class="badge"><t t-esc="record.number_of_days.value"/> days</span>
+ </div>
+ </div>
+ </div>
+ </t>
+ </templates>
+ </kanban>
+ </field>
+ </record>
+
+ <record id="open_company_allocation" model="ir.actions.act_window">
+ <field name="name">Leaves Summary</field>
+ <field name="res_model">hr.leave.report</field>
+ <field name="src_model">hr.employee</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,kanban</field>
+ <field name="view_id" eval="hr_leave_report_tree"/>
+ <field name="context">{
+ 'search_default_group_type': 1,
+ 'search_default_year': 1 ,
+ 'search_default_my_leaves': 1,
+ 'needaction_menu_ref':
+ [
+ 'hr_holidays.menu_open_ask_holidays_new',
+ 'hr_holidays.menu_open_allocation_holidays',
+ 'hr_holidays.menu_request_approve_allocation',
+ 'hr_holidays.menu_open_employee_leave',
+ ]
+ }</field>
+ <field name="help" type="html">
+ <p class="oe_view_nocontent_create">
+ Click here to create a new leave request.
+ </p>
+ </field>
+ <field name="domain">[('holiday_type','=','employee')]</field>
+ <field name="search_view_id" ref="view_hr_holidays_filter_report"/>
+ </record>
+
+ <record id="act_hr_employee_holiday_request" model="ir.actions.act_window">
+ <field name="name">Leaves</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">hr.leave.report</field>
+ <field name="src_model">hr.employee</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ <field name="context">{'search_default_employee_id': [active_id], 'default_employee_id': active_id, 'search_default_group_type': 1,
+ 'search_default_year': 1}</field>
+ <field name="domain">[('holiday_type','=','employee'), ('holiday_status_id.limit', '=', False), ('state','!=', 'refuse')]</field>
+ <field name="search_view_id" ref="view_hr_leave_allocation_filter"/>
+ </record>
+
+ <menuitem
+ id="menu_open_company_allocation"
+ name="Leaves Summary"
+ parent="menu_hr_holidays_my_leaves"
+ action="open_company_allocation"
+ sequence="21"/>
+
+</odoo>
diff --git a/addons/hr_holidays/views/hr_holidays_views.xml b/addons/hr_holidays/views/hr_holidays_views.xml
deleted file mode 100644
index 6a6f22ef263..00000000000
--- a/addons/hr_holidays/views/hr_holidays_views.xml
+++ /dev/null
@@ -1,815 +0,0 @@
-<?xml version='1.0' encoding='UTF-8' ?>
-<odoo>
-
- <!-- Graph report -->
- <record id="view_evaluation_report_graph" model="ir.ui.view">
- <field name="name">hr.holidays.graph</field>
- <field name="model">hr.leave</field>
- <field name="arch" type="xml">
- <graph string="Appraisal Analysis" stacked="True">
- <field name="employee_id" type="row"/>
- <field name="holiday_status_id" type="row"/>
- <field name="date_from" type="col"/>
- <field name="number_of_days" type="measure"/>
- </graph>
- </field>
- </record>
-
- <record id="view_hr_holidays_filter_allocations" model="ir.ui.view">
- <field name="name">hr.holidays.filter_allocations</field>
- <field name="model">hr.leave.allocation</field>
- <field name="arch" type="xml">
- <search string="Search allocations">
- <field name="name"/>
- <filter domain="[('state','in',('confirm', 'validate1'))]" string="To Approve" name="approve"/>
- <filter string="Approved Allocations" domain="[('state', '=', 'validate')]" name="validated"/>
- <separator/>
- <filter name="active_types" string="Active Types" domain="[('holiday_status_id.active', '=', True)]" help="Filters only on allocations that belong to an leave type that is 'active' (active field is True)"/>
- <separator/>
- <filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
- <separator/>
- <filter string="My Department Leaves" name="department" domain="[('department_id.manager_id.user_id', '=', uid)]" help="My Department Leaves"/>
- <filter name="my_team_leaves" string="My Team Leaves" domain="[('employee_id.parent_id.user_id', '=', uid)]" groups="hr_holidays.group_hr_holidays_manager" help="Leaves of Your Team Member"/>
- <separator/>
- <filter name="year" string="Current Year"
- domain="[('holiday_status_id.active', '=', True)]" help="Active Allocations"/>
- <separator/>
- <filter string="My Allocations" name="my_leaves" domain="[('employee_id.user_id', '=', uid)]"/>
- <separator/>
- <field name="employee_id"/>
- <field name="department_id"/>
- <field name="holiday_status_id"/>
- <group expand="0" string="Group By">
- <filter name="group_name" string="Description" context="{'group_by':'name'}"/>
- <filter name="group_employee" string="Employee" context="{'group_by':'employee_id'}"/>
- <filter name="group_category" string="Category" context="{'group_by':'category_id'}"/>
- <filter name="group_second_approver_id" string="Validator" context="{'group_by':'second_approver_id'}"/>
- <filter name="group_type" string="Type" context="{'group_by':'holiday_status_id'}"/>
- </group>
- </search>
- </field>
- </record>
-
- <record id="view_hr_holidays_filter" model="ir.ui.view">
- <field name="name">hr.holidays.filter</field>
- <field name="model">hr.leave</field>
- <field name="arch" type="xml">
- <search string="Search Leave">
- <field name="name"/>
- <filter domain="[('state','in',('confirm','validate1'))]" string="To Approve" name="approve"/>
- <filter string="Approved Leaves" domain="[('state', '=', 'validate')]" name="validated"/>
- <separator/>
- <filter name="active_types" string="Active Types" domain="[('holiday_status_id.active', '=', True)]" help="Filters only on requests that belong to an leave type that is 'active' (active field is True)"/>
- <separator/>
- <filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
- <separator/>
- <filter string="My Department Leaves" name="department" domain="[('department_id.manager_id.user_id', '=', uid)]" help="My Department Leaves"/>
- <filter name="my_team_leaves" string="My Team Leaves" domain="[('employee_id.parent_id.user_id', '=', uid)]" groups="hr_holidays.group_hr_holidays_manager" help="Leaves of Your Team Member"/>
- <separator/>
- <filter string="To Report in Payslip" name="gray" domain="[('payslip_status', '=', False)]" groups="hr_holidays.group_hr_holidays_manager"/>
- <separator/>
- <filter string="To Do" name="todo" domain="[('payslip_status', '=', False), ('holiday_status_id.active', '=', True)]"/>
- <separator/>
- <filter name="year" string="Current Year"
- domain="[('holiday_status_id.active', '=', True)]" help="Active Leaves"/>
- <separator/>
- <filter string="My Leaves" name="my_leaves" domain="[('employee_id.user_id', '=', uid)]"/>
- <separator/>
- <field name="employee_id"/>
- <field name="department_id"/>
- <field name="holiday_status_id"/>
- <group expand="0" string="Group By">
- <filter name="group_name" string="Description" context="{'group_by':'name'}"/>
- <filter name="group_employee" string="Employee" context="{'group_by':'employee_id'}"/>
- <filter name="group_category" string="Category" context="{'group_by':'category_id'}"/>
- <filter name="group_manager" string="Manager" context="{'group_by':'manager_id'}"/>
- <filter name="group_second_approver_id" string="Validator" context="{'group_by':'second_approver_id'}"/>
- <filter name="group_type" string="Type" context="{'group_by':'holiday_status_id'}"/>
- <separator/>
- <filter name="group_date_from" string="Start Month" context="{'group_by':'date_from'}"/>
- </group>
- </search>
- </field>
- </record>
-
- <!-- Holidays: Leave Request -->
-
- <record id="view_holiday_new_calendar" model="ir.ui.view">
- <field name="name">Leave Request</field>
- <field name="model">hr.leave</field>
- <field name="arch" type="xml">
- <calendar string="Leave Request" date_start="date_from" date_stop="date_to" mode="month" quick_add="False" color="employee_id">
- <field name="display_name"/>
- </calendar>
- </field>
- </record>
-
- <record id="edit_holiday_new_allocation" model="ir.ui.view">
- <field name="name">Allocation Request</field>
- <field name="model">hr.leave.allocation</field>
- <field name="arch" type="xml">
- <form string="Allocation Request">
- <field name="can_reset" invisible="1"/>
- <header>
- <button string="Confirm" name="action_confirm" states="draft" type="object" class="oe_highlight"/>
- <button string="Approve" name="action_approve" states="confirm" type="object" groups="hr_holidays.group_hr_holidays_user" class="oe_highlight"/>
- <button string="Validate" name="action_validate" states="validate1" type="object" groups="hr_holidays.group_hr_holidays_manager" class="oe_highlight"/>
- <button string="Refuse" name="action_refuse" states="confirm,validate,validate1" type="object" groups="hr_holidays.group_hr_holidays_user"/>
- <button string="Reset to Draft" name="action_draft" type="object"
- attrs="{'invisible': ['|', ('can_reset', '=', False), ('state', 'not in', ['confirm', 'refuse'])]}"/>
- <field name="state" widget="statusbar" statusbar_visible="draft,confirm,validate"/>
- </header>
- <sheet>
- <div class="oe_title">
- <h1><field name="display_name"/></h1>
- </div>
- <group>
- <group>
- <field name="name" attrs="{'readonly':[('state','!=','draft'),('state','!=','confirm')]}"/>
- <field name="holiday_status_id" context="{'employee_id':employee_id}"/>
- <label for="number_of_days_temp" string="Duration"/>
- <div>
- <div>
- <field name="number_of_days_temp" class="oe_inline"/> days
- </div>
- </div>
- </group>
- <group>
- <field name="holiday_type" string="Mode" groups="hr_holidays.group_hr_holidays_user" context="{'employee_id':employee_id}" />
- <field name="employee_id" attrs="{'required':[('holiday_type','=','employee')],'invisible':[('holiday_type','!=','employee')]}" groups="hr_holidays.group_hr_holidays_user"/>
- <field name="category_id" attrs="{'required':[('holiday_type','=','category')], 'invisible':[('holiday_type','!=','category')]}"/>
- <field name="department_id" groups="hr_holidays.group_hr_holidays_user" attrs="{'invisible':[('holiday_type','=','category')]}"/>
- </group>
- <field name="notes" nolabel="1" colspan="4" placeholder="Add a reason..."/>
- </group>
- </sheet>
- <div class="oe_chatter">
- <field name="message_follower_ids" widget="mail_followers"/>
- <field name="message_ids" widget="mail_thread"/>
- </div>
- </form>
- </field>
- </record>
-
- <record id="edit_holiday_new_request" model="ir.ui.view">
- <field name="name">Leave Request</field>
- <field name="model">hr.leave</field>
- <field name="arch" type="xml">
- <form string="Leave Request">
- <field name="can_reset" invisible="1"/>
- <header>
- <button string="Confirm" name="action_confirm" states="draft" type="object" class="oe_highlight"/>
- <button string="Approve" name="action_approve" states="confirm" type="object" groups="hr_holidays.group_hr_holidays_user" class="oe_highlight"/>
- <button string="Validate" name="action_validate" states="validate1" type="object" groups="hr_holidays.group_hr_holidays_manager" class="oe_highlight"/>
- <button string="Refuse" name="action_refuse" states="confirm,validate,validate1" type="object" groups="hr_holidays.group_hr_holidays_user"/>
- <button string="Reset to Draft" name="action_draft" type="object"
- attrs="{'invisible': ['|', ('can_reset', '=', False), ('state', 'not in', ['confirm', 'refuse'])]}"/>
- <field name="state" widget="statusbar" statusbar_visible="draft,confirm,validate"/>
- </header>
- <sheet>
- <div class="oe_title">
- <h1><field name="display_name"/></h1>
- </div>
- <group>
- <group>
- <field name="name" attrs="{'readonly':[('state','!=','draft'),('state','!=','confirm')]}"/>
- <field name="holiday_status_id" context="{'employee_id':employee_id}"/>
- <label for="number_of_days_temp" string="Duration"/>
- <div>
- <div>
- <field name="date_from" class="oe_inline"/>
- <label string="-" class="oe_inline"/>
- <field name="date_to" class="oe_inline"/>
- </div>
- <div>
- <field name="number_of_days_temp" class="oe_inline"/> days
- </div>
- </div>
- </group>
- <group>
- <field name="holiday_type" attrs="{'readonly':[('state','!=','draft')]}" string="Mode" groups="hr_holidays.group_hr_holidays_user" context="{'employee_id':employee_id}" />
- <field name="employee_id" attrs="{'required':[('holiday_type','=','employee')],'invisible':[('holiday_type','!=','employee')]}" groups="hr_holidays.group_hr_holidays_user"/>
- <field name="category_id" attrs="{'required':[('holiday_type','=','category')], 'readonly': [('state','!=','draft'), ('state','!=','confirm')], 'invisible':[('holiday_type','!=','category')]}"/>
- <field name="department_id" groups="hr_holidays.group_hr_holidays_user" attrs="{'readonly':['|',('state','=', 'validate'),('holiday_type','!=','department')],'invisible':[('holiday_type','=','category')]}"/>
- <field name="payslip_status" groups="hr_holidays.group_hr_holidays_manager" widget="toggle_button"/>
- </group>
- <div groups="hr_holidays.group_hr_holidays_manager">
- <separator string="Comment by Manager"/>
- <field name="report_note" placeholder="e.g. Report to the next month..."/>
- </div>
- </group>
- </sheet>
- <div class="oe_chatter">
- <field name="message_follower_ids" widget="mail_followers"/>
- <field name="message_ids" widget="mail_thread"/>
- </div>
- </form>
- </field>
- </record>
-
- <record id="view_holiday_allocation_tree" model="ir.ui.view">
- <field name="name">hr.leave.allocation.tree</field>
- <field name="model">hr.leave.allocation</field>
- <field name="arch" type="xml">
- <tree decoration-danger="state == 'refuse'" decoration-info="state == 'draft'" string="Allocation Requests"
- decoration-bf="message_needaction == True">
- <field name="employee_id"/>
- <field name="department_id" invisible="1"/>
- <field name="holiday_status_id"/>
- <field name="name"/>
- <field name="number_of_days" string="Allocated Days" sum="Remaining Days"/>
- <field name="message_needaction" invisible="1"/>
- <field name="state"/>
- </tree>
- </field>
- </record>
-
- <record id="hr_holidays_view_list_my_allocation" model="ir.ui.view">
- <field name="name">hr.holidays.my.allocation.tree</field>
- <field name="model">hr.leave.allocation</field>
- <field name="arch" type="xml">
- <tree decoration-danger="state == 'refuse'" decoration-info="state == 'draft'" string="Allocation Requests"
- decoration-bf="message_needaction == True">
- <field name="employee_id"/>
- <field name="holiday_status_id"/>
- <field name="name"/>
- <field name="number_of_days" string="Allocated Days" sum="Remaining Days"/>
- <field name="message_needaction" invisible="1"/>
- <field name="state"/>
- </tree>
- </field>
- </record>
-
- <record id="view_holiday_allocation_tree_customize" model="ir.ui.view">
- <field name="name">hr.leave.allocation.tree.customize</field>
- <field name="model">hr.leave.allocation</field>
- <field name="arch" type="xml">
- <tree string="Allocation Requests" editable="top">
- <field name="employee_id"/>
- <field name="holiday_type"/>
- <field name="holiday_status_id"/>
- <field name="name" readonly="1"/>
- <field name="number_of_days_temp" string="Allocated Days" sum="Remaining Days"/>
- <field name="state"/>
- </tree>
- </field>
- </record>
-
- <record id="view_holiday_simple" model="ir.ui.view">
- <field name="name">hr.holidays.report_tree</field>
- <field name="model">hr.leave.allocation</field>
- <field name="priority">20</field>
- <field name="arch" type="xml">
- <tree decoration-danger="state == 'refuse'" decoration-info="state == 'draft'" string="Leaves Summary">
- <field name="employee_id"/>
- <field name="category_id" invisible="1"/>
- <field name="department_id" invisible="1"/>
- <field name="name"/>
- <field name="number_of_days" string="Number of Days" sum="Remaining Days"/>
- <field name="holiday_status_id"/>
- <field name="state"/>
- </tree>
- </field>
- </record>
-
- <record id="view_hr_holidays_kanban" model="ir.ui.view">
- <field name="name">hr.holidays.kanban</field>
- <field name="model">hr.leave.allocation</field>
- <field name="arch" type="xml">
- <kanban class="o_kanban_mobile">
- <field name="employee_id"/>
- <field name="name"/>
- <field name="number_of_days"/>
- <templates>
- <t t-name="kanban-box">
- <div t-attf-class="oe_kanban_global_click">
- <div>
- <span>
- <img t-att-src="kanban_image('hr.employee', 'image_small', record.employee_id.raw_value)" t-att-title="record.employee_id.value" width="40" height="40" class="oe_kanban_avatar pull-left mr4"/>
- </span>
- <span>
- <div>
- <strong class="o_kanban_record_title"><t t-esc="record.employee_id.value"/></strong>
- <span class="pull-right">
- <field name="state" widget="kanban_label_selection" options="{'classes': {'draft': 'default', 'validate': 'success','confirm': 'default', 'cancel': 'danger'}}"/>
- </span>
- </div>
- <div class="text-muted o_kanban_record_subtitle">
- <t t-esc="record.name.value"/>
- </div>
- </span>
- </div>
- <hr class="mt4 mb8"/>
- <div class="o_kanban_record_bottom mt8 mb4">
- <div t-attf-class="oe_kanban_bottom_left">
- </div>
- <div t-attf-class="oe_kanban_bottom_right">
- <span class="badge"><t t-esc="record.number_of_days.value"/> days</span>
- </div>
- </div>
- </div>
- </t>
- </templates>
- </kanban>
- </field>
- </record>
-
- <record id="view_holiday" model="ir.ui.view">
- <field name="name">hr.holidays.tree</field>
- <field name="model">hr.leave</field>
- <field name="arch" type="xml">
- <tree decoration-danger="state == 'refuse'" decoration-info="state == ' draft'" string="Leave Requests"
- decoration-bf="message_needaction == True">
- <field name="employee_id"/>
- <field name="holiday_type" string="Mode" groups="base.group_no_one"/>
- <field name="holiday_status_id"/>
- <field name="name"/>
- <field name="date_from"/>
- <field name="date_to"/>
- <field name="number_of_days" string="Number of Days" sum="Remaining Days"/>
- <field name="state"/>
- <field name="payslip_status" widget="toggle_button" options='{"active": "Reported in last payslips", "inactive": "To Report in Payslip"}' groups="hr_holidays.group_hr_holidays_manager"/>
- <field name="category_id" invisible="1"/>
- <field name="department_id" invisible="not context.get('set_visible',False)"/>
- <field name="manager_id" invisible="1"/>
- <field name="user_id" invisible="1"/>
- <field name="message_needaction" invisible="1"/>
- </tree>
- </field>
- </record>
-
- <record id="action_hr_holidays_dashboard" model="ir.actions.act_window">
- <field name="name">All Leaves</field>
- <field name="res_model">hr.leave</field>
- <field name="view_type">form</field>
- <field name="view_mode">calendar</field>
- <field name="context">{'search_default_year': 1, 'search_default_group_employee': 1}</field>
- <field name="domain">[('holiday_type','=','employee'), ('state', '!=', 'refuse')]</field>
- <field name="search_view_id" ref="view_hr_holidays_filter"/>
- </record>
-
- <menuitem
- name="Leaves"
- id="menu_hr_holidays_root"
- sequence="95"
- web_icon="hr_holidays,static/description/icon.png"
- groups="base.group_user"/>
-
- <menuitem
- id="menu_hr_holidays_dashboard"
- name="Dashboard"
- parent="menu_hr_holidays_root"
- sequence="1"
- action="action_hr_holidays_dashboard"
- groups="hr_holidays.group_hr_holidays_manager"/>
-
- <menuitem
- id="menu_hr_holidays_my_leaves"
- name="My Leaves"
- parent="menu_hr_holidays_root"
- sequence="2"/>
-
- <menuitem
- id="menu_hr_holidays_approvals"
- name="Managers"
- parent="menu_hr_holidays_root"
- sequence="3"
- groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"/>
-
- <menuitem
- id="menu_hr_holidays_report"
- name="Reporting"
- parent="menu_hr_holidays_root"
- sequence="99"
- groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"/>
-
- <record id="open_ask_holidays" model="ir.actions.act_window">
- <field name="name">Leaves Request</field>
- <field name="res_model">hr.leave</field>
- <field name="view_type">form</field>
- <field name="view_id" ref="edit_holiday_new_request"/>
- <field name="domain">[('user_id', '=', uid)]</field>
- <field name="search_view_id" ref="view_hr_holidays_filter"/>
- <field name="help" type="html">
- <p class="oe_view_nocontent_create">
- Click to create a new leave request.
- </p><p>
- Once you have recorded your leave request, it will be sent
- to a manager for validation. Be sure to set the right leave
- type (recuperation, legal leaves, sickness) and the exact
- number of open days related to your leave.
- </p>
- </field>
- </record>
-
- <record id="action_open_ask_holidays_tree" model="ir.actions.act_window.view">
- <field name="sequence" eval="20"/>
- <field name="view_mode">tree</field>
- <field name="view_id" ref="view_holiday"/>
- <field name="act_window_id" ref="open_ask_holidays"/>
- </record>
-
- <record id="action_open_ask_holidays_form" model="ir.actions.act_window.view">
- <field name="sequence" eval="3"/>
- <field name="view_mode">form</field>
- <field name="view_id" ref="edit_holiday_new_request"/>
- <field name="act_window_id" ref="open_ask_holidays"/>
- </record>
-
- <record id="action_open_ask_holidays_calendar" model="ir.actions.act_window.view">
- <field name="sequence" eval="1"/>
- <field name="view_mode">calendar</field>
- <field name="view_id" ref="view_holiday_new_calendar"/>
- <field name="act_window_id" ref="open_ask_holidays"/>
- </record>
-
- <menuitem
- id="menu_open_ask_holidays_new"
- parent="menu_hr_holidays_my_leaves"
- action="open_ask_holidays"
- sequence="22"/>
-
- <record model="ir.actions.act_window" id="request_approve_holidays">
- <field name="name">Requests to Approve</field>
- <field name="res_model">hr.leave</field>
- <field name="view_type">form</field>
- <field name="context">{
- 'search_default_approve':1,
- }</field>
- <field name="view_id" ref="edit_holiday_new_allocation"/>
- <field name="search_view_id" ref="view_hr_holidays_filter"/>
- </record>
-
- <record id="action_request_approve_holidays_tree" model="ir.actions.act_window.view">
- <field name="sequence" eval="1"/>
- <field name="view_mode">tree</field>
- <field name="view_id" ref="view_holiday"/>
- <field name="act_window_id" ref="request_approve_holidays"/>
- </record>
-
- <record id="action_request_approve_holidays_form" model="ir.actions.act_window.view">
- <field name="sequence" eval="2"/>
- <field name="view_mode">form</field>
- <field name="view_id" ref="edit_holiday_new_allocation"/>
- <field name="act_window_id" ref="request_approve_holidays"/>
- </record>
-
- <record id="open_allocation_holidays" model="ir.actions.act_window">
- <field name="name">Allocation Request</field>
- <field name="res_model">hr.leave.allocation</field>
- <field name="view_type">form</field>
- <field name="view_mode">tree,kanban,form</field>
- <field name="help" type="html">
- <p class="oe_view_nocontent_create">
- Click here to create a new leave allocation request.
- </p>
- </field>
- <field name="domain">[('employee_id.user_id', '=', uid)]</field>
- <field name="view_id" ref="edit_holiday_new_allocation"/>
- <field name="search_view_id" ref="view_hr_holidays_filter_allocations"/>
- </record>
-
- <record id="action_open_allocation_holidays_tree" model="ir.actions.act_window.view">
- <field name="sequence" eval="1"/>
- <field name="view_mode">tree</field>
- <field name="view_id" ref="view_holiday_allocation_tree"/>
- <field name="act_window_id" ref="open_allocation_holidays"/>
- </record>
-
- <record id="action_open_allocation_holidays_form" model="ir.actions.act_window.view">
- <field name="sequence" eval="2"/>
- <field name="view_mode">form</field>
- <field name="view_id" ref="edit_holiday_new_allocation"/>
- <field name="act_window_id" ref="open_allocation_holidays"/>
- </record>
-
- <menuitem
- id="menu_open_allocation_holidays"
- parent="menu_hr_holidays_my_leaves"
- action="open_allocation_holidays"
- sequence="30"/>
-
- <record id="request_approve_allocation" model="ir.actions.act_window">
- <field name="name">Allocation Request</field>
- <field name="res_model">hr.leave.allocation</field>
- <field name="view_type">form</field>
- <field name="context">{
- 'search_default_approve':1,
- }</field>
- <field name="view_id" ref="edit_holiday_new_allocation"/>
- <field name="search_view_id" ref="view_hr_holidays_filter_allocations"/>
- </record>
-
- <record id="action_request_approve_allocation_tree" model="ir.actions.act_window.view">
- <field name="sequence" eval="1"/>
- <field name="view_mode">tree</field>
- <field name="view_id" ref="view_holiday_allocation_tree"/>
- <field name="act_window_id" ref="request_approve_allocation"/>
- </record>
-
- <record id="action_request_approve_allocation_form" model="ir.actions.act_window.view">
- <field name="sequence" eval="2"/>
- <field name="view_mode">form</field>
- <field name="view_id" ref="edit_holiday_new_allocation"/>
- <field name="act_window_id" ref="request_approve_allocation"/>
- </record>
-
- <record id="open_employee_leaves" model="ir.actions.act_window">
- <field name="name">Leaves Overview</field>
- <field name="res_model">hr.leave</field>
- <field name="view_type">form</field>
- <field name="view_mode">tree,kanban,form,calendar</field>
- <field name="view_id" eval="view_holiday"/>
- <field name="help" type="html">
- <p class="oe_view_nocontent_create">
- Click here to create a new leave request.
- </p>
- </field>
- <field name="search_view_id" ref="view_hr_holidays_filter"/>
- </record>
-
- <record id="open_department_holidays_approve" model="ir.actions.act_window">
- <field name="name">Department Leaves</field>
- <field name="res_model">hr.leave</field>
- <field name="view_type">form</field>
- <field name="view_mode">tree,form,calendar</field>
- <field name="context">{'search_default_department':1, 'search_default_approve':1}</field>
- <field name="help" type="html">
- <p class="oe_view_nocontent_create">
- Click here to create a new leave request.
- </p>
- </field>
- <field name="search_view_id" ref="view_hr_holidays_filter"/>
- </record>
-
- <menuitem
- id="menu_open_department_leave_approve"
- name="Leaves to Approve"
- parent="menu_hr_holidays_approvals"
- groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"
- action="open_department_holidays_approve"
- sequence="1"/>
-
- <record id="open_department_holidays_allocation_approve" model="ir.actions.act_window">
- <field name="name">Department Leaves Allocation</field>
- <field name="res_model">hr.leave.allocation</field>
- <field name="view_type">form</field>
- <field name="view_mode">tree,form</field>
- <field name="context">{'search_default_department':1, 'search_default_approve':1}</field>
- <field name="help" type="html">
- <p class="oe_view_nocontent_create">
- Click here to create a new leave allocation.
- </p>
- </field>
- <field name="search_view_id" ref="view_hr_holidays_filter_allocations"/>
- </record>
-
- <menuitem
- id="menu_open_department_leave_allocation_approve" groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"
- name="Allocations to Approve"
- parent="menu_hr_holidays_approvals"
- action="open_department_holidays_allocation_approve"
- sequence="2"/>
-
- <menuitem
- id="menu_open_employee_leave"
- parent="menu_hr_holidays_approvals"
- groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"
- action="open_employee_leaves"
- sequence="4"/>
-
- <!-- Holidays status -->
- <record id="view_holidays_status_filter" model="ir.ui.view">
- <field name="name">hr.leave.type.filter</field>
- <field name="model">hr.leave.type</field>
- <field name="arch" type="xml">
- <search string="Search Leave Type">
- <field name="name" string="Leave Types"/>
- <field name="categ_id"/>
- </search>
- </field>
- </record>
-
- <record id="edit_holiday_status_form" model="ir.ui.view">
- <field name="name">hr.leave.type.form</field>
- <field name="model">hr.leave.type</field>
- <field name="arch" type="xml">
- <form string="Leave Type">
- <sheet>
- <div class="oe_button_box" name="button_box">
- <button class="oe_stat_button" type="object" name="toggle_active" icon="fa-archive">
- <field name="active" widget="boolean_button"
- options='{"terminology": "archive"}'/>
- </button>
- </div>
- <group>
- <group name="description" string="Description">
- <field name="name"/>
- <field name="double_validation"/>
- <field name="limit"/>
- </group>
- <group name="options" string="Options">
- <field name="categ_id"/>
- <field name="color_name"/>
- <field name="company_id" groups="base.group_multi_company"/>
- </group>
- </group>
- </sheet>
- </form>
- </field>
- </record>
-
- <record id="view_holiday_status_tree" model="ir.ui.view">
- <field name="name">hr.leave.type.tree</field>
- <field name="model">hr.leave.type</field>
- <field name="arch" type="xml">
- <tree string="Leave Type">
- <field name="name"/>
- <field name="max_leaves"/>
- <field name="leaves_taken"/>
- <field name="remaining_leaves"/>
- </tree>
- </field>
- </record>
-
- <record id="hr_holiday_status_view_kanban" model="ir.ui.view">
- <field name="name">hr.leave.type.kanban</field>
- <field name="model">hr.leave.type</field>
- <field name="arch" type="xml">
- <kanban class="o_kanban_mobile">
- <templates>
- <t t-name="kanban-box">
- <div t-attf-class="oe_kanban_global_click">
- <div>
- <strong><field name="name"/></strong>
- </div>
- <div>
- <span>Max Leaves: <field name="max_leaves"/></span>
- <span class="pull-right">Leaves Taken: <field name="leaves_taken"/></span>
- </div>
- </div>
- </t>
- </templates>
- </kanban>
- </field>
- </record>
-
- <record id="view_holiday_status_normal_tree" model="ir.ui.view">
- <field name="name">hr.leave.type.normal.tree</field>
- <field name="model">hr.leave.type</field>
- <field name="priority">35</field>
- <field name="arch" type="xml">
- <tree string="Leave Type">
- <field name="name"/>
- <field name="limit"/>
- </tree>
- </field>
- </record>
-
- <record id="view_hr_holidays_status_search" model="ir.ui.view">
- <field name="name">hr.leave.type.search</field>
- <field name="model">hr.leave.type</field>
- <field name="arch" type="xml">
- <search string="Leave Type">
- <field name="name" string="Leave Types"/>
- <field name="categ_id"/>
- </search>
- </field>
- </record>
-
- <record id="open_view_holiday_status" model="ir.actions.act_window">
- <field name="name">Leave Types</field>
- <field name="type">ir.actions.act_window</field>
- <field name="res_model">hr.leave.type</field>
- <field name="view_type">form</field>
- <field name="view_mode">tree,kanban,form</field>
- <field name="view_id" eval="view_holiday_status_normal_tree"/>
- <field name="search_view_id" ref="view_hr_holidays_status_search"/>
- </record>
-
- <menuitem
- id="menu_hr_holidays_configuration"
- name="Configuration"
- parent="menu_hr_holidays_root"
- sequence="100"
- groups="hr_holidays.group_hr_holidays_manager"
- action="open_view_holiday_status"/>
-
- <!-- Shortcuts -->
- <record id="act_hr_employee_holiday_request" model="ir.actions.act_window">
- <field name="name">Leaves</field>
- <field name="type">ir.actions.act_window</field>
- <field name="res_model">hr.leave.report</field>
- <field name="src_model">hr.employee</field>
- <field name="view_type">form</field>
- <field name="view_mode">tree,form</field>
- <field name="context">{'search_default_employee_id': [active_id], 'default_employee_id': active_id, 'search_default_group_type': 1,
- 'search_default_year': 1}</field>
- <field name="domain">[('holiday_type','=','employee'), ('holiday_status_id.limit', '=', False), ('state','!=', 'refuse')]</field>
- <field name="search_view_id" ref="view_hr_holidays_filter_allocations"/>
- </record>
-
- <record id="act_hr_employee_holiday_request_approved" model="ir.actions.act_window">
- <field name="name">Leaves to be reported in Payslip</field>
- <field name="type">ir.actions.act_window</field>
- <field name="res_model">hr.leave</field>
- <field name="src_model">hr.employee</field>
- <field name="view_type">form</field>
- <field name="view_mode">tree,form</field>
- <field name="context">{'search_default_employee_id': [active_id], 'search_default_validated': True, 'search_default_gray': True}</field>
- <field name="domain">[('date_from','&gt;=', context_today().strftime("%Y-%m-1")), ('date_from','&lt;', ((context_today() + relativedelta(months=1)).strftime('%Y-%m-1')) )]</field>
- <field name="view_id" eval="view_holiday_allocation_tree_customize"/>
- </record>
-
- <!-- Assing leave -->
- <record id="hr_holidays_leaves_assign_tree_view" model="ir.ui.view">
- <field name="name">hr.employee.leave.tree</field>
- <field name="model">hr.employee</field>
- <field name="priority" eval="20"/>
- <field name="arch" type="xml">
- <tree string="Assign Leaves" editable="bottom">
- <field name="name" string="Employee"/>
- <field name="remaining_leaves" string="Remaining Legal Leaves"/>
- </tree>
- </field>
- </record>
-
- <record id="hr_holidays_leaves_assign_legal" model="ir.actions.act_window">
- <field name="name">Allocate Leaves for Employees</field>
- <field name="type">ir.actions.act_window</field>
- <field name="res_model">hr.employee</field>
- <field name="view_type">form</field>
- <field name="view_mode">tree,form</field>
- <field name="view_id" ref="hr_holidays_leaves_assign_tree_view"/>
- <field name="help" type="html">
- <p>
- You can assign remaining Legal Leaves for each employee, Odoo
- will automatically create and validate allocation requests.
- </p>
- </field>
- </record>
-
-
- <record id="hr_holidays_action_request_from_department" model="ir.actions.act_window">
- <field name="name">Leave Request to Approve</field>
- <field name="res_model">hr.leave</field>
- <field name="view_type">form</field>
- <field name="view_mode">tree,form</field>
- <field name="context">{
- 'search_default_department_id': [active_id],
- 'default_department_id': active_id,
- 'search_default_approve':1}
- </field>
- <field name="view_id" ref="view_holiday"/>
- <field name="search_view_id" ref="view_hr_holidays_filter"/>
- </record>
-
- <record id="hr_holidays_action_allocation_from_department" model="ir.actions.act_window">
- <field name="name">Allocation Requests to Approve</field>
- <field name="res_model">hr.leave.allocation</field>
- <field name="view_type">form</field>
- <field name="view_mode">tree,form</field>
- <field name="context">{
- 'search_default_department_id': [active_id],
- 'default_department_id': active_id,
- 'search_default_approve':1}
- </field>
- <field name="search_view_id" ref="view_hr_holidays_filter_allocations"/>
- </record>
-
- <record id="hr_employee_action_from_department" model="ir.actions.act_window">
- <field name="name">Absent Employees</field>
- <field name="res_model">hr.employee</field>
- <field name="view_type">form</field>
- <field name="view_mode">kanban,tree,form</field>
- <field name="context">{
- 'search_default_is_absent_totay': 1,
- 'search_default_department_id': [active_id],
- 'default_department_id': active_id}
- </field>
- <field name="search_view_id" ref="hr.view_employee_filter"/>
- </record>
-
- <record id="action_hr_holidays_leaves_analysis" model="ir.actions.act_window">
- <field name="name">Leaves Analysis</field>
- <field name="res_model">hr.leave</field>
- <field name="view_type">form</field>
- <field name="view_mode">graph,pivot</field>
- <field name="domain">[('holiday_type','=','employee')]</field>
- <field name="search_view_id" ref="view_hr_holidays_filter"/>
- </record>
-
- <record id="action_hr_holidays_leaves_analysis_filtered" model="ir.actions.act_window">
- <field name="name">Leaves Analysis</field>
- <field name="res_model">hr.leave</field>
- <field name="view_type">form</field>
- <field name="view_mode">graph,pivot</field>
- <field name="domain">[('holiday_type','=','employee')]</field>
- <field name="context">{
- 'search_default_department_id': [active_id],
- 'default_department_id': active_id}
- </field>
- <field name="search_view_id" ref="view_hr_holidays_filter"/>
- </record>
-
-</odoo>
diff --git a/addons/hr_holidays/views/hr_leave_allocation_views.xml b/addons/hr_holidays/views/hr_leave_allocation_views.xml
new file mode 100644
index 00000000000..606c0328136
--- /dev/null
+++ b/addons/hr_holidays/views/hr_leave_allocation_views.xml
@@ -0,0 +1,215 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<odoo>
+
+ <record id="view_hr_leave_allocation_filter" model="ir.ui.view">
+ <field name="name">hr.holidays.filter_allocations</field>
+ <field name="model">hr.leave.allocation</field>
+ <field name="arch" type="xml">
+ <search string="Search allocations">
+ <field name="name"/>
+ <filter domain="[('state','in',('confirm', 'validate1'))]" string="To Approve" name="approve"/>
+ <filter string="Approved Allocations" domain="[('state', '=', 'validate')]" name="validated"/>
+ <separator/>
+ <filter name="active_types" string="Active Types" domain="[('holiday_status_id.active', '=', True)]" help="Filters only on allocations that belong to an leave type that is 'active' (active field is True)"/>
+ <separator/>
+ <filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
+ <separator/>
+ <filter string="My Department Leaves" name="department" domain="[('department_id.manager_id.user_id', '=', uid)]" help="My Department Leaves"/>
+ <filter name="my_team_leaves" string="My Team Leaves" domain="[('employee_id.parent_id.user_id', '=', uid)]" groups="hr_holidays.group_hr_holidays_manager" help="Leaves of Your Team Member"/>
+ <separator/>
+ <filter name="year" string="Current Year"
+ domain="[('holiday_status_id.active', '=', True)]" help="Active Allocations"/>
+ <separator/>
+ <filter string="My Allocations" name="my_leaves" domain="[('employee_id.user_id', '=', uid)]"/>
+ <separator/>
+ <field name="employee_id"/>
+ <field name="department_id"/>
+ <field name="holiday_status_id"/>
+ <group expand="0" string="Group By">
+ <filter name="group_name" string="Description" context="{'group_by':'name'}"/>
+ <filter name="group_employee" string="Employee" context="{'group_by':'employee_id'}"/>
+ <filter name="group_category" string="Category" context="{'group_by':'category_id'}"/>
+ <filter name="group_second_approver_id" string="Validator" context="{'group_by':'second_approver_id'}"/>
+ <filter name="group_type" string="Type" context="{'group_by':'holiday_status_id'}"/>
+ </group>
+ </search>
+ </field>
+ </record>
+
+ <record id="edit_new_allocation" model="ir.ui.view">
+ <field name="name">Allocation Request</field>
+ <field name="model">hr.leave.allocation</field>
+ <field name="arch" type="xml">
+ <form string="Allocation Request">
+ <field name="can_reset" invisible="1"/>
+ <header>
+ <button string="Confirm" name="action_confirm" states="draft" type="object" class="oe_highlight"/>
+ <button string="Approve" name="action_approve" states="confirm" type="object" groups="hr_holidays.group_hr_holidays_user" class="oe_highlight"/>
+ <button string="Validate" name="action_validate" states="validate1" type="object" groups="hr_holidays.group_hr_holidays_manager" class="oe_highlight"/>
+ <button string="Refuse" name="action_refuse" states="confirm,validate,validate1" type="object" groups="hr_holidays.group_hr_holidays_user"/>
+ <button string="Reset to Draft" name="action_draft" type="object"
+ attrs="{'invisible': ['|', ('can_reset', '=', False), ('state', 'not in', ['confirm', 'refuse'])]}"/>
+ <field name="state" widget="statusbar" statusbar_visible="draft,confirm,validate"/>
+ </header>
+ <sheet>
+ <div class="oe_title">
+ <h1><field name="display_name"/></h1>
+ </div>
+ <group>
+ <group>
+ <field name="name" attrs="{'readonly':[('state','!=','draft'),('state','!=','confirm')]}"/>
+ <field name="holiday_status_id" context="{'employee_id':employee_id}"/>
+ <label for="number_of_days_temp" string="Duration"/>
+ <div>
+ <div>
+ <field name="number_of_days_temp" class="oe_inline"/> days
+ </div>
+ </div>
+ </group>
+ <group>
+ <field name="holiday_type" string="Mode" groups="hr_holidays.group_hr_holidays_user" context="{'employee_id':employee_id}" />
+ <field name="employee_id" attrs="{'required':[('holiday_type','=','employee')],'invisible':[('holiday_type','!=','employee')]}" groups="hr_holidays.group_hr_holidays_user"/>
+ <field name="category_id" attrs="{'required':[('holiday_type','=','category')], 'invisible':[('holiday_type','!=','category')]}"/>
+ <field name="department_id" groups="hr_holidays.group_hr_holidays_user" attrs="{'invisible':[('holiday_type','=','category')]}"/>
+ </group>
+ <field name="notes" nolabel="1" colspan="4" placeholder="Add a reason..."/>
+ </group>
+ </sheet>
+ <div class="oe_chatter">
+ <field name="message_follower_ids" widget="mail_followers"/>
+ <field name="message_ids" widget="mail_thread"/>
+ </div>
+ </form>
+ </field>
+ </record>
+
+ <record id="view_allocation_tree" model="ir.ui.view">
+ <field name="name">hr.leave.allocation.tree</field>
+ <field name="model">hr.leave.allocation</field>
+ <field name="arch" type="xml">
+ <tree decoration-danger="state == 'refuse'" decoration-info="state == 'draft'" string="Allocation Requests"
+ decoration-bf="message_needaction == True">
+ <field name="employee_id"/>
+ <field name="department_id" invisible="1"/>
+ <field name="holiday_status_id"/>
+ <field name="name"/>
+ <field name="number_of_days" string="Allocated Days" sum="Remaining Days"/>
+ <field name="message_needaction" invisible="1"/>
+ <field name="state"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="hr_holidays_view_list_my_allocation" model="ir.ui.view">
+ <field name="name">hr.holidays.my.allocation.tree</field>
+ <field name="model">hr.leave.allocation</field>
+ <field name="arch" type="xml">
+ <tree decoration-danger="state == 'refuse'" decoration-info="state == 'draft'" string="Allocation Requests"
+ decoration-bf="message_needaction == True">
+ <field name="employee_id"/>
+ <field name="holiday_status_id"/>
+ <field name="name"/>
+ <field name="number_of_days" string="Allocated Days" sum="Remaining Days"/>
+ <field name="message_needaction" invisible="1"/>
+ <field name="state"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="view_allocation_tree_customize" model="ir.ui.view">
+ <field name="name">hr.leave.allocation.tree.customize</field>
+ <field name="model">hr.leave.allocation</field>
+ <field name="arch" type="xml">
+ <tree string="Allocation Requests" editable="top">
+ <field name="employee_id"/>
+ <field name="holiday_type"/>
+ <field name="holiday_status_id"/>
+ <field name="name" readonly="1"/>
+ <field name="number_of_days_temp" string="Allocated Days" sum="Remaining Days"/>
+ <field name="state"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="open_allocation" model="ir.actions.act_window">
+ <field name="name">Allocation Request</field>
+ <field name="res_model">hr.leave.allocation</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,kanban,form</field>
+ <field name="help" type="html">
+ <p class="oe_view_nocontent_create">
+ Click here to create a new leave allocation request.
+ </p>
+ </field>
+ <field name="domain">[('employee_id.user_id', '=', uid)]</field>
+ <field name="view_id" ref="edit_new_allocation"/>
+ <field name="search_view_id" ref="view_hr_leave_allocation_filter"/>
+ </record>
+
+ <record id="request_approve_allocation" model="ir.actions.act_window">
+ <field name="name">Allocation Request</field>
+ <field name="res_model">hr.leave.allocation</field>
+ <field name="view_type">form</field>
+ <field name="context">{
+ 'search_default_approve':1,
+ }</field>
+ <field name="view_id" ref="edit_new_allocation"/>
+ <field name="search_view_id" ref="view_hr_leave_allocation_filter"/>
+ </record>
+
+ <record id="open_department_holidays_allocation_approve" model="ir.actions.act_window">
+ <field name="name">Department Leaves Allocation</field>
+ <field name="res_model">hr.leave.allocation</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ <field name="context">{'search_default_department':1, 'search_default_approve':1}</field>
+ <field name="help" type="html">
+ <p class="oe_view_nocontent_create">
+ Click here to create a new leave allocation.
+ </p>
+ </field>
+ <field name="search_view_id" ref="view_hr_leave_allocation_filter"/>
+ </record>
+
+ <record id="hr_holidays_action_allocation_from_department" model="ir.actions.act_window">
+ <field name="name">Allocation Requests to Approve</field>
+ <field name="res_model">hr.leave.allocation</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ <field name="context">{
+ 'search_default_department_id': [active_id],
+ 'default_department_id': active_id,
+ 'search_default_approve':1}
+ </field>
+ <field name="search_view_id" ref="view_hr_leave_allocation_filter"/>
+ </record>
+
+ <record id="action_open_allocation_tree" model="ir.actions.act_window.view">
+ <field name="sequence" eval="1"/>
+ <field name="view_mode">tree</field>
+ <field name="view_id" ref="view_allocation_tree"/>
+ <field name="act_window_id" ref="open_allocation"/>
+ </record>
+
+ <record id="action_open_allocation_form" model="ir.actions.act_window.view">
+ <field name="sequence" eval="2"/>
+ <field name="view_mode">form</field>
+ <field name="view_id" ref="edit_new_allocation"/>
+ <field name="act_window_id" ref="open_allocation"/>
+ </record>
+
+ <record id="action_request_approve_allocation_tree" model="ir.actions.act_window.view">
+ <field name="sequence" eval="1"/>
+ <field name="view_mode">tree</field>
+ <field name="view_id" ref="view_allocation_tree"/>
+ <field name="act_window_id" ref="request_approve_allocation"/>
+ </record>
+
+ <record id="action_request_approve_allocation_form" model="ir.actions.act_window.view">
+ <field name="sequence" eval="2"/>
+ <field name="view_mode">form</field>
+ <field name="view_id" ref="edit_new_allocation"/>
+ <field name="act_window_id" ref="request_approve_allocation"/>
+ </record>
+
+</odoo>
diff --git a/addons/hr_holidays/views/hr_leave_type_views.xml b/addons/hr_holidays/views/hr_leave_type_views.xml
new file mode 100644
index 00000000000..da4e973f70a
--- /dev/null
+++ b/addons/hr_holidays/views/hr_leave_type_views.xml
@@ -0,0 +1,113 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<odoo>
+
+ <!-- Holidays status -->
+ <record id="view_holidays_status_filter" model="ir.ui.view">
+ <field name="name">hr.leave.type.filter</field>
+ <field name="model">hr.leave.type</field>
+ <field name="arch" type="xml">
+ <search string="Search Leave Type">
+ <field name="name" string="Leave Types"/>
+ <field name="categ_id"/>
+ </search>
+ </field>
+ </record>
+
+ <record id="edit_holiday_status_form" model="ir.ui.view">
+ <field name="name">hr.leave.type.form</field>
+ <field name="model">hr.leave.type</field>
+ <field name="arch" type="xml">
+ <form string="Leave Type">
+ <sheet>
+ <div class="oe_button_box" name="button_box">
+ <button class="oe_stat_button" type="object" name="toggle_active" icon="fa-archive">
+ <field name="active" widget="boolean_button"
+ options='{"terminology": "archive"}'/>
+ </button>
+ </div>
+ <group>
+ <group name="description" string="Description">
+ <field name="name"/>
+ <field name="double_validation"/>
+ <field name="limit"/>
+ </group>
+ <group name="options" string="Options">
+ <field name="categ_id"/>
+ <field name="color_name"/>
+ <field name="company_id" groups="base.group_multi_company"/>
+ </group>
+ </group>
+ </sheet>
+ </form>
+ </field>
+ </record>
+
+ <record id="view_holiday_status_tree" model="ir.ui.view">
+ <field name="name">hr.leave.type.tree</field>
+ <field name="model">hr.leave.type</field>
+ <field name="arch" type="xml">
+ <tree string="Leave Type">
+ <field name="name"/>
+ <field name="max_leaves"/>
+ <field name="leaves_taken"/>
+ <field name="remaining_leaves"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="hr_holiday_status_view_kanban" model="ir.ui.view">
+ <field name="name">hr.leave.type.kanban</field>
+ <field name="model">hr.leave.type</field>
+ <field name="arch" type="xml">
+ <kanban class="o_kanban_mobile">
+ <templates>
+ <t t-name="kanban-box">
+ <div t-attf-class="oe_kanban_global_click">
+ <div>
+ <strong><field name="name"/></strong>
+ </div>
+ <div>
+ <span>Max Leaves: <field name="max_leaves"/></span>
+ <span class="pull-right">Leaves Taken: <field name="leaves_taken"/></span>
+ </div>
+ </div>
+ </t>
+ </templates>
+ </kanban>
+ </field>
+ </record>
+
+ <record id="view_holiday_status_normal_tree" model="ir.ui.view">
+ <field name="name">hr.leave.type.normal.tree</field>
+ <field name="model">hr.leave.type</field>
+ <field name="priority">35</field>
+ <field name="arch" type="xml">
+ <tree string="Leave Type">
+ <field name="name"/>
+ <field name="limit"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="view_hr_holidays_status_search" model="ir.ui.view">
+ <field name="name">hr.leave.type.search</field>
+ <field name="model">hr.leave.type</field>
+ <field name="arch" type="xml">
+ <search string="Leave Type">
+ <field name="name" string="Leave Types"/>
+ <field name="categ_id"/>
+ </search>
+ </field>
+ </record>
+
+ <record id="open_view_holiday_status" model="ir.actions.act_window">
+ <field name="name">Leave Types</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">hr.leave.type</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,kanban,form</field>
+ <field name="view_id" eval="view_holiday_status_normal_tree"/>
+ <field name="search_view_id" ref="view_hr_holidays_status_search"/>
+ </record>
+
+</odoo>
diff --git a/addons/hr_holidays/views/hr_leave_views.xml b/addons/hr_holidays/views/hr_leave_views.xml
new file mode 100644
index 00000000000..e11c257b9d7
--- /dev/null
+++ b/addons/hr_holidays/views/hr_leave_views.xml
@@ -0,0 +1,437 @@
+<?xml version='1.0' encoding='UTF-8' ?>
+<odoo>
+
+ <!-- Graph report -->
+ <record id="view_evaluation_report_graph" model="ir.ui.view">
+ <field name="name">hr.holidays.graph</field>
+ <field name="model">hr.leave</field>
+ <field name="arch" type="xml">
+ <graph string="Appraisal Analysis" stacked="True">
+ <field name="employee_id" type="row"/>
+ <field name="holiday_status_id" type="row"/>
+ <field name="date_from" type="col"/>
+ <field name="number_of_days" type="measure"/>
+ </graph>
+ </field>
+ </record>
+
+ <record id="view_hr_holidays_filter" model="ir.ui.view">
+ <field name="name">hr.holidays.filter</field>
+ <field name="model">hr.leave</field>
+ <field name="arch" type="xml">
+ <search string="Search Leave">
+ <field name="name"/>
+ <filter domain="[('state','in',('confirm','validate1'))]" string="To Approve" name="approve"/>
+ <filter string="Approved Leaves" domain="[('state', '=', 'validate')]" name="validated"/>
+ <separator/>
+ <filter name="active_types" string="Active Types" domain="[('holiday_status_id.active', '=', True)]" help="Filters only on requests that belong to an leave type that is 'active' (active field is True)"/>
+ <separator/>
+ <filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
+ <separator/>
+ <filter string="My Department Leaves" name="department" domain="[('department_id.manager_id.user_id', '=', uid)]" help="My Department Leaves"/>
+ <filter name="my_team_leaves" string="My Team Leaves" domain="[('employee_id.parent_id.user_id', '=', uid)]" groups="hr_holidays.group_hr_holidays_manager" help="Leaves of Your Team Member"/>
+ <separator/>
+ <filter string="To Report in Payslip" name="gray" domain="[('payslip_status', '=', False)]" groups="hr_holidays.group_hr_holidays_manager"/>
+ <separator/>
+ <filter string="To Do" name="todo" domain="[('payslip_status', '=', False), ('holiday_status_id.active', '=', True)]"/>
+ <separator/>
+ <filter name="year" string="Current Year"
+ domain="[('holiday_status_id.active', '=', True)]" help="Active Leaves"/>
+ <separator/>
+ <filter string="My Leaves" name="my_leaves" domain="[('employee_id.user_id', '=', uid)]"/>
+ <separator/>
+ <field name="employee_id"/>
+ <field name="department_id"/>
+ <field name="holiday_status_id"/>
+ <group expand="0" string="Group By">
+ <filter name="group_name" string="Description" context="{'group_by':'name'}"/>
+ <filter name="group_employee" string="Employee" context="{'group_by':'employee_id'}"/>
+ <filter name="group_category" string="Category" context="{'group_by':'category_id'}"/>
+ <filter name="group_manager" string="Manager" context="{'group_by':'manager_id'}"/>
+ <filter name="group_second_approver_id" string="Validator" context="{'group_by':'second_approver_id'}"/>
+ <filter name="group_type" string="Type" context="{'group_by':'holiday_status_id'}"/>
+ <separator/>
+ <filter name="group_date_from" string="Start Month" context="{'group_by':'date_from'}"/>
+ </group>
+ </search>
+ </field>
+ </record>
+
+ <!-- Holidays: Leave Request -->
+
+ <record id="view_holiday_new_calendar" model="ir.ui.view">
+ <field name="name">Leave Request</field>
+ <field name="model">hr.leave</field>
+ <field name="arch" type="xml">
+ <calendar string="Leave Request" date_start="date_from" date_stop="date_to" mode="month" quick_add="False" color="employee_id">
+ <field name="display_name"/>
+ </calendar>
+ </field>
+ </record>
+
+ <record id="edit_holiday_new_request" model="ir.ui.view">
+ <field name="name">Leave Request</field>
+ <field name="model">hr.leave</field>
+ <field name="arch" type="xml">
+ <form string="Leave Request">
+ <field name="can_reset" invisible="1"/>
+ <header>
+ <button string="Confirm" name="action_confirm" states="draft" type="object" class="oe_highlight"/>
+ <button string="Approve" name="action_approve" states="confirm" type="object" groups="hr_holidays.group_hr_holidays_user" class="oe_highlight"/>
+ <button string="Validate" name="action_validate" states="validate1" type="object" groups="hr_holidays.group_hr_holidays_manager" class="oe_highlight"/>
+ <button string="Refuse" name="action_refuse" states="confirm,validate,validate1" type="object" groups="hr_holidays.group_hr_holidays_user"/>
+ <button string="Reset to Draft" name="action_draft" type="object"
+ attrs="{'invisible': ['|', ('can_reset', '=', False), ('state', 'not in', ['confirm', 'refuse'])]}"/>
+ <field name="state" widget="statusbar" statusbar_visible="draft,confirm,validate"/>
+ </header>
+ <sheet>
+ <div class="oe_title">
+ <h1><field name="display_name"/></h1>
+ </div>
+ <group>
+ <group>
+ <field name="name" attrs="{'readonly':[('state','!=','draft'),('state','!=','confirm')]}"/>
+ <field name="holiday_status_id" context="{'employee_id':employee_id}"/>
+ <label for="number_of_days_temp" string="Duration"/>
+ <div>
+ <div>
+ <field name="date_from" class="oe_inline"/>
+ <label string="-" class="oe_inline"/>
+ <field name="date_to" class="oe_inline"/>
+ </div>
+ <div>
+ <field name="number_of_days_temp" class="oe_inline"/> days
+ </div>
+ </div>
+ </group>
+ <group>
+ <field name="holiday_type" attrs="{'readonly':[('state','!=','draft')]}" string="Mode" groups="hr_holidays.group_hr_holidays_user" context="{'employee_id':employee_id}" />
+ <field name="employee_id" attrs="{'required':[('holiday_type','=','employee')],'invisible':[('holiday_type','!=','employee')]}" groups="hr_holidays.group_hr_holidays_user"/>
+ <field name="category_id" attrs="{'required':[('holiday_type','=','category')], 'readonly': [('state','!=','draft'), ('state','!=','confirm')], 'invisible':[('holiday_type','!=','category')]}"/>
+ <field name="department_id" groups="hr_holidays.group_hr_holidays_user" attrs="{'readonly':['|',('state','=', 'validate'),('holiday_type','!=','department')],'invisible':[('holiday_type','=','category')]}"/>
+ <field name="payslip_status" groups="hr_holidays.group_hr_holidays_manager" widget="toggle_button"/>
+ </group>
+ <div groups="hr_holidays.group_hr_holidays_manager">
+ <separator string="Comment by Manager"/>
+ <field name="report_note" placeholder="e.g. Report to the next month..."/>
+ </div>
+ </group>
+ </sheet>
+ <div class="oe_chatter">
+ <field name="message_follower_ids" widget="mail_followers"/>
+ <field name="message_ids" widget="mail_thread"/>
+ </div>
+ </form>
+ </field>
+ </record>
+
+ <record id="view_holiday" model="ir.ui.view">
+ <field name="name">hr.holidays.tree</field>
+ <field name="model">hr.leave</field>
+ <field name="arch" type="xml">
+ <tree decoration-danger="state == 'refuse'" decoration-info="state == ' draft'" string="Leave Requests"
+ decoration-bf="message_needaction == True">
+ <field name="employee_id"/>
+ <field name="holiday_type" string="Mode" groups="base.group_no_one"/>
+ <field name="holiday_status_id"/>
+ <field name="name"/>
+ <field name="date_from"/>
+ <field name="date_to"/>
+ <field name="number_of_days" string="Number of Days" sum="Remaining Days"/>
+ <field name="state"/>
+ <field name="payslip_status" widget="toggle_button" options='{"active": "Reported in last payslips", "inactive": "To Report in Payslip"}' groups="hr_holidays.group_hr_holidays_manager"/>
+ <field name="category_id" invisible="1"/>
+ <field name="department_id" invisible="not context.get('set_visible',False)"/>
+ <field name="manager_id" invisible="1"/>
+ <field name="user_id" invisible="1"/>
+ <field name="message_needaction" invisible="1"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="action_hr_holidays_dashboard" model="ir.actions.act_window">
+ <field name="name">All Leaves</field>
+ <field name="res_model">hr.leave</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">calendar</field>
+ <field name="context">{'search_default_year': 1, 'search_default_group_employee': 1}</field>
+ <field name="domain">[('holiday_type','=','employee'), ('state', '!=', 'refuse')]</field>
+ <field name="search_view_id" ref="view_hr_holidays_filter"/>
+ </record>
+
+ <menuitem
+ name="Leaves"
+ id="menu_hr_holidays_root"
+ sequence="95"
+ web_icon="hr_holidays,static/description/icon.png"
+ groups="base.group_user"/>
+
+ <menuitem
+ id="menu_hr_holidays_dashboard"
+ name="Dashboard"
+ parent="menu_hr_holidays_root"
+ sequence="1"
+ action="action_hr_holidays_dashboard"
+ groups="hr_holidays.group_hr_holidays_manager"/>
+
+ <menuitem
+ id="menu_hr_holidays_my_leaves"
+ name="My Leaves"
+ parent="menu_hr_holidays_root"
+ sequence="2"/>
+
+ <menuitem
+ id="menu_hr_holidays_approvals"
+ name="Managers"
+ parent="menu_hr_holidays_root"
+ sequence="3"
+ groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"/>
+
+ <menuitem
+ id="menu_hr_holidays_report"
+ name="Reporting"
+ parent="menu_hr_holidays_root"
+ sequence="99"
+ groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"/>
+
+ <record id="open_ask_holidays" model="ir.actions.act_window">
+ <field name="name">Leaves Request</field>
+ <field name="res_model">hr.leave</field>
+ <field name="view_type">form</field>
+ <field name="view_id" ref="edit_holiday_new_request"/>
+ <field name="domain">[('user_id', '=', uid)]</field>
+ <field name="search_view_id" ref="view_hr_holidays_filter"/>
+ <field name="help" type="html">
+ <p class="oe_view_nocontent_create">
+ Click to create a new leave request.
+ </p><p>
+ Once you have recorded your leave request, it will be sent
+ to a manager for validation. Be sure to set the right leave
+ type (recuperation, legal leaves, sickness) and the exact
+ number of open days related to your leave.
+ </p>
+ </field>
+ </record>
+
+ <record id="action_open_ask_holidays_tree" model="ir.actions.act_window.view">
+ <field name="sequence" eval="20"/>
+ <field name="view_mode">tree</field>
+ <field name="view_id" ref="view_holiday"/>
+ <field name="act_window_id" ref="open_ask_holidays"/>
+ </record>
+
+ <record id="action_open_ask_holidays_form" model="ir.actions.act_window.view">
+ <field name="sequence" eval="3"/>
+ <field name="view_mode">form</field>
+ <field name="view_id" ref="edit_holiday_new_request"/>
+ <field name="act_window_id" ref="open_ask_holidays"/>
+ </record>
+
+ <record id="action_open_ask_holidays_calendar" model="ir.actions.act_window.view">
+ <field name="sequence" eval="1"/>
+ <field name="view_mode">calendar</field>
+ <field name="view_id" ref="view_holiday_new_calendar"/>
+ <field name="act_window_id" ref="open_ask_holidays"/>
+ </record>
+
+ <menuitem
+ id="menu_open_ask_holidays_new"
+ parent="menu_hr_holidays_my_leaves"
+ action="open_ask_holidays"
+ sequence="22"/>
+
+ <record model="ir.actions.act_window" id="request_approve_holidays">
+ <field name="name">Requests to Approve</field>
+ <field name="res_model">hr.leave</field>
+ <field name="view_type">form</field>
+ <field name="context">{
+ 'search_default_approve':1,
+ }</field>
+ <field name="view_id" ref="edit_new_allocation"/>
+ <field name="search_view_id" ref="view_hr_holidays_filter"/>
+ </record>
+
+ <record id="action_request_approve_holidays_tree" model="ir.actions.act_window.view">
+ <field name="sequence" eval="1"/>
+ <field name="view_mode">tree</field>
+ <field name="view_id" ref="view_holiday"/>
+ <field name="act_window_id" ref="request_approve_holidays"/>
+ </record>
+
+ <record id="action_request_approve_holidays_form" model="ir.actions.act_window.view">
+ <field name="sequence" eval="2"/>
+ <field name="view_mode">form</field>
+ <field name="view_id" ref="edit_new_allocation"/>
+ <field name="act_window_id" ref="request_approve_holidays"/>
+ </record>
+
+ <record id="open_employee_leaves" model="ir.actions.act_window">
+ <field name="name">Leaves Overview</field>
+ <field name="res_model">hr.leave</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,kanban,form,calendar</field>
+ <field name="view_id" eval="view_holiday"/>
+ <field name="help" type="html">
+ <p class="oe_view_nocontent_create">
+ Click here to create a new leave request.
+ </p>
+ </field>
+ <field name="search_view_id" ref="view_hr_holidays_filter"/>
+ </record>
+
+ <record id="open_department_holidays_approve" model="ir.actions.act_window">
+ <field name="name">Department Leaves</field>
+ <field name="res_model">hr.leave</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form,calendar</field>
+ <field name="context">{'search_default_department':1, 'search_default_approve':1}</field>
+ <field name="help" type="html">
+ <p class="oe_view_nocontent_create">
+ Click here to create a new leave request.
+ </p>
+ </field>
+ <field name="search_view_id" ref="view_hr_holidays_filter"/>
+ </record>
+
+ <menuitem
+ id="menu_open_department_leave_approve"
+ name="Leaves to Approve"
+ parent="menu_hr_holidays_approvals"
+ groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"
+ action="open_department_holidays_approve"
+ sequence="1"/>
+
+ <menuitem
+ id="menu_open_department_leave_allocation_approve" groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"
+ name="Allocations to Approve"
+ parent="menu_hr_holidays_approvals"
+ action="open_department_holidays_allocation_approve"
+ sequence="2"/>
+
+ <menuitem
+ id="menu_open_employee_leave"
+ parent="menu_hr_holidays_approvals"
+ groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"
+ action="open_employee_leaves"
+ sequence="4"/>
+
+ <menuitem
+ id="menu_hr_holidays_configuration"
+ name="Configuration"
+ parent="menu_hr_holidays_root"
+ sequence="100"
+ groups="hr_holidays.group_hr_holidays_manager"
+ action="open_view_holiday_status"/>
+
+ <!-- Shortcuts -->
+ <record id="act_hr_employee_holiday_request_approved" model="ir.actions.act_window">
+ <field name="name">Leaves to be reported in Payslip</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">hr.leave</field>
+ <field name="src_model">hr.employee</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ <field name="context">{'search_default_employee_id': [active_id], 'search_default_validated': True, 'search_default_gray': True}</field>
+ <field name="domain">[('date_from','&gt;=', context_today().strftime("%Y-%m-1")), ('date_from','&lt;', ((context_today() + relativedelta(months=1)).strftime('%Y-%m-1')) )]</field>
+ <field name="view_id" eval="view_allocation_tree_customize"/>
+ </record>
+
+ <!-- Assing leave -->
+ <record id="hr_holidays_action_request_from_department" model="ir.actions.act_window">
+ <field name="name">Leave Request to Approve</field>
+ <field name="res_model">hr.leave</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ <field name="context">{
+ 'search_default_department_id': [active_id],
+ 'default_department_id': active_id,
+ 'search_default_approve':1}
+ </field>
+ <field name="view_id" ref="view_holiday"/>
+ <field name="search_view_id" ref="view_hr_holidays_filter"/>
+ </record>
+
+ <record id="action_hr_holidays_leaves_analysis" model="ir.actions.act_window">
+ <field name="name">Leaves Analysis</field>
+ <field name="res_model">hr.leave</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">graph,pivot</field>
+ <field name="domain">[('holiday_type','=','employee')]</field>
+ <field name="search_view_id" ref="view_hr_holidays_filter"/>
+ </record>
+
+ <record id="action_hr_holidays_leaves_analysis_filtered" model="ir.actions.act_window">
+ <field name="name">Leaves Analysis</field>
+ <field name="res_model">hr.leave</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">graph,pivot</field>
+ <field name="domain">[('holiday_type','=','employee')]</field>
+ <field name="context">{
+ 'search_default_department_id': [active_id],
+ 'default_department_id': active_id}
+ </field>
+ <field name="search_view_id" ref="view_hr_holidays_filter"/>
+ </record>
+
+ <record id="view_holiday_pivot" model="ir.ui.view">
+ <field name="name">hr.holidays.report_pivot</field>
+ <field name="model">hr.leave</field>
+ <field name="priority">20</field>
+ <field name="arch" type="xml">
+ <pivot string="Leaves Summary">
+ <field name="employee_id" type="row"/>
+ <field name="date_from" type="col"/>
+ <field name="number_of_days" type="measure"/>
+ </pivot>
+ </field>
+ </record>
+
+ <record id="view_holiday_graph" model="ir.ui.view">
+ <field name="name">hr.holidays.report_graph</field>
+ <field name="model">hr.leave</field>
+ <field name="priority">20</field>
+ <field name="arch" type="xml">
+ <graph string="Leaves Summary">
+ <field name="employee_id"/>
+ <field name="number_of_days" type="measure"/>
+ </graph>
+ </field>
+ </record>
+
+ <record id="action_hr_available_holidays_report" model="ir.actions.act_window">
+ <field name="name">Leaves Analysis</field>
+ <field name="res_model">hr.leave</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">graph,pivot,calendar,form</field>
+ <field name="view_id"></field> <!-- force empty -->
+ <field name="context">{'search_default_leaves':1}</field>
+ <field name="search_view_id" ref="view_hr_holidays_filter"/>
+ </record>
+
+ <record id="action_window_leave_pivot" model="ir.actions.act_window.view">
+ <field name="sequence" eval="2"/>
+ <field name="view_mode">pivot</field>
+ <field name="view_id" ref="view_holiday_pivot"/>
+ <field name="act_window_id" ref="action_hr_available_holidays_report"/>
+ </record>
+
+ <record id="action_window_leave_graph" model="ir.actions.act_window.view">
+ <field name="sequence" eval="1"/>
+ <field name="view_mode">graph</field>
+ <field name="view_id" ref="view_holiday_graph"/>
+ <field name="act_window_id" ref="action_hr_available_holidays_report"/>
+ </record>
+
+ <menuitem
+ name="Leaves Analysis"
+ id="menu_hr_available_holidays_report_tree"
+ action="action_hr_available_holidays_report"
+ parent="menu_hr_holidays_report" sequence="5"/>
+
+ <menuitem
+ id="menu_open_allocation"
+ parent="menu_hr_holidays_my_leaves"
+ action="open_allocation"
+ sequence="30"/>
+
+</odoo>
diff --git a/addons/hr_holidays/views/hr_views.xml b/addons/hr_holidays/views/hr_views.xml
index 90b54965535..159681154d3 100644
--- a/addons/hr_holidays/views/hr_views.xml
+++ b/addons/hr_holidays/views/hr_views.xml
@@ -1,5 +1,59 @@
<?xml version='1.0' encoding='UTF-8'?>
<odoo>
+
+ <record id="act_hr_employee_holiday_request" model="ir.actions.act_window">
+ <field name="name">Leaves</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">hr.leave.report</field>
+ <field name="src_model">hr.employee</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ <field name="context">{'search_default_employee_id': [active_id], 'default_employee_id': active_id, 'search_default_group_type': 1,
+ 'search_default_year': 1}</field>
+ <field name="domain">[('holiday_type','=','employee'), ('holiday_status_id.limit', '=', False), ('state','!=', 'refuse')]</field>
+ <field name="search_view_id" ref="view_hr_leave_allocation_filter"/>
+ </record>
+
+ <record id="hr_holidays_leaves_assign_tree_view" model="ir.ui.view">
+ <field name="name">hr.employee.leave.tree</field>
+ <field name="model">hr.employee</field>
+ <field name="priority" eval="20"/>
+ <field name="arch" type="xml">
+ <tree string="Assign Leaves" editable="bottom">
+ <field name="name" string="Employee"/>
+ <field name="remaining_leaves" string="Remaining Legal Leaves"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="hr_holidays_leaves_assign_legal" model="ir.actions.act_window">
+ <field name="name">Allocate Leaves for Employees</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">hr.employee</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">tree,form</field>
+ <field name="view_id" ref="hr_holidays_leaves_assign_tree_view"/>
+ <field name="help" type="html">
+ <p>
+ You can assign remaining Legal Leaves for each employee, Odoo
+ will automatically create and validate allocation requests.
+ </p>
+ </field>
+ </record>
+
+ <record id="hr_employee_action_from_department" model="ir.actions.act_window">
+ <field name="name">Absent Employees</field>
+ <field name="res_model">hr.employee</field>
+ <field name="view_type">form</field>
+ <field name="view_mode">kanban,tree,form</field>
+ <field name="context">{
+ 'search_default_is_absent_totay': 1,
+ 'search_default_department_id': [active_id],
+ 'default_department_id': active_id}
+ </field>
+ <field name="search_view_id" ref="hr.view_employee_filter"/>
+ </record>
+
<!--Hr Department Inherit Kanban view-->
<record id="hr_department_view_kanban" model="ir.ui.view">
<field name="name">hr.department.kanban.inherit</field>