[IMP] resource: Add domain parameter to compute attendace intervals
Purpose ======= More flexibility is useful when using the _attendance_intervals method (resource.calendar) in other modules when inheriting resource.calendar Specification ============= The commit adds a `domain` parameter to the _attendance_intervals method in resource.calendar, allowing to better control attendances from which to compute intervals. + small refactoring: extraction of duplicated code in method
This commit is contained in:
committed by
Yannick Tivisse
parent
07bec7db87
commit
5a9b8a41b6
@@ -197,13 +197,20 @@ class ResourceCalendar(models.Model):
|
||||
# --------------------------------------------------
|
||||
# Computation API
|
||||
# --------------------------------------------------
|
||||
def _attendance_intervals(self, start_dt, end_dt, resource=None):
|
||||
def _attendance_intervals(self, start_dt, end_dt, resource=None, domain=None):
|
||||
""" Return the attendance intervals in the given datetime range.
|
||||
The returned intervals are expressed in the resource's timezone.
|
||||
"""
|
||||
assert start_dt.tzinfo and end_dt.tzinfo
|
||||
combine = datetime.combine
|
||||
|
||||
resource_ids = [resource.id, False] if resource else [False]
|
||||
domain = domain if domain is not None else []
|
||||
domain = domain + [
|
||||
('calendar_id', '=', self.id),
|
||||
('resource_id', 'in', resource_ids),
|
||||
]
|
||||
|
||||
# express all dates and times in the resource's timezone
|
||||
tz = timezone((resource or self).tz)
|
||||
start_dt = start_dt.astimezone(tz)
|
||||
@@ -211,7 +218,7 @@ class ResourceCalendar(models.Model):
|
||||
|
||||
# for each attendance spec, generate the intervals in the date range
|
||||
result = []
|
||||
for attendance in self.attendance_ids:
|
||||
for attendance in self.env['resource.calendar.attendance'].search(domain):
|
||||
start = start_dt.date()
|
||||
if attendance.date_from:
|
||||
start = max(start, attendance.date_from)
|
||||
@@ -404,6 +411,7 @@ class ResourceCalendarAttendance(models.Model):
|
||||
hour_to = fields.Float(string='Work to', required=True)
|
||||
calendar_id = fields.Many2one("resource.calendar", string="Resource's Calendar", required=True, ondelete='cascade')
|
||||
day_period = fields.Selection([('morning', 'Morning'), ('afternoon', 'Afternoon')], required=True, default='morning')
|
||||
resource_id = fields.Many2one('resource.resource', 'Resource')
|
||||
|
||||
@api.onchange('hour_from', 'hour_to')
|
||||
def _onchange_hours(self):
|
||||
|
||||
@@ -53,6 +53,47 @@ class ResourceMixin(models.AbstractModel):
|
||||
default['resource_calendar_id'] = resource.calendar_id.id
|
||||
return super(ResourceMixin, self).copy_data(default)
|
||||
|
||||
def _get_days_data(self, intervals, day_total):
|
||||
"""
|
||||
helper function to compute duration of `intervals`
|
||||
expressed in days and hours.
|
||||
`day_total` is a dict {date: n_hours} with the number of hours for each day.
|
||||
"""
|
||||
day_hours = defaultdict(float)
|
||||
for start, stop, meta in intervals:
|
||||
day_hours[start.date()] += (stop - start).total_seconds() / 3600
|
||||
|
||||
# compute number of days as quarters
|
||||
days = sum(
|
||||
float_utils.round(ROUNDING_FACTOR * day_hours[day] / day_total[day]) / ROUNDING_FACTOR
|
||||
for day in day_hours
|
||||
)
|
||||
return {
|
||||
'days': days,
|
||||
'hours': sum(day_hours.values()),
|
||||
}
|
||||
|
||||
def _get_day_total(self, from_datetime, to_datetime, calendar, resource):
|
||||
"""
|
||||
@return dict with hours of attendance in each day between `from_datetime` and `to_datetime`
|
||||
"""
|
||||
|
||||
# total hours per day: retrieve attendances with one extra day margin,
|
||||
# in order to compute the total hours on the first and last days
|
||||
from_full = from_datetime - timedelta(days=1)
|
||||
to_full = to_datetime + timedelta(days=1)
|
||||
intervals = calendar._attendance_intervals(from_full, to_full, resource)
|
||||
day_total = defaultdict(float)
|
||||
for start, stop, meta in intervals:
|
||||
day_total[start.date()] += (stop - start).total_seconds() / 3600
|
||||
return day_total
|
||||
|
||||
def _timezone_datetime(self, time):
|
||||
if not time.tzinfo:
|
||||
time = time.replace(tzinfo=utc)
|
||||
return time
|
||||
|
||||
|
||||
def get_work_days_data(self, from_datetime, to_datetime, compute_leaves=True, calendar=None, domain=None):
|
||||
"""
|
||||
By default the resource calendar is used, but it can be
|
||||
@@ -68,38 +109,18 @@ class ResourceMixin(models.AbstractModel):
|
||||
calendar = calendar or self.resource_calendar_id
|
||||
|
||||
# naive datetimes are made explicit in UTC
|
||||
if not from_datetime.tzinfo:
|
||||
from_datetime = from_datetime.replace(tzinfo=utc)
|
||||
if not to_datetime.tzinfo:
|
||||
to_datetime = to_datetime.replace(tzinfo=utc)
|
||||
from_datetime = self._timezone_datetime(from_datetime)
|
||||
to_datetime = self._timezone_datetime(to_datetime)
|
||||
|
||||
# total hours per day: retrieve attendances with one extra day margin,
|
||||
# in order to compute the total hours on the first and last days
|
||||
from_full = from_datetime - timedelta(days=1)
|
||||
to_full = to_datetime + timedelta(days=1)
|
||||
intervals = calendar._attendance_intervals(from_full, to_full, resource)
|
||||
day_total = defaultdict(float)
|
||||
for start, stop, meta in intervals:
|
||||
day_total[start.date()] += (stop - start).total_seconds() / 3600
|
||||
day_total = self._get_day_total(from_datetime, to_datetime, calendar, resource)
|
||||
|
||||
# actual hours per day
|
||||
if compute_leaves:
|
||||
intervals = calendar._work_intervals(from_datetime, to_datetime, resource, domain)
|
||||
else:
|
||||
intervals = calendar._attendance_intervals(from_datetime, to_datetime, resource)
|
||||
day_hours = defaultdict(float)
|
||||
for start, stop, meta in intervals:
|
||||
day_hours[start.date()] += (stop - start).total_seconds() / 3600
|
||||
|
||||
# compute number of days as quarters
|
||||
days = sum(
|
||||
float_utils.round(ROUNDING_FACTOR * day_hours[day] / day_total[day]) / ROUNDING_FACTOR
|
||||
for day in day_hours
|
||||
)
|
||||
return {
|
||||
'days': days,
|
||||
'hours': sum(day_hours.values()),
|
||||
}
|
||||
return self._get_days_data(intervals, day_total)
|
||||
|
||||
def get_leave_days_data(self, from_datetime, to_datetime, calendar=None, domain=None):
|
||||
"""
|
||||
@@ -116,36 +137,17 @@ class ResourceMixin(models.AbstractModel):
|
||||
calendar = calendar or self.resource_calendar_id
|
||||
|
||||
# naive datetimes are made explicit in UTC
|
||||
if not from_datetime.tzinfo:
|
||||
from_datetime = from_datetime.replace(tzinfo=utc)
|
||||
if not to_datetime.tzinfo:
|
||||
to_datetime = to_datetime.replace(tzinfo=utc)
|
||||
from_datetime = self._timezone_datetime(from_datetime)
|
||||
to_datetime = self._timezone_datetime(to_datetime)
|
||||
|
||||
# total hours per day: retrieve attendances with one extra day margin,
|
||||
# in order to compute the total hours on the first and last days
|
||||
from_full = from_datetime - timedelta(days=1)
|
||||
to_full = to_datetime + timedelta(days=1)
|
||||
intervals = calendar._attendance_intervals(from_full, to_full, resource)
|
||||
day_total = defaultdict(float)
|
||||
for start, stop, meta in intervals:
|
||||
day_total[start.date()] += (stop - start).total_seconds() / 3600
|
||||
day_total = self._get_day_total(from_datetime, to_datetime, calendar, resource)
|
||||
|
||||
# compute actual hours per day
|
||||
attendances = calendar._attendance_intervals(from_datetime, to_datetime, resource)
|
||||
leaves = calendar._leave_intervals(from_datetime, to_datetime, resource, domain)
|
||||
day_hours = defaultdict(float)
|
||||
for start, stop, meta in (attendances & leaves):
|
||||
day_hours[start.date()] += (stop - start).total_seconds() / 3600
|
||||
|
||||
# compute number of days as quarters
|
||||
days = sum(
|
||||
float_utils.round(ROUNDING_FACTOR * day_hours[day] / day_total[day]) / ROUNDING_FACTOR
|
||||
for day in day_hours
|
||||
)
|
||||
return {
|
||||
'days': days,
|
||||
'hours': sum(day_hours.values()),
|
||||
}
|
||||
return self._get_days_data(attendances & leaves, day_total)
|
||||
|
||||
|
||||
def list_work_time_per_day(self, from_datetime, to_datetime, calendar=None, domain=None):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user