[MIG] resource: Migrated to new api.
This commit is contained in:
committed by
Yannick Tivisse
parent
22ef7c15b5
commit
a4e57ea16c
@@ -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,
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<odoo>
|
||||
|
||||
<!-- Example employee -->
|
||||
|
||||
@@ -121,5 +120,4 @@
|
||||
<field name="date_to" eval="time.strftime('%Y-%m-%d 11:00:00')"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
</odoo>
|
||||
|
||||
+191
-199
@@ -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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<record model="ir.rule" id="resource_own_leaves">
|
||||
@@ -13,4 +13,4 @@
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
</odoo>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<odoo>
|
||||
|
||||
<!-- Search Views -->
|
||||
|
||||
@@ -212,7 +211,7 @@
|
||||
<form string="Leave Detail">
|
||||
<group>
|
||||
<group>
|
||||
<field name="resource_id" on_change="onchange_resource(resource_id)"/>
|
||||
<field name="resource_id"/>
|
||||
<field name="name" string="Reason"/>
|
||||
</group>
|
||||
<group>
|
||||
@@ -259,5 +258,4 @@
|
||||
<menuitem action="resource.action_resource_calendar_form" id="menu_resource_calendar" parent="menu_resource_config" sequence="1"/>
|
||||
<menuitem action="resource.action_resource_calendar_leave_tree" id="menu_view_resource_calendar_leaves_search" parent="menu_resource_config" sequence="2"/>
|
||||
<menuitem action="resource.action_resource_resource_tree" id="menu_resource_resource" parent="menu_resource_config" sequence="3"/>
|
||||
</data>
|
||||
</openerp>
|
||||
</odoo>
|
||||
|
||||
Reference in New Issue
Block a user