From bd5b2c9466eabe56bbb1e91d29a7e422e957550f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Fri, 17 Aug 2018 15:58:16 +0200 Subject: [PATCH] [REF] hr_holidays: simplify leave type configuration Purpose of this commit is to simplify the configuration of leave type and use the job of HR officers / manager by making options clearer and easier to use. Specifications for leave type : * display name as header; * remove accrual option from leave type. It is now configured directly on allocation requests; * add a "X days allocated" stmart button. If you set a general allocation of 20 days, the stat button will display 20 days. Even if you go update the allocation by employee after that. Purpose is to have the value of main allocation done in group, not individual cases of some employees; * add a "Group leaves" stmart button. It leads to a new leave requests in group mode where mode is visible; * have a unique allocation type field replacing the setting allowing to make leave or allocation as well as limit and negative fields; * limit leave duration encoding to two options, either day-based (allowing to take full days or half a day), either hours-based (allowing to take full days, half a day or some specific hours in a given day); More technically * limit and negative fields are removed. It was confusing as limit set to True means you can actually exceed the limit). It can be computed based on allocation_type ('no' meaning you can bypass the limit); * request_unit is simplified. Day and half are merged into day; * accrual and balance limit are moved to allocation model; Specifications for allocation requests * set accrual specific on allocation itself and not coming from the leave type configuration anymore. Indeed we now consider accrual to be done case by case independently from the leave type; * remove negative and balance_limit. Indeed we now consider people have to check by hand what is done by accrual; Leave and allocation are slightly impacted by other changes, such as domain for default leave types. Changes have been made as small as possible. Future commits will still improve leave and allocation models and views, going further in the cleaning process. Some tests are udpated to match the new configuration of leave type. This commit is linked to task ID 1876795 and PR #26571. --- addons/hr_holidays/data/hr_holidays_data.xml | 14 ++- addons/hr_holidays/data/hr_holidays_demo.xml | 7 +- addons/hr_holidays/models/hr.py | 3 +- addons/hr_holidays/models/hr_leave.py | 49 ++------ .../hr_holidays/models/hr_leave_allocation.py | 23 ++-- addons/hr_holidays/models/hr_leave_type.py | 113 +++++++++++------- .../hr_holidays/tests/test_access_rights.py | 4 +- .../tests/test_accrual_allocations.py | 92 ++++++-------- .../hr_holidays/tests/test_holidays_flow.py | 12 +- .../hr_holidays/tests/test_hr_leave_type.py | 2 +- .../views/hr_leave_allocation_views.xml | 45 +++++-- .../hr_holidays/views/hr_leave_type_views.xml | 36 +++--- addons/hr_holidays/views/hr_leave_views.xml | 23 ++-- .../tests/test_timesheet_holidays.py | 6 +- .../views/hr_holidays_views.xml | 10 +- 15 files changed, 221 insertions(+), 218 deletions(-) diff --git a/addons/hr_holidays/data/hr_holidays_data.xml b/addons/hr_holidays/data/hr_holidays_data.xml index 20ae0403cd6..57b2fdb57c5 100644 --- a/addons/hr_holidays/data/hr_holidays_data.xml +++ b/addons/hr_holidays/data/hr_holidays_data.xml @@ -1,11 +1,10 @@ - - + - + fixed black @@ -14,14 +13,16 @@ Sick Leaves - + no red + Compensatory Days - + fixed_allocation + hour lavender @@ -30,8 +31,11 @@ Unpaid + fixed_allocation + both brown + diff --git a/addons/hr_holidays/data/hr_holidays_demo.xml b/addons/hr_holidays/data/hr_holidays_demo.xml index 0ab4e8a574e..29f5ebf723f 100644 --- a/addons/hr_holidays/data/hr_holidays_demo.xml +++ b/addons/hr_holidays/data/hr_holidays_demo.xml @@ -4,7 +4,7 @@ Overtime Compensation - + fixed_allocation blue @@ -13,18 +13,17 @@ Parental Leaves - + fixed brown both - + fixed wheat - diff --git a/addons/hr_holidays/models/hr.py b/addons/hr_holidays/models/hr.py index e593b88b89a..908dc25eadd 100644 --- a/addons/hr_holidays/models/hr.py +++ b/addons/hr_holidays/models/hr.py @@ -104,7 +104,7 @@ class Employee(models.Model): join hr_leave_type s ON (s.id=h.holiday_status_id) WHERE h.state='validate' AND - s.limit=False AND + (s.allocation_type='fixed' OR s.allocation_type='fixed_allocation') AND h.employee_id in %s GROUP BY h.employee_id""", (tuple(self.ids),)) return dict((row['employee_id'], row['days']) for row in self._cr.dictfetchall()) @@ -142,7 +142,6 @@ class Employee(models.Model): def _compute_leaves_count(self): all_leaves = self.env['hr.leave.report'].read_group([ ('employee_id', 'in', self.ids), - ('holiday_status_id.limit', '=', False), ('state', '=', 'validate') ], fields=['number_of_days', 'employee_id'], groupby=['employee_id']) mapping = dict([(leave['employee_id'][0], leave['number_of_days']) for leave in all_leaves]) diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py index 637e4770e06..e343e5c9406 100644 --- a/addons/hr_holidays/models/hr_leave.py +++ b/addons/hr_holidays/models/hr_leave.py @@ -59,17 +59,12 @@ class HolidaysRequest(models.Model): _inherit = ['mail.thread', 'mail.activity.mixin'] _mail_post_access = 'read' - def _default_domain_holiday_status(self): - if self.user_has_groups('hr_holidays.group_hr_holidays_manager'): - return [('valid', '=', True)] - return [('valid', '=', True), ('employee_applicability', 'in', ['leave', 'both'])] - @api.model def default_get(self, fields_list): defaults = super(HolidaysRequest, self).default_get(fields_list) LeaveType = self.env['hr.leave.type'].with_context(employee_id=defaults.get('employee_id'), default_date_from=defaults.get('date_from', fields.Datetime.now())) - lt = LeaveType.search(self._default_domain_holiday_status()) + lt = LeaveType.search([('valid', '=', True)]) defaults['holiday_status_id'] = lt[0].id if len(lt) > 0 else defaults.get('holiday_status_id') return defaults @@ -103,7 +98,7 @@ class HolidaysRequest(models.Model): holiday_status_id = fields.Many2one( "hr.leave.type", string="Leave Type", required=True, readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, - domain=lambda self: self._default_domain_holiday_status()) + domain=[('valid', '=', True)]) leave_type_request_unit = fields.Selection(related='holiday_status_id.request_unit', readonly=True) validation_type = fields.Selection('Validation Type', related='holiday_status_id.validation_type') # HR data @@ -156,13 +151,9 @@ class HolidaysRequest(models.Model): request_date_to_period = fields.Selection([ ('am', 'Morning'), ('pm', 'Afternoon')], string="Date Period End", default='pm') - # request type + duplicate because we cannot hide some entries of a selection field - request_unit_all = fields.Selection([('half', 'Half-day'), - ('day', '1 Day'), - ('period', 'Period')], default='day') - request_unit_day = fields.Selection([('day', '1 Day'), - ('period', 'Period')], default='day') - + # request type + request_unit = fields.Selection([('day', '1 Day'), + ('period', 'Period')], default='day') _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."), @@ -170,7 +161,7 @@ class HolidaysRequest(models.Model): ('date_check', "CHECK ( number_of_days_temp >= 0 )", "If you want to change the number of days you should use the 'period' mode"), ] - @api.onchange('request_unit_all', 'request_date_from_period', 'request_date_to_period', + @api.onchange('request_unit', 'request_date_from_period', 'request_date_to_period', 'holiday_status_id', 'request_date_from', 'request_date_to', 'employee_id') def _onchange_request_parameters(self): date_from = False @@ -205,7 +196,7 @@ class HolidaysRequest(models.Model): first_day = fields.Date.from_string(date_from) last_day = fields.Date.from_string(date_to) - if self.request_unit_all in ['day', 'half']: + if self.request_unit in ['day']: last_day = first_day # find first attendance coming after first_day @@ -213,19 +204,11 @@ class HolidaysRequest(models.Model): # find last attendance coming before last_day attendance_to = next((att for att in reversed(attendances) if int(att.dayofweek) <= last_day.weekday()), attendances[-1]) - if self.request_unit_all == 'day' or (self.request_unit_all == 'period' and self.leave_type_request_unit == 'day'): + if self.request_unit == 'day' or (self.request_unit == 'period' and self.leave_type_request_unit == 'day'): hour_from = float_to_time(attendance_from.hour_from) hour_to = float_to_time(attendance_to.hour_to) - elif self.request_unit_all == 'half': - hour_from = float_to_time(attendance_from.hour_from if self.request_date_from_period == 'am' else attendance_to.hour_from) - hour_to = float_to_time(attendance_from.hour_to if self.request_date_from_period == 'am' else attendance_to.hour_to) - - elif self.request_unit_all == 'period' and self.leave_type_request_unit == 'half': - hour_from = float_to_time(attendance_from.hour_from if self.request_date_from_period == 'am' else attendance_from.hour_to) - hour_to = float_to_time(attendance_to.hour_from if self.request_date_to_period == 'am' else attendance_to.hour_to) - - if self.leave_type_request_unit == 'hour' and self.request_unit_all == 'period': + if self.request_unit == 'period' and self.leave_type_request_unit == 'hour': date_from = fields.Datetime.from_string(date_from) date_to = fields.Datetime.from_string(date_to) else: @@ -236,16 +219,12 @@ class HolidaysRequest(models.Model): self.date_from = date_from self.date_to = date_to - if not (self.leave_type_request_unit == 'hour' and self.request_unit_all == 'period'): + if not (self.request_unit == 'period' and self.leave_type_request_unit == 'hour'): date_from = date_from date_to = date_to self.number_of_days_temp = self._get_number_of_days(date_from, date_to, self.employee_id.id) - @api.onchange('request_unit_day') - def _onchange_request_unit_day(self): - self.request_unit_all = self.request_unit_day - @api.onchange('holiday_type') def _onchange_type(self): if self.holiday_type == 'employee' and not self.employee_id: @@ -276,8 +255,7 @@ class HolidaysRequest(models.Model): self.request_date_to = date_to.date() if date_to else False if (date_from and date_to) and (date_from.day < date_to.day): - self.request_unit_all = 'period' - self.request_unit_day = 'period' + self.request_unit = 'period' # Compute and update the number of days if (date_to and date_from) and (date_from <= date_to): @@ -295,8 +273,7 @@ class HolidaysRequest(models.Model): self.request_date_to = date_to.date() if date_to else False if (date_from and date_to) and (date_from.day < date_to.day): - self.request_unit_all = 'period' - self.request_unit_day = 'period' + self.request_unit = 'period' # Compute and update the number of days if (date_to and date_from) and (date_from <= date_to): @@ -362,7 +339,7 @@ class HolidaysRequest(models.Model): @api.constrains('state', 'number_of_days_temp', 'holiday_status_id') def _check_holidays(self): for holiday in self: - if holiday.holiday_type != 'employee' or not holiday.employee_id or holiday.holiday_status_id.limit or holiday.holiday_status_id.negative: + if holiday.holiday_type != 'employee' or not holiday.employee_id or holiday.holiday_status_id.allocation_type == 'no': 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 \ diff --git a/addons/hr_holidays/models/hr_leave_allocation.py b/addons/hr_holidays/models/hr_leave_allocation.py index 6c94c317813..e288aaf68e0 100644 --- a/addons/hr_holidays/models/hr_leave_allocation.py +++ b/addons/hr_holidays/models/hr_leave_allocation.py @@ -23,17 +23,11 @@ class HolidaysAllocation(models.Model): _inherit = ['mail.thread', 'mail.activity.mixin'] _mail_post_access = 'read' - def _default_domain_holiday_status_id(self): - if self.user_has_groups('hr_holidays.group_hr_holidays_manager'): - return [('valid', '=', True), ('limit', '=', False)] - return [('valid', '=', True), ('employee_applicability', 'in', ['allocation', 'both']), ('limit', '=', False)] - 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) def _default_holiday_status_id(self): - LeaveType = self.env['hr.leave.type'].with_context(employee_id=self._default_employee().id) - lt = LeaveType.search(self._default_domain_holiday_status_id()) + lt = self.env['hr.leave.type'].with_context(employee_id=self._default_employee().id).search([('valid', '=', True)], limit=1) return lt[:1] name = fields.Char('Description') @@ -58,7 +52,7 @@ class HolidaysAllocation(models.Model): holiday_status_id = fields.Many2one( "hr.leave.type", string="Leave Type", required=True, readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, - domain=lambda self: self._default_domain_holiday_status_id(), default=_default_holiday_status_id) + domain=[('valid', '=', True)], default=_default_holiday_status_id) 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') @@ -94,7 +88,10 @@ class HolidaysAllocation(models.Model): 'hr.employee.category', string='Employee Tag', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, help='Category of Employee') # accrual configuration - accrual = fields.Boolean("Accrual", related='holiday_status_id.accrual', store=True, readonly=True) + accrual = fields.Boolean( + "Accrual", readonly=True, + states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}) + accrual_limit = fields.Integer('Balance limit', default=0, help="Maximum of allocation for accrual; 0 means no maximum.") number_per_interval = fields.Float("Number of unit per interval", readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, default=1) interval_number = fields.Integer("Number of unit between two intervals", readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, default=1) unit_per_interval = fields.Selection([ @@ -171,8 +168,8 @@ class HolidaysAllocation(models.Model): values['number_of_days_temp'] = holiday.number_of_days_temp + days_to_give * prorata - if holiday.holiday_status_id.balance_limit > 0: - values['number_of_days_temp'] = min(values['number_of_days_temp'], holiday.holiday_status_id.balance_limit) + if holiday.accrual_limit > 0: + values['number_of_days_temp'] = min(values['number_of_days_temp'], holiday.accrual_limit) values['number_of_hours'] = values['number_of_days_temp'] * holiday.employee_id.resource_calendar_id.hours_per_day or HOURS_PER_DAY @@ -186,8 +183,8 @@ class HolidaysAllocation(models.Model): if holiday.type_request_unit == 'hour': # In this case we need the number of days to reflect the number of hours taken number_of_days = holiday.number_of_hours / holiday.employee_id.resource_calendar_id.hours_per_day or HOURS_PER_DAY - if holiday.holiday_status_id.balance_limit > 0: - number_of_days = min(number_of_days, holiday.holiday_status_id.balance_limit) + if holiday.accrual_limit > 0: + number_of_days = min(number_of_days, holiday.accrual_limit) holiday.number_of_days = number_of_days diff --git a/addons/hr_holidays/models/hr_leave_type.py b/addons/hr_holidays/models/hr_leave_type.py index 48936f1c9e7..83d81af70c6 100644 --- a/addons/hr_holidays/models/hr_leave_type.py +++ b/addons/hr_holidays/models/hr_leave_type.py @@ -3,6 +3,7 @@ # Copyright (c) 2005-2006 Axelor SARL. (http://www.axelor.com) +import datetime import logging from odoo import api, fields, models @@ -42,7 +43,6 @@ class HolidaysType(models.Model): ('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('Exceed Allocation', help="If you select this check box, the system will allow the employees to ask for leaves without allocating some beforehand") 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', @@ -56,17 +56,23 @@ class HolidaysType(models.Model): virtual_remaining_leaves = fields.Float( compute='_compute_leaves', string='Virtual Remaining Leaves', help='Maximum Leaves Allowed - Leaves Already Taken - Leaves Waiting Approval') + group_days_allocation = fields.Float( + compute='_compute_group_days_allocation', string='Days Allocated') + group_days_leave = fields.Float( + compute='_compute_group_days_leave', string='Group Leaves') company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.user.company_id) validation_type = fields.Selection([ ('hr', 'Human Resource officer'), ('manager', 'Employee Manager'), ('both', 'Double Validation')], default='hr', string='Validation By') - employee_applicability = fields.Selection([ - ('both', 'Both'), - ('leave', 'Can be requested'), - ('allocation', 'Can be allocated')], - default=lambda self: 'leave' if self.limit else 'both', - string='Mode', help='This leave type will be available on Leave / Allocation request based on selected value') + allocation_type = fields.Selection([ + ('fixed', 'Fixed by HR'), + ('fixed_allocation', 'Fixed by HR + allocation request'), + ('no', 'No allocation')], + default='fixed', string='Mode', + help='\tFixed by HR: allocated by HR and cannot be bypassed; users can request leaves;' + '\tFixed by HR + allocation request: allocated by HR and users can request leaves and allocations;' + '\tNo allocation: no allocation by default, users can freely request leaves;') validity_start = fields.Date("Start Date", default=fields.Date.today, help='Adding validity to types of leaves so that it cannot be selected outside this time period') validity_stop = fields.Date("End Date") @@ -74,18 +80,9 @@ class HolidaysType(models.Model): time_type = fields.Selection([('leave', 'Leave'), ('other', 'Other')], default='leave', string="Kind of Leave", help="Whether this should be computed as a holiday or as work time (eg: formation)") request_unit = fields.Selection([ - ('day', 'Day'), ('half', 'Half-day'), ('hour', 'Hours')], + ('day', 'Day'), ('hour', 'Hours')], default='day', string='Take Leaves in', required=True) - accrual = fields.Boolean('Accrual', default=False, - help='This option forces this type of leave to be allocated accrually') unpaid = fields.Boolean('Is Unpaid', default=False) - negative = fields.Boolean('Allow Negative', help="This option allows to take more leaves than allocated") - balance_limit = fields.Float('Max Balance Limit', default=0, help="The maximum quantity of allocated days on this allocation, zero meaning infinite amount") - - _sql_constraints = [ - ('no_negative_balance_limit', "CHECK(balance_limit >= 0)", "The max balance limit cannot be negative"), - ('no_accrual_unpaid', 'CHECK(NOT (accrual AND unpaid))', "A leave type cannot be accrual and considered as unpaid leaves") - ] @api.multi @api.constrains('validity_start', 'validity_stop') @@ -96,29 +93,7 @@ class HolidaysType(models.Model): raise ValidationError(_("End of validity period should be greater than start of validity period")) @api.multi - @api.constrains('balance_limit', 'accrual') - def _check_balance_limit(self): - for leave_type in self: - if not leave_type.accrual and leave_type.balance_limit > 0: - raise ValidationError(_("Max balance limit can only be set for accrual leaves")) - - @api.onchange('limit') - def _onchange_limit(self): - if self.limit: - self.employee_applicability = 'leave' - self.accrual = False - - @api.onchange('accrual') - def _onchange_accrual(self): - if self.accrual: - self.limit = False - self.employee_applicability = 'both' - else: - self.negative = False - self.balance_limit = 0 - - @api.multi - @api.depends('validity_start', 'validity_stop', 'limit') + @api.depends('validity_start', 'validity_stop') def _compute_valid(self): dt = self._context.get('default_date_from') or fields.Datetime.now() @@ -193,6 +168,30 @@ class HolidaysType(models.Model): holiday_status.remaining_leaves = result.get('remaining_leaves', 0) holiday_status.virtual_remaining_leaves = result.get('virtual_remaining_leaves', 0) + @api.multi + def _compute_group_days_allocation(self): + grouped_res = self.env['hr.leave.allocation'].read_group( + [('holiday_status_id', 'in', self.ids), ('holiday_type', '!=', 'employee'), ('state', '=', 'validate'), + ('date_from', '>=', fields.Datetime.to_string(datetime.datetime.now().replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0)))], + ['holiday_status_id', 'number_of_days_temp'], + ['holiday_status_id'], + ) + grouped_dict = dict((data['holiday_status_id'][0], data['number_of_days_temp']) for data in grouped_res) + for allocation in self: + allocation.group_days_allocation = grouped_dict.get(allocation.id, 0) + + @api.multi + def _compute_group_days_leave(self): + grouped_res = self.env['hr.leave'].read_group( + [('holiday_status_id', 'in', self.ids), ('holiday_type', '=', 'employee'), ('state', '=', 'validate'), + ('date_from', '>=', fields.Datetime.to_string(datetime.datetime.now().replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0)))], + ['holiday_status_id'], + ['holiday_status_id'], + ) + grouped_dict = dict((data['holiday_status_id'][0], data['holiday_status_id_count']) for data in grouped_res) + for allocation in self: + allocation.group_days_leave = grouped_dict.get(allocation.id, 0) + @api.multi def name_get(self): if not self._context.get('employee_id'): @@ -201,7 +200,7 @@ class HolidaysType(models.Model): res = [] for record in self: name = record.name - if not record.limit: + if record.allocation_type != 'no': name = "%(name)s (%(count)s)" % { 'name': name, 'count': _('%g remaining out of %g') % (float_round(record.virtual_remaining_leaves, precision_digits=2) or 0.0, float_round(record.max_leaves, precision_digits=2) or 0.0) @@ -214,7 +213,7 @@ class HolidaysType(models.Model): """ Override _search to order the results, according to some employee. The order is the following - - limit (limited leaves first, such as Legal Leaves) + - allocation fixed first, then allowing allocation, then free allocation - virtual remaining leaves (higher the better, so using reverse on sorted) This override is necessary because those fields are not stored and depends @@ -225,6 +224,34 @@ class HolidaysType(models.Model): 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) + sort_key = lambda l: (l.allocation_type == 'fixed', l.allocation_type == 'fixed_allocation', l.virtual_remaining_leaves) return leaves.sorted(key=sort_key, reverse=True).ids return leave_ids + + @api.multi + def action_see_days_allocated(self): + self.ensure_one() + action = self.env.ref('hr_holidays.hr_leave_allocation_action_all').read()[0] + action['domain'] = [ + ('holiday_type', '!=', 'employee'), + ('holiday_status_id', '=', self.ids[0]), + ('date_from', '>=', fields.Datetime.to_string(datetime.datetime.now().replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0))) + ] + action['context'] = { + 'default_holiday_type': 'department', + 'default_holiday_status_id': self.ids[0], + } + return action + + @api.multi + def action_see_group_leaves(self): + self.ensure_one() + action = self.env.ref('hr_holidays.hr_leave_action_all').read()[0] + action['domain'] = [ + ('holiday_status_id', '=', self.ids[0]), + ('date_from', '>=', fields.Datetime.to_string(datetime.datetime.now().replace(month=1, day=1, hour=0, minute=0, second=0, microsecond=0))) + ] + action['context'] = { + 'default_holiday_status_id': self.ids[0], + } + return action diff --git a/addons/hr_holidays/tests/test_access_rights.py b/addons/hr_holidays/tests/test_access_rights.py index 0631fa10006..997bbf5bbcc 100644 --- a/addons/hr_holidays/tests/test_access_rights.py +++ b/addons/hr_holidays/tests/test_access_rights.py @@ -17,8 +17,8 @@ class TestLeavesRights(TestHrHolidaysBase): super(TestLeavesRights, self).setUp() self.leave_type = self.env['hr.leave.type'].create({ 'name': 'Unlimited', - 'limit': True, 'validation_type': 'hr', + 'allocation_type': 'no', }) self.rd_dept.manager_id = False self.hr_dept.manager_id = False @@ -316,9 +316,9 @@ class TestMultiCompany(TestHrHolidaysBase): }) self.leave_type = self.env['hr.leave.type'].create({ 'name': 'Unlimited - Company New', - 'limit': True, 'company_id': self.new_company.id, 'validation_type': 'hr', + 'allocation_type': 'no', }) self.rd_dept.manager_id = False self.hr_dept.manager_id = False diff --git a/addons/hr_holidays/tests/test_accrual_allocations.py b/addons/hr_holidays/tests/test_accrual_allocations.py index 8250f88f0a7..06ce87d49be 100644 --- a/addons/hr_holidays/tests/test_accrual_allocations.py +++ b/addons/hr_holidays/tests/test_accrual_allocations.py @@ -19,13 +19,12 @@ class TestAccrualAllocations(TestHrHolidaysBase): self.accrual_type = LeaveType.create({ 'name': 'accrual', - 'limit': False, - 'accrual': True, + 'allocation_type': 'fixed', }) self.unpaid_type = LeaveType.create({ 'name': 'unpaid', - 'limit': True, + 'allocation_type': 'no', 'unpaid': True, }) @@ -45,40 +44,30 @@ class TestAccrualAllocations(TestHrHolidaysBase): """ % (newdate, id)) def test_accrual_base_no_leaves(self): - """ - Test if we can allocate some leaves accrually to an employee - """ - Allocation = self.env['hr.leave.allocation'].sudo(self.user_hrmanager_id).with_context(tracking_disable=True) - - alloc_0 = Allocation.create({ + """ Test if we can allocate some leaves accrually to an employee """ + alloc = self.env['hr.leave.allocation'].sudo(self.user_hrmanager_id).with_context(tracking_disable=True).create({ 'name': 'Accrual allocation for employee', 'employee_id': self.employee_emp_id, 'holiday_status_id': self.accrual_type.id, + 'accrual': True, 'number_of_days_temp': 0, 'number_per_interval': 1, 'interval_number': 1, 'unit_per_interval': 'days', 'interval_unit': 'weeks', }) + alloc.action_approve() + alloc._update_accrual() - alloc_0.action_approve() - - Allocation._update_accrual() - - self.assertEqual(alloc_0.number_of_days, 1, 'Employee should have been allocated one leave day') + self.assertEqual(alloc.number_of_days, 1, 'Employee should have been allocated one leave day') def test_accrual_base_leaves(self): - """ - Test if the accrual allocation take the unpaid leaves into account - when allocating leaves - """ - Allocation = self.env['hr.leave.allocation'].sudo(self.user_hrmanager_id).with_context(tracking_disable=True) - Leave = self.env['hr.leave'].sudo(self.user_hrmanager_id).with_context(tracking_disable=True) - - alloc_0 = Allocation.create({ + """ Test if the accrual allocation take the unpaid leaves into account when allocating leaves """ + alloc = self.env['hr.leave.allocation'].sudo(self.user_hrmanager_id).with_context(tracking_disable=True).create({ 'name': 'Accrual allocation for employee with leaves', 'employee_id': self.employee_hruser_id, 'holiday_status_id': self.accrual_type.id, + 'accrual': True, 'number_of_days_temp': 0, 'number_per_interval': 1, 'interval_number': 1, @@ -86,13 +75,13 @@ class TestAccrualAllocations(TestHrHolidaysBase): 'interval_unit': 'weeks', }) - alloc_0.action_approve() + alloc.action_approve() employee = self.env['hr.employee'].browse(self.employee_hruser_id) # Getting the previous work date df = employee.resource_calendar_id.plan_days(-2, fields.Datetime.now()).date() - leave_0 = Leave.create({ + leave = self.env['hr.leave'].sudo(self.user_hrmanager_id).with_context(tracking_disable=True).create({ 'name': 'Leave for hruser', 'employee_id': self.employee_hruser_id, 'holiday_status_id': self.unpaid_type.id, @@ -101,11 +90,11 @@ class TestAccrualAllocations(TestHrHolidaysBase): 'number_of_days_temp': 1, }) - leave_0.action_approve() + leave.action_approve() - Allocation._update_accrual() + alloc._update_accrual() - self.assertEqual(alloc_0.number_of_days, .8, 'As employee took some unpaid leaves last week, he should be allocated only .8 days') + self.assertEqual(alloc.number_of_days, .8, 'As employee took some unpaid leaves last week, he should be allocated only .8 days') def test_accrual_many(self): """ @@ -117,6 +106,7 @@ class TestAccrualAllocations(TestHrHolidaysBase): 'name': '1 day per 2 weeks', 'employee_id': self.employee_emp_id, 'holiday_status_id': self.accrual_type.id, + 'accrual': True, 'number_of_days_temp': 0, 'number_per_interval': 1, 'interval_number': 2, @@ -128,6 +118,7 @@ class TestAccrualAllocations(TestHrHolidaysBase): 'name': '4 hours per week', 'employee_id': self.employee_emp_id, 'holiday_status_id': self.accrual_type.id, + 'accrual': True, 'number_of_days_temp': 0, 'number_per_interval': 4, 'interval_number': 1, @@ -139,6 +130,7 @@ class TestAccrualAllocations(TestHrHolidaysBase): 'name': '2 day per 1 month', 'employee_id': self.employee_emp_id, 'holiday_status_id': self.accrual_type.id, + 'accrual': True, 'number_of_days_temp': 0, 'number_per_interval': 2, 'interval_number': 1, @@ -150,6 +142,7 @@ class TestAccrualAllocations(TestHrHolidaysBase): 'name': '20 days per year', 'employee_id': self.employee_emp_id, 'holiday_status_id': self.accrual_type.id, + 'accrual': True, 'number_of_days_temp': 0, 'number_per_interval': 20, 'interval_number': 1, @@ -179,6 +172,7 @@ class TestAccrualAllocations(TestHrHolidaysBase): 'name': 'one shot one kill', 'employee_id': self.employee_emp_id, 'holiday_status_id': self.accrual_type.id, + 'accrual': True, 'number_of_days_temp': 0, 'number_per_interval': 1, 'interval_number': 1, @@ -193,29 +187,23 @@ class TestAccrualAllocations(TestHrHolidaysBase): self.assertEqual(alloc_0.number_of_days, 0, 'Employee is new he should not get any accrual leaves') def test_accrual_multi(self): - """ - Test if the cron does not allocate leaves every time it's called but only - when necessary - """ - Allocation = self.env['hr.leave.allocation'].sudo(self.user_hrmanager_id).with_context(tracking_disable=True) - - alloc_0 = Allocation.create({ + """ Test if the cron does not allocate leaves every time it's called but only when necessary """ + alloc = self.env['hr.leave.allocation'].sudo(self.user_hrmanager_id).with_context(tracking_disable=True).create({ 'name': '2 days per week', 'employee_id': self.employee_emp_id, 'holiday_status_id': self.accrual_type.id, + 'accrual': True, 'number_of_days_temp': 0, 'number_per_interval': 1, 'interval_number': 2, 'unit_per_interval': 'days', 'interval_unit': 'weeks', }) + alloc.action_approve() + alloc._update_accrual() + alloc._update_accrual() - alloc_0.action_approve() - - Allocation._update_accrual() - Allocation._update_accrual() - - self.assertEqual(alloc_0.number_of_days, 1, 'Cron only allocates 1 days every two weeks') + self.assertEqual(alloc.number_of_days, 1, 'Cron only allocates 1 days every two weeks') def test_accrual_validation(self): """ @@ -242,32 +230,20 @@ class TestAccrualAllocations(TestHrHolidaysBase): self.assertEqual(alloc_0.number_of_days, 0, 'Cron validity passed, should not allocate any leave') def test_accrual_balance_limit(self): - """ - Test if accrual allocation does not allocate more than the balance limit - """ - Allocation = self.env['hr.leave.allocation'].sudo(self.user_hrmanager_id).with_context(tracking_disable=True) - LeaveType = self.env['hr.leave.type'].sudo(self.user_hrmanager_id).with_context(tracking_disable=True) - - accrual_max_five = LeaveType.create({ - 'name': 'max 5 days', - 'limit': False, - 'accrual': True, - 'balance_limit': 5, - }) - - allocation = Allocation.create({ + """ Test if accrual allocation does not allocate more than the balance limit""" + allocation = self.env['hr.leave.allocation'].sudo(self.user_hrmanager_id).with_context(tracking_disable=True).create({ 'name': 'accrual 5 max', 'employee_id': self.employee_emp_id, - 'holiday_status_id': accrual_max_five.id, + 'holiday_status_id': self.accrual_type.id, + 'accrual': True, + 'accrual_limit': 5, 'number_of_days_temp': 0, 'number_per_interval': 6, 'interval_number': 1, 'unit_per_interval': 'days', 'interval_unit': 'weeks', }) - allocation.action_approve() - - Allocation._update_accrual() + allocation._update_accrual() self.assertEqual(allocation.number_of_days, 5, 'Should have allocated only 5 days as balance limit is 5') diff --git a/addons/hr_holidays/tests/test_holidays_flow.py b/addons/hr_holidays/tests/test_holidays_flow.py index 0c3cff00ad9..03cf5b2b7a0 100644 --- a/addons/hr_holidays/tests/test_holidays_flow.py +++ b/addons/hr_holidays/tests/test_holidays_flow.py @@ -33,34 +33,34 @@ class TestHolidaysFlow(TestHrHolidaysBase): with self.assertRaises(AccessError): HolidaysStatus.sudo(self.user_hruser_id).create({ 'name': 'UserCheats', - 'limit': True, + 'allocation_type': 'no', }) # HrManager creates some holiday statuses HolidayStatusManagerGroup = HolidaysStatus.sudo(self.user_hrmanager_id) HolidayStatusManagerGroup.create({ 'name': 'WithMeetingType', - 'limit': True, + 'allocation_type': 'no', 'categ_id': self.env['calendar.event.type'].sudo(self.user_hrmanager_id).create({'name': 'NotLimitedMeetingType'}).id }) self.holidays_status_1 = HolidayStatusManagerGroup.create({ 'name': 'NotLimitedHR', - 'limit': True, + 'allocation_type': 'no', 'validation_type': 'hr', }) self.holidays_status_2 = HolidayStatusManagerGroup.create({ 'name': 'Limited', - 'limit': False, + 'allocation_type': 'fixed', 'validation_type': 'both', }) self.holidays_status_3 = HolidayStatusManagerGroup.create({ 'name': 'NotLimitedManager', - 'limit': True, + 'allocation_type': 'no', 'validation_type': 'manager', }) self.holiday_status_4 = HolidayStatusManagerGroup.create({ 'name': 'TimeNotLimited', - 'limit': True, + 'allocation_type': 'no', 'validation_type': 'manager', 'validity_start': fields.Datetime.from_string('2017-01-01 00:00:00'), 'validity_stop': fields.Datetime.from_string('2017-06-01 00:00:00'), diff --git a/addons/hr_holidays/tests/test_hr_leave_type.py b/addons/hr_holidays/tests/test_hr_leave_type.py index 3232ddc39f0..72fbaa4aee6 100644 --- a/addons/hr_holidays/tests/test_hr_leave_type.py +++ b/addons/hr_holidays/tests/test_hr_leave_type.py @@ -12,8 +12,8 @@ class TestHrLeaveType(TestHrHolidaysBase): def test_time_type(self): leave_type = self.env['hr.leave.type'].create({ 'name': 'Legal Leaves', - 'limit': True, 'time_type': 'leave', + 'allocation_type': 'no', }) leave_1 = self.env['hr.leave'].create({ diff --git a/addons/hr_holidays/views/hr_leave_allocation_views.xml b/addons/hr_holidays/views/hr_leave_allocation_views.xml index 33a9eccad9c..dd167e073c7 100644 --- a/addons/hr_holidays/views/hr_leave_allocation_views.xml +++ b/addons/hr_holidays/views/hr_leave_allocation_views.xml @@ -55,7 +55,6 @@
-
+ +
+

+
- - + - - - + + - - + + - + @@ -48,11 +61,6 @@ - - - - - @@ -88,7 +96,7 @@ - + diff --git a/addons/hr_holidays/views/hr_leave_views.xml b/addons/hr_holidays/views/hr_leave_views.xml index e182fde6329..910ad643279 100644 --- a/addons/hr_holidays/views/hr_leave_views.xml +++ b/addons/hr_holidays/views/hr_leave_views.xml @@ -111,24 +111,23 @@ Hours - - +
- From - - - + From + + + + attrs="{'readonly': [('state', '=', 'validate')], 'invisible': ['|', '|', ('leave_type_request_unit', '=', 'day'), '&', ('leave_type_request_unit', '=', 'half'), ('request_unit', '=', 'day'), '&', ('leave_type_request_unit', '=', 'hour'), ('request_unit', '!=', 'half')]}"/>
- To - - - + To + + + + attrs="{'readonly': [('state', '=', 'validate')], 'invisible': ['|', ('leave_type_request_unit', '!=', 'half'), ('request_unit', '!=', 'period')]}"/>
diff --git a/addons/project_timesheet_holidays/tests/test_timesheet_holidays.py b/addons/project_timesheet_holidays/tests/test_timesheet_holidays.py index a7e484f18d4..a17deae822e 100644 --- a/addons/project_timesheet_holidays/tests/test_timesheet_holidays.py +++ b/addons/project_timesheet_holidays/tests/test_timesheet_holidays.py @@ -16,7 +16,7 @@ class TestTimesheetHolidaysCreate(common.TransactionCase): """Ensure that when a status is created, it fullfills the project and task constrains""" status = self.env['hr.leave.type'].create({ 'name': 'A nice Leave Type', - 'limit': True + 'allocation_type': 'no' }) company = self.env.user.company_id @@ -42,14 +42,14 @@ class TestTimesheetHolidays(TestTimesheet): self.hr_leave_type_with_ts = self.env['hr.leave.type'].create({ 'name': 'Leave Type with timesheet generation', - 'limit': True, + 'allocation_type': 'no', 'timesheet_generate': True, 'timesheet_project_id': self.internal_project.id, 'timesheet_task_id': self.internal_task_leaves.id, }) self.hr_leave_type_no_ts = self.env['hr.leave.type'].create({ 'name': 'Leave Type without timesheet generation', - 'limit': True, + 'allocation_type': 'no', 'timesheet_generate': False, 'timesheet_project_id': False, 'timesheet_task_id': False, diff --git a/addons/project_timesheet_holidays/views/hr_holidays_views.xml b/addons/project_timesheet_holidays/views/hr_holidays_views.xml index 1e010067a57..a74b6aca55a 100644 --- a/addons/project_timesheet_holidays/views/hr_holidays_views.xml +++ b/addons/project_timesheet_holidays/views/hr_holidays_views.xml @@ -6,12 +6,10 @@ hr.leave.type - - - - - - + + + +