[IMP] resource : some docstrings cleaning + void calendar

taken into account in days scheduling + finished yesys

bzr revid: tde@openerp.com-20140122132918-4c1ag6y5gqasi32d
This commit is contained in:
Thibault Delavallée 2014-01-22 14:29:18 +01:00
parent ec2c2d4e86
commit 9fbbba1ee0
3 changed files with 122 additions and 48 deletions

View File

@ -254,7 +254,7 @@ class resource_calendar(osv.osv):
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):
context=None, default_interval=None):
"""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.
@ -279,12 +279,16 @@ class resource_calendar(osv.osv):
computing the leaves. If not set, only general
leaves are computed. If set, generic and
specific leaves are computed.
:param tuple default_interval: if no id, try to return a default working
day using default_interval[0] as beginning
hour, and default_interval[1] as ending hour.
Example: default_interval = (8, 16).
Otherwise, a void list of working intervals
is returned when id is None.
:return list intervals: a list of tuples (start_datetime, end_datetime)
of work intervals
"""
if id is None:
return []
if isinstance(id, (list, tuple)):
id = id[0]
@ -305,6 +309,12 @@ class resource_calendar(osv.osv):
intervals = []
work_dt = start_dt.replace(hour=0, minute=0, second=0)
# no calendar: try to use the default_interval, then return directly
if id is None:
if default_interval:
intervals.append((start_dt.replace(hour=default_interval[0]), start_dt.replace(hour=default_interval[1])))
return intervals
working_intervals = []
for calendar_working_day in self.get_attendances_for_weekdays(cr, uid, id, [start_dt.weekday()], context):
working_interval = (
@ -336,6 +346,10 @@ class resource_calendar(osv.osv):
res += interval[1] - interval[0]
return (res.total_seconds() / 3600.0)
# --------------------------------------------------
# Hours scheduling
# --------------------------------------------------
def _schedule_hours(self, cr, uid, id, hours, day_dt=None,
compute_leaves=False, resource_id=None,
context=None):
@ -343,7 +357,7 @@ class resource_calendar(osv.osv):
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 int 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.
@ -381,7 +395,7 @@ class resource_calendar(osv.osv):
working_intervals = self.get_working_intervals_of_day(cr, uid, id, **call_args)
if id is None: # no calendar -> consider 8 working hours
if id is None: # no calendar -> consider working 8 hours
remaining_hours -= 8.0
elif working_intervals:
if backwards:
@ -419,7 +433,8 @@ class resource_calendar(osv.osv):
scheduling. """
return self._schedule_hours(cr, uid, id, hours, day_dt, compute_leaves, resource_id, context)
def get_working_hours(self, cr, uid, id, start_dt, end_dt, compute_leaves=False, resource_id=None, context=None):
def get_working_hours(self, cr, uid, id, start_dt, end_dt, compute_leaves=False,
resource_id=None, context=None):
hours = 0.0
for day in rrule.rrule(rrule.DAILY, dtstart=start_dt,
until=end_dt + datetime.timedelta(days=1),
@ -427,11 +442,18 @@ class resource_calendar(osv.osv):
hours += self.get_working_hours_of_date(cr, uid, id, start_dt=day, compute_leaves=compute_leaves, resource_id=resource_id)
return hours
def _schedule_days(self, cr, uid, id, days, day_date=None, compute_leaves=False, resource_id=None, context=None):
# --------------------------------------------------
# Days scheduling
# --------------------------------------------------
def _schedule_days(self, cr, uid, id, days, day_date=None, compute_leaves=False,
resource_id=None, default_interval=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 int days: number of days to schedule. Use a negative number to compute
a backwards scheduling.
: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.
@ -442,13 +464,19 @@ class resource_calendar(osv.osv):
computing the leaves. If not set, only general
leaves are computed. If set, generic and
specific leaves are computed.
:param tuple default_interval: if no id, try to return a default working
day using default_interval[0] as beginning
hour, and default_interval[1] as ending hour.
Example: default_interval = (8, 16).
Otherwise, a void list of working intervals
is returned when id is None.
: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.
Implementation note: rrule.rrule is not used because rrule it des not seem
to allow getting back in time.
"""
if day_date is None:
day_date = datetime.datetime.now()
@ -463,7 +491,11 @@ class resource_calendar(osv.osv):
current_datetime = day_date.replace(hour=0, minute=0, second=0)
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,
default_interval=default_interval)
if id is None or working_intervals: # no calendar -> no working hours, but day is considered as worked
planned_days += 1
intervals += working_intervals
@ -477,33 +509,37 @@ class resource_calendar(osv.osv):
return intervals
def schedule_days_get_date(self, cr, uid, id, days, day_date=None, compute_leaves=False, resource_id=None, context=None):
"""Wrapper on _schedule_days: return the beginning/ending datetime of
def schedule_days_get_date(self, cr, uid, id, days, day_date=None, compute_leaves=False,
resource_id=None, default_interval=None, context=None):
""" Wrapper on _schedule_days: return the beginning/ending datetime of
a days scheduling. """
res = self._schedule_days(cr, uid, id, days, day_date, compute_leaves, resource_id, context)
return res[-1][1]
res = self._schedule_days(cr, uid, id, days, day_date, compute_leaves, resource_id, default_interval, context)
return res and res[-1][1] or []
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
def schedule_days(self, cr, uid, id, days, day_date=None, compute_leaves=False,
resource_id=None, default_interval=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)
return self._schedule_days(cr, uid, id, days, day_date, compute_leaves, resource_id, default_interval, context)
# --------------------------------------------------
# Compatibility / to clean / to remove
# --------------------------------------------------
def working_hours_on_day(self, cr, uid, resource_calendar_id, day, context=None):
""" Compatibility method - will be removed for OpenERP v8.
Computation was done for the whole day, ignoring hour/minutes.
Used in hr_payroll/hr_payroll.py """
""" Used in hr_payroll/hr_payroll.py
:deprecated: OpenERP saas-3. Use get_working_hours_of_date instead. Note:
since saas-3, take hour/minutes into account, not just the whole day."""
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 interval_min_get(self, cr, uid, id, dt_from, hours, resource=False):
""" Compatibility method - will be removed for OpenERP v8.
Schedule hours backwards. Note: now count leave hours instead of all-day leaves.
Used in mrp_operations/mrp_operations.py. """
""" Schedule hours backwards. Used in mrp_operations/mrp_operations.py.
:deprecated: OpenERP saas-3. Use schedule_hours instead. Note: since
saas-3, counts leave hours instead of all-day leaves."""
return self.schedule_hours(
cr, uid, id, hours * -1.0,
day_dt=dt_from.replace(minute=0, second=0),
@ -511,9 +547,11 @@ class resource_calendar(osv.osv):
)
def interval_get_multi(self, cr, uid, date_and_hours_by_cal, resource=False, byday=True):
""" Compatibility method - will be removed for OpenERP v8.
Note: used in mrp_operations/mrp_operations.py (default parameters) and in interval_get()
Byday was not used. Now counts Leave hours instead of all-day leaves. """
""" Used in mrp_operations/mrp_operations.py (default parameters) and in
interval_get()
:deprecated: OpenERP saas-3. Use get_working_hours_of_date instead. Note:
Byday was not used. Since saas-3, counts Leave hours instead of all-day leaves."""
res = {}
for dt_str, hours, calendar_id in date_and_hours_by_cal:
result = self.schedule_hours(
@ -525,20 +563,25 @@ class resource_calendar(osv.osv):
return res
def interval_get(self, cr, uid, id, dt_from, hours, resource=False, byday=True):
""" Compatibility method - will be removed for OpenERP v8. Unifier of interval_get_multi.
Used in: mrp_operations/mrp_operations.py, crm/crm_lead.py (res given) """
""" Unifier of interval_get_multi. Used in: mrp_operations/mrp_operations.py,
crm/crm_lead.py (res given).
:deprecated: OpenERP saas-3. Use get_working_hours instead."""
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
def interval_hours_get(self, cr, uid, id, dt_from, dt_to, resource=False):
""" Compatibility method - will be removed for OpenERP v8. Unused wrapper """
""" Unused wrapper.
:deprecated: OpenERP saas-3. Use get_working_hours instead."""
return self._interval_hours_get(cr, uid, id, dt_from, dt_to, resource_id=resource)
def _interval_hours_get(self, cr, uid, id, dt_from, dt_to, resource_id=False, timezone_from_uid=None, exclude_leaves=True, context=None):
""" Compatibility method - will be removed for OpenERP v8.
Computes working hours between two dates, taking always same hour/minuts.
Note: now resets hour/minuts. Now counts leave hours instead of all-day leaves. """
return self.get_working_hours(cr, uid, id, dt_from, dt_to, compute_leaves=exclude_leaves, resource_id=resource_id, context=context)
""" Computes working hours between two dates, taking always same hour/minuts.
:deprecated: OpenERP saas-3. Use get_working_hours instead. Note: since saas-3,
now resets hour/minuts. Now counts leave hours instead of all-day leaves."""
return self.get_working_hours(cr, uid, id, dt_from, dt_to, compute_leaves=(not exclude_leaves), resource_id=resource_id, context=context)
class resource_calendar_attendance(osv.osv):

View File

@ -52,7 +52,7 @@ class TestResourceCommon(common.TransactionCase):
self.leave3_end = datetime.strptime('2013-03-01 11:30:00', '%Y-%m-%d %H:%M:%S')
# Resource data
# Calendar working days: 1 (8-16), 4 (8-13, 21-22)
# Calendar working days: 1 (8-16 -> 8hours), 4 (8-13, 16-23 -> 12hours)
self.calendar_id = self.resource_calendar.create(
cr, uid, {
'name': 'TestCalendar',

View File

@ -293,7 +293,7 @@ class TestResource(TestResourceCommon):
self.assertEqual(td.total_seconds() / 3600.0, 40.0, 'resource_calendar: wrong hours scheduling')
# --------------------------------------------------
# Test1: schedule hours forward (old interval_get)
# Test2: schedule hours forward (old interval_get)
# --------------------------------------------------
# res = self.resource_calendar.interval_get(cr, uid, self.calendar_id, self.date1, 40, resource=False, byday=True)
@ -366,23 +366,55 @@ class TestResource(TestResourceCommon):
td += item[1] - item[0]
self.assertEqual(td.total_seconds() / 3600.0, 40.0, 'resource_calendar: wrong hours scheduling')
res = self.resource_calendar._interval_hours_get(cr, uid, self.calendar_id, self.date1.replace(hour=6, minute=0), self.date2.replace(hour=23, minute=0) + relativedelta(days=7), resource_id=self.resource1_id, exclude_leaves=False)
# print res
# --------------------------------------------------
# Test3: working hours (old _interval_hours_get)
# --------------------------------------------------
res = self.resource_calendar.get_working_hours(cr, uid, self.calendar_id, self.date1.replace(hour=6, minute=0), self.date2.replace(hour=23, minute=0) + relativedelta(days=7), compute_leaves=False, resource_id=self.resource1_id)
# print res
# old API: resource without leaves
# res: 2 weeks -> 40 hours
res = self.resource_calendar._interval_hours_get(
cr, uid, self.calendar_id,
self.date1.replace(hour=6, minute=0),
self.date2.replace(hour=23, minute=0) + relativedelta(days=7),
resource_id=self.resource1_id, exclude_leaves=True)
self.assertEqual(res, 40.0, 'resource_calendar: wrong _interval_hours_get compatibility computation')
res = self.resource_calendar._interval_hours_get(cr, uid, self.calendar_id, self.date1.replace(hour=6, minute=0), self.date2.replace(hour=23, minute=0) + relativedelta(days=7), resource_id=self.resource1_id, exclude_leaves=True)
# print res
# new API: resource without leaves
# res: 2 weeks -> 40 hours
res = self.resource_calendar.get_working_hours(
cr, uid, self.calendar_id,
self.date1.replace(hour=6, minute=0),
self.date2.replace(hour=23, minute=0) + relativedelta(days=7),
compute_leaves=False, resource_id=self.resource1_id)
self.assertEqual(res, 40.0, 'resource_calendar: wrong get_working_hours computation')
res = self.resource_calendar.get_working_hours(cr, uid, self.calendar_id, self.date1.replace(hour=6, minute=0), self.date2.replace(hour=23, minute=0) + relativedelta(days=7), compute_leaves=True, resource_id=self.resource1_id)
# print res
# old API: resource and leaves
# res: 2 weeks -> 40 hours - (3+4) leave hours
res = self.resource_calendar._interval_hours_get(
cr, uid, self.calendar_id,
self.date1.replace(hour=6, minute=0),
self.date2.replace(hour=23, minute=0) + relativedelta(days=7),
resource_id=self.resource1_id, exclude_leaves=False)
self.assertEqual(res, 33.0, 'resource_calendar: wrong _interval_hours_get compatibility computation')
# new API: resource and leaves
# res: 2 weeks -> 40 hours - (3+4) leave hours
res = self.resource_calendar.get_working_hours(
cr, uid, self.calendar_id,
self.date1.replace(hour=6, minute=0),
self.date2.replace(hour=23, minute=0) + relativedelta(days=7),
compute_leaves=True, resource_id=self.resource1_id)
self.assertEqual(res, 33.0, 'resource_calendar: wrong get_working_hours computation')
def test_50_calendar_schedule_days(self):
""" Testing calendar days scheduling """
cr, uid = self.cr, self.uid
_format = '%Y-%m-%d %H:%M:%S'
# --------------------------------------------------
# Test1: with calendar
# --------------------------------------------------
res = self.resource_calendar.schedule_days_get_date(cr, uid, self.calendar_id, 5, day_date=self.date1)
self.assertEqual(res.date(), datetime.strptime('2013-02-26 00:0:00', _format).date(), 'resource_calendar: wrong days scheduling')
@ -392,10 +424,9 @@ class TestResource(TestResourceCommon):
self.assertEqual(res.date(), datetime.strptime('2013-03-01 00:0:00', _format).date(), 'resource_calendar: wrong days scheduling')
# --------------------------------------------------
# Misc
# Test2: without calendar and default scheduling
# --------------------------------------------------
# # Without calendar, should only count days
# print '---------------'
# res = self.resource_calendar.schedule_days_get_date(cr, uid, None, 5, day_date=self.date1)
# print res
# Without calendar, should only count days -> 12 -> 16, 5 days
res = self.resource_calendar.schedule_days_get_date(cr, uid, None, 5, day_date=self.date1, default_interval=(8, 16))
self.assertEqual(res, datetime.strptime('2013-02-16 16:00:00', _format), 'resource_calendar: wrong days scheduling')