diff --git a/addons/resource/__openerp__.py b/addons/resource/__openerp__.py
index 836b0d216e0..503704222f5 100644
--- a/addons/resource/__openerp__.py
+++ b/addons/resource/__openerp__.py
@@ -2,10 +2,9 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
- 'name' : 'Resource',
- 'version' : '1.1',
- 'category' : 'Hidden',
- 'website' : 'http://www.openerp.com',
+ 'name': 'Resource',
+ 'version': '1.1',
+ 'category': 'Hidden',
'description': """
Module for resource management.
===============================
@@ -25,6 +24,4 @@ associated to every resource. It also manages the leaves of every resource.
'test/resource.yml',
'test/duplicate_resource.yml',
],
- 'installable': True,
- 'auto_install': False,
}
diff --git a/addons/resource/data/resource_demo.xml b/addons/resource/data/resource_demo.xml
index 509e57ca262..7869468483c 100644
--- a/addons/resource/data/resource_demo.xml
+++ b/addons/resource/data/resource_demo.xml
@@ -1,6 +1,5 @@
-
-
+
@@ -121,5 +120,4 @@
-
-
+
diff --git a/addons/resource/models/resource.py b/addons/resource/models/resource.py
index 3f41e2c1552..b10637cdbaa 100644
--- a/addons/resource/models/resource.py
+++ b/addons/resource/models/resource.py
@@ -2,21 +2,19 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import datetime
-import functools
+import pytz
+
from datetime import timedelta
from dateutil import rrule
from dateutil.relativedelta import relativedelta
-from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
from operator import itemgetter
-from openerp import tools
-from openerp.osv import fields, osv
-from openerp.tools.float_utils import float_compare
-from openerp.tools.translate import _
-from openerp.exceptions import UserError
-import pytz
+from odoo import api, fields, models, _
+from odoo.exceptions import ValidationError
+from odoo.tools.float_utils import float_compare
-class resource_calendar(osv.osv):
+
+class ResourceCalendar(models.Model):
""" Calendar model for a resource. It has
- attendance_ids: list of resource.calendar.attendance that are a working
@@ -30,19 +28,15 @@ class resource_calendar(osv.osv):
_name = "resource.calendar"
_description = "Resource Calendar"
- _columns = {
- 'name': fields.char("Name", required=True),
- 'company_id': fields.many2one('res.company', 'Company', required=False),
- 'attendance_ids': fields.one2many('resource.calendar.attendance', 'calendar_id', 'Working Time', copy=True),
- 'manager': fields.many2one('res.users', 'Workgroup Manager'),
- 'leave_ids': fields.one2many(
- 'resource.calendar.leaves', 'calendar_id', 'Leaves',
- help=''
- ),
- }
- _defaults = {
- 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'resource.calendar', context=context)
- }
+ name = fields.Char(required=True)
+ company_id = fields.Many2one('res.company', string='Company',
+ default=lambda self: self.env['res.company']._company_default_get())
+ attendance_ids = fields.One2many(
+ 'resource.calendar.attendance', 'calendar_id', string='Working Time',
+ copy=True)
+ manager = fields.Many2one('res.users', string='Workgroup Manager')
+ leave_ids = fields.One2many(
+ 'resource.calendar.leaves', 'calendar_id', string='Leaves')
# --------------------------------------------------
# Utility methods
@@ -72,7 +66,8 @@ class resource_calendar(osv.osv):
cleaned.append(tuple(working_interval))
return cleaned
- def interval_remove_leaves(self, cr, uid, interval, leave_intervals, context=None):
+ @api.model
+ def interval_remove_leaves(self, interval, leave_intervals):
""" Utility method that remove leave intervals from a base interval:
- clean the leave intervals, to have an ordered list of not-overlapping
@@ -112,7 +107,6 @@ class resource_calendar(osv.osv):
current_interval[1] = leave[0]
intervals.append((current_interval[0], current_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[0] = leave[1]
if current_interval and current_interval[0] < interval[1]: # remove intervals moved outside base interval due to leaves
@@ -139,14 +133,14 @@ class resource_calendar(osv.osv):
is greater than the possible scheduling in the intervals, no extra-scheduling
is done, and results == intervals. """
results = []
- res = datetime.timedelta()
- limit = datetime.timedelta(hours=hour)
+ res = timedelta()
+ limit = 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=seconds(limit-res)))
+ interval = (interval[0], interval[1] + relativedelta(seconds=seconds(limit - res)))
elif res > limit:
- interval = (interval[0] + relativedelta(seconds=seconds(res-limit)), interval[1])
+ interval = (interval[0] + relativedelta(seconds=seconds(res - limit)), interval[1])
results.append(interval)
if res > limit:
break
@@ -156,41 +150,40 @@ class resource_calendar(osv.osv):
# Date and hours computation
# --------------------------------------------------
- def get_attendances_for_weekday(self, cr, uid, id, date, context=None):
+ @api.multi
+ def get_attendances_for_weekday(self, date):
""" Given a list of weekdays, return matching resource.calendar.attendance"""
- calendar = self.browse(cr, uid, id, context=None)
+ self.ensure_one()
weekday = date.weekday()
- date = date.strftime(DEFAULT_SERVER_DATE_FORMAT)
- res = []
- for att in calendar.attendance_ids:
- if int(att.dayofweek) == weekday:
- if not ((att.date_from and date < att.date_from) or (att.date_to and date > att.date_to)):
- res.append(att)
- return res
+ date = fields.Date.today()
+ attendances = self.env['resource.calendar.attendance']
+ for attendance in self.attendance_ids.filtered(lambda att: int(att.dayofweek) == weekday and
+ not ((att.date_from and date < att.date_from) or (att.date_to and date > att.date_to))):
+ attendances |= attendance
+ return attendances
- def get_weekdays(self, cr, uid, id, default_weekdays=None, context=None):
+ @api.multi
+ def get_weekdays(self, default_weekdays=None):
""" Return the list of weekdays that contain at least one working interval.
If no id is given (no calendar), return default weekdays. """
- if id is None:
+ if not self:
return default_weekdays if default_weekdays is not None else [0, 1, 2, 3, 4]
- calendar = self.browse(cr, uid, id, context=None)
- weekdays = set()
- for attendance in calendar.attendance_ids:
- weekdays.add(int(attendance.dayofweek))
+ self.ensure_one()
+ weekdays = set(map(int, (self.attendance_ids.mapped('dayofweek'))))
return list(weekdays)
- def get_next_day(self, cr, uid, id, day_date, context=None):
+ @api.multi
+ def get_next_day(self, day_date):
""" Get following date of day_date, based on resource.calendar. If no
calendar is provided, just return the next day.
- :param int id: id of a resource.calendar. If not given, simply add one day
- to the submitted date.
:param date day_date: current day as a date
:return date: next day of calendar, or just next day """
- if not id:
+ if not self:
return day_date + relativedelta(days=1)
- weekdays = self.get_weekdays(cr, uid, id, context)
+ self.ensure_one()
+ weekdays = self.get_weekdays()
base_index = -1
for weekday in weekdays:
@@ -205,18 +198,18 @@ class resource_calendar(osv.osv):
return day_date + relativedelta(days=days)
- def get_previous_day(self, cr, uid, id, day_date, context=None):
+ @api.multi
+ def get_previous_day(self, day_date):
""" Get previous date of day_date, based on resource.calendar. If no
calendar is provided, just return the previous day.
- :param int id: id of a resource.calendar. If not given, simply remove
- one day from the submitted date.
:param date day_date: current day as a date
:return date: previous day of calendar, or just previous day """
- if not id:
+ if not self:
return day_date + relativedelta(days=-1)
- weekdays = self.get_weekdays(cr, uid, id, context)
+ self.ensure_one()
+ weekdays = self.get_weekdays()
weekdays.reverse()
base_index = -1
@@ -232,9 +225,9 @@ class resource_calendar(osv.osv):
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):
+ @api.multi
+ def get_leave_intervals(self, resource_id=None,
+ start_datetime=None, end_datetime=None):
"""Get the leaves of the calendar. Leaves can be filtered on the resource,
the start datetime or the end datetime.
@@ -250,27 +243,27 @@ class resource_calendar(osv.osv):
:return list leaves: list of tuples (start_datetime, end_datetime) of
leave intervals
"""
- resource_calendar = self.browse(cr, uid, id, context=context)
+ self.ensure_one()
leaves = []
- for leave in resource_calendar.leave_ids:
+ for leave in self.leave_ids:
if leave.resource_id and not resource_id == leave.resource_id.id:
continue
- date_from = datetime.datetime.strptime(leave.date_from, tools.DEFAULT_SERVER_DATETIME_FORMAT)
+ date_from = fields.Datetime.from_string(leave.date_from)
if end_datetime and date_from > end_datetime:
continue
- date_to = datetime.datetime.strptime(leave.date_to, tools.DEFAULT_SERVER_DATETIME_FORMAT)
+ date_to = fields.Datetime.from_string(leave.date_to)
if start_datetime and date_to < start_datetime:
continue
leaves.append((date_from, date_to))
return leaves
- def get_working_intervals_of_day(self, cr, uid, id, start_dt=None, end_dt=None,
+ @api.multi
+ def get_working_intervals_of_day(self, start_dt=None, end_dt=None,
leaves=None, compute_leaves=False, resource_id=None,
- default_interval=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.
- :param int id: resource.calendar id; take the first one if is a list
:param datetime start_dt: datetime object that is the beginning hours
for the working intervals computation; any
working interval beginning before start_dt
@@ -300,8 +293,6 @@ class resource_calendar(osv.osv):
:return list intervals: a list of tuples (start_datetime, end_datetime)
of work intervals """
- if isinstance(id, (list, tuple)):
- id = id[0]
# Computes start_dt, end_dt (with default values if not set) + off-interval work limits
work_limits = []
@@ -321,17 +312,17 @@ class resource_calendar(osv.osv):
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 not self:
working_interval = []
if default_interval:
working_interval = (start_dt.replace(hour=default_interval[0], minute=0, second=0),
start_dt.replace(hour=default_interval[1], minute=0, second=0))
- intervals = self.interval_remove_leaves(cr, uid, working_interval, work_limits, context=context)
+ intervals = self.interval_remove_leaves(working_interval, work_limits)
return intervals
working_intervals = []
- tz_info = fields.datetime.context_timestamp(cr, uid, work_dt, context=context).tzinfo
- for calendar_working_day in self.get_attendances_for_weekday(cr, uid, id, start_dt, context=context):
+ tz_info = fields.Datetime.context_timestamp(self, work_dt).tzinfo
+ for calendar_working_day in self.get_attendances_for_weekday(start_dt):
dt_f = work_dt.replace(hour=0, minute=0, second=0) + timedelta(seconds=(calendar_working_day.hour_from * 3600))
dt_t = work_dt.replace(hour=0, minute=0, second=0) + timedelta(seconds=(calendar_working_day.hour_to * 3600))
@@ -341,41 +332,42 @@ class resource_calendar(osv.osv):
dt_t.replace(tzinfo=tz_info).astimezone(pytz.UTC).replace(tzinfo=None),
calendar_working_day.id
)
- working_intervals += self.interval_remove_leaves(cr, uid, working_interval, work_limits, context=context)
+ working_intervals += self.interval_remove_leaves(working_interval, work_limits)
# find leave intervals
if leaves is None and compute_leaves:
- leaves = self.get_leave_intervals(cr, uid, id, resource_id=resource_id, context=context)
+ leaves = self.get_leave_intervals(resource_id=resource_id)
# filter according to leaves
for interval in working_intervals:
- work_intervals = self.interval_remove_leaves(cr, uid, interval, leaves, context=context)
+ work_intervals = self.interval_remove_leaves(interval, leaves)
intervals += work_intervals
return intervals
- def get_working_hours_of_date(self, cr, uid, id, start_dt=None, end_dt=None,
+ @api.multi
+ def get_working_hours_of_date(self, start_dt=None, end_dt=None,
leaves=None, compute_leaves=False, resource_id=None,
- default_interval=None, context=None):
+ default_interval=None):
""" 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
then calculates the number of hours contained in those intervals. """
- res = datetime.timedelta()
+ res = timedelta()
intervals = self.get_working_intervals_of_day(
- cr, uid, id,
start_dt, end_dt, leaves,
compute_leaves, resource_id,
- default_interval, context)
+ default_interval)
for interval in intervals:
res += interval[1] - interval[0]
return seconds(res) / 3600.0
- def get_working_hours(self, cr, uid, id, start_dt, end_dt, compute_leaves=False,
- resource_id=None, default_interval=None, context=None):
+ @api.multi
+ def get_working_hours(self, start_dt, end_dt, compute_leaves=False,
+ resource_id=None, default_interval=None):
hours = 0.0
for day in rrule.rrule(rrule.DAILY, dtstart=start_dt,
- until=(end_dt + datetime.timedelta(days=1)).replace(hour=0, minute=0, second=0),
- byweekday=self.get_weekdays(cr, uid, id, context=context)):
+ until=(end_dt + timedelta(days=1)).replace(hour=0, minute=0, second=0),
+ byweekday=self.get_weekdays()):
day_start_dt = day.replace(hour=0, minute=0, second=0)
if start_dt and day.date() == start_dt.date():
day_start_dt = start_dt
@@ -383,20 +375,19 @@ class resource_calendar(osv.osv):
if end_dt and day.date() == end_dt.date():
day_end_dt = end_dt
hours += self.get_working_hours_of_date(
- cr, uid, id, start_dt=day_start_dt, end_dt=day_end_dt,
+ start_dt=day_start_dt, end_dt=day_end_dt,
compute_leaves=compute_leaves, resource_id=resource_id,
- default_interval=default_interval,
- context=context)
+ default_interval=default_interval)
return hours
# --------------------------------------------------
# Hours scheduling
# --------------------------------------------------
-
- def _schedule_hours(self, cr, uid, id, hours, day_dt=None,
+ @api.multi
+ def _schedule_hours(self, hours, day_dt=None,
compute_leaves=False, resource_id=None,
- default_interval=None, context=None):
+ default_interval=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.
@@ -436,7 +427,7 @@ class resource_calendar(osv.osv):
iterations = 0
current_datetime = day_dt
- call_args = dict(compute_leaves=compute_leaves, resource_id=resource_id, default_interval=default_interval, context=context)
+ call_args = dict(compute_leaves=compute_leaves, resource_id=resource_id, default_interval=default_interval)
while float_compare(remaining_hours, 0.0, precision_digits=2) in (1, 0) and iterations < 1000:
if backwards:
@@ -444,9 +435,9 @@ class resource_calendar(osv.osv):
else:
call_args['start_dt'] = current_datetime
- working_intervals = self.get_working_intervals_of_day(cr, uid, id, **call_args)
+ working_intervals = self.get_working_intervals_of_day(**call_args)
- if id is None and not working_intervals: # no calendar -> consider working 8 hours
+ if not self and not working_intervals: # no calendar -> consider working 8 hours
remaining_hours -= 8.0
elif working_intervals:
if backwards:
@@ -455,7 +446,7 @@ class resource_calendar(osv.osv):
if backwards:
new_working_intervals.reverse()
- res = datetime.timedelta()
+ res = timedelta()
for interval in working_intervals:
res += interval[1] - interval[0]
remaining_hours -= (seconds(res) / 3600.0)
@@ -465,36 +456,38 @@ class resource_calendar(osv.osv):
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))
+ current_datetime = datetime.datetime.combine(self.get_previous_day(current_datetime), datetime.time(23, 59, 59))
else:
- current_datetime = datetime.datetime.combine(self.get_next_day(cr, uid, id, current_datetime, context), datetime.time())
+ current_datetime = datetime.datetime.combine(self.get_next_day(current_datetime), datetime.time())
# avoid infinite loops
iterations += 1
return intervals
- def schedule_hours_get_date(self, cr, uid, id, hours, day_dt=None,
+ @api.multi
+ def schedule_hours_get_date(self, hours, day_dt=None,
compute_leaves=False, resource_id=None,
- default_interval=None, context=None):
+ default_interval=None):
""" 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, default_interval, context)
+ res = self._schedule_hours(hours, day_dt, compute_leaves, resource_id, default_interval)
return res and res[0][0] or False
- def schedule_hours(self, cr, uid, id, hours, day_dt=None,
+ @api.multi
+ def schedule_hours(self, hours, day_dt=None,
compute_leaves=False, resource_id=None,
- default_interval=None, context=None):
+ default_interval=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, default_interval, context)
-
+ return self._schedule_hours(hours, day_dt, compute_leaves, resource_id, default_interval)
# --------------------------------------------------
# Days scheduling
# --------------------------------------------------
- def _schedule_days(self, cr, uid, id, days, day_date=None, compute_leaves=False,
- resource_id=None, default_interval=None, context=None):
+ @api.multi
+ def _schedule_days(self, days, day_date=None, compute_leaves=False,
+ resource_id=None, default_interval=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.
@@ -536,157 +529,165 @@ class resource_calendar(osv.osv):
while planned_days < days and iterations < 100:
working_intervals = self.get_working_intervals_of_day(
- cr, uid, id, current_datetime,
+ current_datetime,
compute_leaves=compute_leaves, resource_id=resource_id,
- default_interval=default_interval,
- context=context)
- if id is None or working_intervals: # no calendar -> no working hours, but day is considered as worked
+ default_interval=default_interval)
+ if not self or working_intervals: # no calendar -> no working hours, but day is considered as worked
planned_days += 1
intervals += working_intervals
# get next day
if backwards:
- current_datetime = self.get_previous_day(cr, uid, id, current_datetime, context)
+ current_datetime = self.get_previous_day(current_datetime)
else:
- current_datetime = self.get_next_day(cr, uid, id, current_datetime, context)
+ current_datetime = self.get_next_day(current_datetime)
# avoid infinite loops
iterations += 1
return intervals
- def schedule_days_get_date(self, cr, uid, id, days, day_date=None, compute_leaves=False,
- resource_id=None, default_interval=None, context=None):
+ @api.multi
+ def schedule_days_get_date(self, days, day_date=None, compute_leaves=False,
+ resource_id=None, default_interval=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, default_interval, context)
+ res = self._schedule_days(days, day_date, compute_leaves, resource_id, default_interval)
return res and res[-1][1] or False
- def schedule_days(self, cr, uid, id, days, day_date=None, compute_leaves=False,
- resource_id=None, default_interval=None, context=None):
+ @api.multi
+ def schedule_days(self, days, day_date=None, compute_leaves=False,
+ resource_id=None, default_interval=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, default_interval, context)
+ return self._schedule_days(days, day_date, compute_leaves, resource_id, default_interval)
# --------------------------------------------------
# Compatibility / to clean / to remove
# --------------------------------------------------
- def working_hours_on_day(self, cr, uid, resource_calendar_id, day, context=None):
+ @api.multi
+ def working_hours_on_day(self, day):
""" Used in hr_payroll/hr_payroll.py
- :deprecated: OpenERP saas-3. Use get_working_hours_of_date instead. Note:
+ :deprecated: Odoo 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)
+ return self.get_working_hours_of_date(start_dt=day)
- def interval_min_get(self, cr, uid, id, dt_from, hours, resource=False):
+ @api.multi
+ def interval_min_get(self, dt_from, hours, resource=False):
""" Schedule hours backwards. Used in mrp_operations/mrp_operations.py.
- :deprecated: OpenERP saas-3. Use schedule_hours instead. Note: since
+ :deprecated: Odoo 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,
+ hours * -1.0,
day_dt=dt_from.replace(minute=0, second=0),
compute_leaves=True, resource_id=resource,
default_interval=(8, 16)
)
- def interval_get_multi(self, cr, uid, date_and_hours_by_cal, resource=False, byday=True):
+ @api.model
+ def interval_get_multi(self, date_and_hours_by_cal, resource=False, byday=True):
""" Used in mrp_operations/mrp_operations.py (default parameters) and in
interval_get()
- :deprecated: OpenERP saas-3. Use schedule_hours instead. Note:
+ :deprecated: Odoo saas-3. Use schedule_hours 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(
- cr, uid, calendar_id, hours,
- day_dt=datetime.datetime.strptime(dt_str, '%Y-%m-%d %H:%M:%S').replace(second=0),
+ result = self.browse(calendar_id).schedule_hours(
+ hours,
+ day_dt=fields.Datetime.from_string(dt_str).replace(second=0),
compute_leaves=True, resource_id=resource,
default_interval=(8, 16)
)
res[(dt_str, hours, calendar_id)] = result
return res
- def interval_get(self, cr, uid, id, dt_from, hours, resource=False, byday=True):
+ @api.multi
+ def interval_get(self, dt_from, hours, resource=False, byday=True):
""" 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."""
+ :deprecated: Odoo saas-3. Use get_working_hours instead."""
+ self.ensure_one()
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)]
+ [(fields.Datetime.to_string(dt_from), hours, self.id)], resource, byday)[(fields.Datetime.to_string(dt_from), hours, self.id)]
return res
- def interval_hours_get(self, cr, uid, id, dt_from, dt_to, resource=False):
+ @api.multi
+ def interval_hours_get(self, dt_from, dt_to, resource=False):
""" 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)
+ :deprecated: Odoo saas-3. Use get_working_hours instead."""
+ return self._interval_hours_get(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):
- """ Computes working hours between two dates, taking always same hour/minutes.
-
- :deprecated: OpenERP saas-3. Use get_working_hours instead. Note: since saas-3,
+ @api.multi
+ def _interval_hours_get(self, dt_from, dt_to, resource_id=False, timezone_from_uid=None, exclude_leaves=True):
+ """ Computes working hours between two dates, taking always same hour/minuts.
+ :deprecated: Odoo 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,
+ dt_from, dt_to,
compute_leaves=(not exclude_leaves), resource_id=resource_id,
- default_interval=(8, 16), context=context)
+ default_interval=(8, 16))
-class resource_calendar_attendance(osv.osv):
+class ResourceCalendarAttendance(models.Model):
_name = "resource.calendar.attendance"
_description = "Work Detail"
-
- _columns = {
- 'name' : fields.char("Name", required=True),
- 'dayofweek': fields.selection([('0','Monday'),('1','Tuesday'),('2','Wednesday'),('3','Thursday'),('4','Friday'),('5','Saturday'),('6','Sunday')], 'Day of Week', required=True, select=True),
- 'date_from' : fields.date('Starting Date'),
- 'date_to': fields.date('End Date'),
- 'hour_from' : fields.float('Work from', required=True, help="Start and End time of working.", select=True),
- 'hour_to' : fields.float("Work to", required=True),
- 'calendar_id' : fields.many2one("resource.calendar", "Resource's Calendar", required=True),
- }
-
_order = 'dayofweek, hour_from'
- _defaults = {
- 'dayofweek' : '0'
- }
+ name = fields.Char(required=True)
+ dayofweek = fields.Selection([
+ ('0', 'Monday'),
+ ('1', 'Tuesday'),
+ ('2', 'Wednesday'),
+ ('3', 'Thursday'),
+ ('4', 'Friday'),
+ ('5', 'Saturday'),
+ ('6', 'Sunday')
+ ], 'Day of Week', required=True, index=True, default='0')
+ date_from = fields.Date(string='Starting Date')
+ date_to = fields.Date(string='End Date')
+ 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)
+
def hours_time_string(hours):
""" convert a number of hours (float) into a string with format '%H:%M' """
minutes = int(round(hours * 60))
return "%02d:%02d" % divmod(minutes, 60)
-class resource_resource(osv.osv):
+
+class ResourceResource(models.Model):
_name = "resource.resource"
_description = "Resource Detail"
- _columns = {
- 'name': fields.char("Name", required=True),
- 'code': fields.char('Code', size=16, copy=False),
- 'active' : fields.boolean('Active', track_visibility='onchange',
- help="If the active field is set to False, it will allow you to hide the resource record without removing it."),
- 'company_id' : fields.many2one('res.company', 'Company'),
- 'resource_type': fields.selection([('user','Human'),('material','Material')], 'Resource Type', required=True),
- 'user_id' : fields.many2one('res.users', 'User', help='Related user name for the resource to manage its access.'),
- 'time_efficiency' : fields.float('Efficiency Factor', size=8, required=True, help="An efficiency of 50% means the resource is two times smaller than his usual speed."),
- 'calendar_id' : fields.many2one("resource.calendar", "Working Time", help="Define the schedule of resource"),
- }
- _defaults = {
- 'resource_type' : 'user',
- 'time_efficiency' : 100,
- 'active' : True,
- 'company_id': lambda self, cr, uid, context: self.pool.get('res.company')._company_default_get(cr, uid, 'resource.resource', context=context)
- }
+ name = fields.Char(required=True)
+ code = fields.Char(copy=False)
+ active = fields.Boolean(track_visibility='onchange', default=True,
+ help="If the active field is set to False, it will allow you to hide the resource record without removing it.")
+ company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env['res.company']._company_default_get())
+ resource_type = fields.Selection([
+ ('user', 'Human'),
+ ('material', 'Material')
+ ], string='Resource Type', required=True, default='user')
+ user_id = fields.Many2one('res.users', string='User', help='Related user name for the resource to manage its access.')
+ time_efficiency = fields.Float(string='Efficiency Factor', required=True, default=100,
+ help="This field depict the efficiency of the resource to complete tasks. e.g resource put alone on a phase of 5 days with 5 tasks assigned to him, will show a load of 100% for this phase by default, but if we put a efficiency of 200%, then his load will only be 50%.")
+ calendar_id = fields.Many2one("resource.calendar", string='Working Time', help="Define the schedule of resource")
- def copy(self, cr, uid, id, default=None, context=None):
+ @api.multi
+ def copy(self, default=None):
+ self.ensure_one()
if default is None:
default = {}
- if not default.get('name', False):
- default.update(name=_('%s (copy)') % (self.browse(cr, uid, id, context=context).name))
- return super(resource_resource, self).copy(cr, uid, id, default, context)
+ if not default.get('name'):
+ default.update(name=_('%s (copy)') % (self.name))
+ return super(ResourceResource, self).copy(default)
def _is_work_day(self, date):
""" Whether the provided date is a work day for the subject resource.
@@ -719,37 +720,28 @@ class resource_resource(osv.osv):
if intervals and intervals[0]:
yield dt.date()
-class resource_calendar_leaves(osv.osv):
+class ResourceCalendarLeaves(models.Model):
_name = "resource.calendar.leaves"
_description = "Leave Detail"
- _columns = {
- 'name' : fields.char("Name"),
- 'company_id' : fields.related('calendar_id','company_id',type='many2one',relation='res.company',string="Company", store=True, readonly=True),
- 'calendar_id' : fields.many2one("resource.calendar", "Working Time"),
- 'date_from' : fields.datetime('Start Date', required=True),
- 'date_to' : fields.datetime('End Date', required=True),
- 'resource_id' : fields.many2one("resource.resource", "Resource", help="If empty, this is a generic holiday for the company. If a resource is set, the holiday/leave is only for this resource"),
- }
- def check_dates(self, cr, uid, ids, context=None):
- for leave in self.browse(cr, uid, ids, context=context):
- if leave.date_from and leave.date_to and leave.date_from > leave.date_to:
- return False
- return True
+ name = fields.Char()
+ company_id = fields.Many2one('res.company', related='calendar_id.company_id', string="Company", store=True, readonly=True)
+ calendar_id = fields.Many2one('resource.calendar', string='Working Time')
+ date_from = fields.Datetime(string='Start Date', required=True)
+ date_to = fields.Datetime(string='End Date', required=True)
+ resource_id = fields.Many2one("resource.resource", string='Resource',
+ help="If empty, this is a generic holiday for the company. If a resource is set, the holiday/leave is only for this resource")
- _constraints = [
- (check_dates, 'Error! leave start-date must be lower then leave end-date.', ['date_from', 'date_to'])
- ]
+ @api.constrains('date_from', 'date_to')
+ def check_dates(self):
+ if self.filtered(lambda leave: leave.date_from > leave.date_to):
+ raise ValidationError(_('Error! leave start-date must be lower then leave end-date.'))
- def onchange_resource(self, cr, uid, ids, resource, context=None):
- result = {}
- if resource:
- resource_pool = self.pool.get('resource.resource')
- result['calendar_id'] = resource_pool.browse(cr, uid, resource, context=context).calendar_id.id
- return {'value': result}
- return {'value': {'calendar_id': []}}
+ @api.onchange('resource_id')
+ def onchange_resource(self):
+ self.calendar_id = self.resource_id.calendar_id
def seconds(td):
- assert isinstance(td, datetime.timedelta)
+ assert isinstance(td, timedelta)
return (td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10.**6
diff --git a/addons/resource/security/resource_security.xml b/addons/resource/security/resource_security.xml
index 43bfa6c3a4b..d33fdebc296 100644
--- a/addons/resource/security/resource_security.xml
+++ b/addons/resource/security/resource_security.xml
@@ -1,5 +1,5 @@
-
+
@@ -13,4 +13,4 @@
-
+
diff --git a/addons/resource/views/resource_views.xml b/addons/resource/views/resource_views.xml
index 5eeec8aaf05..6dbb2c29a91 100644
--- a/addons/resource/views/resource_views.xml
+++ b/addons/resource/views/resource_views.xml
@@ -1,6 +1,5 @@
-
-
+
@@ -212,7 +211,7 @@
-
+