diff options
| author | Anh Thao Pham (pta) <pta@odoo.com> | 2020-02-11 16:00:53 +0000 |
|---|---|---|
| committer | Yannick Tivisse <yti@odoo.com> | 2020-04-20 10:32:45 +0000 |
| commit | 3bd345597fa932abdaf780f72bacf331690f549b (patch) | |
| tree | b94e535b7ad2a7d4667595341ff66b5eded537cc /addons/hr_holidays/models | |
| parent | f50c3298bb938cd044636956719d986bb91160ae (diff) | |
[REF] hr*: convert onchange methods to stored-editable computed fields
Impacted modules:
hr, hr_contract, hr_recruitment, hr_payroll, fleet, hr_skills, hr_appraisal, ....
Several onchanges have been converted to computed fields in the following modules :
Community :
- hr
- hr_contract
- hr_recruitment
- hr_work_entry
- hr_maintenance
- hr_expense
- hr_expense_check
- hr_holidays
- sale_expense
- account_analytic_default_hr_expense
Enterprise:
- hr_contract_salary
- hr_referral
- hr_payroll
- hr_payroll_expense
- test_l10n_be_hr_payroll_account
There are still 2 onchanges with complex behavior that couldn't be converted easily:
- an onchange that updates "tz" (timezone) that is defined as a related field
to "resource_id.tz". Apparently it is useless except to initialize the default
value of "tz".
- an onchange that updates "name" that is defined as a related field to
"resource_id.name".
the applicant.
closes odoo/odoo#45414
Taskid: 2169099
Related: odoo/enterprise#8572
Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
Diffstat (limited to 'addons/hr_holidays/models')
| -rw-r--r-- | addons/hr_holidays/models/hr_leave.py | 346 | ||||
| -rw-r--r-- | addons/hr_holidays/models/hr_leave_allocation.py | 172 |
2 files changed, 258 insertions, 260 deletions
diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py index 31d27441f34..8f28767c252 100644 --- a/addons/hr_holidays/models/hr_leave.py +++ b/addons/hr_holidays/models/hr_leave.py @@ -24,7 +24,7 @@ from odoo.osv import expression _logger = logging.getLogger(__name__) # Used to agglomerate the attendances in order to find the hour_from and hour_to -# See _onchange_request_parameters +# See _compute_date_from_to DummyAttendance = namedtuple('DummyAttendance', 'hour_from, hour_to, dayofweek, day_period, week_type') class HolidaysRequest(models.Model): @@ -80,9 +80,6 @@ class HolidaysRequest(models.Model): defaults['state'] = 'confirm' if lt and lt.leave_validation_type != 'no_validation' else 'draft' return defaults - 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_get_request_parameters(self, values): new_values = dict(values) global_from, global_to = False, False @@ -112,7 +109,7 @@ class HolidaysRequest(models.Model): ('refuse', 'Refused'), ('validate1', 'Second Approval'), ('validate', 'Approved') - ], string='Status', readonly=True, tracking=True, copy=False, default='draft', + ], string='Status', compute='_compute_state', store=True, tracking=True, copy=False, help="The status is set to 'To Submit', when a time off request is created." + "\nThe status is 'To Approve', when time off request is confirmed by user." + "\nThe status is 'Refused', when time off request is refused by manager." + @@ -120,35 +117,36 @@ class HolidaysRequest(models.Model): payslip_status = fields.Boolean('Reported in last payslips', help='Green this button when the time off has been taken into account in the payslip.', copy=False) report_note = fields.Text('HR Comments', copy=False, groups="hr_holidays.group_hr_holidays_manager") user_id = fields.Many2one('res.users', string='User', related='employee_id.user_id', related_sudo=True, compute_sudo=True, store=True, default=lambda self: self.env.uid, readonly=True) - manager_id = fields.Many2one('hr.employee') + manager_id = fields.Many2one('hr.employee', compute='_compute_from_employee_id', store=True, readonly=False) # leave type configuration holiday_status_id = fields.Many2one( - "hr.leave.type", string="Time Off Type", required=True, readonly=True, - states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, + "hr.leave.type", compute='_compute_from_employee_id', store=True, string="Time Off Type", required=True, readonly=False, + states={'cancel': [('readonly', True)], 'refuse': [('readonly', True)], 'validate1': [('readonly', True)], 'validate': [('readonly', True)]}, domain=[('valid', '=', True)]) validation_type = fields.Selection(string='Validation Type', related='holiday_status_id.leave_validation_type', readonly=False) # HR data employee_id = fields.Many2one( - 'hr.employee', string='Employee', index=True, readonly=True, ondelete="restrict", - states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, default=_default_employee, tracking=True) + 'hr.employee', compute='_compute_from_holiday_type', store=True, string='Employee', index=True, readonly=False, ondelete="restrict", + states={'cancel': [('readonly', True)], 'refuse': [('readonly', True)], 'validate1': [('readonly', True)], 'validate': [('readonly', True)]}, + tracking=True) tz_mismatch = fields.Boolean(compute='_compute_tz_mismatch') tz = fields.Selection(_tz_get, compute='_compute_tz') department_id = fields.Many2one( - 'hr.department', string='Department', readonly=True, - states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}) + 'hr.department', compute='_compute_department_id', store=True, string='Department', readonly=False, + states={'cancel': [('readonly', True)], 'refuse': [('readonly', True)], 'validate1': [('readonly', True)], 'validate': [('readonly', True)]}) notes = fields.Text('Reasons', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}) # duration date_from = fields.Datetime( - 'Start Date', readonly=True, index=True, copy=False, required=True, + 'Start Date', compute='_compute_date_from_to', store=True, readonly=False, index=True, copy=False, required=True, tracking=True, default=fields.Datetime.now, - states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, tracking=True) + states={'cancel': [('readonly', True)], 'refuse': [('readonly', True)], 'validate1': [('readonly', True)], 'validate': [('readonly', True)]}) date_to = fields.Datetime( - 'End Date', readonly=True, copy=False, required=True, + 'End Date', compute='_compute_date_from_to', store=True, readonly=False, copy=False, required=True, tracking=True, default=fields.Datetime.now, - states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, tracking=True) + states={'cancel': [('readonly', True)], 'refuse': [('readonly', True)], 'validate1': [('readonly', True)], 'validate': [('readonly', True)]}) number_of_days = fields.Float( - 'Duration (Days)', copy=False, tracking=True, + 'Duration (Days)', compute='_compute_number_of_days', store=True, readonly=False, copy=False, tracking=True, help='Number of days of the time off request. Used in the calculation. To manually correct the duration, use this field.') number_of_days_display = fields.Float( 'Duration in days', compute='_compute_number_of_days_display', readonly=True, @@ -171,10 +169,10 @@ class HolidaysRequest(models.Model): 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') category_id = fields.Many2one( - 'hr.employee.category', string='Employee Tag', readonly=True, + 'hr.employee.category', compute='_compute_from_holiday_type', store=True, string='Employee Tag', states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, help='Category of Employee') mode_company_id = fields.Many2one( - 'res.company', string='Company Mode', readonly=True, + 'res.company', compute='_compute_from_holiday_type', store=True, string='Company Mode', states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}) first_approver_id = fields.Many2one( 'hr.employee', string='First Approval', readonly=True, copy=False, @@ -246,9 +244,9 @@ class HolidaysRequest(models.Model): ('am', 'Morning'), ('pm', 'Afternoon')], string="Date Period Start", default='am') # request type - request_unit_half = fields.Boolean('Half Day') - request_unit_hours = fields.Boolean('Custom Hours') - request_unit_custom = fields.Boolean('Days-long custom hours') + request_unit_half = fields.Boolean('Half Day', compute='_compute_request_unit_half', store=True, readonly=False) + request_unit_hours = fields.Boolean('Custom Hours', compute='_compute_request_unit_hours', store=True, readonly=False) + request_unit_custom = fields.Boolean('Days-long custom hours', compute='_compute_request_unit_custom', store=True, readonly=False) _sql_constraints = [ ('type_value', @@ -297,158 +295,153 @@ class HolidaysRequest(models.Model): leaves = self.search(domain) return [('id', 'in', leaves.ids)] - @api.onchange('holiday_status_id') - def _onchange_holiday_status_id(self): - self.request_unit_half = False - self.request_unit_hours = False - self.request_unit_custom = False - self.state = 'confirm' if self.validation_type != 'no_validation' else 'draft' - - @api.onchange('request_date_from_period', 'request_hour_from', 'request_hour_to', - 'request_date_from', 'request_date_to', - 'employee_id') - def _onchange_request_parameters(self): - if not self.request_date_from: - self.date_from = False - return + @api.depends('holiday_status_id') + def _compute_state(self): + for holiday in self: + holiday.state = 'confirm' if holiday.validation_type != 'no_validation' else 'draft' + + @api.depends('request_date_from_period', 'request_hour_from', 'request_hour_to', 'request_date_from', 'request_date_to', + 'request_unit_half', 'request_unit_hours', 'request_unit_custom', 'employee_id') + def _compute_date_from_to(self): + for holiday in self: + if holiday.request_date_from > holiday.request_date_to: + holiday.request_date_to = holiday.request_date_from + if not holiday.request_date_from: + holiday.date_from = False + elif not holiday.request_unit_half and not holiday.request_unit_hours and not holiday.request_date_to: + holiday.date_to = False + else: + if holiday.request_unit_half or holiday.request_unit_hours: + holiday.request_date_to = holiday.request_date_from + resource_calendar_id = holiday.employee_id.resource_calendar_id or self.env.company.resource_calendar_id + domain = [('calendar_id', '=', resource_calendar_id.id), ('display_type', '=', False)] + attendances = self.env['resource.calendar.attendance'].read_group(domain, ['ids:array_agg(id)', 'hour_from:min(hour_from)', 'hour_to:max(hour_to)', 'week_type', 'dayofweek', 'day_period'], ['week_type', 'dayofweek', 'day_period'], lazy=False) + + # Must be sorted by dayofweek ASC and day_period DESC + attendances = sorted([DummyAttendance(group['hour_from'], group['hour_to'], group['dayofweek'], group['day_period'], group['week_type']) for group in attendances], key=lambda att: (att.dayofweek, att.day_period != 'morning')) + + default_value = DummyAttendance(0, 0, 0, 'morning', False) + + if resource_calendar_id.two_weeks_calendar: + # find week type of start_date + start_week_type = int(math.floor((holiday.request_date_from.toordinal() - 1) / 7) % 2) + attendance_actual_week = [att for att in attendances if att.week_type is False or int(att.week_type) == start_week_type] + attendance_actual_next_week = [att for att in attendances if att.week_type is False or int(att.week_type) != start_week_type] + # First, add days of actual week coming after date_from + attendance_filtred = [att for att in attendance_actual_week if int(att.dayofweek) >= holiday.request_date_from.weekday()] + # Second, add days of the other type of week + attendance_filtred += list(attendance_actual_next_week) + # Third, add days of actual week (to consider days that we have remove first because they coming before date_from) + attendance_filtred += list(attendance_actual_week) + + end_week_type = int(math.floor((holiday.request_date_to.toordinal() - 1) / 7) % 2) + attendance_actual_week = [att for att in attendances if att.week_type is False or int(att.week_type) == end_week_type] + attendance_actual_next_week = [att for att in attendances if att.week_type is False or int(att.week_type) != end_week_type] + attendance_filtred_reversed = list(reversed([att for att in attendance_actual_week if int(att.dayofweek) <= holiday.request_date_to.weekday()])) + attendance_filtred_reversed += list(reversed(attendance_actual_next_week)) + attendance_filtred_reversed += list(reversed(attendance_actual_week)) + + # find first attendance coming after first_day + attendance_from = attendance_filtred[0] + # find last attendance coming before last_day + attendance_to = attendance_filtred_reversed[0] + else: + # find first attendance coming after first_day + attendance_from = next((att for att in attendances if int(att.dayofweek) >= holiday.request_date_from.weekday()), attendances[0] if attendances else default_value) + # find last attendance coming before last_day + attendance_to = next((att for att in reversed(attendances) if int(att.dayofweek) <= holiday.request_date_to.weekday()), attendances[-1] if attendances else default_value) + + compensated_request_date_from = holiday.request_date_from + compensated_request_date_to = holiday.request_date_to + + if holiday.request_unit_half: + if holiday.request_date_from_period == 'am': + hour_from = float_to_time(attendance_from.hour_from) + hour_to = float_to_time(attendance_from.hour_to) + else: + hour_from = float_to_time(attendance_to.hour_from) + hour_to = float_to_time(attendance_to.hour_to) + elif holiday.request_unit_hours: + hour_from = float_to_time(float(holiday.request_hour_from)) + hour_to = float_to_time(float(holiday.request_hour_to)) + elif holiday.request_unit_custom: + hour_from = holiday.date_from.time() + hour_to = holiday.date_to.time() + compensated_request_date_from = holiday._adjust_date_based_on_tz(holiday.request_date_from, hour_from) + compensated_request_date_to = holiday._adjust_date_based_on_tz(holiday.request_date_to, hour_to) + else: + hour_from = float_to_time(attendance_from.hour_from) + hour_to = float_to_time(attendance_to.hour_to) - if self.request_unit_half or self.request_unit_hours: - self.request_date_to = self.request_date_from + holiday.date_from = timezone(holiday.tz).localize(datetime.combine(compensated_request_date_from, hour_from)).astimezone(UTC).replace(tzinfo=None) + holiday.date_to = timezone(holiday.tz).localize(datetime.combine(compensated_request_date_to, hour_to)).astimezone(UTC).replace(tzinfo=None) - if not self.request_date_to: - self.date_to = False - return + @api.depends('holiday_status_id', 'request_unit_hours', 'request_unit_custom') + def _compute_request_unit_half(self): + for holiday in self: + if holiday.holiday_status_id or holiday.request_unit_hours or holiday.request_unit_custom: + holiday.request_unit_half = False - if self.request_date_from > self.request_date_to: - self.request_date_to = self.request_date_from - - resource_calendar_id = self.employee_id.resource_calendar_id or self.env.company.resource_calendar_id - domain = [('calendar_id', '=', resource_calendar_id.id), ('display_type', '=', False)] - attendances = self.env['resource.calendar.attendance'].read_group(domain, ['ids:array_agg(id)', 'hour_from:min(hour_from)', 'hour_to:max(hour_to)', 'week_type', 'dayofweek', 'day_period'], ['week_type', 'dayofweek', 'day_period'], lazy=False) - - # Must be sorted by dayofweek ASC and day_period DESC - attendances = sorted([DummyAttendance(group['hour_from'], group['hour_to'], group['dayofweek'], group['day_period'], group['week_type']) for group in attendances], key=lambda att: (att.dayofweek, att.day_period != 'morning')) - - default_value = DummyAttendance(0, 0, 0, 'morning', False) - - if resource_calendar_id.two_weeks_calendar: - # find week type of start_date - start_week_type = int(math.floor((self.request_date_from.toordinal() - 1) / 7) % 2) - attendance_actual_week = [att for att in attendances if att.week_type is False or int(att.week_type) == start_week_type] - attendance_actual_next_week = [att for att in attendances if att.week_type is False or int(att.week_type) != start_week_type] - # First, add days of actual week coming after date_from - attendance_filtred = [att for att in attendance_actual_week if int(att.dayofweek) >= self.request_date_from.weekday()] - # Second, add days of the other type of week - attendance_filtred += list(attendance_actual_next_week) - # Third, add days of actual week (to consider days that we have remove first because they coming before date_from) - attendance_filtred += list(attendance_actual_week) - - end_week_type = int(math.floor((self.request_date_to.toordinal() - 1) / 7) % 2) - attendance_actual_week = [att for att in attendances if att.week_type is False or int(att.week_type) == end_week_type] - attendance_actual_next_week = [att for att in attendances if att.week_type is False or int(att.week_type) != end_week_type] - attendance_filtred_reversed = list(reversed([att for att in attendance_actual_week if int(att.dayofweek) <= self.request_date_to.weekday()])) - attendance_filtred_reversed += list(reversed(attendance_actual_next_week)) - attendance_filtred_reversed += list(reversed(attendance_actual_week)) - - # find first attendance coming after first_day - attendance_from = attendance_filtred[0] - # find last attendance coming before last_day - attendance_to = attendance_filtred_reversed[0] - else: - # find first attendance coming after first_day - attendance_from = next((att for att in attendances if int(att.dayofweek) >= self.request_date_from.weekday()), attendances[0] if attendances else default_value) - # find last attendance coming before last_day - attendance_to = next((att for att in reversed(attendances) if int(att.dayofweek) <= self.request_date_to.weekday()), attendances[-1] if attendances else default_value) - - compensated_request_date_from = self.request_date_from - compensated_request_date_to = self.request_date_to - - if self.request_unit_half: - if self.request_date_from_period == 'am': - hour_from = float_to_time(attendance_from.hour_from) - hour_to = float_to_time(attendance_from.hour_to) + @api.depends('holiday_status_id', 'request_unit_half', 'request_unit_custom') + def _compute_request_unit_hours(self): + for holiday in self: + if holiday.holiday_status_id or holiday.request_unit_half or holiday.request_unit_custom: + holiday.request_unit_hours = False + + @api.depends('holiday_status_id', 'request_unit_half', 'request_unit_hours') + def _compute_request_unit_custom(self): + for holiday in self: + if holiday.holiday_status_id or holiday.request_unit_half or holiday.request_unit_hours: + holiday.request_unit_custom = False + + @api.depends('holiday_type') + def _compute_from_holiday_type(self): + for holiday in self: + if holiday.holiday_type == 'employee': + if not holiday.employee_id: + holiday.employee_id = self.env.user.employee_id + holiday.mode_company_id = False + holiday.category_id = False + elif holiday.holiday_type == 'company': + holiday.employee_id = False + if not holiday.mode_company_id: + holiday.mode_company_id = self.env.company.id + holiday.category_id = False + elif holiday.holiday_type == 'department': + holiday.employee_id = False + holiday.mode_company_id = False + holiday.category_id = False + elif holiday.holiday_type == 'category': + holiday.employee_id = False + holiday.mode_company_id = False else: - hour_from = float_to_time(attendance_to.hour_from) - hour_to = float_to_time(attendance_to.hour_to) - elif self.request_unit_hours: - hour_from = float_to_time(float(self.request_hour_from)) - hour_to = float_to_time(float(self.request_hour_to)) - elif self.request_unit_custom: - hour_from = self.date_from.time() - hour_to = self.date_to.time() - compensated_request_date_from = self._adjust_date_based_on_tz(self.request_date_from, hour_from) - compensated_request_date_to = self._adjust_date_based_on_tz(self.request_date_to, hour_to) - else: - hour_from = float_to_time(attendance_from.hour_from) - hour_to = float_to_time(attendance_to.hour_to) - - self.date_from = timezone(self.tz).localize(datetime.combine(compensated_request_date_from, hour_from)).astimezone(UTC).replace(tzinfo=None) - self.date_to = timezone(self.tz).localize(datetime.combine(compensated_request_date_to, hour_to)).astimezone(UTC).replace(tzinfo=None) - - self._onchange_leave_dates() - - @api.onchange('request_unit_half') - def _onchange_request_unit_half(self): - if self.request_unit_half: - self.request_unit_hours = False - self.request_unit_custom = False - self._onchange_request_parameters() - - @api.onchange('request_unit_hours') - def _onchange_request_unit_hours(self): - if self.request_unit_hours: - self.request_unit_half = False - self.request_unit_custom = False - self._onchange_request_parameters() - - @api.onchange('request_unit_custom') - def _onchange_request_unit_custom(self): - if self.request_unit_custom: - self.request_unit_half = False - self.request_unit_hours = False - self._onchange_request_parameters() - - @api.onchange('holiday_type') - def _onchange_type(self): - if self.holiday_type == 'employee': - if not self.employee_id: - self.employee_id = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1).id - self.mode_company_id = False - self.category_id = False - elif self.holiday_type == 'company': - self.employee_id = False - if not self.mode_company_id: - self.mode_company_id = self.env.company.id - self.category_id = False - elif self.holiday_type == 'department': - self.employee_id = False - self.mode_company_id = False - self.category_id = False - if not self.department_id: - self.department_id = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1).department_id.id - elif self.holiday_type == 'category': - self.employee_id = False - self.mode_company_id = False - self.department_id = False - - def _sync_employee_details(self): + holiday.employee_id = self.env.context.get('default_employee_id') or self.env.user.employee_id + + @api.depends('employee_id') + def _compute_from_employee_id(self): for holiday in self: holiday.manager_id = holiday.employee_id.parent_id.id + if holiday.employee_id.user_id != self.env.user and self._origin.employee_id != holiday.employee_id: + holiday.holiday_status_id = False + + @api.depends('employee_id', 'holiday_type') + def _compute_department_id(self): + for holiday in self: if holiday.employee_id: holiday.department_id = holiday.employee_id.department_id + elif holiday.holiday_type == 'department': + if not holiday.department_id: + holiday.department_id = self.env.user.employee_id.department_id + else: + holiday.department_id = False - @api.onchange('employee_id') - def _onchange_employee_id(self): - self._sync_employee_details() - if self.employee_id.user_id != self.env.user and self._origin.employee_id != self.employee_id: - self.holiday_status_id = False - - @api.onchange('date_from', 'date_to', 'employee_id') - def _onchange_leave_dates(self): - if self.date_from and self.date_to: - self.number_of_days = self._get_number_of_days(self.date_from, self.date_to, self.employee_id.id)['days'] - else: - self.number_of_days = 0 + @api.depends('date_from', 'date_to', 'employee_id') + def _compute_number_of_days(self): + for holiday in self: + if holiday.date_from and holiday.date_to: + holiday.number_of_days = holiday._get_number_of_days(holiday.date_from, holiday.date_to, holiday.employee_id.id)['days'] + else: + holiday.number_of_days = 0 @api.depends('tz') @api.depends_context('uid') @@ -459,12 +452,13 @@ class HolidaysRequest(models.Model): @api.depends('request_unit_custom', 'employee_id', 'holiday_type', 'department_id.company_id.resource_calendar_id.tz', 'mode_company_id.resource_calendar_id.tz') def _compute_tz(self): for leave in self: + tz = False if leave.request_unit_custom: tz = 'UTC' # custom -> already in UTC elif leave.holiday_type == 'employee': tz = leave.employee_id.tz elif leave.holiday_type == 'department': - tz= leave.department_id.company_id.resource_calendar_id.tz + tz = leave.department_id.company_id.resource_calendar_id.tz elif leave.holiday_type == 'company': tz = leave.mode_company_id.resource_calendar_id.tz leave.tz = tz or self.env.company.resource_calendar_id.tz or self.env.user.tz or 'UTC' @@ -710,15 +704,7 @@ class HolidaysRequest(models.Model): self._check_double_validation_rules(employee_id, values.get('state', False)) holiday = super(HolidaysRequest, self.with_context(mail_create_nosubscribe=True)).create(values) - if self._context.get('import_file'): - holiday._onchange_leave_dates() if not self._context.get('leave_fast_create'): - # FIXME remove these, as they should not be needed - if employee_id: - holiday.with_user(SUPERUSER_ID)._sync_employee_details() - if 'number_of_days' not in values and ('date_from' in values or 'date_to' in values): - holiday.with_user(SUPERUSER_ID)._onchange_leave_dates() - # Everything that is done here must be done using sudo because we might # have different create and write rights # eg : holidays_user can create a leave request with validation_type = 'manager' for someone else @@ -762,9 +748,6 @@ class HolidaysRequest(models.Model): for holiday in self: if employee_id: holiday.add_follower(employee_id) - self._sync_employee_details() - if 'number_of_days' not in values and ('date_from' in values or 'date_to' in values): - holiday._onchange_leave_dates() return result def unlink(self): @@ -924,7 +907,7 @@ class HolidaysRequest(models.Model): def action_validate(self): current_employee = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1) - if any(holiday.state not in ['confirm', 'validate1'] for holiday in self): + if any(holiday.state not in ['confirm', 'validate1'] and holiday.validation_type != 'no_validation' for holiday in self): raise UserError(_('Time off request must be confirmed in order to approve it.')) self.write({'state': 'validate'}) @@ -969,7 +952,8 @@ class HolidaysRequest(models.Model): 'date_to': holiday.date_from.date() + timedelta(days=-1), })[0] before_leave = self.env['hr.leave'].new(before_leave_vals) - before_leave._onchange_request_parameters() + before_leave._compute_date_from_to() + # Could happen for part-time contract, that time off is not necessary # anymore. # Imagine you work on monday-wednesday-friday only. @@ -992,7 +976,7 @@ class HolidaysRequest(models.Model): 'date_to': conflicting_leave.date_to.date(), })[0] after_leave = self.env['hr.leave'].new(after_leave_vals) - after_leave._onchange_request_parameters() + after_leave._compute_date_from_to() # Could happen for part-time contract, that time off is not necessary # anymore. if after_leave.date_from < after_leave.date_to: @@ -1004,7 +988,7 @@ class HolidaysRequest(models.Model): for split_leave in split_leaves: if target_state == 'draft': continue - if target_state == 'confirm': + if target_state == 'confirm' and split_leave.state == 'draft': split_leave.action_confirm() elif target_state == 'validate1': split_leave.action_confirm() diff --git a/addons/hr_holidays/models/hr_leave_allocation.py b/addons/hr_holidays/models/hr_leave_allocation.py index 0f69967cbeb..fd54a1c492f 100644 --- a/addons/hr_holidays/models/hr_leave_allocation.py +++ b/addons/hr_holidays/models/hr_leave_allocation.py @@ -26,9 +26,6 @@ class HolidaysAllocation(models.Model): _inherit = ['mail.thread', 'mail.activity.mixin'] _mail_post_access = 'read' - 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): if self.user_has_groups('hr_holidays.group_hr_holidays_user'): domain = [('valid', '=', True)] @@ -59,20 +56,20 @@ class HolidaysAllocation(models.Model): 'Start Date', readonly=True, index=True, copy=False, default=fields.Date.context_today, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, tracking=True) date_to = fields.Datetime( - 'End Date', readonly=True, copy=False, - states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, tracking=True) + 'End Date', compute='_compute_from_holiday_status_id', store=True, readonly=False, copy=False, tracking=True, + states={'cancel': [('readonly', True)], 'refuse': [('readonly', True)], 'validate1': [('readonly', True)], 'validate': [('readonly', True)]}) holiday_status_id = fields.Many2one( - "hr.leave.type", string="Time Off Type", required=True, readonly=True, - states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, - domain=_holiday_status_id_domain, default=_default_holiday_status_id) + "hr.leave.type", compute='_compute_from_employee_id', store=True, string="Time Off Type", required=True, readonly=False, + states={'cancel': [('readonly', True)], 'refuse': [('readonly', True)], 'validate1': [('readonly', True)], 'validate': [('readonly', True)]}, + domain=_holiday_status_id_domain) employee_id = fields.Many2one( - 'hr.employee', string='Employee', index=True, readonly=True, ondelete="restrict", - states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}, default=_default_employee, tracking=True) - manager_id = fields.Many2one('hr.employee', string='Manager', readonly=True) + 'hr.employee', compute='_compute_from_holiday_type', store=True, string='Employee', index=True, readonly=False, ondelete="restrict", tracking=True, + states={'cancel': [('readonly', True)], 'refuse': [('readonly', True)], 'validate1': [('readonly', True)], 'validate': [('readonly', True)]}) + manager_id = fields.Many2one('hr.employee', compute='_compute_from_employee_id', store=True, string='Manager') notes = fields.Text('Reasons', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}) # duration number_of_days = fields.Float( - 'Number of Days', tracking=True, default=1, + 'Number of Days', compute='_compute_from_holiday_status_id', store=True, readonly=False, tracking=True, help='Duration in days. Reference field to use when necessary.') number_of_days_display = fields.Float( 'Duration (days)', compute='_compute_number_of_days_display', @@ -109,14 +106,14 @@ class HolidaysAllocation(models.Model): "\n- By Department: all employees of the specified department" "\n- By Employee Tag: all employees of the specific employee group category") mode_company_id = fields.Many2one( - 'res.company', string='Company Mode', readonly=True, - states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}) + 'res.company', compute='_compute_from_holiday_type', store=True, string='Company Mode', readonly=False, + states={'cancel': [('readonly', True)], 'refuse': [('readonly', True)], 'validate1': [('readonly', True)], 'validate': [('readonly', True)]}) department_id = fields.Many2one( - 'hr.department', string='Department', readonly=True, + 'hr.department', compute='_compute_department_id', store=True, string='Department', 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)]}) + 'hr.employee.category', compute='_compute_from_holiday_type', store=True, string='Employee Tag', readonly=False, + states={'cancel': [('readonly', True)], 'refuse': [('readonly', True)], 'validate1': [('readonly', True)], 'validate': [('readonly', True)]}) # accrual configuration allocation_type = fields.Selection( [ @@ -125,18 +122,22 @@ class HolidaysAllocation(models.Model): ], string="Allocation Type", default="regular", required=True, 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) + number_per_interval = fields.Float("Number of unit per interval", compute='_compute_from_holiday_status_id', store=True, readonly=False, + states={'cancel': [('readonly', True)], 'refuse': [('readonly', True)], 'validate1': [('readonly', True)], 'validate': [('readonly', True)]}) + interval_number = fields.Integer("Number of unit between two intervals", compute='_compute_from_holiday_status_id', store=True, readonly=False, + states={'cancel': [('readonly', True)], 'refuse': [('readonly', True)], 'validate1': [('readonly', True)], 'validate': [('readonly', True)]}) unit_per_interval = fields.Selection([ ('hours', 'Hours'), ('days', 'Days') - ], string="Unit of time added at each interval", default='hours', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}) + ], compute='_compute_from_holiday_status_id', store=True, string="Unit of time added at each interval", readonly=False, + states={'cancel': [('readonly', True)], 'refuse': [('readonly', True)], 'validate1': [('readonly', True)], 'validate': [('readonly', True)]}) interval_unit = fields.Selection([ ('days', 'Days'), ('weeks', 'Weeks'), ('months', 'Months'), ('years', 'Years') - ], string="Unit of time between two intervals", default='weeks', readonly=True, states={'draft': [('readonly', False)], 'confirm': [('readonly', False)]}) + ], compute='_compute_from_holiday_status_id', store=True, string="Unit of time between two intervals", readonly=False, + states={'cancel': [('readonly', True)], 'refuse': [('readonly', True)], 'validate1': [('readonly', True)], 'validate': [('readonly', True)]}) nextcall = fields.Date("Date of the next accrual allocation", default=False, readonly=True) max_leaves = fields.Float(compute='_compute_leaves') leaves_taken = fields.Float(compute='_compute_leaves') @@ -294,66 +295,81 @@ class HolidaysAllocation(models.Model): else: allocation.can_approve = True - @api.onchange('number_of_hours_display') - def _onchange_number_of_hours_display(self): + @api.depends('holiday_type') + def _compute_from_holiday_type(self): for allocation in self: - allocation.number_of_days = allocation.number_of_hours_display / (allocation.employee_id.resource_calendar_id.hours_per_day or HOURS_PER_DAY) + if allocation.holiday_type == 'employee': + if not allocation.employee_id: + allocation.employee_id = self.env.user.employee_id + allocation.mode_company_id = False + allocation.category_id = False + if allocation.holiday_type == 'company': + allocation.employee_id = False + if not allocation.mode_company_id: + allocation.mode_company_id = self.env.user.company_id + allocation.category_id = False + elif allocation.holiday_type == 'department': + allocation.employee_id = False + allocation.mode_company_id = False + allocation.category_id = False + elif allocation.holiday_type == 'category': + allocation.employee_id = False + allocation.mode_company_id = False + elif not allocation.employee_id and not allocation._origin.employee_id: + allocation.employee_id = self.env.context.get('default_employee_id') or self.env.user.employee_id - @api.onchange('number_of_days_display') - def _onchange_number_of_days_display(self): + @api.depends('holiday_type', 'employee_id') + def _compute_department_id(self): for allocation in self: - allocation.number_of_days = allocation.number_of_days_display - - @api.onchange('holiday_type') - def _onchange_type(self): - if self.holiday_type == 'employee': - if not self.employee_id: - self.employee_id = self.env.user.employee_id.id - self.mode_company_id = False - self.category_id = False - elif self.holiday_type == 'company': - self.employee_id = False - if not self.mode_company_id: - self.mode_company_id = self.env.user.company_id.id - self.category_id = False - elif self.holiday_type == 'department': - self.employee_id = False - self.mode_company_id = False - self.category_id = False - if not self.department_id: - self.department_id = self.env.user.employee_id.department_id.id - elif self.holiday_type == 'category': - self.employee_id = False - self.mode_company_id = False - self.department_id = False - - @api.onchange('employee_id') - def _onchange_employee(self): - self.manager_id = self.employee_id and self.employee_id.parent_id - if self.employee_id.user_id != self.env.user and self._origin.employee_id != self.employee_id: - self.holiday_status_id = False - if self.holiday_type == 'employee': - self.department_id = self.employee_id.department_id - - @api.onchange('holiday_status_id') - def _onchange_holiday_status_id(self): - if self.holiday_status_id.validity_stop and self.date_to: - new_date_to = datetime.combine(self.holiday_status_id.validity_stop, time.max) - if new_date_to < self.date_to: - self.date_to = new_date_to - - if self.allocation_type == 'accrual': - self.number_of_days = 0 - - if self.holiday_status_id.request_unit == 'hour': - self.unit_per_interval = 'hours' + if allocation.holiday_type == 'employee': + allocation.department_id = allocation.employee_id.department_id + elif allocation.holiday_type == 'department': + if not allocation.department_id: + allocation.department_id = self.env.user.employee_id.department_id + elif allocation.holiday_type == 'category': + allocation.department_id = False + + @api.depends('employee_id') + def _compute_from_employee_id(self): + default_holiday_status_id = self._default_holiday_status_id() + for holiday in self: + holiday.manager_id = holiday.employee_id and holiday.employee_id.parent_id + if holiday.employee_id.user_id != self.env.user and holiday._origin.employee_id != holiday.employee_id: + holiday.holiday_status_id = False + elif not holiday.holiday_status_id and not holiday._origin.holiday_status_id: + holiday.holiday_status_id = default_holiday_status_id + + @api.depends('holiday_status_id', 'allocation_type') + def _compute_from_holiday_status_id(self): + for allocation in self: + # set default values + if not allocation.number_of_days and not allocation._origin.number_of_days: + allocation.number_of_days = 1 + if not allocation.interval_number and not allocation._origin.interval_number: + allocation.interval_number = 1 + if not allocation.number_per_interval and not allocation._origin.number_per_interval: + allocation.number_per_interval = 1 + if not allocation.unit_per_interval and not allocation._origin.unit_per_interval: + allocation.unit_per_interval = 'hours' + if not allocation.interval_unit and not allocation._origin.interval_unit: + allocation.interval_unit = 'weeks' + + if allocation.holiday_status_id.validity_stop and allocation.date_to: + new_date_to = datetime.combine(allocation.holiday_status_id.validity_stop, time.max) + if new_date_to < allocation.date_to: + allocation.date_to = new_date_to + + if allocation.allocation_type == 'accrual': + allocation.number_of_days = 0 + if allocation.holiday_status_id.request_unit == 'hour': + allocation.unit_per_interval = 'hours' + else: + allocation.unit_per_interval = 'days' else: - self.unit_per_interval = 'days' - else: - self.interval_number = 1 - self.interval_unit = 'weeks' - self.number_per_interval = 1 - self.unit_per_interval = 'hours' + allocation.interval_number = 1 + allocation.interval_unit = 'weeks' + allocation.number_per_interval = 1 + allocation.unit_per_interval = 'hours' #################################################### # ORM Overrides methods @@ -416,8 +432,6 @@ class HolidaysAllocation(models.Model): self._check_approval_update(values['state']) result = super(HolidaysAllocation, self).write(values) self.add_follower(employee_id) - if 'employee_id' in values: - self._onchange_employee() return result def unlink(self): |
