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