[MERGE] hr_holidays: split model hr.holidays and clean code

* Remove unused 'Remaining Leaves' report
* Split hr.holidays into hr.leave and hr.leave.allocation
* Split/Rename models/views according to guidelines
* Remove 'only-used-once' inline methods
This commit is contained in:
RomainLibert
2018-01-15 10:40:01 +01:00
32 changed files with 1923 additions and 1385 deletions
+4 -3
View File
@@ -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,8 +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_leaves_report_views.xml',
'report/hr_holidays_views.xml',
'report/hr_leave_reports.xml',
],
'demo': [
'data/hr_holidays_demo.xml',
+26 -6
View File
@@ -7,9 +7,9 @@ from odoo import http
class HrHolidaysController(http.Controller):
@http.route('/hr_holidays/validate', type='http', auth='user', methods=['GET'])
def hr_holidays_validate(self, res_id, token):
comparison, record, redirect = MailController._check_token_and_record_or_redirect('hr.holidays', int(res_id), token)
@http.route('/leave/validate', type='http', auth='user', methods=['GET'])
def hr_holidays_request_validate(self, res_id, token):
comparison, record, redirect = MailController._check_token_and_record_or_redirect('hr.leave', int(res_id), token)
if comparison and record:
try:
record.action_approve()
@@ -17,9 +17,29 @@ class HrHolidaysController(http.Controller):
return MailController._redirect_to_messaging()
return redirect
@http.route('/hr_holidays/refuse', type='http', auth='user', methods=['GET'])
def hr_holidays_refuse(self, res_id, token):
comparison, record, redirect = MailController._check_token_and_record_or_redirect('hr.holidays', int(res_id), token)
@http.route('/leave/refuse', type='http', auth='user', methods=['GET'])
def hr_holidays_request_refuse(self, res_id, token):
comparison, record, redirect = MailController._check_token_and_record_or_redirect('hr.leave', int(res_id), token)
if comparison and record:
try:
record.action_refuse()
except Exception:
return MailController._redirect_to_messaging()
return redirect
@http.route('/allocation/validate', type='http', auth='user', methods=['GET'])
def hr_holidays_allocation_validate(self, res_id, token):
comparison, record, redirect = MailController._check_token_and_record_or_redirect('hr.leave.allocation', int(res_id), token)
if comparison and record:
try:
record.action_approve()
except Exception:
return MailController._redirect_to_messaging()
return redirect
@http.route('/allocation/refuse', type='http', auth='user', methods=['GET'])
def hr_holidays_allocation_refuse(self, res_id, token):
comparison, record, redirect = MailController._check_token_and_record_or_redirect('hr.leave.allocation', int(res_id), token)
if comparison and record:
try:
record.action_refuse()
+87 -24
View File
@@ -3,93 +3,156 @@
<data noupdate="1">
<!-- Casual leave -->
<record id="holiday_status_cl" model="hr.holidays.status">
<record id="holiday_status_cl" model="hr.leave.type">
<field name="name" eval="'Legal Leaves '+time.strftime('%Y')"/>
<field name="limit" eval="False"/>
<field name="color_name">black</field>
</record>
<!-- Sick leave -->
<record id="holiday_status_sl" model="hr.holidays.status">
<record id="holiday_status_sl" model="hr.leave.type">
<field name="name">Sick Leaves</field>
<field name="limit" eval="True"/>
<field name="color_name">red</field>
</record>
<!-- Compensatory Days -->
<record id="holiday_status_comp" model="hr.holidays.status">
<record id="holiday_status_comp" model="hr.leave.type">
<field name="name">Compensatory Days</field>
<field name="limit" eval="False"/>
<field name="color_name">lavender</field>
</record>
<!--Unpaid Leave -->
<record id="holiday_status_unpaid" model="hr.holidays.status">
<record id="holiday_status_unpaid" model="hr.leave.type">
<field name="name">Unpaid</field>
<field name="limit" eval="True"/>
<field name="color_name">brown</field>
</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.holidays</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.holidays</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.holidays</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.holidays</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_holidays_confirmed')"/>
<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_holidays_first_validated" model="mail.message.subtype">
<field name="name">Leaves/Allocations First Approval</field>
<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_holidays_first_validated')"/>
<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_holidays_approved" model="mail.message.subtype">
<field name="name">Leaves/Allocation Approved</field>
<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_holidays_approved')"/>
<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_holidays_refused" model="mail.message.subtype">
<field name="name">Leaves/Allocation Refused</field>
<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_holidays_refused')"/>
<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_leave_allocation_confirmed')"/>
<field name="relation_field">department_id</field>
<field name="sequence" eval="6"/>
</record>
<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_leave_allocation_first_validated')"/>
<field name="relation_field">department_id</field>
<field name="sequence" eval="7"/>
</record>
<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_leave_allocation_approved')"/>
<field name="relation_field">department_id</field>
<field name="sequence" eval="8"/>
</record>
<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_leave_allocation_refused')"/>
<field name="relation_field">department_id</field>
<field name="sequence" eval="9"/>
</record>
+7 -16
View File
@@ -2,62 +2,53 @@
<odoo>
<data noupdate="1">
<record id="hr_holidays_employee1_allocation_cl" model="hr.holidays">
<record id="hr_holidays_employee1_allocation_cl" model="hr.leave.allocation">
<field name="name">Legal Leaves for Peter Parker</field>
<field name="holiday_status_id" ref="holiday_status_cl"/>
<field name="type">add</field>
<field name="number_of_days_temp">20</field>
<field name="employee_id" ref="hr.employee_root"/>
<field name="state">confirm</field>
</record>
<record id="hr_holidays_employee1_int_tour" model="hr.holidays">
<record id="hr_holidays_employee1_int_tour" model="hr.leave.allocation">
<field name="name">International Tour</field>
<field name="holiday_status_id" ref="holiday_status_comp"/>
<field eval="time.strftime('%Y-%m-26')" name="date_from"/>
<field eval="time.strftime('%Y-%m-28')" name="date_to"/>
<field name="type">add</field>
<field name="number_of_days_temp">7</field>
<field name="employee_id" ref="hr.employee_root"/>
<field name="state">confirm</field>
</record>
<record id="hr_holidays_employee1_vc" model="hr.holidays">
<record id="hr_holidays_employee1_vc" model="hr.leave.allocation">
<field name="name">Summer Vacation</field>
<field name="holiday_status_id" ref="holiday_status_unpaid"/>
<field eval="time.strftime('%Y-%m-23')" name="date_from"/>
<field eval="time.strftime('%Y-%m-25')" name="date_to"/>
<field name="type">add</field>
<field name="number_of_days_temp">7</field>
<field name="employee_id" ref="hr.employee_root"/>
</record>
<!-- approve the first 2 leave allocations -->
<function model="hr.holidays" name="action_approve" eval="[[ref('hr_holidays.hr_holidays_employee1_allocation_cl'), ref('hr_holidays.hr_holidays_employee1_int_tour')]]"/>
<function model="hr.leave.allocation" name="action_approve" eval="[[ref('hr_holidays.hr_holidays_employee1_allocation_cl'), ref('hr_holidays.hr_holidays_employee1_int_tour')]]"/>
<record id="hr_holidays_employee1_cl" model="hr.holidays">
<record id="hr_holidays_employee1_cl" model="hr.leave">
<field name="name">Trip with Family</field>
<field name="holiday_status_id" ref="holiday_status_comp"/>
<field eval="time.strftime('%Y-%m-01')" name="date_from"/>
<field eval="time.strftime('%Y-%m-03')" name="date_to"/>
<field name="type">remove</field>
<field name="number_of_days_temp">3</field>
<field name="employee_id" ref="hr.employee_root"/>
</record>
<record id="hr_holidays_employee1_sl" model="hr.holidays">
<record id="hr_holidays_employee1_sl" model="hr.leave">
<field name="name">Doctor Appointment</field>
<field name="holiday_status_id" ref="holiday_status_sl"/>
<field eval="time.strftime('%Y-%m-20')" name="date_from"/>
<field eval="time.strftime('%Y-%m-22')" name="date_to"/>
<field name="type">remove</field>
<field name="number_of_days_temp">3</field>
<field name="employee_id" ref="hr.employee_root"/>
<field name="state">confirm</field>
</record>
<!-- approve the leave request -->
<function model="hr.holidays" name="action_approve" eval="[[ref('hr_holidays.hr_holidays_employee1_sl')]]"/>
<function model="hr.leave" name="action_approve" eval="[[ref('hr_holidays.hr_holidays_employee1_sl')]]"/>
</data>
</odoo>
+3 -1
View File
@@ -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
+28 -23
View File
@@ -23,22 +23,23 @@ class Department(models.Model):
@api.multi
def _compute_leave_count(self):
Holiday = self.env['hr.holidays']
Requests = self.env['hr.leave']
Allocations = self.env['hr.leave.allocation']
today_date = datetime.datetime.utcnow().date()
today_start = fields.Datetime.to_string(today_date) # get the midnight of the current utc day
today_end = fields.Datetime.to_string(today_date + relativedelta(hours=23, minutes=59, seconds=59))
leave_data = Holiday.read_group(
leave_data = Requests.read_group(
[('department_id', 'in', self.ids),
('state', '=', 'confirm'), ('type', '=', 'remove')],
('state', '=', 'confirm')],
['department_id'], ['department_id'])
allocation_data = Holiday.read_group(
allocation_data = Allocations.read_group(
[('department_id', 'in', self.ids),
('state', '=', 'confirm'), ('type', '=', 'add')],
('state', '=', 'confirm')],
['department_id'], ['department_id'])
absence_data = Holiday.read_group(
absence_data = Requests.read_group(
[('department_id', 'in', self.ids), ('state', 'not in', ['cancel', 'refuse']),
('date_from', '<=', today_end), ('date_to', '>=', today_start), ('type', '=', 'remove')],
('date_from', '<=', today_end), ('date_to', '>=', today_start)],
['department_id'], ['department_id'])
res_leave = dict((data['department_id'][0], data['department_id_count']) for data in leave_data)
@@ -74,7 +75,7 @@ class Employee(models.Model):
('validate', 'Approved'),
('cancel', 'Cancelled')
])
current_leave_id = fields.Many2one('hr.holidays.status', compute='_compute_leave_status', string="Current Leave Type")
current_leave_id = fields.Many2one('hr.leave.type', compute='_compute_leave_status', string="Current Leave Type")
leave_date_from = fields.Date('From Date', compute='_compute_leave_status')
leave_date_to = fields.Date('To Date', compute='_compute_leave_status')
leaves_count = fields.Float('Number of Leaves', compute='_compute_leaves_count')
@@ -90,8 +91,16 @@ class Employee(models.Model):
sum(h.number_of_days) AS days,
h.employee_id
FROM
hr_holidays h
join hr_holidays_status s ON (s.id=h.holiday_status_id)
(
SELECT holiday_status_id, number_of_days,
state, employee_id
FROM hr_leave_allocation
UNION
SELECT holiday_status_id, number_of_days,
state, employee_id
FROM hr_leave
) h
join hr_leave_type s ON (s.id=h.holiday_status_id)
WHERE
h.state='validate' AND
s.limit=False AND
@@ -107,7 +116,7 @@ class Employee(models.Model):
@api.multi
def _inverse_remaining_leaves(self):
status_list = self.env['hr.holidays.status'].search([('limit', '=', False)])
status_list = self.env['hr.leave.type'].search([('limit', '=', False)])
# Create leaves (adding remaining leaves) or raise (reducing remaining leaves)
actual_remaining = self._get_remaining_leaves()
for employee in self.filtered(lambda employee: employee.remaining_leaves):
@@ -125,11 +134,10 @@ class Employee(models.Model):
# if a status is found, then compute remaing leave for current employee
difference = employee.remaining_leaves - actual_remaining.get(employee.id, 0)
if difference > 0:
leave = self.env['hr.holidays'].create({
leave = self.env['hr.leave.allocation'].create({
'name': _('Allocation for %s') % employee.name,
'employee_id': employee.id,
'holiday_status_id': status.id,
'type': 'add',
'holiday_type': 'employee',
'number_of_days_temp': difference
})
@@ -142,11 +150,10 @@ class Employee(models.Model):
@api.multi
def _compute_leave_status(self):
# Used SUPERUSER_ID to forcefully get status of other user's leave, to bypass record rule
holidays = self.env['hr.holidays'].sudo().search([
holidays = self.env['hr.leave'].sudo().search([
('employee_id', 'in', self.ids),
('date_from', '<=', fields.Datetime.now()),
('date_to', '>=', fields.Datetime.now()),
('type', '=', 'remove'),
('state', 'not in', ('cancel', 'refuse'))
])
leave_data = {}
@@ -165,12 +172,12 @@ class Employee(models.Model):
@api.multi
def _compute_leaves_count(self):
leaves = self.env['hr.holidays'].read_group([
all_leaves = self.env['hr.leave.report'].read_group([
('employee_id', 'in', self.ids),
('holiday_status_id.limit', '=', False),
('state', '=', 'validate')
], fields=['number_of_days', 'employee_id'], groupby=['employee_id'])
mapping = dict([(leave['employee_id'][0], leave['number_of_days']) for leave in leaves])
mapping = dict([(leave['employee_id'][0], leave['number_of_days']) for leave in all_leaves])
for employee in self:
employee.leaves_count = mapping.get(employee.id)
@@ -188,12 +195,11 @@ class Employee(models.Model):
today_date = datetime.datetime.utcnow().date()
today_start = fields.Datetime.to_string(today_date) # get the midnight of the current utc day
today_end = fields.Datetime.to_string(today_date + relativedelta(hours=23, minutes=59, seconds=59))
data = self.env['hr.holidays'].read_group([
data = self.env['hr.leave'].read_group([
('employee_id', 'in', self.ids),
('state', 'not in', ['cancel', 'refuse']),
('date_from', '<=', today_end),
('date_to', '>=', today_start),
('type', '=', 'remove')
('date_to', '>=', today_start)
], ['employee_id'], ['employee_id'])
result = dict.fromkeys(self.ids, False)
for item in data:
@@ -207,11 +213,10 @@ class Employee(models.Model):
today_date = datetime.datetime.utcnow().date()
today_start = fields.Datetime.to_string(today_date) # get the midnight of the current utc day
today_end = fields.Datetime.to_string(today_date + relativedelta(hours=23, minutes=59, seconds=59))
holidays = self.env['hr.holidays'].sudo().search([
holidays = self.env['hr.leave'].sudo().search([
('employee_id', '!=', False),
('state', 'not in', ['cancel', 'refuse']),
('date_from', '<=', today_end),
('date_to', '>=', today_start),
('type', '=', 'remove')
('date_to', '>=', today_start)
])
return [('id', 'in', holidays.mapped('employee_id').ids)]
@@ -17,139 +17,10 @@ _logger = logging.getLogger(__name__)
HOURS_PER_DAY = 8
class HolidaysType(models.Model):
_name = "hr.holidays.status"
_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)
holidays = self.env['hr.holidays'].search([
('employee_id', '=', employee_id),
('state', 'in', ['confirm', 'validate1', 'validate']),
('holiday_status_id', 'in', self.ids)
])
for holiday in holidays:
status_dict = result[holiday.holiday_status_id.id]
if holiday.type == 'add':
if holiday.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'] += holiday.number_of_days_temp
status_dict['max_leaves'] += holiday.number_of_days_temp
status_dict['remaining_leaves'] += holiday.number_of_days_temp
elif holiday.type == 'remove': # number of days is negative
status_dict['virtual_remaining_leaves'] -= holiday.number_of_days_temp
if holiday.state == 'validate':
status_dict['leaves_taken'] += holiday.number_of_days_temp
status_dict['remaining_leaves'] -= holiday.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 Holidays(models.Model):
_name = "hr.holidays"
class HolidaysRequest(models.Model):
_name = "hr.leave"
_description = "Leave"
_order = "type desc, date_from desc"
_order = "date_from desc"
_inherit = ['mail.thread']
def _default_employee(self):
@@ -172,11 +43,11 @@ class Holidays(models.Model):
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,
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,
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.holidays.status", string="Leave Type", required=True, readonly=True,
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')
@@ -188,15 +59,9 @@ class Holidays(models.Model):
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')
type = fields.Selection([
('remove', 'Leave Request'),
('add', 'Allocation Request')
], string='Request Type', required=True, readonly=True, index=True, track_visibility='always', default='remove',
states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]},
help="Choose 'Leave Request' if someone wants to take an off-day. "
"\nChoose 'Allocation Request' if you want to increase the number of leaves available for someone")
parent_id = fields.Many2one('hr.holidays', string='Parent')
linked_request_ids = fields.One2many('hr.holidays', 'parent_id', string='Linked Requests')
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')
@@ -214,14 +79,18 @@ class Holidays(models.Model):
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', 'type')
@api.depends('number_of_days_temp')
def _compute_number_of_days(self):
for holiday in self:
if holiday.type == 'remove':
holiday.number_of_days = -holiday.number_of_days_temp
else:
holiday.number_of_days = holiday.number_of_days_temp
holiday.number_of_days = -holiday.number_of_days_temp
@api.multi
def _compute_can_reset(self):
@@ -234,39 +103,6 @@ class Holidays(models.Model):
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),
('type', '=', holiday.type),
('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 holiday.type != 'remove' 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 ( (type='add') OR (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:
@@ -285,18 +121,6 @@ class Holidays(models.Model):
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
@@ -328,6 +152,43 @@ class Holidays(models.Model):
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
####################################################
@@ -336,20 +197,12 @@ class Holidays(models.Model):
def name_get(self):
res = []
for leave in self:
if leave.type == 'remove':
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)))
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, _("Allocation of %s : %.2f day(s) To %s") % (leave.holiday_status_id.name, leave.number_of_days_temp, leave.employee_id.name)))
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
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)
@@ -360,20 +213,16 @@ class Holidays(models.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(Holidays, self.with_context(mail_create_nolog=True, mail_create_nosubscribe=True)).create(values)
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)
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(Holidays, self).write(values)
result = super(HolidaysRequest, self).write(values)
self.add_follower(employee_id)
return result
@@ -381,7 +230,7 @@ class Holidays(models.Model):
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(Holidays, self).unlink()
return super(HolidaysRequest, self).unlink()
@api.multi
def copy_data(self, default=None):
@@ -410,105 +259,10 @@ class Holidays(models.Model):
""" 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'})
@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,
'type': self.type,
'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' and holiday.type == 'remove':
holiday._validate_leave_request()
elif holiday.holiday_type in ['category', 'department']:
leaves = self.env['hr.holidays']
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' and holiday.type='remove')
""" Validate leave requests (holiday_type='employee')
by creating a calendar event and a resource leaves. """
for holiday in self.filtered(lambda request: request.type == 'remove' and request.holiday_type == 'employee'):
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})
@@ -536,9 +290,96 @@ class Holidays(models.Model):
(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 action_approve(self):
# if double_validation: this method is the first approval approval
# if not double_validation: this method calls action_validate() below
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.'))
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 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.'))
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()
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.'))
current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
for holiday in self:
@@ -557,11 +398,6 @@ class Holidays(models.Model):
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
####################################################
@@ -569,28 +405,28 @@ class Holidays(models.Model):
@api.multi
def _track_subtype(self, init_values):
if 'state' in init_values and self.state == 'validate':
return 'hr_holidays.mt_holidays_approved'
return 'hr_holidays.mt_leave_approved'
elif 'state' in init_values and self.state == 'validate1':
return 'hr_holidays.mt_holidays_first_validated'
return 'hr_holidays.mt_leave_first_validated'
elif 'state' in init_values and self.state == 'confirm':
return 'hr_holidays.mt_holidays_confirmed'
return 'hr_holidays.mt_leave_confirmed'
elif 'state' in init_values and self.state == 'refuse':
return 'hr_holidays.mt_holidays_refused'
return super(Holidays, self)._track_subtype(init_values)
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(Holidays, self)._notification_recipients(message, groups)
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/validate')
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='/hr_holidays/refuse')
ref_action = self._notification_link_helper('controller', controller='/leave/refuse')
hr_actions += [{'url': ref_action, 'title': _('Refuse')}]
new_group = (
@@ -602,9 +438,8 @@ class Holidays(models.Model):
@api.multi
def _message_notification_recipients(self, message, recipients):
result = super(Holidays, self)._message_notification_recipients(message, recipients)
leave_type = self.env[message.model].browse(message.res_id).type
title = _("See Leave") if leave_type == 'remove' else _("See Allocation")
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
@@ -0,0 +1,299 @@
# -*- 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 action_approve(self):
# if double_validation: this method is the first approval approval
# if not double_validation: this method calls action_validate() below
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.'))
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 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.'))
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):
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.'))
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
####################################################
# 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
+147
View File
@@ -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
+1 -1
View File
@@ -9,4 +9,4 @@ class CalendarLeaves(models.Model):
_inherit = "resource.calendar.leaves"
_description = "Leave Detail"
holiday_id = fields.Many2one("hr.holidays", string='Leave Request')
holiday_id = fields.Many2one("hr.leave", string='Leave Request')
+1 -1
View File
@@ -2,4 +2,4 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import holidays_summary_report
from . import hr_holidays_leaves_report
from . import hr_leave_report
@@ -19,7 +19,7 @@ class HrHolidaySummaryReport(models.AbstractModel):
'end_date': fields.Date.to_string(st_date + relativedelta(days=59)),
'holiday_type': 'Confirmed and Approved' if holiday_type == 'both' else holiday_type
}
def _date_is_day_off(self, date):
return date.weekday() in (calendar.SATURDAY, calendar.SUNDAY,)
@@ -58,9 +58,9 @@ class HrHolidaySummaryReport(models.AbstractModel):
res[index]['color'] = '#ababab'
# count and get leave summary details.
holiday_type = ['confirm','validate'] if holiday_type == 'both' else ['confirm'] if holiday_type == 'Confirmed' else ['validate']
holidays = self.env['hr.holidays'].search([
holidays = self.env['hr.leave'].search([
('employee_id', '=', empid), ('state', 'in', holiday_type),
('type', '=', 'remove'), ('date_from', '<=', str(end_date)),
('date_from', '<=', str(end_date)),
('date_to', '>=', str(start_date))
])
for holiday in holidays:
@@ -102,7 +102,7 @@ class HrHolidaySummaryReport(models.AbstractModel):
def _get_holidays_status(self):
res = []
for holiday in self.env['hr.holidays.status'].search([]):
for holiday in self.env['hr.leave.type'].search([]):
res.append({'color': holiday.color_name, 'name': holiday.name})
return res
@@ -112,7 +112,7 @@ 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')
holidays = self.env['hr.holidays'].browse(self.ids)
holidays = self.env['hr.leave'].browse(self.ids)
return {
'doc_ids': self.ids,
'doc_model': holidays_report.model,
@@ -1,38 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models, tools
class HrHolidaysRemainingLeavesUser(models.Model):
_name = "hr.holidays.remaining.leaves.user"
_description = "Total holidays by type"
_auto = False
name = fields.Char('Employee', readonly=True)
no_of_leaves = fields.Integer('Remaining leaves', readonly=True)
user_id = fields.Many2one('res.users', string='User', readonly=True)
leave_type = fields.Char('Leave Type', readonly=True)
def init(self):
tools.drop_view_if_exists(self._cr, 'hr_holidays_remaining_leaves_user')
self._cr.execute("""
CREATE or REPLACE view hr_holidays_remaining_leaves_user as (
SELECT
min(hrs.id) as id,
rr.name as name,
sum(hrs.number_of_days) as no_of_leaves,
rr.user_id as user_id,
hhs.name as leave_type
FROM
hr_holidays as hrs, hr_employee as hre,
resource_resource as rr,hr_holidays_status as hhs
WHERE
hrs.employee_id = hre.id and
hre.resource_id = rr.id and
hhs.id = hrs.holiday_status_id
GROUP BY
rr.name,rr.user_id,hhs.name
)
""")
@@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_hr_holidays_remaining_leaves_user_tree" model="ir.ui.view">
<field name="name">hr.holidays.remaining.leaves.user.tree</field>
<field name="model">hr.holidays.remaining.leaves.user</field>
<field name="arch" type="xml">
<tree string="Leaves by Type">
<field name="name"/>
<field name="leave_type"/>
<field name="no_of_leaves"/>
</tree>
</field>
</record>
<record id="view_hr_holidays_remaining_leaves_user_graph" model="ir.ui.view">
<field name="name">hr.holidays.remaining.leaves.user.graph</field>
<field name="model">hr.holidays.remaining.leaves.user</field>
<field name="arch" type="xml">
<graph string="Leaves by Type" type="bar" stacked="True">
<field name="name" type="row"/>
<field name="no_of_leaves" type="measure"/>
<field name="leave_type" type="col"/>
</graph>
</field>
</record>
</odoo>
@@ -4,7 +4,20 @@
<report
id="action_report_holidayssummary"
string="Leaves Summary"
model="hr.holidays"
model="hr.leave"
report_type="qweb-pdf"
name="hr_holidays.report_holidayssummary"
file="hr_holidays.report_holidayssummary"
menu="False"/>
<record id="action_report_holidayssummary" model="ir.actions.report">
<field name="paperformat_id" ref="hr_holidays.paperformat_hrsummary"/>
</record>
<report
id="action_report_holidayssummary2"
string="Leaves Summary"
model="hr.leave.allocation"
report_type="qweb-pdf"
name="hr_holidays.report_holidayssummary"
file="hr_holidays.report_holidayssummary"
@@ -1,59 +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.holidays</field>
<field name="priority">20</field>
<field name="arch" type="xml">
<pivot string="Leaves Summary">
<field name="employee_id" type="row"/>
<field name="type" 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.holidays</field>
<field name="priority">20</field>
<field name="arch" type="xml">
<graph string="Leaves Summary">
<field name="employee_id"/>
<field name="type"/>
<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.holidays</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
id="menu_hr_available_holidays_report_tree"
action="action_hr_available_holidays_report"
parent="menu_hr_holidays_report" sequence="5"/>
</odoo>
@@ -0,0 +1,78 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import fields, models, tools
class LeaveReport(models.Model):
_name = "hr.leave.report"
_auto = False
employee_id = fields.Many2one('hr.employee', string="Employee", readonly=True)
name = fields.Char('Description', readonly=True)
number_of_days = fields.Float('Number of Days', readonly=True)
type = fields.Selection([
('allocation', 'Allocation Request'),
('request', 'Leave Request')
], string='Request Type', readonly=True)
department_id = fields.Many2one('hr.department', string='Department', readonly=True)
category_id = fields.Many2one('hr.employee.category', string='Employee Tag', readonly=True)
holiday_status_id = fields.Many2one("hr.leave.type", string="Leave Type", readonly=True)
state = fields.Selection([
('draft', 'To Submit'),
('cancel', 'Cancelled'),
('confirm', 'To Approve'),
('refuse', 'Refused'),
('validate1', 'Second Approval'),
('validate', 'Approved')
], string='Status', readonly=True)
holiday_type = fields.Selection([
('employee', 'By Employee'),
('category', 'By Employee Tag')
], string='Allocation Mode', readonly=True)
date_from = fields.Datetime('Start Date', readonly=True)
date_to = fields.Datetime('End Date', readonly=True)
payslip_status = fields.Boolean('Reported in last payslips', readonly=True)
def init(self):
tools.drop_view_if_exists(self._cr, 'hr_leave_report')
self._cr.execute("""
CREATE or REPLACE view hr_leave_report as (
SELECT row_number() over(ORDER BY leaves.employee_id) as id,
leaves.employee_id as employee_id, leaves.name as name,
leaves.number_of_days as number_of_days, leaves.type as type,
leaves.category_id as category_id, leaves.department_id as department_id,
leaves.holiday_status_id as holiday_status_id, leaves.state as state,
leaves.holiday_type as holiday_type, leaves.date_from as date_from,
leaves.date_to as date_to, leaves.payslip_status as payslip_status
from (select
allocation.employee_id as employee_id,
allocation.name as name,
allocation.number_of_days as number_of_days,
allocation.category_id as category_id,
allocation.department_id as department_id,
allocation.holiday_status_id as holiday_status_id,
allocation.state as state,
allocation.holiday_type,
null as date_from,
null as date_to,
FALSE as payslip_status,
'allocation' as type
from hr_leave_allocation as allocation
union select
request.employee_id as employee_id,
request.name as name,
request.number_of_days as number_of_days,
request.category_id as category_id,
request.department_id as department_id,
request.holiday_status_id as holiday_status_id,
request.state as state,
request.holiday_type,
request.date_from as date_from,
request.date_to as date_to,
request.payslip_status as payslip_status,
'request' as type
from hr_leave as request) leaves
);
""")
@@ -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>
@@ -17,9 +17,9 @@
<field name="groups_id" eval="[(4,ref('hr_holidays.group_hr_holidays_manager'))]"/>
</record>
<record id="property_rule_holidays_employee" model="ir.rule">
<record id="property_rule_request_employee" model="ir.rule">
<field name="name">Employee Leaves</field>
<field name="model_id" ref="model_hr_holidays"/>
<field name="model_id" ref="model_hr_leave"/>
<field name="domain_force">[('employee_id.user_id','=',user.id)]</field>
<field name="perm_create" eval="False"/>
<field name="perm_write" eval="False"/>
@@ -27,17 +27,42 @@
<field name="groups" eval="[(4,ref('base.group_user'))]"/>
</record>
<record id="property_rule_holidays_employee_write" model="ir.rule">
<record id="property_rule_request_employee_write" model="ir.rule">
<field name="name">Employee Leaves Create, Write, Unlink</field>
<field name="model_id" ref="model_hr_holidays"/>
<field name="model_id" ref="model_hr_leave"/>
<field name="domain_force">[('employee_id.user_id','=',user.id), ('state', 'in', ['draft', 'confirm', 'cancel', 'refuse'])]</field>
<field name="perm_read" eval="False"/>
<field name="groups" eval="[(4,ref('base.group_user'))]"/>
</record>
<record id="property_rule_holidays_officer" model="ir.rule">
<record id="property_rule_request_officer" model="ir.rule">
<field name="name">Leaves Officer</field>
<field name="model_id" ref="model_hr_holidays"/>
<field name="model_id" ref="model_hr_leave"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[(4,ref('hr_holidays.group_hr_holidays_user'))]"/>
</record>
<record id="property_rule_allocation_employee" model="ir.rule">
<field name="name">Employee Allocations</field>
<field name="model_id" ref="model_hr_leave_allocation"/>
<field name="domain_force">[('employee_id.user_id','=',user.id)]</field>
<field name="perm_create" eval="False"/>
<field name="perm_write" eval="False"/>
<field name="perm_unlink" eval="False"/>
<field name="groups" eval="[(4,ref('base.group_user'))]"/>
</record>
<record id="property_rule_allocation_employee_write" model="ir.rule">
<field name="name">Employee Allocations Create, Write, Unlink</field>
<field name="model_id" ref="model_hr_leave_allocation"/>
<field name="domain_force">[('employee_id.user_id','=',user.id), ('state', 'in', ['draft', 'confirm', 'cancel', 'refuse'])]</field>
<field name="perm_read" eval="False"/>
<field name="groups" eval="[(4,ref('base.group_user'))]"/>
</record>
<record id="property_rule_allocation_officer" model="ir.rule">
<field name="name">Leaves Officer</field>
<field name="model_id" ref="model_hr_leave_allocation"/>
<field name="domain_force">[(1,'=',1)]</field>
<field name="groups" eval="[(4,ref('hr_holidays.group_hr_holidays_user'))]"/>
</record>
@@ -51,7 +76,7 @@
<record id="hr_holidays_status_rule_multi_company" model="ir.rule">
<field name="name">Leaves multi company rule</field>
<field name="model_id" ref="model_hr_holidays_status"/>
<field name="model_id" ref="model_hr_leave_type"/>
<field name="global" eval="True"/>
<field name="domain_force">['|',('company_id','=',False),('company_id','in',[user.company_id.id])]</field>
</record>
+12 -10
View File
@@ -1,10 +1,12 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_hr_holidays_user,hr.holidays.user,model_hr_holidays,hr_holidays.group_hr_holidays_user,1,1,1,1
access_hr_holidays_employee,hr.holidays.employee,model_hr_holidays,base.group_user,1,1,1,1
access_hr_holydays_status_employee,hr.holidays.status employee,model_hr_holidays_status,base.group_user,1,0,0,0
access_hr_holydays_status_manager,hr.holidays.status manager,model_hr_holidays_status,hr_holidays.group_hr_holidays_manager,1,1,1,1
access_hr_holidays_remain_user,hr.holidays.ramain.user,model_hr_holidays_remaining_leaves_user,hr_holidays.group_hr_holidays_user,1,1,1,1
access_resource_calendar_leaves_user,resource_calendar_leaves_user,resource.model_resource_calendar_leaves,hr_holidays.group_hr_holidays_user,1,1,1,1
access_calendar_event_hr_user,calendar.event.hr.user,calendar.model_calendar_event,hr_holidays.group_hr_holidays_user,1,1,1,1
access_calendar_event_type_manager,calendar.event.type.manager,calendar.model_calendar_event_type,hr_holidays.group_hr_holidays_manager,1,1,1,1
access_calendar_attendee_hr_user,calendar.attendee.hr.user,calendar.model_calendar_attendee,hr_holidays.group_hr_holidays_user,1,1,1,1
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_hr_holidays_user_request,hr.holidays.user.request,model_hr_leave,hr_holidays.group_hr_holidays_user,1,1,1,1
access_hr_holidays_employee_request,hr.holidays.employee.request,model_hr_leave,base.group_user,1,1,1,1
access_hr_holidays_user_allocation,hr.holidays.user.allocation,model_hr_leave_allocation,hr_holidays.group_hr_holidays_user,1,1,1,1
access_hr_holidays_employee_allocation,hr.holidays.employee.allocation,model_hr_leave_allocation,base.group_user,1,1,1,1
access_hr_holidays_status_employee,hr.holidays.status employee,model_hr_leave_type,base.group_user,1,0,0,0
access_hr_holidays_status_manager,hr.holidays.status manager,model_hr_leave_type,hr_holidays.group_hr_holidays_manager,1,1,1,1
access_hr_leave_report,access_hr_leave_report,model_hr_leave_report,,1,0,0,0
access_resource_calendar_leaves_user,resource_calendar_leaves_user,resource.model_resource_calendar_leaves,hr_holidays.group_hr_holidays_user,1,1,1,1
access_calendar_event_hr_user,calendar.event.hr.user,calendar.model_calendar_event,hr_holidays.group_hr_holidays_user,1,1,1,1
access_calendar_event_type_manager,calendar.event.type.manager,calendar.model_calendar_event_type,hr_holidays.group_hr_holidays_manager,1,1,1,1
access_calendar_attendee_hr_user,calendar.attendee.hr.user,calendar.model_calendar_attendee,hr_holidays.group_hr_holidays_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_hr_holidays_user access_hr_holidays_user_request hr.holidays.user hr.holidays.user.request model_hr_holidays model_hr_leave hr_holidays.group_hr_holidays_user 1 1 1 1
3 access_hr_holidays_employee access_hr_holidays_employee_request hr.holidays.employee hr.holidays.employee.request model_hr_holidays model_hr_leave base.group_user 1 1 1 1
4 access_hr_holydays_status_employee access_hr_holidays_user_allocation hr.holidays.status employee hr.holidays.user.allocation model_hr_holidays_status model_hr_leave_allocation base.group_user hr_holidays.group_hr_holidays_user 1 0 1 0 1 0 1
5 access_hr_holydays_status_manager access_hr_holidays_employee_allocation hr.holidays.status manager hr.holidays.employee.allocation model_hr_holidays_status model_hr_leave_allocation hr_holidays.group_hr_holidays_manager base.group_user 1 1 1 1
6 access_hr_holidays_remain_user access_hr_holidays_status_employee hr.holidays.ramain.user hr.holidays.status employee model_hr_holidays_remaining_leaves_user model_hr_leave_type hr_holidays.group_hr_holidays_user base.group_user 1 1 0 1 0 1 0
7 access_resource_calendar_leaves_user access_hr_holidays_status_manager resource_calendar_leaves_user hr.holidays.status manager resource.model_resource_calendar_leaves model_hr_leave_type hr_holidays.group_hr_holidays_user hr_holidays.group_hr_holidays_manager 1 1 1 1
8 access_calendar_event_hr_user access_hr_leave_report calendar.event.hr.user access_hr_leave_report calendar.model_calendar_event model_hr_leave_report hr_holidays.group_hr_holidays_user 1 1 0 1 0 1 0
9 access_calendar_event_type_manager access_resource_calendar_leaves_user calendar.event.type.manager resource_calendar_leaves_user calendar.model_calendar_event_type resource.model_resource_calendar_leaves hr_holidays.group_hr_holidays_manager hr_holidays.group_hr_holidays_user 1 1 1 1
10 access_calendar_attendee_hr_user access_calendar_event_hr_user calendar.attendee.hr.user calendar.event.hr.user calendar.model_calendar_attendee calendar.model_calendar_event hr_holidays.group_hr_holidays_user 1 1 1 1
11 access_calendar_event_type_manager calendar.event.type.manager calendar.model_calendar_event_type hr_holidays.group_hr_holidays_manager 1 1 1 1
12 access_calendar_attendee_hr_user calendar.attendee.hr.user calendar.model_calendar_attendee hr_holidays.group_hr_holidays_user 1 1 1 1
+10 -11
View File
@@ -14,8 +14,9 @@ class TestHolidaysFlow(TestHrHolidaysBase):
@mute_logger('odoo.addons.base.models.ir_model', 'odoo.models')
def test_00_leave_request_flow(self):
""" Testing leave request flow """
Holidays = self.env['hr.holidays']
HolidaysStatus = self.env['hr.holidays.status']
Requests = self.env['hr.leave']
Allocations = self.env['hr.leave.allocation']
HolidaysStatus = self.env['hr.leave.type']
def _check_holidays_status(holiday_status, ml, lt, rl, vrl):
self.assertEqual(holiday_status.max_leaves, ml,
@@ -56,7 +57,7 @@ class TestHolidaysFlow(TestHrHolidaysBase):
# --------------------------------------------------
# Employee creates a leave request for another employee -> should crash
HolidaysEmployeeGroup = Holidays.sudo(self.user_employee_id)
HolidaysEmployeeGroup = Requests.sudo(self.user_employee_id)
with self.assertRaises(ValidationError):
HolidaysEmployeeGroup.create({
'name': 'Hol10',
@@ -66,7 +67,7 @@ class TestHolidaysFlow(TestHrHolidaysBase):
'date_to': datetime.today(),
'number_of_days_temp': 1,
})
Holidays.search([('name', '=', 'Hol10')]).unlink()
Requests.search([('name', '=', 'Hol10')]).unlink()
# Employee creates a leave request in a no-limit category
hol1_employee_group = HolidaysEmployeeGroup.create({
@@ -110,20 +111,19 @@ class TestHolidaysFlow(TestHrHolidaysBase):
'name': 'Hol22',
'employee_id': self.employee_emp_id,
'holiday_status_id': self.holidays_status_2.id,
'date_from': (datetime.today() + relativedelta(days=0)).strftime('%Y-%m-%d %H:%M'),
'date_to': (datetime.today() + relativedelta(days=1)),
'date_from': (datetime.today() + relativedelta(days=1)).strftime('%Y-%m-%d %H:%M'),
'date_to': (datetime.today() + relativedelta(days=2)),
'number_of_days_temp': 1,
})
# Clean transaction
Holidays.search([('name', 'in', ['Hol21', 'Hol22'])]).unlink()
Requests.search([('name', 'in', ['Hol21', 'Hol22'])]).unlink()
# HrUser allocates some leaves to the employee
aloc1_user_group = Holidays.sudo(self.user_hruser_id).create({
aloc1_user_group = Allocations.sudo(self.user_hruser_id).create({
'name': 'Days for limited category',
'employee_id': self.employee_emp_id,
'holiday_status_id': self.holidays_status_2.id,
'type': 'add',
'number_of_days_temp': 2,
})
# HrUser validates the first step
@@ -191,13 +191,12 @@ class TestHolidaysFlow(TestHrHolidaysBase):
# cl can be of maximum 20 days for employee_root
hol3_status = self.env.ref('hr_holidays.holiday_status_cl').with_context(employee_id=employee_id)
# I assign the dates in the holiday request for 1 day
hol3 = Holidays.create({
hol3 = Requests.create({
'name': 'Sick Leave',
'holiday_status_id': hol3_status.id,
'date_from': datetime.today().strftime('%Y-%m-10 10:00:00'),
'date_to': datetime.today().strftime('%Y-%m-11 19:00:00'),
'employee_id': employee_id,
'type': 'remove',
'number_of_days_temp': 1
})
# I find a small mistake on my leave request to I click on "Refuse" button to correct a mistake.
@@ -1,798 +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.holidays</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.holidays</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 allocations and 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 and Allocations"/>
<separator/>
<filter string="My Leaves" name="my_leaves" domain="[('user_id', '=', uid)]"/>
<separator/>
<filter name="leaves" string="Leaves" domain="[('type', '=', 'remove')]"/>
<filter name="allocations" string="Allocations" domain="[('type', '=', 'add')]"/>
<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.holidays</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>
<!-- Holidays: Allocation Request -->
<record id="edit_holiday_new" model="ir.ui.view">
<field name="name">Leave Request</field>
<field name="model">hr.holidays</field>
<field name="arch" type="xml">
<form string="Leave Request">
<field name="can_reset" invisible="1"/>
<field name="type" 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 attrs="{'invisible': [('type', '=', 'add')]}">
<field name="date_from" attrs="{'required':[('type', '=', 'remove')]}" class="oe_inline"/>
<label string="-" class="oe_inline"/>
<field name="date_to" attrs="{'required':[('type', '=', 'remove')]}" 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':[('type', '=', 'remove'),('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': [('type', '=', 'remove'),('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" attrs="{'invisible':[('type','=','add')]}" widget="toggle_button"/>
</group>
<field name="notes" nolabel="1" colspan="4" placeholder="Add a reason..." attrs="{'invisible': [('type', '=', 'remove')]}"/>
<div groups="hr_holidays.group_hr_holidays_manager" attrs="{'invisible':[('type','=','add')]}">
<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.holidays.allocation.tree</field>
<field name="model">hr.holidays</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="date_from"/>
<field name="date_to"/>
<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.holidays</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.holidays.allocation.tree.customize</field>
<field name="model">hr.holidays</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="date_from" required="1"/>
<field name="date_to" required="1"/>
<field name="number_of_days_temp" string="Allocated 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="report_note" groups="hr_holidays.group_hr_holidays_manager"/>
</tree>
</field>
</record>
<record id="view_holiday_simple" model="ir.ui.view">
<field name="name">hr.holidays.report_tree</field>
<field name="model">hr.holidays</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="type"/>
<field name="holiday_status_id" invisible="1"/>
<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>
<record id="view_hr_holidays_kanban" model="ir.ui.view">
<field name="name">hr.holidays.kanban</field>
<field name="model">hr.holidays</field>
<field name="arch" type="xml">
<kanban class="o_kanban_mobile">
<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="view_holiday" model="ir.ui.view">
<field name="name">hr.holidays.tree</field>
<field name="model">hr.holidays</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.holidays</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'), ('type', '=', 'remove'), ('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.holidays</field>
<field name="view_type">form</field>
<field name="view_id" ref="edit_holiday_new"/>
<field name="context">{
'default_type': 'remove',
}</field>
<field name="domain">[('type','=','remove'), ('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"/>
<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.holidays</field>
<field name="view_type">form</field>
<field name="context">{
'default_type': 'remove',
'search_default_approve':1,
}</field>
<field name="domain">[('type','=','remove')]</field>
<field name="view_id" ref="edit_holiday_new"/>
<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"/>
<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.holidays</field>
<field name="view_type">form</field>
<field name="view_mode">tree,kanban,form</field>
<field name="context">{
'default_type':'add',
}</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">[('type','=','add'), ('user_id', '=', uid)]</field>
<field name="view_id" ref="edit_holiday_new"/>
<field name="search_view_id" ref="view_hr_holidays_filter"/>
</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"/>
<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.holidays</field>
<field name="view_type">form</field>
<field name="context">{
'default_type': 'add',
'search_default_approve':1,
}</field>
<field name="domain">[('type','=','add')]</field>
<field name="view_id" ref="edit_holiday_new"/>
<field name="search_view_id" ref="view_hr_holidays_filter"/>
</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"/>
<field name="act_window_id" ref="request_approve_allocation"/>
</record>
<record id="open_company_allocation" model="ir.actions.act_window">
<field name="name">Leaves Summary</field>
<field name="res_model">hr.holidays</field>
<field name="view_type">form</field>
<field name="view_mode">tree,kanban,form</field>
<field name="view_id" eval="view_holiday_simple"/>
<field name="context">{
'search_default_group_type': 1,
'search_default_year': 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="domain">[('holiday_type','=','employee'), ('user_id', '=', uid)]</field>
<field name="search_view_id" ref="view_hr_holidays_filter"/>
</record>
<menuitem
id="menu_open_company_allocation"
parent="menu_hr_holidays_my_leaves"
action="open_company_allocation"
sequence="10"/>
<record id="open_employee_leaves" model="ir.actions.act_window">
<field name="name">Leaves Overview</field>
<field name="res_model">hr.holidays</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="context">{'default_type': 'remove',}</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">[('type','=','remove')]</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.holidays</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="domain">[('type','=','remove')]</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.holidays</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,calendar</field>
<field name="context">{'default_type':'add', '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="domain">[('type','=','add')]</field>
<field name="search_view_id" ref="view_hr_holidays_filter"/>
</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.holidays.status.filter</field>
<field name="model">hr.holidays.status</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.holidays.status.form</field>
<field name="model">hr.holidays.status</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.holidays.status.tree</field>
<field name="model">hr.holidays.status</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.holidays.status.kanban</field>
<field name="model">hr.holidays.status</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.holidays.status.normal.tree</field>
<field name="model">hr.holidays.status</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.holidays.status.search</field>
<field name="model">hr.holidays.status</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.holidays.status</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.holidays</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"/>
</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.holidays</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.holidays</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="context">{
'default_type': 'remove',
'search_default_department_id': [active_id],
'default_department_id': active_id,
'search_default_approve':1}
</field>
<field name="domain">[('type','=','remove')]</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.holidays</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="context">{
'default_type': 'add',
'search_default_department_id': [active_id],
'default_department_id': active_id,
'search_default_approve':1}
</field>
<field name="domain">[('type','=','add')]</field>
<field name="search_view_id" ref="view_hr_holidays_filter"/>
</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.holidays</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.holidays</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>
@@ -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>
@@ -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>
+437
View File
@@ -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>
+54
View File
@@ -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>
@@ -6,7 +6,7 @@
<!-- Holidays Category -->
<record model="hr.holidays.status" id="holiday_status_heures_sup">
<record model="hr.leave.type" id="holiday_status_heures_sup">
<field name="name">Heures Supplementaires</field>
<field name="limit">True</field>
<field name="color_name">lightblue</field>
@@ -12,9 +12,9 @@ def post_init(cr, registry):
from odoo import api, SUPERUSER_ID
env = api.Environment(cr, SUPERUSER_ID, {})
for leave_type in env['hr.holidays.status'].search([('timesheet_generate', '=', True), ('timesheet_project_id', '=', False)]):
company = leave_type.company_id or env.user.company_id
leave_type.write({
for hr_leave_type in env['hr.leave.type'].search([('timesheet_generate', '=', True), ('timesheet_project_id', '=', False)]):
company = hr_leave_type.company_id or env.user.company_id
hr_leave_type.write({
'timesheet_project_id': company.leave_timesheet_project_id.id,
'timesheet_task_id': company.leave_timesheet_task_id.id,
})
@@ -8,7 +8,7 @@ from odoo.exceptions import UserError
class AccountAnalyticLine(models.Model):
_inherit = 'account.analytic.line'
holiday_id = fields.Many2one("hr.holidays", string='Leave Request')
holiday_id = fields.Many2one("hr.leave", string='Leave Request')
@api.multi
def unlink(self):
@@ -6,7 +6,7 @@ from odoo.exceptions import ValidationError
class HolidaysType(models.Model):
_inherit = "hr.holidays.status"
_inherit = "hr.leave.type"
def _default_project_id(self):
company = self.company_id if self.company_id else self.env.user.company_id
@@ -39,7 +39,7 @@ class HolidaysType(models.Model):
class Holidays(models.Model):
_inherit = "hr.holidays"
_inherit = "hr.leave"
timesheet_ids = fields.One2many('account.analytic.line', 'holiday_id', string="Analytic Lines")
@@ -50,8 +50,7 @@ class Holidays(models.Model):
"""
# create the timesheet on the vacation project
for holiday in self.filtered(
lambda request: request.type == 'remove' and
request.holiday_type == 'employee' and
lambda request: request.holiday_type == 'employee' and
request.holiday_status_id.timesheet_project_id and
request.holiday_status_id.timesheet_task_id):
holiday_project = holiday.holiday_status_id.timesheet_project_id
@@ -14,7 +14,7 @@ class TestTimesheetHolidaysCreate(common.TransactionCase):
def test_status_create(self):
"""Ensure that when a status is created, it fullfills the project and task constrains"""
status = self.env['hr.holidays.status'].create({
status = self.env['hr.leave.type'].create({
'name': 'A nice Leave Type',
'limit': True
})
@@ -39,14 +39,14 @@ class TestTimesheetHolidays(TestTimesheet):
self.internal_project = self.env.user.company_id.leave_timesheet_project_id
self.internal_task_leaves = self.env.user.company_id.leave_timesheet_task_id
self.leave_type_with_ts = self.env['hr.holidays.status'].create({
self.hr_leave_type_with_ts = self.env['hr.leave.type'].create({
'name': 'Leave Type with timesheet generation',
'limit': True,
'timesheet_generate': True,
'timesheet_project_id': self.internal_project.id,
'timesheet_task_id': self.internal_task_leaves.id,
})
self.leave_type_no_ts = self.env['hr.holidays.status'].create({
self.hr_leave_type_no_ts = self.env['hr.leave.type'].create({
'name': 'Leave Type without timesheet generation',
'limit': True,
'timesheet_generate': False,
@@ -55,31 +55,30 @@ class TestTimesheetHolidays(TestTimesheet):
})
# HR Officer allocates some leaves to the employee 1
self.Holidays = self.env['hr.holidays'].with_context(mail_create_nolog=True, mail_notrack=True)
self.leave_allocation_with_ts = self.Holidays.sudo().create({
self.Requests = self.env['hr.leave'].with_context(mail_create_nolog=True, mail_notrack=True)
self.Allocations = self.env['hr.leave.allocation'].with_context(mail_create_nolog=True, mail_notrack=True)
self.hr_leave_allocation_with_ts = self.Allocations.sudo().create({
'name': 'Days for limited category with timesheet',
'employee_id': self.empl_employee.id,
'holiday_status_id': self.leave_type_with_ts.id,
'type': 'add',
'holiday_status_id': self.hr_leave_type_with_ts.id,
'number_of_days_temp': 10,
})
self.leave_allocation_with_ts.action_approve()
self.leave_allocation_no_ts = self.Holidays.sudo().create({
self.hr_leave_allocation_with_ts.action_approve()
self.hr_leave_allocation_no_ts = self.Allocations.sudo().create({
'name': 'Days for limited category without timesheet',
'employee_id': self.empl_employee.id,
'holiday_status_id': self.leave_type_no_ts.id,
'type': 'add',
'holiday_status_id': self.hr_leave_type_no_ts.id,
'number_of_days_temp': 10,
})
self.leave_allocation_no_ts.action_approve()
self.hr_leave_allocation_no_ts.action_approve()
def test_validate_with_timesheet(self):
# employee creates a leave request
number_of_days = (self.leave_end_datetime - self.leave_start_datetime).days+1
holiday = self.Holidays.sudo(self.user_employee.id).create({
holiday = self.Requests.sudo(self.user_employee.id).create({
'name': 'Leave 1',
'employee_id': self.empl_employee.id,
'holiday_status_id': self.leave_type_with_ts.id,
'holiday_status_id': self.hr_leave_type_with_ts.id,
'date_from': self.leave_start_datetime,
'date_to': self.leave_end_datetime,
'number_of_days_temp': number_of_days,
@@ -94,10 +93,10 @@ class TestTimesheetHolidays(TestTimesheet):
def test_validate_without_timesheet(self):
# employee creates a leave request
number_of_days = (self.leave_end_datetime - self.leave_start_datetime).days
holiday = self.Holidays.sudo(self.user_employee.id).create({
holiday = self.Requests.sudo(self.user_employee.id).create({
'name': 'Leave 1',
'employee_id': self.empl_employee.id,
'holiday_status_id': self.leave_type_no_ts.id,
'holiday_status_id': self.hr_leave_type_no_ts.id,
'date_from': self.leave_start_datetime,
'date_to': self.leave_end_datetime,
'number_of_days_temp': number_of_days,
@@ -2,8 +2,8 @@
<odoo>
<record id="hr_holiday_status_view_form_inherit" model="ir.ui.view">
<field name="name">hr.holidays.status.form</field>
<field name="model">hr.holidays.status</field>
<field name="name">hr.leave.type.form</field>
<field name="model">hr.leave.type</field>
<field name="inherit_id" ref="hr_holidays.edit_holiday_status_form"/>
<field name="arch" type="xml">
<xpath expr="//group[@name='description']" position="after">