[IMP] hr_holidays: add support for taking leaves differently
We would like to be able to take leaves using different units, currently we only support taking leaves by hours because we use datetimes. We need to support three units: * days * half days * hours These units will be defined on the leave type. Task #40995 Closes #21760
This commit is contained in:
@@ -2,6 +2,22 @@
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="hr_holiday_status_hl" model="hr.leave.type">
|
||||
<field name="name">Overtime Compensation</field>
|
||||
<field name="limit" eval="False"/>
|
||||
<field name="color_name">blue</field>
|
||||
<field name="validity_start" eval="time.strftime('%Y-01-01')"/>
|
||||
<field name="validity_stop" eval="time.strftime('%Y-12-31')"/>
|
||||
<field name="request_unit">hour</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_holiday_status_dv" model="hr.leave.type">
|
||||
<field name="name">Parental Leaves</field>
|
||||
<field name="limit" eval="True"/>
|
||||
<field name="color_name">brown</field>
|
||||
<field name="validation_type">both</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_holidays_employee1_allocation_cl" model="hr.leave.allocation">
|
||||
<field name="name">Legal Leaves for Peter Parker</field>
|
||||
<field name="holiday_status_id" ref="holiday_status_cl"/>
|
||||
@@ -18,6 +34,14 @@
|
||||
<field name="state">confirm</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_holidays_employee1_allocation_hl" model="hr.leave.allocation">
|
||||
<field name="name">Overtime Compensation</field>
|
||||
<field name="holiday_status_id" ref="hr_holiday_status_hl"/>
|
||||
<field name="number_of_hours">160</field>
|
||||
<field name="number_of_days_temp">20</field>
|
||||
<field name="employee_id" ref="hr.employee_root"/>
|
||||
</record>
|
||||
|
||||
<record id="hr_holidays_employee1_vc" model="hr.leave.allocation">
|
||||
<field name="name">Summer Vacation</field>
|
||||
<field name="holiday_status_id" ref="holiday_status_unpaid"/>
|
||||
@@ -25,8 +49,8 @@
|
||||
<field name="employee_id" ref="hr.employee_root"/>
|
||||
</record>
|
||||
|
||||
<!-- approve the first 2 leave allocations -->
|
||||
<function model="hr.leave.allocation" name="action_approve" eval="[[ref('hr_holidays.hr_holidays_employee1_allocation_cl'), ref('hr_holidays.hr_holidays_employee1_int_tour')]]"/>
|
||||
<!-- approve the first 3 leave allocations -->
|
||||
<function model="hr.leave.allocation" name="action_approve" eval="[[ref('hr_holidays.hr_holidays_employee1_allocation_cl'), ref('hr_holidays.hr_holidays_employee1_int_tour'), ref('hr_holidays_employee1_allocation_hl')]]"/>
|
||||
|
||||
<record id="hr_holidays_employee1_cl" model="hr.leave">
|
||||
<field name="name">Trip with Family</field>
|
||||
|
||||
@@ -5,14 +5,16 @@
|
||||
|
||||
import logging
|
||||
import math
|
||||
from datetime import timedelta
|
||||
|
||||
from datetime import timedelta, datetime, time
|
||||
from pytz import timezone, UTC
|
||||
|
||||
from odoo import api, fields, models
|
||||
from odoo.exceptions import UserError, ValidationError
|
||||
from odoo.tools import float_compare
|
||||
from odoo.tools.translate import _
|
||||
|
||||
from odoo.addons.resource.models.resource import HOURS_PER_DAY
|
||||
from odoo.addons.resource.models.resource import float_to_time
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -85,6 +87,9 @@ class HolidaysRequest(models.Model):
|
||||
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')
|
||||
number_of_hours = fields.Float(
|
||||
'Hours Allocation', copy=False, readonly=True, compute='_compute_number_of_hours',
|
||||
help='Number of hours of the leave request according to your working schedule.')
|
||||
meeting_id = fields.Many2one('calendar.event', string='Meeting')
|
||||
|
||||
parent_id = fields.Many2one('hr.leave', string='Parent')
|
||||
@@ -107,19 +112,181 @@ class HolidaysRequest(models.Model):
|
||||
can_reset = fields.Boolean('Can reset', compute='_compute_can_reset')
|
||||
can_approve = fields.Boolean('Can Approve', compute='_compute_can_approve')
|
||||
|
||||
# Those fields are mostly for the interface only
|
||||
|
||||
# Interface fields used when not using hour-based computation
|
||||
request_date_from = fields.Date('Request Start Date')
|
||||
request_date_to = fields.Date('Request End Date')
|
||||
|
||||
leave_type_request_unit = fields.Selection(related='holiday_status_id.request_unit', readonly=True)
|
||||
|
||||
# These fields are only used only when the leave is taken in half days
|
||||
request_date_from_period = fields.Selection([('am', 'Morning'),
|
||||
('pm', 'Afternoon')], string="Date Period Start", default='am')
|
||||
request_date_to_period = fields.Selection([('am', 'Morning'),
|
||||
('pm', 'Afternoon')], string="Date Period End", default='pm')
|
||||
|
||||
request_unit_all = fields.Selection([('half', 'Half-day'),
|
||||
('day', '1 Day'),
|
||||
('period', 'Period')])
|
||||
# Duplicate field because we cannot hide some entries of a selection field
|
||||
request_unit_day = 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."),
|
||||
('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."),
|
||||
('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',
|
||||
'holiday_status_id', 'request_date_from', 'request_date_to', 'employee_id')
|
||||
def _onchange_request_parameters(self):
|
||||
date_from = False
|
||||
date_to = False
|
||||
|
||||
if self.request_date_from:
|
||||
if self.date_from:
|
||||
date_from = fields.Datetime.to_string(datetime.combine(fields.Date.from_string(self.request_date_from), fields.Datetime.from_string(self.date_from).time()))
|
||||
else:
|
||||
date_from = self.request_date_from
|
||||
|
||||
if self.request_date_to:
|
||||
if self.date_to:
|
||||
date_to = fields.Datetime.to_string(datetime.combine(fields.Date.from_string(self.request_date_to), fields.Datetime.from_string(self.date_to).time()))
|
||||
else:
|
||||
date_to = self.request_date_to
|
||||
|
||||
if not self.request_date_from or not self.request_date_to:
|
||||
if date_from:
|
||||
self.date_from = date_from
|
||||
if date_to:
|
||||
self.date_to = date_to
|
||||
self.number_of_days_temp = 0
|
||||
return
|
||||
|
||||
domain = [('calendar_id', '=', self.employee_id.resource_calendar_id.id)]
|
||||
attendances = self.env['resource.calendar.attendance'].search(domain, order='dayofweek, day_period DESC')
|
||||
|
||||
first_day = fields.Date.from_string(date_from)
|
||||
last_day = fields.Date.from_string(date_to)
|
||||
|
||||
if self.request_unit_all in ['day', 'half']:
|
||||
last_day = first_day
|
||||
|
||||
# find first attendance coming after first_day
|
||||
attendance_from = next((att for att in attendances if int(att.dayofweek) >= first_day.weekday()), attendances[0])
|
||||
# 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'):
|
||||
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':
|
||||
date_from = fields.Datetime.from_string(date_from)
|
||||
date_to = fields.Datetime.from_string(date_to)
|
||||
else:
|
||||
date_from = timezone(self.env.user.tz).localize(datetime.combine(first_day, hour_from)).astimezone(UTC)
|
||||
date_to = timezone(self.env.user.tz).localize(datetime.combine(last_day, hour_to)).astimezone(UTC)
|
||||
|
||||
self.date_from = date_from
|
||||
self.date_to = date_to
|
||||
|
||||
if not (self.leave_type_request_unit == 'hour' and self.request_unit_all == 'period'):
|
||||
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:
|
||||
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('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 = fields.Datetime.from_string(self.date_from)
|
||||
date_to = fields.Datetime.from_string(self.date_to)
|
||||
|
||||
# No date_to set so far: automatically compute one 8 hours later
|
||||
if date_from and not date_to:
|
||||
date_to = date_from + timedelta(hours=self.employee_id.resource_calendar_id.hours_per_day)
|
||||
self.date_to = date_to
|
||||
|
||||
self.request_date_from = date_from
|
||||
self.request_date_to = date_to
|
||||
|
||||
if (date_from and date_to) and (date_from.day < date_to.day):
|
||||
self.request_unit_all = 'period'
|
||||
self.request_unit_day = 'period'
|
||||
|
||||
# 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 = fields.Datetime.from_string(self.date_from)
|
||||
date_to = fields.Datetime.from_string(self.date_to)
|
||||
|
||||
self.request_date_from = date_from
|
||||
self.request_date_to = date_to
|
||||
|
||||
if (date_from and date_to) and (date_from.day < date_to.day):
|
||||
self.request_unit_all = 'period'
|
||||
self.request_unit_day = 'period'
|
||||
|
||||
# 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('employee_id')
|
||||
def _onchange_employee_id(self):
|
||||
self.manager_id = self.employee_id and self.employee_id.parent_id
|
||||
self.department_id = self.employee_id.department_id
|
||||
|
||||
@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
|
||||
@api.depends('number_of_days_temp')
|
||||
def _compute_number_of_hours(self):
|
||||
for holiday in self:
|
||||
holiday.number_of_hours = holiday.number_of_days_temp * self.employee_id.resource_calendar_id.hours_per_day
|
||||
|
||||
@api.multi
|
||||
def _compute_can_reset(self):
|
||||
""" User can reset a leave request if it is its own leave request
|
||||
@@ -141,56 +308,6 @@ class HolidaysRequest(models.Model):
|
||||
manager = self.user_has_groups('hr_holidays.group_hr_holidays_manager')
|
||||
holiday.can_approve = (holiday.employee_id.user_id.id != self.env.uid) or manager
|
||||
|
||||
@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_id(self):
|
||||
self.manager_id = self.employee_id and self.employee_id.parent_id
|
||||
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:
|
||||
@@ -218,14 +335,11 @@ class HolidaysRequest(models.Model):
|
||||
|
||||
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_data(from_dt, to_dt)['days']
|
||||
return employee.get_work_days_data(date_from, date_to)['days']
|
||||
|
||||
time_delta = to_dt - from_dt
|
||||
time_delta = date_to - date_from
|
||||
return math.ceil(time_delta.days + float(time_delta.seconds) / 86400)
|
||||
|
||||
####################################################
|
||||
@@ -302,11 +416,14 @@ class HolidaysRequest(models.Model):
|
||||
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:
|
||||
date_from = fields.Datetime.from_string(leave.date_from)
|
||||
date_to = fields.Datetime.from_string(leave.date_to)
|
||||
|
||||
self.env['resource.calendar.leaves'].create({
|
||||
'name': leave.name,
|
||||
'date_from': leave.date_from,
|
||||
'date_from': fields.Datetime.to_string(date_from),
|
||||
'holiday_id': leave.id,
|
||||
'date_to': leave.date_to,
|
||||
'date_to': fields.Datetime.to_string(date_to),
|
||||
'resource_id': leave.employee_id.resource_id.id,
|
||||
'calendar_id': leave.employee_id.resource_calendar_id.id,
|
||||
'time_type': leave.holiday_status_id.time_type,
|
||||
@@ -334,7 +451,7 @@ class HolidaysRequest(models.Model):
|
||||
'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,
|
||||
'duration': self.number_of_days_temp * self.employee_id.resource_calendar_id.hours_per_day,
|
||||
'description': self.notes,
|
||||
'user_id': self.user_id.id,
|
||||
'start': self.date_from,
|
||||
|
||||
@@ -65,6 +65,7 @@ class HolidaysAllocation(models.Model):
|
||||
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')
|
||||
number_of_hours = fields.Float('Number of Hours', help="Number of hours of the leave allocation according to your working schedule.")
|
||||
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)]})
|
||||
@@ -84,6 +85,7 @@ class HolidaysAllocation(models.Model):
|
||||
validation_type = fields.Selection('Validation Type', related='holiday_status_id.validation_type')
|
||||
can_reset = fields.Boolean('Can reset', compute='_compute_can_reset')
|
||||
can_approve = fields.Boolean('Can Approve', compute='_compute_can_approve')
|
||||
type_request_unit = fields.Selection(related='holiday_status_id.request_unit')
|
||||
|
||||
_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) )",
|
||||
@@ -92,10 +94,13 @@ class HolidaysAllocation(models.Model):
|
||||
]
|
||||
|
||||
@api.multi
|
||||
@api.depends('number_of_days_temp')
|
||||
@api.depends('number_of_days_temp', 'type_request_unit', 'number_of_hours')
|
||||
def _compute_number_of_days(self):
|
||||
for holiday in self:
|
||||
holiday.number_of_days = holiday.number_of_days_temp
|
||||
if holiday.type_request_unit == 'hour':
|
||||
holiday.number_of_days = holiday.number_of_hours / holiday.employee_id.resource_calendar_id.hours_per_day
|
||||
else:
|
||||
holiday.number_of_days = holiday.number_of_days_temp
|
||||
|
||||
@api.multi
|
||||
def _compute_can_reset(self):
|
||||
@@ -136,6 +141,14 @@ class HolidaysAllocation(models.Model):
|
||||
if self.holiday_type == 'employee':
|
||||
self.department_id = self.employee_id.department_id
|
||||
|
||||
@api.onchange('number_of_days_temp')
|
||||
def _onchange_number_of_days_temp(self):
|
||||
self.number_of_hours = self.number_of_days_temp * self.employee_id.resource_calendar_id.hours_per_day
|
||||
|
||||
@api.onchange('number_of_hours')
|
||||
def _onchange_number_of_hours(self):
|
||||
self.number_of_days_temp = self.number_of_hours / self.employee_id.resource_calendar_id.hours_per_day
|
||||
|
||||
####################################################
|
||||
# ORM Overrides methods
|
||||
####################################################
|
||||
@@ -144,7 +157,10 @@ class HolidaysAllocation(models.Model):
|
||||
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)))
|
||||
if leave.type_request_unit == 'hour':
|
||||
res.append((leave.id, _("Allocation of %s : %.2f hour(s) To %s") % (leave.holiday_status_id.name, leave.number_of_hours, leave.employee_id.name)))
|
||||
else:
|
||||
res.append((leave.id, _("Allocation of %s : %.2f day(s) To %s") % (leave.holiday_status_id.name, leave.number_of_days_temp, leave.employee_id.name)))
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
|
||||
@@ -78,6 +78,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')], default='day', string='Take Leaves in', required=True)
|
||||
|
||||
@api.multi
|
||||
@api.constrains('validity_start', 'validity_stop')
|
||||
@@ -100,11 +103,13 @@ class HolidaysType(models.Model):
|
||||
for holiday_type in self:
|
||||
if holiday_type.validity_start and holiday_type.validity_stop:
|
||||
holiday_type.valid = ((dt < holiday_type.validity_stop) and (dt > holiday_type.validity_start))
|
||||
elif holiday_type.validity_start and (dt > holiday_type.validity_start):
|
||||
holiday_type.valid = False
|
||||
else:
|
||||
holiday_type.valid = not holiday_type.validity_stop
|
||||
holiday_type.valid = True
|
||||
|
||||
def _search_valid(self, operator, value):
|
||||
dt = self._context.get('default_date_from', fields.Date.today())
|
||||
dt = self._context.get('default_date_from', fields.Date.today()) or fields.Date.today()
|
||||
signs = ['>=', '<='] if operator == '=' else ['<=', '>=']
|
||||
|
||||
return ['|', ('validity_stop', operator, False), '&',
|
||||
|
||||
@@ -69,12 +69,16 @@
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="type_request_unit" invisible="1"/>
|
||||
<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"/>
|
||||
<label string="Duration"/>
|
||||
<div>
|
||||
<div>
|
||||
<field name="number_of_days_temp" class="oe_inline"/> days
|
||||
<div attrs="{'invisible': [('type_request_unit', '=', 'hour')]}">
|
||||
<field name="number_of_days_temp" class="oe_inline" attrs="{'readonly': [('state', '=', 'validate')]}"/> days
|
||||
</div>
|
||||
<div attrs="{'invisible': [('type_request_unit', '!=', 'hour')]}">
|
||||
<field name="number_of_hours" class="oe_inline" attrs="{'readonly': [('state', '=', 'validate')]}"/> hours
|
||||
</div>
|
||||
</div>
|
||||
</group>
|
||||
|
||||
@@ -33,12 +33,13 @@
|
||||
<field name="time_type" groups="base.group_no_one"/>
|
||||
<field name="limit"/>
|
||||
<field name="sequence" attrs="{'invisible': [('limit', '=', False)]}" groups="base.group_no_one"/>
|
||||
<field name="request_unit"/>
|
||||
</group>
|
||||
<group name="validation" string="Validation">
|
||||
<field name="validation_type" widget="radio"/>
|
||||
</group>
|
||||
<group name="visibility" string="Visibility">
|
||||
<field name="employee_applicability" widget="radio" attrs="{'readonly': [('limit', '=', True)]}"/>
|
||||
<field name="employee_applicability" widget="radio" attrs="{'readonly': [('limit', '=', True)]}" force_save="1"/>
|
||||
</group>
|
||||
<group name="calendar" string="Calendar">
|
||||
<field name="categ_id"/>
|
||||
@@ -102,7 +103,6 @@
|
||||
<field name="validation_type" />
|
||||
<field name="validity_start" />
|
||||
<field name="validity_stop" />
|
||||
<field name="valid"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -104,13 +104,36 @@
|
||||
<field name="holiday_status_id" context="{'employee_id':employee_id, 'default_date_from':date_from}"/>
|
||||
<label for="number_of_days_temp" string="Duration"/>
|
||||
<div>
|
||||
<field name="leave_type_request_unit" invisible="1"/>
|
||||
<div>
|
||||
<field name="date_from" class="oe_inline"/>
|
||||
<label string="-" class="oe_inline"/>
|
||||
<field name="date_to" class="oe_inline"/>
|
||||
<field name="request_unit_all" widget="radio" attrs="{'readonly': [('state', '=', 'validate')], 'invisible': [('leave_type_request_unit', '=', 'day')]}"/>
|
||||
<field name="request_unit_day" widget="radio" attrs="{'readonly': [('state', '=', 'validate')], 'invisible': [('leave_type_request_unit', '!=', 'day')]}"/>
|
||||
|
||||
<div>
|
||||
<div class="o_row">
|
||||
<span attrs="{'invisible': [('request_unit_all', '!=', 'period')]}">From </span>
|
||||
<field name="request_date_from" attrs="{'readonly': [('state', '=', 'validate')], 'invisible': [('leave_type_request_unit', '=', 'hour'), ('request_unit_all', '=', 'period')]}"/>
|
||||
<field name="date_from" attrs="{'readonly': [('state', '=', 'validate')], 'invisible': ['|', ('leave_type_request_unit', '!=', 'hour'), ('request_unit_all', '!=', 'period')]}"/>
|
||||
<span class="ml8" attrs="{'invisible': ['|', '&', ('request_unit_all', '!=', 'half'), ('leave_type_request_unit', '!=', 'half'), '&', ('request_unit_all', '=', 'day'), ('leave_type_request_unit', '=', 'half')]}"/>
|
||||
<field name="request_date_from_period" options="{'horizontal': True}"
|
||||
attrs="{'readonly': [('state', '=', 'validate')], 'invisible': ['|', '|', ('leave_type_request_unit', '=', 'day'), '&', ('leave_type_request_unit', '=', 'half'), ('request_unit_all', '=', 'day'), '&', ('leave_type_request_unit', '=', 'hour'), ('request_unit_all', '!=', 'half')]}"/>
|
||||
</div>
|
||||
<div class="o_row">
|
||||
<span attrs="{'invisible': [('request_unit_all', '!=', 'period')]}">To </span>
|
||||
<field name="request_date_to" attrs="{'readonly': [('state', '=', 'validate')], 'invisible': ['|', ('request_unit_all', '!=', 'period'), ('leave_type_request_unit', '=', 'hour')]}"/>
|
||||
<field name="date_to" attrs="{'readonly': [('state', '=', 'validate')], 'invisible': ['|', ('leave_type_request_unit', '!=', 'hour'), ('request_unit_all', '!=', 'period')], 'required': ['|', ('request_unit_all', '=', 'period'), ('request_unit_day', '=', 'period')]}"/>
|
||||
<span class="ml8" attrs="{'invisible': ['|', ('leave_type_request_unit', '!=', 'half'), ('request_unit_all', '!=', 'period')]}"/>
|
||||
<field name="request_date_to_period" options="{'horizontal': True}"
|
||||
attrs="{'readonly': [('state', '=', 'validate')], 'invisible': ['|', ('leave_type_request_unit', '!=', 'half'), ('request_unit_all', '!=', 'period')]}"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div attrs="{'invisible': [('leave_type_request_unit', '=', 'hour')]}">
|
||||
<field name="number_of_days_temp" class="oe_inline"/> day(s)
|
||||
</div>
|
||||
<div>
|
||||
<field name="number_of_days_temp" class="oe_inline"/> days
|
||||
<div attrs="{'invisible': [('leave_type_request_unit', '!=', 'hour')]}">
|
||||
<field name="number_of_hours" class="oe_inline"/> hour(s)
|
||||
</div>
|
||||
</div>
|
||||
</group>
|
||||
|
||||
@@ -27,16 +27,16 @@
|
||||
<field name="hours_per_day">7.0</field>
|
||||
<field name="attendance_ids"
|
||||
eval="[
|
||||
(0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': 'Monday Evening', 'dayofweek': '0', 'hour_from': 13, 'hour_to': 16}),
|
||||
(0, 0, {'name': 'Tuesday Morning', 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': 'Tuesday Evening', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 16}),
|
||||
(0, 0, {'name': 'Wednesday Morning', 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': 'Wednesday Evening', 'dayofweek': '2', 'hour_from': 13, 'hour_to': 16}),
|
||||
(0, 0, {'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': 'Thursday Evening', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 16}),
|
||||
(0, 0, {'name': 'Friday Morning', 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': 'Friday Evening', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 16})
|
||||
(0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Monday Evening', 'dayofweek': '0', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Tuesday Morning', 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Tuesday Evening', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Wednesday Morning', 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Wednesday Evening', 'dayofweek': '2', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Thursday Evening', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Friday Morning', 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Friday Evening', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 16, 'day_period': 'afternoon'})
|
||||
]"
|
||||
/>
|
||||
</record>
|
||||
@@ -47,16 +47,16 @@
|
||||
<field name="hours_per_day">7.6</field>
|
||||
<field name="attendance_ids"
|
||||
eval="[
|
||||
(0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': 'Monday Evening', 'dayofweek': '0', 'hour_from': 13, 'hour_to': 16.6}),
|
||||
(0, 0, {'name': 'Tuesday Morning', 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': 'Tuesday Evening', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 16.6}),
|
||||
(0, 0, {'name': 'Wednesday Morning', 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': 'Wednesday Evening', 'dayofweek': '2', 'hour_from': 13, 'hour_to': 16.6}),
|
||||
(0, 0, {'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': 'Thursday Evening', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 16.6}),
|
||||
(0, 0, {'name': 'Friday Morning', 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': 'Friday Evening', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 16.6})
|
||||
(0, 0, {'name': 'Monday Morning', 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Monday Evening', 'dayofweek': '0', 'hour_from': 13, 'hour_to': 16.6, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Tuesday Morning', 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Tuesday Evening', 'dayofweek': '1', 'hour_from': 13, 'hour_to': 16.6, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Wednesday Morning', 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Wednesday Evening', 'dayofweek': '2', 'hour_from': 13, 'hour_to': 16.6, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Thursday Morning', 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Thursday Evening', 'dayofweek': '3', 'hour_from': 13, 'hour_to': 16.6, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': 'Friday Morning', 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': 'Friday Evening', 'dayofweek': '4', 'hour_from': 13, 'hour_to': 16.6, 'day_period': 'afternoon'})
|
||||
]"
|
||||
/>
|
||||
</record>
|
||||
|
||||
@@ -142,16 +142,16 @@ class ResourceCalendar(models.Model):
|
||||
|
||||
def _get_default_attendance_ids(self):
|
||||
return [
|
||||
(0, 0, {'name': _('Monday Morning'), 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': _('Monday Evening'), 'dayofweek': '0', 'hour_from': 13, 'hour_to': 17}),
|
||||
(0, 0, {'name': _('Tuesday Morning'), 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': _('Tuesday Evening'), 'dayofweek': '1', 'hour_from': 13, 'hour_to': 17}),
|
||||
(0, 0, {'name': _('Wednesday Morning'), 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': _('Wednesday Evening'), 'dayofweek': '2', 'hour_from': 13, 'hour_to': 17}),
|
||||
(0, 0, {'name': _('Thursday Morning'), 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': _('Thursday Evening'), 'dayofweek': '3', 'hour_from': 13, 'hour_to': 17}),
|
||||
(0, 0, {'name': _('Friday Morning'), 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12}),
|
||||
(0, 0, {'name': _('Friday Evening'), 'dayofweek': '4', 'hour_from': 13, 'hour_to': 17})
|
||||
(0, 0, {'name': _('Monday Morning'), 'dayofweek': '0', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': _('Monday Evening'), 'dayofweek': '0', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': _('Tuesday Morning'), 'dayofweek': '1', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': _('Tuesday Evening'), 'dayofweek': '1', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': _('Wednesday Morning'), 'dayofweek': '2', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': _('Wednesday Evening'), 'dayofweek': '2', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': _('Thursday Morning'), 'dayofweek': '3', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': _('Thursday Evening'), 'dayofweek': '3', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'}),
|
||||
(0, 0, {'name': _('Friday Morning'), 'dayofweek': '4', 'hour_from': 8, 'hour_to': 12, 'day_period': 'morning'}),
|
||||
(0, 0, {'name': _('Friday Evening'), 'dayofweek': '4', 'hour_from': 13, 'hour_to': 17, 'day_period': 'afternoon'})
|
||||
]
|
||||
|
||||
name = fields.Char(required=True)
|
||||
@@ -395,6 +395,7 @@ class ResourceCalendarAttendance(models.Model):
|
||||
hour_from = fields.Float(string='Work from', required=True, index=True, help="Start and End time of working.")
|
||||
hour_to = fields.Float(string='Work to', required=True)
|
||||
calendar_id = fields.Many2one("resource.calendar", string="Resource's Calendar", required=True, ondelete='cascade')
|
||||
day_period = fields.Selection([('morning', 'Morning'), ('afternoon', 'Afternoon')], required=True, default='morning')
|
||||
|
||||
@api.onchange('hour_from', 'hour_to')
|
||||
def _onchange_hours(self):
|
||||
|
||||
@@ -8,8 +8,8 @@ from pytz import utc
|
||||
from odoo import api, fields, models
|
||||
from odoo.tools import float_utils
|
||||
|
||||
# This will generate quarter of days
|
||||
ROUNDING_FACTOR = 4
|
||||
# This will generate eights of days
|
||||
ROUNDING_FACTOR = 8
|
||||
|
||||
|
||||
class ResourceMixin(models.AbstractModel):
|
||||
|
||||
@@ -325,7 +325,7 @@ class TestResMixin(TestResourceCommon):
|
||||
datetime_tz(2018, 4, 6, 16, 0, 0, tzinfo=self.john.tz),
|
||||
)
|
||||
# still showing as 5 days because of rounding, but we see only 39 hours
|
||||
self.assertEqual(data, {'days': 5, 'hours': 39})
|
||||
self.assertEqual(data, {'days': 4.875, 'hours': 39})
|
||||
|
||||
# Looking at John's calendar
|
||||
|
||||
@@ -335,7 +335,7 @@ class TestResMixin(TestResourceCommon):
|
||||
datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.jean.tz),
|
||||
datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.jean.tz),
|
||||
)
|
||||
self.assertEqual(data, {'days': 1.5, 'hours': 13})
|
||||
self.assertEqual(data, {'days': 1.375, 'hours': 13})
|
||||
|
||||
# Viewing it as Patel
|
||||
# Views from 2018/04/01 11:00:00 to 2018/04/06 10:00:00
|
||||
@@ -343,7 +343,7 @@ class TestResMixin(TestResourceCommon):
|
||||
datetime_tz(2018, 4, 2, 0, 0, 0, tzinfo=self.patel.tz),
|
||||
datetime_tz(2018, 4, 6, 23, 0, 0, tzinfo=self.patel.tz),
|
||||
)
|
||||
self.assertEqual(data, {'days': 1.25, 'hours': 10})
|
||||
self.assertEqual(data, {'days': 1.125, 'hours': 10})
|
||||
|
||||
# Viewing it as John
|
||||
data = self.john.get_work_days_data(
|
||||
|
||||
@@ -193,6 +193,7 @@
|
||||
<field name="hour_to" widget="float_time"/>
|
||||
<field name="date_from"/>
|
||||
<field name="date_to"/>
|
||||
<field name="day_period"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -212,6 +213,7 @@
|
||||
<field name="hour_from" widget="float_time" class="oe_inline"/> -
|
||||
<field name="hour_to" widget="float_time" class="oe_inline"/>
|
||||
</div>
|
||||
<field name="day_period"/>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
|
||||
Reference in New Issue
Block a user