[IMP] resource: Add possibility to pass in a domain

It will enable to retrieve data from the resource module based on a
domain that we pass to the model. It can be used in order to filter on
certain leave types only.

  * In resource_mixin and resource add a way to use a domain in order to
  filter the leaves
  * In resource_mixin add a method to get the work/leaves hours count
This commit is contained in:
RomainLibert
2018-02-19 10:43:25 +01:00
parent 813065cb5b
commit 6d870463d0
3 changed files with 110 additions and 41 deletions
+31 -24
View File
@@ -291,7 +291,7 @@ class ResourceCalendar(models.Model):
return day_date + relativedelta(days=days)
@api.multi
def _get_leave_intervals(self, resource_id=None, start_datetime=None, end_datetime=None):
def _get_leave_intervals(self, resource_id=None, start_datetime=None, end_datetime=None, domain=None):
"""Get the leaves of the calendar. Leaves can be filtered on the resource,
and on a start and end datetime.
@@ -303,16 +303,18 @@ class ResourceCalendar(models.Model):
:return list leaves: list of time intervals """
self.ensure_one()
if resource_id:
domain = ['|', ('resource_id', '=', resource_id), ('resource_id', '=', False)]
search_domain = ['|', ('resource_id', '=', resource_id), ('resource_id', '=', False)]
else:
domain = [('resource_id', '=', False)]
search_domain = [('resource_id', '=', False)]
if start_datetime:
# domain += [('date_to', '>', fields.Datetime.to_string(to_naive_utc(start_datetime, self.env.user)))]
domain += [('date_to', '>', fields.Datetime.to_string(start_datetime + timedelta(days=-1)))]
search_domain += [('date_to', '>', fields.Datetime.to_string(start_datetime + timedelta(days=-1)))]
if end_datetime:
# domain += [('date_from', '<', fields.Datetime.to_string(to_naive_utc(end_datetime, self.env.user)))]
domain += [('date_from', '<', fields.Datetime.to_string(end_datetime + timedelta(days=1)))]
leaves = self.env['resource.calendar.leaves'].search(domain + [('calendar_id', '=', self.id)])
search_domain += [('date_from', '<', fields.Datetime.to_string(end_datetime + timedelta(days=1)))]
if domain:
search_domain += domain
leaves = self.env['resource.calendar.leaves'].search(search_domain + [('calendar_id', '=', self.id)])
filtered_leaves = self.env['resource.calendar.leaves']
for leave in leaves:
@@ -343,7 +345,7 @@ class ResourceCalendar(models.Model):
yield self._interval_new(dt_f, dt_t, {'attendances': calendar_working_day})
@api.multi
def _get_day_work_intervals(self, day_date, start_time=None, end_time=None, compute_leaves=False, resource_id=None):
def _get_day_work_intervals(self, day_date, start_time=None, end_time=None, compute_leaves=False, resource_id=None, domain=None):
""" Get the working intervals of the day given by day_date based on
current calendar. Input should be given in current user timezone and
output is given in naive UTC, ready to be used by the orm or webclient.
@@ -371,7 +373,8 @@ class ResourceCalendar(models.Model):
leaves = self._get_leave_intervals(
resource_id=resource_id,
start_datetime=datetime.datetime.combine(day_date, start_time),
end_datetime=datetime.datetime.combine(day_date, end_time))
end_datetime=datetime.datetime.combine(day_date, end_time),
domain=domain)
working_intervals = [
sub_interval
for interval in working_intervals
@@ -383,7 +386,7 @@ class ResourceCalendar(models.Model):
to_naive_utc(interval[1], self.env.user),
interval[2]) for interval in working_intervals]
def _get_day_leave_intervals(self, day_date, start_time, end_time, resource_id):
def _get_day_leave_intervals(self, day_date, start_time, end_time, resource_id, domain=None):
""" Get the leave intervals of the day given by day_date based on current
calendar. Input should be given in current user timezone and
output is given in naive UTC, ready to be used by the orm or webclient.
@@ -406,7 +409,8 @@ class ResourceCalendar(models.Model):
leaves_intervals = self._get_leave_intervals(
resource_id=resource_id,
start_datetime=datetime.datetime.combine(day_date, start_time),
end_datetime=datetime.datetime.combine(day_date, end_time))
end_datetime=datetime.datetime.combine(day_date, end_time),
domain=domain)
final_intervals = [i for i in
[self._interval_and(leave_interval, work_interval)
@@ -423,7 +427,7 @@ class ResourceCalendar(models.Model):
# Main computation API
# --------------------------------------------------
def _iter_work_intervals(self, start_dt, end_dt, resource_id, compute_leaves=True):
def _iter_work_intervals(self, start_dt, end_dt, resource_id, compute_leaves=True, domain=None):
""" Lists the current resource's work intervals between the two provided
datetimes (inclusive) expressed in UTC, for each worked day. """
if not end_dt:
@@ -448,11 +452,12 @@ class ResourceCalendar(models.Model):
start_time=start_time,
end_time=end_time,
compute_leaves=compute_leaves,
resource_id=resource_id)
resource_id=resource_id,
domain=domain)
if intervals:
yield intervals
def _iter_leave_intervals(self, start_dt, end_dt, resource_id):
def _iter_leave_intervals(self, start_dt, end_dt, resource_id, domain=None):
""" Lists the current resource's leave intervals between the two provided
datetimes (inclusive) expressed in UTC. """
if not end_dt:
@@ -476,22 +481,23 @@ class ResourceCalendar(models.Model):
day.date(),
start_time,
end_time,
resource_id)
resource_id,
domain=domain)
if intervals:
yield intervals
def _iter_work_hours_count(self, from_datetime, to_datetime, resource_id):
def _iter_work_hours_count(self, from_datetime, to_datetime, resource_id, domain=None):
""" Lists the current resource's work hours count between the two provided
datetime expressed in naive UTC. """
for interval in self._iter_work_intervals(from_datetime, to_datetime, resource_id):
for interval in self._iter_work_intervals(from_datetime, to_datetime, resource_id, domain=domain):
td = timedelta()
for work_interval in interval:
td += work_interval[1] - work_interval[0]
yield (interval[0][0].date(), td.total_seconds() / 3600.0)
def _iter_work_days(self, from_date, to_date, resource_id):
def _iter_work_days(self, from_date, to_date, resource_id, domain=None):
""" Lists the current resource's work days between the two provided
dates (inclusive) expressed in naive UTC.
@@ -507,7 +513,7 @@ class ResourceCalendar(models.Model):
for interval in self._iter_work_intervals(
datetime.datetime(from_date.year, from_date.month, from_date.day),
datetime.datetime(to_date.year, to_date.month, to_date.day),
resource_id):
resource_id, domain=domain):
yield interval[0][0].date()
@api.multi
@@ -519,11 +525,11 @@ class ResourceCalendar(models.Model):
return bool(next(self._iter_work_days(date, date, resource_id), False))
@api.multi
def get_work_hours_count(self, start_dt, end_dt, resource_id, compute_leaves=True):
def get_work_hours_count(self, start_dt, end_dt, resource_id, compute_leaves=True, domain=None):
""" Count number of work hours between two datetimes. For compute_leaves,
resource_id: see _get_day_work_intervals. """
res = timedelta()
for intervals in self._iter_work_intervals(start_dt, end_dt, resource_id, compute_leaves=compute_leaves):
for intervals in self._iter_work_intervals(start_dt, end_dt, resource_id, compute_leaves=compute_leaves, domain=domain):
for interval in intervals:
res += interval[1] - interval[0]
return res.total_seconds() / 3600.0
@@ -533,7 +539,7 @@ class ResourceCalendar(models.Model):
# --------------------------------------------------
@api.multi
def _schedule_hours(self, hours, day_dt, compute_leaves=False, resource_id=None):
def _schedule_hours(self, hours, day_dt, compute_leaves=False, resource_id=None, domain=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. For compute_leaves, resource_id:
@@ -555,7 +561,7 @@ class ResourceCalendar(models.Model):
day_dt_tz = to_naive_user_tz(day_dt, self.env.user)
current_datetime = day_dt_tz
call_args = dict(compute_leaves=compute_leaves, resource_id=resource_id)
call_args = dict(compute_leaves=compute_leaves, resource_id=resource_id, domain=domain)
while float_compare(remaining_hours, 0.0, precision_digits=2) in (1, 0) and iterations < 1000:
if backwards:
@@ -595,7 +601,7 @@ class ResourceCalendar(models.Model):
return False
@api.multi
def _schedule_days(self, days, day_dt, compute_leaves=False, resource_id=None):
def _schedule_days(self, days, day_dt, compute_leaves=False, resource_id=None, domain=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. For compute_leaves, resource_id:
@@ -619,7 +625,8 @@ class ResourceCalendar(models.Model):
while planned_days < abs(days) and iterations < 100:
working_intervals = self._get_day_work_intervals(
current_datetime.date(),
compute_leaves=compute_leaves, resource_id=resource_id)
compute_leaves=compute_leaves, resource_id=resource_id,
domain=domain)
if not self or working_intervals: # no calendar -> no working hours, but day is considered as worked
planned_days += 1
intervals += working_intervals
+37 -17
View File
@@ -41,19 +41,25 @@ class ResourceMixin(models.AbstractModel):
default['resource_calendar_id'] = resource.calendar_id.id
return super(ResourceMixin, self).copy_data(default)
def get_work_days_count(self, from_datetime, to_datetime, calendar=None):
def get_work_days_count(self, from_datetime, to_datetime, calendar=None, domain=None):
""" Return the number of work days for the resource, taking into account
leaves. An optional calendar can be given in case multiple calendars can
be used on the resource. """
return self.get_work_days_data(from_datetime, to_datetime, calendar=calendar)['days']
return self.get_work_days_data(from_datetime, to_datetime, calendar=calendar, domain=domain)['days']
def get_work_days_data(self, from_datetime, to_datetime, calendar=None):
def get_work_hours_count(self, from_datetime, to_datetime, calendar=None, domain=None):
""" Return the number of work hours for the resource, taking into account
leaves. An optional calendar can be given in case multiple calendars can
be used on the resource. """
return self.get_work_days_data(from_datetime, to_datetime, calendar=calendar, domain=domain)['hours']
def get_work_days_data(self, from_datetime, to_datetime, calendar=None, domain=None):
days_count = 0.0
total_work_time = timedelta()
calendar = calendar or self.resource_calendar_id
for day_intervals in calendar._iter_work_intervals(
from_datetime, to_datetime, self.resource_id.id,
compute_leaves=True):
compute_leaves=True, domain=domain):
theoric_hours = self.get_day_work_hours_count(day_intervals[0][0].date(), calendar=calendar)
work_time = sum((interval[1] - interval[0] for interval in day_intervals), timedelta())
total_work_time += work_time
@@ -63,38 +69,52 @@ class ResourceMixin(models.AbstractModel):
'hours': total_work_time.total_seconds() / 3600,
}
def iter_works(self, from_datetime, to_datetime, calendar=None):
def iter_works(self, from_datetime, to_datetime, calendar=None, domain=None):
calendar = calendar or self.resource_calendar_id
return calendar._iter_work_intervals(from_datetime, to_datetime, self.resource_id.id)
return calendar._iter_work_intervals(from_datetime, to_datetime, self.resource_id.id, domain=domain)
def iter_work_hours_count(self, from_datetime, to_datetime, calendar=None):
def iter_work_hours_count(self, from_datetime, to_datetime, calendar=None, domain=None):
calendar = calendar or self.resource_calendar_id
return calendar._iter_work_hours_count(from_datetime, to_datetime, self.resource_id.id)
return calendar._iter_work_hours_count(from_datetime, to_datetime, self.resource_id.id, domain=domain)
def get_leaves_day_count(self, from_datetime, to_datetime, calendar=None):
def get_leaves_day_count(self, from_datetime, to_datetime, calendar=None, domain=None):
""" Return the number of leave days for the resource, taking into account
attendances. An optional calendar can be given in case multiple calendars
can be used on the resource. """
return self.get_leaves_days_data(from_datetime, to_datetime, calendar=calendar, domain=domain)['days']
def get_leaves_hours_count(self, from_datetime, to_datetime, calendar=None, domain=None):
""" Return the number of leave hours for the resource, taking into account
attendances. An optional calendar can be given in case multiple calendars can
be used on the resource. """
return self.get_leaves_days_data(from_datetime, to_datetime, calendar=calendar, domain=domain)['hours']
def get_leaves_days_data(self, from_datetime, to_datetime, calendar=None, domain=None):
days_count = 0.0
total_leave_time = timedelta()
calendar = calendar or self.resource_calendar_id
for day_intervals in calendar._iter_leave_intervals(from_datetime, to_datetime, self.resource_id.id):
for day_intervals in calendar._iter_leave_intervals(from_datetime, to_datetime, self.resource_id.id, domain=domain):
theoric_hours = self.get_day_work_hours_count(day_intervals[0][0].date(), calendar=calendar)
leave_time = sum((interval[1] - interval[0] for interval in day_intervals), timedelta())
total_leave_time += leave_time
days_count += float_utils.round((leave_time.total_seconds() / 3600 / theoric_hours) * 4) / 4
return days_count
return {
'days': days_count,
'hours': total_leave_time.total_seconds() / 3600,
}
def iter_leaves(self, from_datetime, to_datetime, calendar=None):
def iter_leaves(self, from_datetime, to_datetime, calendar=None, domain=None):
calendar = calendar or self.resource_calendar_id
return calendar._iter_leave_intervals(from_datetime, to_datetime, self.resource_id.id)
return calendar._iter_leave_intervals(from_datetime, to_datetime, self.resource_id.id, domain=domain)
def get_start_work_hour(self, day_dt, calendar=None):
def get_start_work_hour(self, day_dt, calendar=None, domain=None):
calendar = calendar or self.resource_calendar_id
work_intervals = calendar._get_day_work_intervals(day_dt, resource_id=self.resource_id.id)
work_intervals = calendar._get_day_work_intervals(day_dt, resource_id=self.resource_id.id, domain=domain)
return work_intervals and work_intervals[0][0]
def get_end_work_hour(self, day_dt, calendar=None):
def get_end_work_hour(self, day_dt, calendar=None, domain=None):
calendar = calendar or self.resource_calendar_id
work_intervals = calendar._get_day_work_intervals(day_dt, resource_id=self.resource_id.id)
work_intervals = calendar._get_day_work_intervals(day_dt, resource_id=self.resource_id.id, domain=domain)
return work_intervals and work_intervals[-1][1]
def get_day_work_hours_count(self, day_date, calendar=None):
+42
View File
@@ -698,6 +698,48 @@ class TestResourceMixin(TestResourceCommon):
self.assertEqual(res['days'], 0.75)
self.assertEqual(res['hours'], 6.0)
def test_days_count_with_domain(self):
self.test.resource_id.write({
'user_id': self.lost_user.id,
})
self.env['resource.calendar.leaves'].sudo(self.lost_user).create({
'name': 'first',
'calendar_id': self.test.resource_calendar_id.id,
'resource_id': self.test.resource_id.id,
'date_from': to_naive_utc(Datetime.from_string('2018-02-05 08:00:00'), self.lost_user),
'date_to': to_naive_utc(Datetime.from_string('2018-02-09 18:00:00'), self.lost_user)
})
self.env['resource.calendar.leaves'].sudo(self.lost_user).create({
'name': 'second',
'calendar_id': self.test.resource_calendar_id.id,
'resource_id': self.test.resource_id.id,
'date_from': to_naive_utc(Datetime.from_string('2018-01-12 08:00:00'), self.lost_user),
'date_to': to_naive_utc(Datetime.from_string('2018-01-13 18:00:00'), self.lost_user)
})
res = self.test.get_work_days_count(
to_naive_utc(Datetime.from_string('2018-02-04 06:00:00'), self.env.user),
to_naive_utc(Datetime.from_string('2018-02-14 20:00:00'), self.env.user),
domain=[('name', '=', '')]
)
self.assertEqual(res, 3.0)
res = self.test.get_work_days_count(
to_naive_utc(Datetime.from_string('2018-02-04 06:00:00'), self.env.user),
to_naive_utc(Datetime.from_string('2018-02-14 20:00:00'), self.env.user),
domain=[('name', '=', 'first')]
)
self.assertEqual(res, 1.0)
res = self.test.get_leaves_day_count(
to_naive_utc(Datetime.from_string('2018-02-04 06:00:00'), self.env.user),
to_naive_utc(Datetime.from_string('2018-02-14 20:00:00'), self.env.user),
domain=[('name', '=', 'first')]
)
self.assertEqual(res, 2.0)
class TestGlobalLeaves(TestResourceCommon):
def setUp(self):