mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-22 07:32:08 +02:00
19.0 vanilla
This commit is contained in:
parent
d1963a3c3a
commit
2d3ee4855a
7430 changed files with 2687981 additions and 2965473 deletions
|
|
@ -1,30 +1,23 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from collections import defaultdict
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from pytz import utc
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
def timezone_datetime(time):
|
||||
if not time.tzinfo:
|
||||
time = time.replace(tzinfo=utc)
|
||||
return time
|
||||
from odoo.tools.date_utils import localized
|
||||
|
||||
|
||||
class ResourceMixin(models.AbstractModel):
|
||||
_name = "resource.mixin"
|
||||
_name = 'resource.mixin'
|
||||
_description = 'Resource Mixin'
|
||||
|
||||
resource_id = fields.Many2one(
|
||||
'resource.resource', 'Resource',
|
||||
auto_join=True, index=True, ondelete='restrict', required=True)
|
||||
bypass_search_access=True, index=True, ondelete='restrict', required=True)
|
||||
company_id = fields.Many2one(
|
||||
'res.company', 'Company',
|
||||
default=lambda self: self.env.company,
|
||||
index=True, related='resource_id.company_id', store=True, readonly=False)
|
||||
index=True, related='resource_id.company_id', precompute=True, store=True, readonly=False)
|
||||
resource_calendar_id = fields.Many2one(
|
||||
'resource.calendar', 'Working Hours',
|
||||
default=lambda self: self.env.company.resource_calendar_id,
|
||||
|
|
@ -67,20 +60,25 @@ class ResourceMixin(models.AbstractModel):
|
|||
return resource_vals
|
||||
|
||||
def copy_data(self, default=None):
|
||||
if default is None:
|
||||
default = {}
|
||||
default = dict(default or {})
|
||||
vals_list = super().copy_data(default=default)
|
||||
|
||||
resource_default = {}
|
||||
if 'company_id' in default:
|
||||
resource_default['company_id'] = default['company_id']
|
||||
if 'resource_calendar_id' in default:
|
||||
resource_default['calendar_id'] = default['resource_calendar_id']
|
||||
resource = self.resource_id.copy(resource_default)
|
||||
resources = [record.resource_id for record in self]
|
||||
resources_to_copy = self.env['resource.resource'].concat(*resources)
|
||||
new_resources = resources_to_copy.copy(resource_default)
|
||||
for resource, vals in zip(new_resources, vals_list):
|
||||
vals['resource_id'] = resource.id
|
||||
vals['company_id'] = resource.company_id.id
|
||||
vals['resource_calendar_id'] = resource.calendar_id.id
|
||||
return vals_list
|
||||
|
||||
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_calendars(self, date_from=None):
|
||||
return {resource.id: resource.resource_calendar_id for resource in self}
|
||||
|
||||
def _get_work_days_data_batch(self, from_datetime, to_datetime, compute_leaves=True, calendar=None, domain=None):
|
||||
"""
|
||||
|
|
@ -98,19 +96,22 @@ class ResourceMixin(models.AbstractModel):
|
|||
result = {}
|
||||
|
||||
# naive datetimes are made explicit in UTC
|
||||
from_datetime = timezone_datetime(from_datetime)
|
||||
to_datetime = timezone_datetime(to_datetime)
|
||||
from_datetime = localized(from_datetime)
|
||||
to_datetime = localized(to_datetime)
|
||||
|
||||
mapped_resources = defaultdict(lambda: self.env['resource.resource'])
|
||||
for record in self:
|
||||
mapped_resources[calendar or record.resource_calendar_id] |= record.resource_id
|
||||
if calendar:
|
||||
mapped_resources = {calendar: self.resource_id}
|
||||
else:
|
||||
calendar_by_resource = self._get_calendars(from_datetime)
|
||||
mapped_resources = defaultdict(lambda: self.env['resource.resource'])
|
||||
for resource in self:
|
||||
mapped_resources[calendar_by_resource[resource.id]] |= resource.resource_id
|
||||
|
||||
for calendar, calendar_resources in mapped_resources.items():
|
||||
if not calendar:
|
||||
for calendar_resource in calendar_resources:
|
||||
result[calendar_resource.id] = {'days': 0, 'hours': 0}
|
||||
continue
|
||||
day_total = calendar._get_resources_day_total(from_datetime, to_datetime, calendar_resources)
|
||||
|
||||
# actual hours per day
|
||||
if compute_leaves:
|
||||
|
|
@ -119,10 +120,10 @@ class ResourceMixin(models.AbstractModel):
|
|||
intervals = calendar._attendance_intervals_batch(from_datetime, to_datetime, calendar_resources)
|
||||
|
||||
for calendar_resource in calendar_resources:
|
||||
result[calendar_resource.id] = calendar._get_days_data(intervals[calendar_resource.id], day_total[calendar_resource.id])
|
||||
result[calendar_resource.id] = calendar._get_attendance_intervals_days_data(intervals[calendar_resource.id])
|
||||
|
||||
# convert "resource: result" into "employee: result"
|
||||
return {mapped_employees[r.id]: result[r.id] for r in resources}
|
||||
return {mapped_employees[r.id]: result[r.id] for r in resources}
|
||||
|
||||
def _get_leave_days_data_batch(self, from_datetime, to_datetime, calendar=None, domain=None):
|
||||
"""
|
||||
|
|
@ -140,24 +141,30 @@ class ResourceMixin(models.AbstractModel):
|
|||
result = {}
|
||||
|
||||
# naive datetimes are made explicit in UTC
|
||||
from_datetime = timezone_datetime(from_datetime)
|
||||
to_datetime = timezone_datetime(to_datetime)
|
||||
from_datetime = localized(from_datetime)
|
||||
to_datetime = localized(to_datetime)
|
||||
|
||||
mapped_resources = defaultdict(lambda: self.env['resource.resource'])
|
||||
for record in self:
|
||||
mapped_resources[calendar or record.resource_calendar_id] |= record.resource_id
|
||||
|
||||
for calendar, calendar_resources in mapped_resources.items():
|
||||
day_total = calendar._get_resources_day_total(from_datetime, to_datetime, calendar_resources)
|
||||
# handle fully flexible resources by returning the length of the whole interval
|
||||
# since we do not take into account leaves for fully flexible resources
|
||||
if not calendar:
|
||||
days = (to_datetime - from_datetime).days
|
||||
hours = (to_datetime - from_datetime).total_seconds() / 3600
|
||||
for calendar_resource in calendar_resources:
|
||||
result[calendar_resource.id] = {'days': days, 'hours': hours}
|
||||
continue
|
||||
|
||||
# compute actual hours per day
|
||||
attendances = calendar._attendance_intervals_batch(from_datetime, to_datetime, calendar_resources)
|
||||
leaves = calendar._leave_intervals_batch(from_datetime, to_datetime, calendar_resources, domain)
|
||||
|
||||
for calendar_resource in calendar_resources:
|
||||
result[calendar_resource.id] = calendar._get_days_data(
|
||||
attendances[calendar_resource.id] & leaves[calendar_resource.id],
|
||||
day_total[calendar_resource.id]
|
||||
result[calendar_resource.id] = calendar._get_attendance_intervals_days_data(
|
||||
attendances[calendar_resource.id] & leaves[calendar_resource.id]
|
||||
)
|
||||
|
||||
# convert "resource: result" into "employee: result"
|
||||
|
|
@ -171,7 +178,7 @@ class ResourceMixin(models.AbstractModel):
|
|||
for record in self
|
||||
}
|
||||
|
||||
def list_work_time_per_day(self, from_datetime, to_datetime, calendar=None, domain=None):
|
||||
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.
|
||||
|
|
@ -182,21 +189,28 @@ class ResourceMixin(models.AbstractModel):
|
|||
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
|
||||
result = {}
|
||||
records_by_calendar = defaultdict(lambda: self.env[self._name])
|
||||
for record in self:
|
||||
records_by_calendar[calendar or record.resource_calendar_id or record.company_id.resource_calendar_id] += record
|
||||
|
||||
# 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)
|
||||
|
||||
compute_leaves = self.env.context.get('compute_leaves', True)
|
||||
intervals = calendar._work_intervals_batch(from_datetime, to_datetime, resource, domain, compute_leaves=compute_leaves)[resource.id]
|
||||
result = defaultdict(float)
|
||||
for start, stop, meta in intervals:
|
||||
result[start.date()] += (stop - start).total_seconds() / 3600
|
||||
return sorted(result.items())
|
||||
|
||||
for calendar, records in records_by_calendar.items():
|
||||
resources = self.resource_id
|
||||
all_intervals = calendar._work_intervals_batch(from_datetime, to_datetime, resources, domain, compute_leaves=compute_leaves)
|
||||
for record in records:
|
||||
intervals = all_intervals[record.resource_id.id]
|
||||
record_result = defaultdict(float)
|
||||
for start, stop, _meta in intervals:
|
||||
record_result[start.date()] += (stop - start).total_seconds() / 3600
|
||||
result[record.id] = sorted(record_result.items())
|
||||
return result
|
||||
|
||||
def list_leaves(self, from_datetime, to_datetime, calendar=None, domain=None):
|
||||
"""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue