[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:
RomainLibert
2018-06-06 17:04:07 +02:00
parent d8a5141c17
commit b6642f1b70
12 changed files with 305 additions and 113 deletions
+26 -2
View File
@@ -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>
+178 -61
View File
@@ -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
+7 -2
View File
@@ -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>
+28 -5
View File
@@ -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': ['|', '&amp;', ('request_unit_all', '!=', 'half'), ('leave_type_request_unit', '!=', 'half'), '&amp;', ('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'), '&amp;', ('leave_type_request_unit', '=', 'half'), ('request_unit_all', '=', 'day'), '&amp;', ('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>
+20 -20
View File
@@ -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>
+11 -10
View File
@@ -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):
+2 -2
View File
@@ -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):
+3 -3
View File
@@ -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(
+2
View File
@@ -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>