[IMP] resource: overall fix of hours/days computation + scheduling + added tests

bzr revid: tde@openerp.com-20130830133250-ixehp7imac0duygd
This commit is contained in:
Thibault Delavallée 2013-08-30 15:32:50 +02:00
parent 0cb0a5994f
commit af79a26a1f
4 changed files with 588 additions and 243 deletions

View File

@ -19,13 +19,14 @@
# #
############################################################################## ##############################################################################
import pytz import datetime
from datetime import date, datetime, timedelta # from datetime import datetime, timedelta
from dateutil import rrule from dateutil import rrule
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
import itertools import itertools
import math import math
from operator import itemgetter from operator import itemgetter
import pytz
from faces import * from faces import *
from openerp import tools from openerp import tools
@ -38,10 +39,10 @@ class resource_calendar(osv.osv):
_name = "resource.calendar" _name = "resource.calendar"
_description = "Resource Calendar" _description = "Resource Calendar"
_columns = { _columns = {
'name' : fields.char("Name", size=64, required=True), 'name': fields.char("Name", size=64, required=True),
'company_id' : fields.many2one('res.company', 'Company', required=False), 'company_id': fields.many2one('res.company', 'Company', required=False),
'attendance_ids' : fields.one2many('resource.calendar.attendance', 'calendar_id', 'Working Time'), 'attendance_ids': fields.one2many('resource.calendar.attendance', 'calendar_id', 'Working Time'),
'manager' : fields.many2one('res.users', 'Workgroup Manager'), 'manager': fields.many2one('res.users', 'Workgroup Manager'),
'leave_ids': fields.one2many( 'leave_ids': fields.one2many(
'resource.calendar.leaves', 'calendar_id', 'Leaves', 'resource.calendar.leaves', 'calendar_id', 'Leaves',
help='' help=''
@ -56,27 +57,29 @@ class resource_calendar(osv.osv):
# -------------------------------------------------- # --------------------------------------------------
def interval_clean(self, intervals): def interval_clean(self, intervals):
""" Utility method that removes overlapping inside datetime intervals. """ Utility method that sorts and removes overlapping inside datetime
intervals. The intervals are sorted based on increasing starting datetime.
Overlapping intervals are merged into a single one.
:param list intervals: list of datetime intervals. Each interval is a :param list intervals: list of intervals; each interval is a tuple
tuple(datetime_from, datetime_to) (datetime_from, datetime_to)
:return list final_list: list of intervals without overlap :return list cleaned: list of sorted intervals without overlap
""" """
intervals = sorted(intervals) # TODO: check sorted method intervals = sorted(intervals, key=itemgetter(0)) # sort on first datetime
final_list = [] cleaned = []
working_interval = None working_interval = None
while intervals: while intervals:
current_interval = intervals.pop(0) current_interval = intervals.pop(0)
if not working_interval: # init if not working_interval: # init
working_interval = [current_interval[0], current_interval[1]] working_interval = [current_interval[0], current_interval[1]]
elif working_interval[1] < current_interval[0]: # interval is disjoint elif working_interval[1] < current_interval[0]: # interval is disjoint
final_list.append(tuple(working_interval)) cleaned.append(tuple(working_interval))
working_interval = [current_interval[0], current_interval[1]] working_interval = [current_interval[0], current_interval[1]]
elif working_interval[1] < current_interval[1]: # union of greater intervals elif working_interval[1] < current_interval[1]: # union of greater intervals
working_interval[1] = current_interval[1] working_interval[1] = current_interval[1]
if working_interval: # handle void lists if working_interval: # handle void lists
final_list.append(tuple(working_interval)) cleaned.append(tuple(working_interval))
return final_list return cleaned
def interval_remove_leaves(self, interval, leave_intervals): def interval_remove_leaves(self, interval, leave_intervals):
""" Utility method that remove leave intervals from a base interval: """ Utility method that remove leave intervals from a base interval:
@ -98,87 +101,120 @@ class resource_calendar(osv.osv):
:param tuple interval: a tuple (beginning datetime, ending datetime) that :param tuple interval: a tuple (beginning datetime, ending datetime) that
is the base interval from which the leave intervals is the base interval from which the leave intervals
will be removed will be removed
:param list interval: a list of tuples (beginning datetime, ending datetime) :param list leave_intervals: a list of tuples (beginning datetime, ending datetime)
that are intervals to remove from the base interval that are intervals to remove from the base interval
:return list final_list: a list of tuples (begin datetime, end datetime) :return list intervals: a list of tuples (begin datetime, end datetime)
that are the remaining valid intervals that are the remaining valid intervals
""" """
if not interval: if not interval:
return interval return interval
if leave_intervals is None: if leave_intervals is None:
leave_intervals = [] leave_intervals = []
final_list = [] intervals = []
leave_intervals = self.interval_clean(leave_intervals) leave_intervals = self.interval_clean(leave_intervals)
current_interval = [interval[0], interval[1]] current_interval = [interval[0], interval[1]]
# print '\tcurrent_intreval', current_interval
for leave in leave_intervals: for leave in leave_intervals:
# print '\thandling leave', leave
if leave[1] <= current_interval[0]: if leave[1] <= current_interval[0]:
# print '\t\tbefore, skipping'
continue continue
if leave[0] >= current_interval[1]: if leave[0] >= current_interval[1]:
# print '\t\tafter, abort'
break break
if current_interval[0] < leave[0] < current_interval[1]: if current_interval[0] < leave[0] < current_interval[1]:
# print '\t\tbeginning inside'
current_interval[1] = leave[0] current_interval[1] = leave[0]
final_list.append((current_interval[0], current_interval[1])) intervals.append((current_interval[0], current_interval[1]))
current_interval = [leave[1], interval[1]] current_interval = [leave[1], interval[1]]
if current_interval[0] <= leave[1] <= current_interval[1]: # if current_interval[0] <= leave[1] <= current_interval[1]:
# print '\t\tending inside' if current_interval[0] <= leave[1]:
current_interval[0] = leave[1] current_interval[0] = leave[1]
if current_interval and current_interval[0] < interval[1]: # remove intervals moved outside base interval due to leaves if current_interval and current_interval[0] < interval[1]: # remove intervals moved outside base interval due to leaves
final_list.append((current_interval[0], current_interval[1])) intervals.append((current_interval[0], current_interval[1]))
return final_list return intervals
def interval_schedule_hours(self, intervals, hour, remove_at_end=True):
""" Schedule hours in intervals. The last matching interval is truncated
to match the specified hours.
It is possible to truncate the last interval at its beginning or ending.
However this does nothing on the given interval order that should be
submitted accordingly.
:param list intervals: a list of tuples (beginning datetime, ending datetime)
:param int/float hours: number of hours to schedule
:param boolean remove_at_end: remove extra hours at the end of the last
matching interval. Otherwise, do it at the
beginning.
:return list results: a list of intervals
"""
results = []
res = datetime.timedelta()
limit = datetime.timedelta(hours=hour)
for interval in intervals:
res += interval[1] - interval[0]
if res > limit and remove_at_end:
interval = (interval[0], interval[1] + relativedelta(seconds=(limit-res).total_seconds()))
elif res > limit:
interval = (interval[0] + relativedelta(seconds=(res-limit).total_seconds()), interval[1])
results.append(interval)
if res > limit:
break
return results
# -------------------------------------------------- # --------------------------------------------------
# Date and hours computation # Date and hours computation
# -------------------------------------------------- # --------------------------------------------------
def get_next_day(self, cr, uid, id, day_date, context=None): def get_attendances_for_weekdays(self, cr, uid, id, weekdays, context=None):
if id is None: """ Given a list of weekdays, return matching resource.calendar.attendance"""
return day_date + relativedelta(days=1) calendar = self.browse(cr, uid, id, context=None)
return [att for att in calendar.attendance_ids if int(att.dayofweek) in weekdays]
def get_weekdays(self, cr, uid, id, context=None):
""" Return the list of weekdays that contain at least one working interval """
calendar = self.browse(cr, uid, id, context=None) calendar = self.browse(cr, uid, id, context=None)
weekdays = set() weekdays = set()
for attendance in calendar.attendance_ids: for attendance in calendar.attendance_ids:
weekdays.add(int(attendance.dayofweek)) weekdays.add(int(attendance.dayofweek))
weekdays = list(weekdays) return list(weekdays)
if day_date.weekday() in weekdays: def get_next_day(self, cr, uid, id, day_date, context=None):
base_index = weekdays.index(day_date.weekday()) """ Get following date of day_date, based on resource.calendar. If no
else: calendar is provided, just return the next day.
base_index = -1
for weekday in weekdays: :param date day_date: current day as a date
if weekday > day_date.weekday(): """
break if id is None:
base_index += 1 return day_date + relativedelta(days=1)
weekdays = self.get_weekdays(cr, uid, id, context)
base_index = -1
for weekday in weekdays:
if weekday > day_date.weekday():
break
base_index += 1
new_index = (base_index + 1) % len(weekdays) new_index = (base_index + 1) % len(weekdays)
days = (weekdays[new_index] - day_date.weekday()) days = (weekdays[new_index] - day_date.weekday())
if days < 0: if days < 0:
days = 7 + days days = 7 + days
return day_date + relativedelta(days=days) return day_date + relativedelta(days=days)
def get_previous_day(self, cr, uid, id, day_date, context=None): def get_previous_day(self, cr, uid, id, day_date, context=None):
""" Get previous date of day_date, based on resource.calendar. If no
calendar is provided, just return the previous day.
:param date day_date: current day as a date
"""
if id is None: if id is None:
return day_date + relativedelta(days=-1) return day_date + relativedelta(days=-1)
calendar = self.browse(cr, uid, id, context=None) weekdays = self.get_weekdays(cr, uid, id, context)
weekdays = set()
for attendance in calendar.attendance_ids:
weekdays.add(int(attendance.dayofweek))
weekdays = list(weekdays)
weekdays.reverse() weekdays.reverse()
if day_date.weekday() in weekdays: base_index = -1
base_index = weekdays.index(day_date.weekday()) for weekday in weekdays:
else: if weekday < day_date.weekday():
base_index = -1 break
for weekday in weekdays: base_index += 1
if weekday < day_date.weekday():
break
base_index += 1
new_index = (base_index + 1) % len(weekdays) new_index = (base_index + 1) % len(weekdays)
days = (weekdays[new_index] - day_date.weekday()) days = (weekdays[new_index] - day_date.weekday())
@ -187,35 +223,55 @@ class resource_calendar(osv.osv):
return day_date + relativedelta(days=days) return day_date + relativedelta(days=days)
def _get_leave_intervals(self, cr, uid, id, resource_id=None, start_datetime=None, end_datetime=None, context=None): def get_leave_intervals(self, cr, uid, id, resource_id=None,
start_datetime=None, end_datetime=None,
context=None):
"""Get the leaves of the calendar. Leaves can be filtered on the resource, """Get the leaves of the calendar. Leaves can be filtered on the resource,
the start datetime or the end datetime. the start datetime or the end datetime.
:param int resource_id: if set, global + specific leaves will be taken :param int resource_id: the id of the resource to take into account when
into account computing the leaves. If not set, only general
TODO: COMPLETE ME leaves are computed. If set, generic and
specific leaves are computed.
:param datetime start_datetime: if provided, do not take into account leaves
ending before this date.
:param datetime end_datetime: if provided, do not take into account leaves
beginning after this date.
:return list leaves: list of tuples (start_datetime, end_datetime) of
leave intervals
""" """
resource_calendar = self.browse(cr, uid, id, context=context) resource_calendar = self.browse(cr, uid, id, context=context)
leaves = [] leaves = []
for leave in resource_calendar.leave_ids: for leave in resource_calendar.leave_ids:
if resource_id and leave.resource_id and not resource_id == leave.resource_id.id: if leave.resource_id and not resource_id == leave.resource_id.id:
continue continue
date_from = datetime.strptime(leave.date_from, tools.DEFAULT_SERVER_DATETIME_FORMAT) date_from = datetime.datetime.strptime(leave.date_from, tools.DEFAULT_SERVER_DATETIME_FORMAT)
if start_datetime and date_from < start_datetime: if end_datetime and date_from > end_datetime:
continue continue
if end_datetime and date_end > end_datetime: date_to = datetime.datetime.strptime(leave.date_to, tools.DEFAULT_SERVER_DATETIME_FORMAT)
if start_datetime and date_to < start_datetime:
continue continue
date_to = datetime.strptime(leave.date_to, tools.DEFAULT_SERVER_DATETIME_FORMAT)
leaves.append((date_from, date_to)) leaves.append((date_from, date_to))
return leaves return leaves
def get_working_intervals_of_day(self, cr, uid, id, day_date=None, leaves=None, compute_leaves=False, resource_id=None, context=None): def get_working_intervals_of_day(self, cr, uid, id, start_dt=None, end_dt=None,
leaves=None, compute_leaves=False, resource_id=None,
context=None):
"""Get the working intervals of the day based on calendar. This method """Get the working intervals of the day based on calendar. This method
handle leaves that come directly from the leaves parameter or can be computed. handle leaves that come directly from the leaves parameter or can be computed.
:param int id: resource.calendar id; take the first one if is a list :param int id: resource.calendar id; take the first one if is a list
:param date day_date: date object that is the day for which this method :param datetime start_dt: datetime object that is the beginning hours
computes the working intervals; is None, set to today for the working intervals computation; any
working interval beginning before start_dt
will be truncated. If not set, set to end_dt
or today() if no end_dt at 00.00.00.
:param datetime end_dt: datetime object that is the ending hour
for the working intervals computation; any
working interval ending after end_dt
will be truncated. If not set, set to start_dt()
at 23.59.59.
:param list leaves: a list of tuples(start_datetime, end_datetime) that :param list leaves: a list of tuples(start_datetime, end_datetime) that
represent leaves. represent leaves.
:param boolean compute_leaves: if set and if leaves is None, compute the :param boolean compute_leaves: if set and if leaves is None, compute the
@ -224,40 +280,45 @@ class resource_calendar(osv.osv):
no leaves are taken into account. no leaves are taken into account.
:param int resource_id: the id of the resource to take into account when :param int resource_id: the id of the resource to take into account when
computing the leaves. If not set, only general computing the leaves. If not set, only general
leaves will be computed. leaves are computed. If set, generic and
specific leaves are computed.
:returns list intervals: a list of tuples (start_datetime, end_datetime) :return list intervals: a list of tuples (start_datetime, end_datetime)
that are intervals of work of work intervals
""" """
if id is None: if id is None:
return [] return []
if isinstance(id, (list, tuple)): if isinstance(id, (list, tuple)):
id = id[0] id = id[0]
if day_date is None:
day_date = date.today()
resource_calendar = self.browse(cr, uid, id, context=context)
intervals = []
# find working intervals # Computes start_dt, end_dt (with default values if not set) + off-interval work limits
date_dict = { work_limits = []
'Y': day_date.year, if start_dt is None and end_dt is not None:
'm': day_date.month, start_dt = end_dt.replace(hour=0, minute=0, second=0)
'd': day_date.day, elif start_dt is None:
} start_dt = datetime.datetime.now().replace(hour=0, minute=0, second=0)
else:
work_limits.append((start_dt.replace(hour=0, minute=0, second=0), start_dt))
if end_dt is None:
end_dt = start_dt.replace(hour=23, minute=59, second=59)
else:
work_limits.append((end_dt, end_dt.replace(hour=23, minute=59, second=59)))
assert start_dt.date() == end_dt.date(), 'get_working_intervals_of_day is restricted to one day'
intervals = []
work_dt = start_dt.replace(hour=0, minute=0, second=0)
working_intervals = [] working_intervals = []
for calendar_working_day in resource_calendar.attendance_ids: for calendar_working_day in self.get_attendances_for_weekdays(cr, uid, id, [start_dt.weekday()], context):
if int(calendar_working_day.dayofweek) == day_date.weekday(): working_interval = (
date_dict.update({ work_dt.replace(hour=int(calendar_working_day.hour_from)),
'HF': calendar_working_day.hour_from, work_dt.replace(hour=int(calendar_working_day.hour_to))
'HT': calendar_working_day.hour_to, )
}) working_intervals += self.interval_remove_leaves(working_interval, work_limits)
date_from = datetime.strptime('%(Y)04d-%(m)02d-%(d)02d %(HF)02d:00:00' % date_dict, '%Y-%m-%d %H:%M:%S')
date_to = datetime.strptime('%(Y)04d-%(m)02d-%(d)02d %(HT)02d:00:00' % date_dict, '%Y-%m-%d %H:%M:%S')
working_intervals.append((date_from, date_to))
# find leave intervals # find leave intervals
if leaves is None and compute_leaves: if leaves is None and compute_leaves:
leaves = self._get_leave_intervals(cr, uid, id, resource_id=resource_id, context=None) leaves = self.get_leave_intervals(cr, uid, id, resource_id=resource_id, context=None)
# filter according to leaves # filter according to leaves
for interval in working_intervals: for interval in working_intervals:
@ -266,70 +327,135 @@ class resource_calendar(osv.osv):
return intervals return intervals
def get_working_hours_of_date(self, cr, uid, id, day_date=None, leaves=None, compute_leaves=False, resource_id=None, context=None): def get_working_hours_of_date(self, cr, uid, id, start_dt=None, end_dt=None,
leaves=None, compute_leaves=False, resource_id=None,
context=None):
"""Get the working hours of the day based on calendar. This method uses """Get the working hours of the day based on calendar. This method uses
get_working_intervals_of_day to have the work intervals of the day. It get_working_intervals_of_day to have the work intervals of the day. It
then calculates the number of hours contained in those intervals. """ then calculates the number of hours contained in those intervals. """
res = timedelta() res = datetime.timedelta()
intervals = self.get_working_intervals_of_day(cr, uid, id, day_date, leaves, compute_leaves, resource_id, context) intervals = self.get_working_intervals_of_day(cr, uid, id, start_dt, end_dt, leaves, compute_leaves, resource_id, context)
for interval in intervals: for interval in intervals:
res += interval[1] - interval[0] res += interval[1] - interval[0]
return (res.total_seconds() / 3600.0) return (res.total_seconds() / 3600.0)
def schedule_hours(self, cr, uid, id, hours, start_datetime=None, end_datetime=None, compute_leaves=False, resource_id=None, context=None): def _schedule_hours(self, cr, uid, id, hours, day_dt=None,
compute_leaves=False, resource_id=None,
context=None):
"""Schedule hours of work, using a calendar and an optional resource to
compute working and leave days. This method can be used backwards, i.e.
scheduling days before a deadline.
:param timedelta hours: number of hours to schedule
:param datetime day_dt: reference date to compute working days. If days is
> 0 date is the starting date. If days is < 0
date is the ending date.
:param boolean compute_leaves: if set, compute the leaves based on calendar
and resource. Otherwise no leaves are taken
into account.
:param int resource_id: the id of the resource to take into account when
computing the leaves. If not set, only general
leaves are computed. If set, generic and
specific leaves are computed.
:return tuple (datetime, intervals): datetime is the beginning/ending date
of the schedulign; intervals are the
working intervals of the scheduling.
Note: Why not using rrule.rrule ? Because rrule does not seem to allow
getting back in time.
""" """
""" if day_dt is None:
if start_datetime is None: day_dt = datetime.datetime.now()
start_datetime = datetime.now() backwards = (hours < 0)
work_hours = 0 hours = abs(hours)
intervals = []
remaining_hours = hours * 1.0
iterations = 0 iterations = 0
final_intervals = [] current_datetime = day_dt
# compute work days call_args = dict(compute_leaves=compute_leaves, resource_id=resource_id, context=context)
work_days = set()
resource_calendar = self.browse(cr, uid, id, context=context)
for attendance in resource_calendar.attendance_ids:
work_days.add(int(attendance.dayofweek))
# prepare rrule arguments while float_compare(remaining_hours, 0.0, precision_digits=2) in (1, 0) and iterations < 1000:
rrule_args = { if backwards:
'byweekday': work_days, call_args['end_dt'] = current_datetime
'dtstart': start_datetime, else:
} call_args['start_dt'] = current_datetime
if end_date:
rrule_args['until'] = end_datetime
else:
rrule_args['count'] = 1024
for day in rrule.rrule(rrule.DAILY, **rrule_args): working_intervals = self.get_working_intervals_of_day(cr, uid, id, **call_args)
working_intervals = self.get_working_intervals_of_day(cr, uid, id, day_date=day, compute_leaves=compute_leaves, resource_id=resource_id, context=context)
if not working_intervals: if id is None: # no calendar -> consider 8 working hours
continue remaining_hours -= 8.0
# Compute worked hours, compare to requested number of hours elif working_intervals:
res = timedelta() if backwards:
for interval in working_intervals: working_intervals.reverse()
res += interval[1] - interval[0] new_working_intervals = self.interval_schedule_hours(working_intervals, remaining_hours, not backwards)
work_hours += (res.total_seconds() / 3600.0) if backwards:
final_intervals += working_intervals new_working_intervals.reverse()
if float_compare(work_hours, hours * 1.0, precision_digits=2) in (0, 1) or (iterations >= 50):
break res = datetime.timedelta()
for interval in working_intervals:
res += interval[1] - interval[0]
remaining_hours -= (res.total_seconds() / 3600.0)
if backwards:
intervals = new_working_intervals + intervals
else:
intervals = intervals + new_working_intervals
# get next day
if backwards:
current_datetime = datetime.datetime.combine(self.get_previous_day(cr, uid, id, current_datetime, context), datetime.time(23, 59, 59))
else:
current_datetime = datetime.datetime.combine(self.get_next_day(cr, uid, id, current_datetime, context), datetime.time())
# avoid infinite loops
iterations += 1 iterations += 1
return final_intervals return intervals
def _schedule_days(self, cr, uid, id, days, date=None, compute_leaves=False, resource_id=None, context=None): def schedule_hours_get_date(self, cr, uid, id, hours, day_dt=None, compute_leaves=False, resource_id=None, context=None):
"""Schedule days of work. """Wrapper on _schedule_hours: return the beginning/ending datetime of
an hours scheduling. """
res = self._schedule_hours(cr, uid, id, hours, day_dt, compute_leaves, resource_id, context)
return res[0][0]
This method can be used backwards, i.e. scheduling days before a deadline. def schedule_hours(self, cr, uid, id, hours, day_dt=None, compute_leaves=False, resource_id=None, context=None):
"""Wrapper on _schedule_hours: return the working intervals of an hours
scheduling. """
return self._schedule_hours(cr, uid, id, hours, day_dt, compute_leaves, resource_id, context)
def _schedule_days(self, cr, uid, id, days, day_date=None, compute_leaves=False, resource_id=None, context=None):
"""Schedule days of work, using a calendar and an optional resource to
compute working and leave days. This method can be used backwards, i.e.
scheduling days before a deadline.
:param date day_date: reference date to compute working days. If days is > 0
date is the starting date. If days is < 0 date is the
ending date.
:param boolean compute_leaves: if set, compute the leaves based on calendar
and resource. Otherwise no leaves are taken
into account.
:param int resource_id: the id of the resource to take into account when
computing the leaves. If not set, only general
leaves are computed. If set, generic and
specific leaves are computed.
:return tuple (datetime, intervals): datetime is the beginning/ending date
of the schedulign; intervals are the
working intervals of the scheduling.
TDE NOTE: Why not using rrule.rrule ? Because rrule does not seem to
allow getting back in time.
""" """
backwards = False if day_date is None:
if days < 0: day_date = datetime.datetime.now()
backwards = True backwards = (days < 0)
days = abs(days) days = abs(days)
intervals = [] intervals = []
planned_days = 0 planned_days = 0
iterations = 0 iterations = 0
current_datetime = date if backwards:
current_datetime = day_date.replace(hour=23, minute=59, second=59)
else:
current_datetime = day_date.replace(hour=0, minute=0, second=0)
while planned_days < days and iterations < 1000: while planned_days < days and iterations < 1000:
working_intervals = self.get_working_intervals_of_day(cr, uid, id, current_datetime, compute_leaves=compute_leaves, resource_id=resource_id, context=context) working_intervals = self.get_working_intervals_of_day(cr, uid, id, current_datetime, compute_leaves=compute_leaves, resource_id=resource_id, context=context)
@ -338,25 +464,38 @@ class resource_calendar(osv.osv):
intervals += working_intervals intervals += working_intervals
# get next day # get next day
if backwards: if backwards:
current_datetime = self.get_previous_day(cr, uid, id, current_datetime) current_datetime = self.get_previous_day(cr, uid, id, current_datetime, context)
else: else:
current_datetime = self.get_next_day(cr, uid, id, current_datetime) current_datetime = self.get_next_day(cr, uid, id, current_datetime, context)
# avoid infinite loops
iterations += 1
return (current_datetime, intervals) return intervals
def schedule_days(self, cr, uid, id, days, date=None, compute_leaves=False, resource_id=None, context=None): def schedule_days_get_date(self, cr, uid, id, days, day_date=None, compute_leaves=False, resource_id=None, context=None):
res = self._schedule_days(cr, uid, id, days, date, compute_leaves, resource_id, context) """Wrapper on _schedule_days: return the beginning/ending datetime of
return res[0] a days scheduling. """
res = self._schedule_days(cr, uid, id, days, day_date, compute_leaves, resource_id, context)
return res[-1][1].date()
def schedule_days(self, cr, uid, id, days, day_date=None, compute_leaves=False, resource_id=None, context=None):
"""Wrapper on _schedule_days: return the working intervals of a days
scheduling. """
return self._schedule_days(cr, uid, id, days, day_date, compute_leaves, resource_id, context)
# -------------------------------------------------- # --------------------------------------------------
# Compaqtibility / to clean / to remove # Compaqtibility / to clean / to remove
# -------------------------------------------------- # --------------------------------------------------
def working_hours_on_day(self, cr, uid, resource_calendar_id, day, context=None): def working_hours_on_day(self, cr, uid, resource_calendar_id, day, context=None):
""" Compatibility method - will be removed for OpenERP v8 """ Compatibility method - will be removed for OpenERP v8. Computation
was done for the whole day, therefore setting start_dt at the beginning
of the day.
TDE TODO: hr_payroll/hr_payroll.py TDE TODO: hr_payroll/hr_payroll.py
""" """
return self.get_working_hours_of_date(cr, uid, resource_calendar_id.id, day_date=day, context=None) if isinstance(day, datetime.datetime):
day = day.replace(hour=0, minute=0)
return self.get_working_hours_of_date(cr, uid, resource_calendar_id.id, start_dt=day, context=None)
def _get_leaves(self, cr, uid, id, resource): def _get_leaves(self, cr, uid, id, resource):
"""Private Method to Calculate resource Leaves days """Private Method to Calculate resource Leaves days
@ -366,6 +505,7 @@ class resource_calendar(osv.osv):
@return : returns the list of dates, where resource on leave in @return : returns the list of dates, where resource on leave in
resource.calendar.leaves object (e.g.['%Y-%m-%d', '%Y-%m-%d']) resource.calendar.leaves object (e.g.['%Y-%m-%d', '%Y-%m-%d'])
TDE TODO: internal only
""" """
resource_cal_leaves = self.pool.get('resource.calendar.leaves') resource_cal_leaves = self.pool.get('resource.calendar.leaves')
dt_leave = [] dt_leave = []
@ -374,10 +514,10 @@ class resource_calendar(osv.osv):
res_leaves = resource_cal_leaves.browse(cr, uid, resource_leave_ids) res_leaves = resource_cal_leaves.browse(cr, uid, resource_leave_ids)
for leave in res_leaves: for leave in res_leaves:
dtf = datetime.strptime(leave.date_from, '%Y-%m-%d %H:%M:%S') dtf = datetime.datetime.strptime(leave.date_from, '%Y-%m-%d %H:%M:%S')
dtt = datetime.strptime(leave.date_to, '%Y-%m-%d %H:%M:%S') dtt = datetime.datetime.strptime(leave.date_to, '%Y-%m-%d %H:%M:%S')
no = dtt - dtf no = dtt - dtf
[dt_leave.append((dtf + timedelta(days=x)).strftime('%Y-%m-%d')) for x in range(int(no.days + 1))] [dt_leave.append((dtf + datetime.timedelta(days=x)).strftime('%Y-%m-%d')) for x in range(int(no.days + 1))]
dt_leave.sort() dt_leave.sort()
return dt_leave return dt_leave
@ -399,10 +539,11 @@ class resource_calendar(osv.osv):
params params
TDE TODO: used in mrp_operations/mrp_operations.py TDE TODO: used in mrp_operations/mrp_operations.py
TDE NOTE: do not count leave hours, a leave is considered all-day
""" """
if not id: if not id:
td = int(hours)*3 td = int(hours)*3
return [(dt_from - timedelta(hours=td), dt_from)] return [(dt_from - datetime.timedelta(hours=td), dt_from)]
dt_leave = self._get_leaves(cr, uid, id, resource) dt_leave = self._get_leaves(cr, uid, id, resource)
dt_leave.reverse() dt_leave.reverse()
todo = hours todo = hours
@ -420,17 +561,17 @@ class resource_calendar(osv.osv):
dt_check = dt_from.strftime('%Y-%m-%d') dt_check = dt_from.strftime('%Y-%m-%d')
for leave in dt_leave: for leave in dt_leave:
if dt_check == leave: if dt_check == leave:
dt_check = datetime.strptime(dt_check, '%Y-%m-%d') + timedelta(days=1) dt_check = datetime.datetime.strptime(dt_check, '%Y-%m-%d') + datetime.timedelta(days=1)
leave_flag = True leave_flag = True
if leave_flag: if leave_flag:
break break
else: else:
d1 = datetime(dt_from.year, dt_from.month, dt_from.day, int(math.floor(hour_from)), int((hour_from%1) * 60)) d1 = datetime.datetime(dt_from.year, dt_from.month, dt_from.day, int(math.floor(hour_from)), int((hour_from%1) * 60))
d2 = datetime(dt_from.year, dt_from.month, dt_from.day, int(math.floor(m)), int((m%1) * 60)) d2 = datetime.datetime(dt_from.year, dt_from.month, dt_from.day, int(math.floor(m)), int((m%1) * 60))
result.append((d1, d2)) result.append((d1, d2))
current_hour = hour_from current_hour = hour_from
todo -= (m-hour_from) todo -= (m-hour_from)
dt_from -= timedelta(days=1) dt_from -= datetime.timedelta(days=1)
current_hour = 24 current_hour = 24
maxrecur -= 1 maxrecur -= 1
result.reverse() result.reverse()
@ -438,7 +579,10 @@ class resource_calendar(osv.osv):
# def interval_get(self, cr, uid, id, dt_from, hours, resource=False, byday=True): # def interval_get(self, cr, uid, id, dt_from, hours, resource=False, byday=True):
def interval_get_multi(self, cr, uid, date_and_hours_by_cal, resource=False, byday=True): def interval_get_multi(self, cr, uid, date_and_hours_by_cal, resource=False, byday=True):
""" TDE NOTE: used in mrp_operations/mrp_operations.py and in interval_get() """ """ TDE NOTE: used in mrp_operations/mrp_operations.py (default parameters) and in interval_get()
TDE NOTE: byday is not used in this method...
TDE NOTE: do not count leave hours, a leave is considered all-day
"""
def group(lst, key): def group(lst, key):
lst.sort(key=itemgetter(key)) lst.sort(key=itemgetter(key))
grouped = itertools.groupby(lst, itemgetter(key)) grouped = itertools.groupby(lst, itemgetter(key))
@ -452,10 +596,10 @@ class resource_calendar(osv.osv):
results = {} results = {}
for d, hours, id in date_and_hours_by_cal: for d, hours, id in date_and_hours_by_cal:
dt_from = datetime.strptime(d, '%Y-%m-%d %H:%M:%S') dt_from = datetime.datetime.strptime(d, '%Y-%m-%d %H:%M:%S')
if not id: if not id:
td = int(hours)*3 td = int(hours)*3
results[(d, hours, id)] = [(dt_from, dt_from + timedelta(hours=td))] results[(d, hours, id)] = [(dt_from, dt_from + datetime.timedelta(hours=td))]
continue continue
dt_leave = self._get_leaves(cr, uid, id, resource) dt_leave = self._get_leaves(cr, uid, id, resource)
@ -473,17 +617,17 @@ class resource_calendar(osv.osv):
dt_check = dt_from.strftime('%Y-%m-%d') dt_check = dt_from.strftime('%Y-%m-%d')
for leave in dt_leave: for leave in dt_leave:
if dt_check == leave: if dt_check == leave:
dt_check = datetime.strptime(dt_check, '%Y-%m-%d') + timedelta(days=1) dt_check = datetime.datetime.strptime(dt_check, '%Y-%m-%d') + datetime.timedelta(days=1)
leave_flag = True leave_flag = True
if leave_flag: if leave_flag:
break break
else: else:
d1 = datetime(dt_from.year, dt_from.month, dt_from.day, int(math.floor(m)), int((m%1) * 60)) d1 = datetime.datetime(dt_from.year, dt_from.month, dt_from.day, int(math.floor(m)), int((m%1) * 60))
d2 = datetime(dt_from.year, dt_from.month, dt_from.day, int(math.floor(hour_to)), int((hour_to%1) * 60)) d2 = datetime.datetime(dt_from.year, dt_from.month, dt_from.day, int(math.floor(hour_to)), int((hour_to%1) * 60))
result.append((d1, d2)) result.append((d1, d2))
current_hour = hour_to current_hour = hour_to
todo -= (hour_to - m) todo -= (hour_to - m)
dt_from += timedelta(days=1) dt_from += datetime.timedelta(days=1)
current_hour = 0 current_hour = 0
maxrecur -= 1 maxrecur -= 1
results[(d, hours, id)] = result results[(d, hours, id)] = result
@ -500,7 +644,7 @@ class resource_calendar(osv.osv):
@return : list of scheduled working timing based on resource calendar. @return : list of scheduled working timing based on resource calendar.
TDE NOTE: mrp_operations/mrp_operations.py, crm/crm_lead.py TDE NOTE: mrp_operations/mrp_operations.py, crm/crm_lead.py (res given)
""" """
res = self.interval_get_multi(cr, uid, [(dt_from.strftime('%Y-%m-%d %H:%M:%S'), hours, id)], resource, byday)[(dt_from.strftime('%Y-%m-%d %H:%M:%S'), hours, id)] res = self.interval_get_multi(cr, uid, [(dt_from.strftime('%Y-%m-%d %H:%M:%S'), hours, id)], resource, byday)[(dt_from.strftime('%Y-%m-%d %H:%M:%S'), hours, id)]
return res return res
@ -538,6 +682,7 @@ class resource_calendar(osv.osv):
resource if supplied. resource if supplied.
TDE NOTE: used in project_issue/project_issue.py TDE NOTE: used in project_issue/project_issue.py
TDE NOTE: day-long leaves
""" """
utc_tz = pytz.timezone('UTC') utc_tz = pytz.timezone('UTC')
local_tz = utc_tz local_tz = utc_tz
@ -591,7 +736,7 @@ class resource_calendar(osv.osv):
interval_start = utc_to_local_zone(dt_from) interval_start = utc_to_local_zone(dt_from)
interval_end = utc_to_local_zone(dt_to) interval_end = utc_to_local_zone(dt_to)
hours_timedelta = timedelta() hours_timedelta = datetime.timedelta()
# Get leaves for requested resource # Get leaves for requested resource
dt_leaves = set([]) dt_leaves = set([])
@ -599,7 +744,7 @@ class resource_calendar(osv.osv):
dt_leaves = set(self._get_leaves(cr, uid, id, resource=resource_id)) dt_leaves = set(self._get_leaves(cr, uid, id, resource=resource_id))
for day in rrule.rrule(rrule.DAILY, dtstart=interval_start, for day in rrule.rrule(rrule.DAILY, dtstart=interval_start,
until=interval_end+timedelta(days=1), until=interval_end+datetime.timedelta(days=1),
byweekday=hours_range_per_weekday.keys()): byweekday=hours_range_per_weekday.keys()):
if exclude_leaves and day.strftime('%Y-%m-%d') in dt_leaves: if exclude_leaves and day.strftime('%Y-%m-%d') in dt_leaves:
# XXX: futher improve leave management to allow for partial day leave # XXX: futher improve leave management to allow for partial day leave
@ -724,10 +869,10 @@ class resource_resource(osv.osv):
], context=context) ], context=context)
leaves = resource_calendar_leaves_pool.read(cr, uid, leave_ids, ['date_from', 'date_to'], context=context) leaves = resource_calendar_leaves_pool.read(cr, uid, leave_ids, ['date_from', 'date_to'], context=context)
for i in range(len(leaves)): for i in range(len(leaves)):
dt_start = datetime.strptime(leaves[i]['date_from'], '%Y-%m-%d %H:%M:%S') dt_start = datetime.datetime.strptime(leaves[i]['date_from'], '%Y-%m-%d %H:%M:%S')
dt_end = datetime.strptime(leaves[i]['date_to'], '%Y-%m-%d %H:%M:%S') dt_end = datetime.datetime.strptime(leaves[i]['date_to'], '%Y-%m-%d %H:%M:%S')
no = dt_end - dt_start no = dt_end - dt_start
[leave_list.append((dt_start + timedelta(days=x)).strftime('%Y-%m-%d')) for x in range(int(no.days + 1))] [leave_list.append((dt_start + datetime.timedelta(days=x)).strftime('%Y-%m-%d')) for x in range(int(no.days + 1))]
leave_list.sort() leave_list.sort()
return leave_list return leave_list

View File

@ -26,7 +26,7 @@
dt = now - timedelta(days=now.weekday()) dt = now - timedelta(days=now.weekday())
for resource in resources: for resource in resources:
result = calendar_pool.working_hours_on_day(cr, uid, resource.calendar_id, dt, context) result = calendar_pool.working_hours_on_day(cr, uid, resource.calendar_id, dt, context)
assert result == 9.0, 'Wrong calculation of day work hour availability of the Resource.' assert result == 9.0, 'Wrong calculation of day work hour availability of the Resource (found %d).' % result
- -
Now, resource "Developer" drafted leave on Thursday in this week. Now, resource "Developer" drafted leave on Thursday in this week.
- -

View File

@ -39,15 +39,15 @@ class TestResourceCommon(common.TransactionCase):
self.resource_leaves = self.registry('resource.calendar.leaves') self.resource_leaves = self.registry('resource.calendar.leaves')
# Some demo data # Some demo data
self.date1 = datetime.strptime('2013-02-12 09:22:13', '%Y-%m-%d %H:%M:%S') # weekday() returns 1, isoweekday() returns 2 self.date1 = datetime.strptime('2013-02-12 09:08:07', '%Y-%m-%d %H:%M:%S') # weekday() returns 1, isoweekday() returns 2
self.date2 = datetime.strptime('2013-02-15 09:22:13', '%Y-%m-%d %H:%M:%S') # weekday() returns 4, isoweekday() returns 5 self.date2 = datetime.strptime('2013-02-15 10:11:12', '%Y-%m-%d %H:%M:%S') # weekday() returns 4, isoweekday() returns 5
# Leave1: 19/02/2013, from 9 to 12, is a day 0 # Leave1: 19/02/2013, from 9 to 12, is a day 1
self.leave1_start = datetime.strptime('2013-02-19 09:00:00', '%Y-%m-%d %H:%M:%S') self.leave1_start = datetime.strptime('2013-02-19 09:00:00', '%Y-%m-%d %H:%M:%S')
self.leave1_end = datetime.strptime('2013-02-19 12:00:00', '%Y-%m-%d %H:%M:%S') self.leave1_end = datetime.strptime('2013-02-19 12:00:00', '%Y-%m-%d %H:%M:%S')
# Leave2: 22/02/2013, from 9 to 15, is a day 3 # Leave2: 22/02/2013, from 9 to 15, is a day 4
self.leave2_start = datetime.strptime('2013-02-22 09:00:00', '%Y-%m-%d %H:%M:%S') self.leave2_start = datetime.strptime('2013-02-22 09:00:00', '%Y-%m-%d %H:%M:%S')
self.leave2_end = datetime.strptime('2013-02-22 15:00:00', '%Y-%m-%d %H:%M:%S') self.leave2_end = datetime.strptime('2013-02-22 15:00:00', '%Y-%m-%d %H:%M:%S')
# Leave3: 25/02/2013 (day6) -> 01/03/2013 (day3) # Leave3: 25/02/2013 (day0) -> 01/03/2013 (day4)
self.leave3_start = datetime.strptime('2013-02-25 13:00:00', '%Y-%m-%d %H:%M:%S') self.leave3_start = datetime.strptime('2013-02-25 13:00:00', '%Y-%m-%d %H:%M:%S')
self.leave3_end = datetime.strptime('2013-03-01 11:30:00', '%Y-%m-%d %H:%M:%S') self.leave3_end = datetime.strptime('2013-03-01 11:30:00', '%Y-%m-%d %H:%M:%S')

View File

@ -19,7 +19,7 @@
# #
############################################################################## ##############################################################################
from datetime import datetime from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from openerp.addons.resource.tests.common import TestResourceCommon from openerp.addons.resource.tests.common import TestResourceCommon
@ -51,17 +51,17 @@ class TestResource(TestResourceCommon):
] ]
# Test: interval cleaning # Test: interval cleaning
result = self.resource_calendar.interval_clean(intervals) cleaned_intervals = self.resource_calendar.interval_clean(intervals)
self.assertEqual(len(result), 3, 'resource_calendar: wrong interval cleaning') self.assertEqual(len(cleaned_intervals), 3, 'resource_calendar: wrong interval cleaning')
# First interval: 03, unchanged # First interval: 03, unchanged
self.assertEqual(result[0][0], datetime.strptime('2013-02-03 08:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong interval cleaning') self.assertEqual(cleaned_intervals[0][0], datetime.strptime('2013-02-03 08:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong interval cleaning')
self.assertEqual(result[0][1], datetime.strptime('2013-02-03 10:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong interval cleaning') self.assertEqual(cleaned_intervals[0][1], datetime.strptime('2013-02-03 10:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong interval cleaning')
# Second intreval: 04, 08-14, combining 08-12 and 11-14, 09-11 being inside 08-12 # Second intreval: 04, 08-14, combining 08-12 and 11-14, 09-11 being inside 08-12
self.assertEqual(result[1][0], datetime.strptime('2013-02-04 08:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong interval cleaning') self.assertEqual(cleaned_intervals[1][0], datetime.strptime('2013-02-04 08:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong interval cleaning')
self.assertEqual(result[1][1], datetime.strptime('2013-02-04 14:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong interval cleaning') self.assertEqual(cleaned_intervals[1][1], datetime.strptime('2013-02-04 14:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong interval cleaning')
# Third interval: 04, 17-21, 18-19 being inside 17-21 # Third interval: 04, 17-21, 18-19 being inside 17-21
self.assertEqual(result[2][0], datetime.strptime('2013-02-04 17:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong interval cleaning') self.assertEqual(cleaned_intervals[2][0], datetime.strptime('2013-02-04 17:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong interval cleaning')
self.assertEqual(result[2][1], datetime.strptime('2013-02-04 21:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong interval cleaning') self.assertEqual(cleaned_intervals[2][1], datetime.strptime('2013-02-04 21:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong interval cleaning')
# Test: disjoint removal # Test: disjoint removal
working_interval = (datetime.strptime('2013-02-04 08:00:00', '%Y-%m-%d %H:%M:%S'), datetime.strptime('2013-02-04 18:00:00', '%Y-%m-%d %H:%M:%S')) working_interval = (datetime.strptime('2013-02-04 08:00:00', '%Y-%m-%d %H:%M:%S'), datetime.strptime('2013-02-04 18:00:00', '%Y-%m-%d %H:%M:%S'))
@ -71,6 +71,27 @@ class TestResource(TestResourceCommon):
self.assertEqual(result[0][0], datetime.strptime('2013-02-04 14:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong leave removal from interval') self.assertEqual(result[0][0], datetime.strptime('2013-02-04 14:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong leave removal from interval')
self.assertEqual(result[0][1], datetime.strptime('2013-02-04 17:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong leave removal from interval') self.assertEqual(result[0][1], datetime.strptime('2013-02-04 17:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong leave removal from interval')
# Test: schedule hours on intervals
result = self.resource_calendar.interval_schedule_hours(cleaned_intervals, 5.5)
self.assertEqual(len(result), 2, 'resource_calendar: wrong hours scheduling in interval')
# First interval: 03, 8-10 untouches
self.assertEqual(result[0][0], datetime.strptime('2013-02-03 08:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong leave removal from interval')
self.assertEqual(result[0][1], datetime.strptime('2013-02-03 10:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong leave removal from interval')
# First interval: 04, 08-11:30
self.assertEqual(result[1][0], datetime.strptime('2013-02-04 08:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong leave removal from interval')
self.assertEqual(result[1][1], datetime.strptime('2013-02-04 11:30:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong leave removal from interval')
# Test: schedule hours on intervals, backwards
cleaned_intervals.reverse()
result = self.resource_calendar.interval_schedule_hours(cleaned_intervals, 5.5, remove_at_end=False)
self.assertEqual(len(result), 2, 'resource_calendar: wrong hours scheduling in interval')
# First interval: 03, 8-10 untouches
self.assertEqual(result[0][0], datetime.strptime('2013-02-04 17:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong leave removal from interval')
self.assertEqual(result[0][1], datetime.strptime('2013-02-04 21:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong leave removal from interval')
# First interval: 04, 08-11:30
self.assertEqual(result[1][0], datetime.strptime('2013-02-04 12:30:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong leave removal from interval')
self.assertEqual(result[1][1], datetime.strptime('2013-02-04 14:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong leave removal from interval')
def test_10_calendar_basics(self): def test_10_calendar_basics(self):
""" Testing basic method of resource.calendar """ """ Testing basic method of resource.calendar """
cr, uid = self.cr, self.uid cr, uid = self.cr, self.uid
@ -80,110 +101,289 @@ class TestResource(TestResourceCommon):
# -------------------------------------------------- # --------------------------------------------------
# Test: next day: next day after day1 is day4 # Test: next day: next day after day1 is day4
date = self.resource_calendar.get_next_day(cr, uid, self.calendar_id, day_date=self.date1) date = self.resource_calendar.get_next_day(cr, uid, self.calendar_id, day_date=self.date1.date())
self.assertEqual(date, self.date2, 'resource_calendar: wrong next day computing') self.assertEqual(date, self.date2.date(), 'resource_calendar: wrong next day computing')
# Test: next day: next day after day4 is (day1+7) # Test: next day: next day after day4 is (day1+7)
date = self.resource_calendar.get_next_day(cr, uid, self.calendar_id, day_date=self.date2) date = self.resource_calendar.get_next_day(cr, uid, self.calendar_id, day_date=self.date2.date())
self.assertEqual(date, self.date1 + relativedelta(days=7), 'resource_calendar: wrong next day computing') self.assertEqual(date, self.date1.date() + relativedelta(days=7), 'resource_calendar: wrong next day computing')
# Test: next day: next day after day4+1 is (day1+7) # Test: next day: next day after day4+1 is (day1+7)
date = self.resource_calendar.get_next_day(cr, uid, self.calendar_id, day_date=self.date2 + relativedelta(days=1)) date = self.resource_calendar.get_next_day(cr, uid, self.calendar_id, day_date=self.date2.date() + relativedelta(days=1))
self.assertEqual(date, self.date1 + relativedelta(days=7), 'resource_calendar: wrong next day computing') self.assertEqual(date, self.date1.date() + relativedelta(days=7), 'resource_calendar: wrong next day computing')
# Test: next day: next day after day1-1 is day1 # Test: next day: next day after day1-1 is day1
date = self.resource_calendar.get_next_day(cr, uid, self.calendar_id, day_date=self.date1 + relativedelta(days=-1)) date = self.resource_calendar.get_next_day(cr, uid, self.calendar_id, day_date=self.date1.date() + relativedelta(days=-1))
self.assertEqual(date, self.date1, 'resource_calendar: wrong next day computing') self.assertEqual(date, self.date1.date(), 'resource_calendar: wrong next day computing')
# -------------------------------------------------- # --------------------------------------------------
# Test2: get_previous_day # Test2: get_previous_day
# -------------------------------------------------- # --------------------------------------------------
# Test: previous day: previous day before day1 is (day4-7) # Test: previous day: previous day before day1 is (day4-7)
date = self.resource_calendar.get_previous_day(cr, uid, self.calendar_id, day_date=self.date1) date = self.resource_calendar.get_previous_day(cr, uid, self.calendar_id, day_date=self.date1.date())
self.assertEqual(date, self.date2 + relativedelta(days=-7), 'resource_calendar: wrong previous day computing') self.assertEqual(date, self.date2.date() + relativedelta(days=-7), 'resource_calendar: wrong previous day computing')
# Test: previous day: previous day before day4 is day1 # Test: previous day: previous day before day4 is day1
date = self.resource_calendar.get_previous_day(cr, uid, self.calendar_id, day_date=self.date2) date = self.resource_calendar.get_previous_day(cr, uid, self.calendar_id, day_date=self.date2.date())
self.assertEqual(date, self.date1, 'resource_calendar: wrong previous day computing') self.assertEqual(date, self.date1.date(), 'resource_calendar: wrong previous day computing')
# Test: previous day: previous day before day4+1 is day4 # Test: previous day: previous day before day4+1 is day4
date = self.resource_calendar.get_previous_day(cr, uid, self.calendar_id, day_date=self.date2 + relativedelta(days=1)) date = self.resource_calendar.get_previous_day(cr, uid, self.calendar_id, day_date=self.date2.date() + relativedelta(days=1))
self.assertEqual(date, self.date2, 'resource_calendar: wrong previous day computing') self.assertEqual(date, self.date2.date(), 'resource_calendar: wrong previous day computing')
# Test: previous day: previous day before day1-1 is (day4-7) # Test: previous day: previous day before day1-1 is (day4-7)
date = self.resource_calendar.get_previous_day(cr, uid, self.calendar_id, day_date=self.date1 + relativedelta(days=-1)) date = self.resource_calendar.get_previous_day(cr, uid, self.calendar_id, day_date=self.date1.date() + relativedelta(days=-1))
self.assertEqual(date, self.date2 + relativedelta(days=-7), 'resource_calendar: wrong previous day computing') self.assertEqual(date, self.date2.date() + relativedelta(days=-7), 'resource_calendar: wrong previous day computing')
# --------------------------------------------------
# Test3: misc
# --------------------------------------------------
weekdays = self.resource_calendar.get_weekdays(cr, uid, self.calendar_id)
self.assertEqual(weekdays, [1, 4], 'resource_calendar: wrong weekdays computing')
attendances = self.resource_calendar.get_attendances_for_weekdays(cr, uid, self.calendar_id, [2, 3, 4, 5])
self.assertEqual(set([att.id for att in attendances]), set([self.att2_id, self.att3_id]),
'resource_calendar: wrong attendances filtering by weekdays computing')
def test_20_calendar_working_intervals(self): def test_20_calendar_working_intervals(self):
""" Testing working intervals computing method of resource.calendar """ """ Testing working intervals computing method of resource.calendar """
cr, uid = self.cr, self.uid cr, uid = self.cr, self.uid
_format = '%Y-%m-%d %H:%M:%S'
# Test: day0 without leaves: 1 interval # Test: day0 without leaves: 1 interval
intervals = self.resource_calendar.get_working_intervals_of_day(cr, uid, self.calendar_id, day_date=self.date1) intervals = self.resource_calendar.get_working_intervals_of_day(cr, uid, self.calendar_id, start_dt=self.date1)
self.assertEqual(len(intervals), 1, 'resource_calendar: wrong working intervals') self.assertEqual(len(intervals), 1, 'resource_calendar: wrong working intervals')
self.assertEqual(intervals[0][0], datetime.strptime('2013-02-12 08:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong working intervals') self.assertEqual(intervals[0][0], datetime.strptime('2013-02-12 09:08:07', _format), 'resource_calendar: wrong working intervals')
self.assertEqual(intervals[0][1], datetime.strptime('2013-02-12 16:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong working intervals') self.assertEqual(intervals[0][1], datetime.strptime('2013-02-12 16:00:00', _format), 'resource_calendar: wrong working intervals')
# Test: day3 without leaves: 2 interval # Test: day3 without leaves: 2 interval
intervals = self.resource_calendar.get_working_intervals_of_day(cr, uid, self.calendar_id, day_date=self.date2) intervals = self.resource_calendar.get_working_intervals_of_day(cr, uid, self.calendar_id, start_dt=self.date2)
self.assertEqual(len(intervals), 2, 'resource_calendar: wrong working intervals') self.assertEqual(len(intervals), 2, 'resource_calendar: wrong working intervals')
self.assertEqual(intervals[0][0], datetime.strptime('2013-02-15 08:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong working intervals') self.assertEqual(intervals[0][0], datetime.strptime('2013-02-15 10:11:12', _format), 'resource_calendar: wrong working intervals')
self.assertEqual(intervals[0][1], datetime.strptime('2013-02-15 13:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong working intervals') self.assertEqual(intervals[0][1], datetime.strptime('2013-02-15 13:00:00', _format), 'resource_calendar: wrong working intervals')
self.assertEqual(intervals[1][0], datetime.strptime('2013-02-15 16:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong working intervals') self.assertEqual(intervals[1][0], datetime.strptime('2013-02-15 16:00:00', _format), 'resource_calendar: wrong working intervals')
self.assertEqual(intervals[1][1], datetime.strptime('2013-02-15 23:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong working intervals') self.assertEqual(intervals[1][1], datetime.strptime('2013-02-15 23:00:00', _format), 'resource_calendar: wrong working intervals')
# Test: day0 with leaves outside range: 1 interval # Test: day0 with leaves outside range: 1 interval
intervals = self.resource_calendar.get_working_intervals_of_day(cr, uid, self.calendar_id, day_date=self.date1, compute_leaves=True) intervals = self.resource_calendar.get_working_intervals_of_day(cr, uid, self.calendar_id, start_dt=self.date1.replace(hour=0), compute_leaves=True)
self.assertEqual(len(intervals), 1, 'resource_calendar: wrong working intervals') self.assertEqual(len(intervals), 1, 'resource_calendar: wrong working intervals')
self.assertEqual(intervals[0][0], datetime.strptime('2013-02-12 08:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong working intervals') self.assertEqual(intervals[0][0], datetime.strptime('2013-02-12 08:00:00', _format), 'resource_calendar: wrong working intervals')
self.assertEqual(intervals[0][1], datetime.strptime('2013-02-12 16:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong working intervals') self.assertEqual(intervals[0][1], datetime.strptime('2013-02-12 16:00:00', _format), 'resource_calendar: wrong working intervals')
# Test: day0 with leaves: 2 intrevals because of leave between 9 ans 12 # Test: day0 with leaves: 2 intervals because of leave between 9 ans 12, ending at 15:45:30
intervals = self.resource_calendar.get_working_intervals_of_day(cr, uid, self.calendar_id, day_date=self.date1 + relativedelta(days=7), compute_leaves=True) intervals = self.resource_calendar.get_working_intervals_of_day(cr, uid, self.calendar_id,
start_dt=self.date1.replace(hour=8) + relativedelta(days=7),
end_dt=self.date1.replace(hour=15, minute=45, second=30) + relativedelta(days=7),
compute_leaves=True)
self.assertEqual(len(intervals), 2, 'resource_calendar: wrong working intervals') self.assertEqual(len(intervals), 2, 'resource_calendar: wrong working intervals')
self.assertEqual(intervals[0][0], datetime.strptime('2013-02-19 08:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong working intervals') self.assertEqual(intervals[0][0], datetime.strptime('2013-02-19 08:08:07', _format), 'resource_calendar: wrong working intervals')
self.assertEqual(intervals[0][1], datetime.strptime('2013-02-19 09:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong working intervals') self.assertEqual(intervals[0][1], datetime.strptime('2013-02-19 09:00:00', _format), 'resource_calendar: wrong working intervals')
self.assertEqual(intervals[1][0], datetime.strptime('2013-02-19 12:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong working intervals') self.assertEqual(intervals[1][0], datetime.strptime('2013-02-19 12:00:00', _format), 'resource_calendar: wrong working intervals')
self.assertEqual(intervals[1][1], datetime.strptime('2013-02-19 16:00:00', '%Y-%m-%d %H:%M:%S'), 'resource_calendar: wrong working intervals') self.assertEqual(intervals[1][1], datetime.strptime('2013-02-19 15:45:30', _format), 'resource_calendar: wrong working intervals')
def test_30_calendar_working_days(self):
""" Testing calendar hours computation on a working day """
cr, uid = self.cr, self.uid
_format = '%Y-%m-%d %H:%M:%S'
# Test: day1, beginning at 10:30 -> work from 10:30 (arrival) until 16:00
intervals = self.resource_calendar.get_working_intervals_of_day(cr, uid, self.calendar_id, start_dt=self.date1.replace(hour=10, minute=30, second=0))
self.assertEqual(len(intervals), 1, 'resource_calendar: wrong working interval / day computing')
self.assertEqual(intervals[0][0], datetime.strptime('2013-02-12 10:30:00', _format), 'resource_calendar: wrong working interval / day computing')
self.assertEqual(intervals[0][1], datetime.strptime('2013-02-12 16:00:00', _format), 'resource_calendar: wrong working interval / day computing')
# Test: hour computation for same interval, should give 5.5
wh = self.resource_calendar.get_working_hours_of_date(cr, uid, self.calendar_id, start_dt=self.date1.replace(hour=10, minute=30, second=0))
self.assertEqual(wh, 5.5, 'resource_calendar: wrong working interval / day time computing')
# Test: day1+7 on leave, without leave computation
intervals = self.resource_calendar.get_working_intervals_of_day(
cr, uid, self.calendar_id,
start_dt=self.date1.replace(hour=7, minute=0, second=0) + relativedelta(days=7)
)
# Result: day1 (08->16)
self.assertEqual(len(intervals), 1, 'resource_calendar: wrong working interval/day computing')
self.assertEqual(intervals[0][0], datetime.strptime('2013-02-19 08:00:00', _format), 'resource_calendar: wrong working interval / day computing')
self.assertEqual(intervals[0][1], datetime.strptime('2013-02-19 16:00:00', _format), 'resource_calendar: wrong working interval / day computing')
# Test: day1+7 on leave, with generic leave computation
intervals = self.resource_calendar.get_working_intervals_of_day(
cr, uid, self.calendar_id,
start_dt=self.date1.replace(hour=7, minute=0, second=0) + relativedelta(days=7),
compute_leaves=True
)
# Result: day1 (08->09 + 12->16)
self.assertEqual(len(intervals), 2, 'resource_calendar: wrong working interval/day computing')
self.assertEqual(intervals[0][0], datetime.strptime('2013-02-19 08:00:00', _format), 'resource_calendar: wrong working interval / day computing')
self.assertEqual(intervals[0][1], datetime.strptime('2013-02-19 09:00:00', _format), 'resource_calendar: wrong working interval / day computing')
self.assertEqual(intervals[1][0], datetime.strptime('2013-02-19 12:00:00', _format), 'resource_calendar: wrong working interval / day computing')
self.assertEqual(intervals[1][1], datetime.strptime('2013-02-19 16:00:00', _format), 'resource_calendar: wrong working interval / day computing')
# Test: day1+14 on leave, with generic leave computation
intervals = self.resource_calendar.get_working_intervals_of_day(
cr, uid, self.calendar_id,
start_dt=self.date1.replace(hour=7, minute=0, second=0) + relativedelta(days=14),
compute_leaves=True
)
# Result: day1 (08->16)
self.assertEqual(len(intervals), 1, 'resource_calendar: wrong working interval/day computing')
self.assertEqual(intervals[0][0], datetime.strptime('2013-02-26 08:00:00', _format), 'resource_calendar: wrong working interval / day computing')
self.assertEqual(intervals[0][1], datetime.strptime('2013-02-26 16:00:00', _format), 'resource_calendar: wrong working interval / day computing')
# Test: day1+14 on leave, with resource leave computation
intervals = self.resource_calendar.get_working_intervals_of_day(
cr, uid, self.calendar_id,
start_dt=self.date1.replace(hour=7, minute=0, second=0) + relativedelta(days=14),
compute_leaves=True,
resource_id=self.resource1_id
)
# Result: nothing, because on leave
self.assertEqual(len(intervals), 0, 'resource_calendar: wrong working interval/day computing')
def test_40_calendar_hours_scheduling(self):
""" Testing calendar hours scheduling """
cr, uid = self.cr, self.uid
_format = '%Y-%m-%d %H:%M:%S'
# --------------------------------------------------
# Test0: schedule hours backwards (old interval_min_get)
# Done without calendar
# --------------------------------------------------
# Done without calendar
# res = self.resource_calendar.interval_min_get(cr, uid, None, self.date1, 40, resource=False)
# res: (datetime.datetime(2013, 2, 7, 9, 8, 7), datetime.datetime(2013, 2, 12, 9, 8, 7))
# --------------------------------------------------
# Test1: schedule hours backwards (old interval_min_get)
# --------------------------------------------------
# res = self.resource_calendar.interval_min_get(cr, uid, self.calendar_id, self.date1, 40, resource=False)
# (datetime.datetime(2013, 1, 29, 9, 0), datetime.datetime(2013, 1, 29, 16, 0))
# (datetime.datetime(2013, 2, 1, 8, 0), datetime.datetime(2013, 2, 1, 13, 0))
# (datetime.datetime(2013, 2, 1, 16, 0), datetime.datetime(2013, 2, 1, 23, 0))
# (datetime.datetime(2013, 2, 5, 8, 0), datetime.datetime(2013, 2, 5, 16, 0))
# (datetime.datetime(2013, 2, 8, 8, 0), datetime.datetime(2013, 2, 8, 13, 0))
# (datetime.datetime(2013, 2, 8, 16, 0), datetime.datetime(2013, 2, 8, 23, 0))
# (datetime.datetime(2013, 2, 12, 8, 0), datetime.datetime(2013, 2, 12, 9, 0))
res = self.resource_calendar.schedule_hours(cr, uid, self.calendar_id, -40, day_dt=self.date1.replace(minute=0, second=0))
# current day, limited at 09:00 because of day_dt specified -> 1 hour
self.assertEqual(res[-1][0], datetime.strptime('2013-02-12 08:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[-1][1], datetime.strptime('2013-02-12 09:00:00', _format), 'resource_calendar: wrong hours scheduling')
# previous days: 5+7 hours / 8 hours / 5+7 hours -> 32 hours
self.assertEqual(res[-2][0], datetime.strptime('2013-02-08 16:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[-2][1], datetime.strptime('2013-02-08 23:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[-3][0], datetime.strptime('2013-02-08 08:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[-3][1], datetime.strptime('2013-02-08 13:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[-4][0], datetime.strptime('2013-02-05 08:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[-4][1], datetime.strptime('2013-02-05 16:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[-5][0], datetime.strptime('2013-02-01 16:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[-5][1], datetime.strptime('2013-02-01 23:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[-6][0], datetime.strptime('2013-02-01 08:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[-6][1], datetime.strptime('2013-02-01 13:00:00', _format), 'resource_calendar: wrong hours scheduling')
# 7 hours remaining
self.assertEqual(res[-7][0], datetime.strptime('2013-01-29 09:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[-7][1], datetime.strptime('2013-01-29 16:00:00', _format), 'resource_calendar: wrong hours scheduling')
# Compute scheduled hours
td = timedelta()
for item in res:
td += item[1] - item[0]
self.assertEqual(td.total_seconds() / 3600.0, 40.0, 'resource_calendar: wrong hours scheduling')
# --------------------------------------------------
# Test1: schedule hours forward (old interval_get)
# --------------------------------------------------
# res = self.resource_calendar.interval_get(cr, uid, self.calendar_id, self.date1, 40, resource=False, byday=True)
# (datetime.datetime(2013, 2, 12, 9, 0), datetime.datetime(2013, 2, 12, 16, 0))
# (datetime.datetime(2013, 2, 15, 8, 0), datetime.datetime(2013, 2, 15, 13, 0))
# (datetime.datetime(2013, 2, 15, 16, 0), datetime.datetime(2013, 2, 15, 23, 0))
# (datetime.datetime(2013, 2, 22, 8, 0), datetime.datetime(2013, 2, 22, 13, 0))
# (datetime.datetime(2013, 2, 22, 16, 0), datetime.datetime(2013, 2, 22, 23, 0))
# (datetime.datetime(2013, 2, 26, 8, 0), datetime.datetime(2013, 2, 26, 16, 0))
# (datetime.datetime(2013, 3, 1, 8, 0), datetime.datetime(2013, 3, 1, 9, 0))
res = self.resource_calendar.schedule_hours(
cr, uid, self.calendar_id, 40,
day_dt=self.date1.replace(minute=0, second=0)
)
self.assertEqual(res[0][0], datetime.strptime('2013-02-12 09:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[0][1], datetime.strptime('2013-02-12 16:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[1][0], datetime.strptime('2013-02-15 08:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[1][1], datetime.strptime('2013-02-15 13:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[2][0], datetime.strptime('2013-02-15 16:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[2][1], datetime.strptime('2013-02-15 23:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[3][0], datetime.strptime('2013-02-19 08:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[3][1], datetime.strptime('2013-02-19 16:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[4][0], datetime.strptime('2013-02-22 08:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[4][1], datetime.strptime('2013-02-22 13:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[5][0], datetime.strptime('2013-02-22 16:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[5][1], datetime.strptime('2013-02-22 23:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[6][0], datetime.strptime('2013-02-26 08:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[6][1], datetime.strptime('2013-02-26 09:00:00', _format), 'resource_calendar: wrong hours scheduling')
td = timedelta()
for item in res:
td += item[1] - item[0]
self.assertEqual(td.total_seconds() / 3600.0, 40.0, 'resource_calendar: wrong hours scheduling')
# res = self.resource_calendar.interval_get(cr, uid, self.calendar_id, self.date1, 40, resource=self.resource1_id, byday=True)
# (datetime.datetime(2013, 2, 12, 9, 0), datetime.datetime(2013, 2, 12, 16, 0))
# (datetime.datetime(2013, 2, 15, 8, 0), datetime.datetime(2013, 2, 15, 13, 0))
# (datetime.datetime(2013, 2, 15, 16, 0), datetime.datetime(2013, 2, 15, 23, 0))
# (datetime.datetime(2013, 3, 1, 8, 0), datetime.datetime(2013, 3, 1, 13, 0))
# (datetime.datetime(2013, 3, 1, 16, 0), datetime.datetime(2013, 3, 1, 23, 0))
# (datetime.datetime(2013, 3, 5, 8, 0), datetime.datetime(2013, 3, 5, 16, 0))
# (datetime.datetime(2013, 3, 8, 8, 0), datetime.datetime(2013, 3, 8, 9, 0))
res = self.resource_calendar.schedule_hours(
cr, uid, self.calendar_id, 40,
day_dt=self.date1.replace(minute=0, second=0),
compute_leaves=True,
resource_id=self.resource1_id
)
self.assertEqual(res[0][0], datetime.strptime('2013-02-12 09:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[0][1], datetime.strptime('2013-02-12 16:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[1][0], datetime.strptime('2013-02-15 08:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[1][1], datetime.strptime('2013-02-15 13:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[2][0], datetime.strptime('2013-02-15 16:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[2][1], datetime.strptime('2013-02-15 23:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[3][0], datetime.strptime('2013-02-19 08:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[3][1], datetime.strptime('2013-02-19 09:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[4][0], datetime.strptime('2013-02-19 12:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[4][1], datetime.strptime('2013-02-19 16:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[5][0], datetime.strptime('2013-02-22 08:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[5][1], datetime.strptime('2013-02-22 09:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[6][0], datetime.strptime('2013-02-22 16:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[6][1], datetime.strptime('2013-02-22 23:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[7][0], datetime.strptime('2013-03-01 11:30:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[7][1], datetime.strptime('2013-03-01 13:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[8][0], datetime.strptime('2013-03-01 16:00:00', _format), 'resource_calendar: wrong hours scheduling')
self.assertEqual(res[8][1], datetime.strptime('2013-03-01 22:30:00', _format), 'resource_calendar: wrong hours scheduling')
td = timedelta()
for item in res:
td += item[1] - item[0]
self.assertEqual(td.total_seconds() / 3600.0, 40.0, 'resource_calendar: wrong hours scheduling')
def test_40_calendar_schedule_days(self): def test_40_calendar_schedule_days(self):
""" Testing calendar days scheduling """ """ Testing calendar days scheduling """
cr, uid = self.cr, self.uid cr, uid = self.cr, self.uid
_format = '%Y-%m-%d %H:%M:%S'
print '---------------' res = self.resource_calendar.schedule_days_get_date(cr, uid, self.calendar_id, 5, day_date=self.date1)
res = self.resource_calendar.schedule_days(cr, uid, self.calendar_id, 5, date=self.date1) self.assertEqual(res, datetime.strptime('2013-02-26 00:0:00', _format).date(), 'resource_calendar: wrong days scheduling')
print res
res = self.resource_calendar.schedule_days_get_date(
cr, uid, self.calendar_id, 5, day_date=self.date1,
compute_leaves=True, resource_id=self.resource1_id)
self.assertEqual(res, datetime.strptime('2013-03-01 00:0:00', _format).date(), 'resource_calendar: wrong days scheduling')
# -------------------------------------------------- # --------------------------------------------------
# Misc # Misc
# -------------------------------------------------- # --------------------------------------------------
# Without calendar, should only count days # # Without calendar, should only count days
print '---------------' # print '---------------'
res = self.resource_calendar.schedule_days(cr, uid, None, 5, date=self.date1) # res = self.resource_calendar.schedule_days_get_date(cr, uid, None, 5, day_date=self.date1)
print res # print res
# @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
# def test_20_calendar(self):
# """ Testing calendar and time computation """
# cr, uid = self.cr, self.uid
# wh = self.resource_calendar.get_working_hours_of_date(cr, uid, self.calendar_id, day_date=self.date1)
# self.assertEqual(wh, 8, 'cacamou')
# wh = self.resource_calendar.get_working_hours_of_date(cr, uid, self.calendar_id, day_date=self.date2+relativedelta(days=7))
# self.assertEqual(wh, 12, 'cacamou')
# # print '---------------------'
# # print self.date1
# # res = self.resource_calendar.interval_min_get(cr, uid, self.calendar_id, self.date1, 40, resource=False)
# # print res
# print '----------------------'
# res = self.resource_calendar.schedule_hours(cr, uid, self.calendar_id, 40, start_datetime=self.date1)
# print res
# print '----------------------'
# # print self.date1
# # res = self.resource_calendar.interval_get(cr, uid, self.calendar_id, self.date1, 40, resource=False, byday=True)
# # print res