This commit adapts the business code to changes introduced by the parent commit in order to keep the same behaviour as before. All readonly=False fields will have to be checked afterwards to confirm that the business case requires write access to the source field.
203 lines
8.4 KiB
Python
203 lines
8.4 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
from collections import defaultdict
|
|
from datetime import timedelta
|
|
from pytz import utc
|
|
|
|
from odoo import api, fields, models
|
|
from odoo.tools import float_utils
|
|
|
|
# This will generate eights of days
|
|
ROUNDING_FACTOR = 8
|
|
|
|
|
|
class ResourceMixin(models.AbstractModel):
|
|
_name = "resource.mixin"
|
|
_description = 'Resource Mixin'
|
|
|
|
resource_id = fields.Many2one(
|
|
'resource.resource', 'Resource',
|
|
auto_join=True, index=True, ondelete='restrict', required=True)
|
|
company_id = fields.Many2one(
|
|
'res.company', 'Company',
|
|
default=lambda self: self.env['res.company']._company_default_get(),
|
|
index=True, related='resource_id.company_id', store=True, readonly=False)
|
|
resource_calendar_id = fields.Many2one(
|
|
'resource.calendar', 'Working Hours',
|
|
default=lambda self: self.env['res.company']._company_default_get().resource_calendar_id,
|
|
index=True, related='resource_id.calendar_id', store=True, readonly=False)
|
|
tz = fields.Selection(
|
|
string='Timezone', related='resource_id.tz', readonly=False,
|
|
help="This field is used in order to define in which timezone the resources will work.")
|
|
|
|
@api.model
|
|
def create(self, values):
|
|
if not values.get('resource_id'):
|
|
resource_vals = {'name': values.get(self._rec_name)}
|
|
tz = (values.pop('tz', False) or
|
|
self.env['resource.calendar'].browse(values.get('resource_calendar_id')).tz)
|
|
if tz:
|
|
resource_vals['tz'] = tz
|
|
resource = self.env['resource.resource'].create(resource_vals)
|
|
values['resource_id'] = resource.id
|
|
return super(ResourceMixin, self).create(values)
|
|
|
|
@api.multi
|
|
def copy_data(self, default=None):
|
|
if default is None:
|
|
default = {}
|
|
resource = self.resource_id.copy()
|
|
default['resource_id'] = resource.id
|
|
default['company_id'] = resource.company_id.id
|
|
default['resource_calendar_id'] = resource.calendar_id.id
|
|
return super(ResourceMixin, self).copy_data(default)
|
|
|
|
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
|
|
changed using the `calendar` argument.
|
|
|
|
`domain` is used in order to recognise the leaves to take,
|
|
None means default value ('time_type', '=', 'leave')
|
|
|
|
Returns a dict {'days': n, 'hours': h} containing the
|
|
quantity of working time expressed as days and as hours.
|
|
"""
|
|
resource = self.resource_id
|
|
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)
|
|
|
|
# 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
|
|
|
|
# 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()),
|
|
}
|
|
|
|
def get_leave_days_data(self, from_datetime, to_datetime, calendar=None, domain=None):
|
|
"""
|
|
By default the resource calendar is used, but it can be
|
|
changed using the `calendar` argument.
|
|
|
|
`domain` is used in order to recognise the leaves to take,
|
|
None means default value ('time_type', '=', 'leave')
|
|
|
|
Returns a dict {'days': n, 'hours': h} containing the number of leaves
|
|
expressed as days and as hours.
|
|
"""
|
|
resource = self.resource_id
|
|
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)
|
|
|
|
# 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
|
|
|
|
# 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()),
|
|
}
|
|
|
|
def list_work_time_per_day(self, from_datetime, to_datetime, calendar=None, domain=None):
|
|
"""
|
|
By default the resource calendar is used, but it can be
|
|
changed using the `calendar` argument.
|
|
|
|
`domain` is used in order to recognise the leaves to take,
|
|
None means default value ('time_type', '=', 'leave')
|
|
|
|
Returns a list of tuples (day, hours) for each day
|
|
containing at least an attendance.
|
|
"""
|
|
resource = self.resource_id
|
|
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)
|
|
|
|
intervals = calendar._work_intervals(from_datetime, to_datetime, resource, domain)
|
|
result = defaultdict(float)
|
|
for start, stop, meta in intervals:
|
|
result[start.date()] += (stop - start).total_seconds() / 3600
|
|
return sorted(result.items())
|
|
|
|
def list_leaves(self, from_datetime, to_datetime, calendar=None, domain=None):
|
|
"""
|
|
By default the resource calendar is used, but it can be
|
|
changed using the `calendar` argument.
|
|
|
|
`domain` is used in order to recognise the leaves to take,
|
|
None means default value ('time_type', '=', 'leave')
|
|
|
|
Returns a list of tuples (day, hours, resource.calendar.leaves)
|
|
for each leave in the calendar.
|
|
"""
|
|
resource = self.resource_id
|
|
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)
|
|
|
|
attendances = calendar._attendance_intervals(from_datetime, to_datetime, resource)
|
|
leaves = calendar._leave_intervals(from_datetime, to_datetime, resource, domain)
|
|
result = []
|
|
for start, stop, leave in (leaves & attendances):
|
|
hours = (stop - start).total_seconds() / 3600
|
|
result.append((start.date(), hours, leave))
|
|
return result
|