Steps : - On a new DB (no demo data). Say created at 15:00. - Go to planning. - Add a shift for today. - Set the dates from 08:00 to 17:00. Issue : The 'Allocated Time' = 02:00. Cause : We used to count from the resource creation min to the departure max. So, in this case, not from 08:00, but from 15:00. While this might seem logical, it confuses users in onboarding. Fix : Calculate the whole time, regardless of the resource lifespan. Notes : - Similar issues solved with this commit : > Once the shift validated, the avatar progress bar uses the same allocated time. > Same in Project Task gantt view. - When contract is installed, if the resource does not have a contract, the Allocated Time = 0. Now, it is calculated from the resource calendar. task-2983993 closes odoo/odoo#102732 X-original-commit: 6a61d1c4c56075ade36f6f32e7a61f1f6c771a6f Related: odoo/enterprise#32547 Signed-off-by: Laurent Stukkens (ltu) <ltu@odoo.com>
57 lines
3.1 KiB
Python
57 lines
3.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
from collections import defaultdict
|
|
from datetime import datetime
|
|
from pytz import timezone
|
|
|
|
from odoo import models
|
|
from odoo.addons.resource.models.resource import Intervals
|
|
|
|
class ResourceResource(models.Model):
|
|
_inherit = 'resource.resource'
|
|
|
|
def _get_calendars_validity_within_period(self, start, end, default_company=None):
|
|
assert start.tzinfo and end.tzinfo
|
|
if not self:
|
|
return super()._get_calendars_validity_within_period(start, end, default_company=default_company)
|
|
calendars_within_period_per_resource = defaultdict(lambda: defaultdict(Intervals)) # keys are [resource id:integer][calendar:self.env['resource.calendar']]
|
|
# Employees that have ever had an active contract
|
|
employee_ids_with_active_contracts = {
|
|
contract['employee_id'][0] for contract in
|
|
self.env['hr.contract']._read_group(
|
|
domain=[
|
|
('employee_id', 'in', self.employee_id.ids),
|
|
'|', ('state', '=', 'open'),
|
|
'|', ('state', '=', 'close'),
|
|
'&', ('state', '=', 'draft'), ('kanban_state', '=', 'done')
|
|
],
|
|
fields=['employee_id'], groupby=['employee_id']
|
|
)
|
|
}
|
|
resource_without_contract = self.filtered(
|
|
lambda r: not r.employee_id\
|
|
or not r.employee_id.id in employee_ids_with_active_contracts\
|
|
or r.employee_id.employee_type not in ['employee', 'student']
|
|
)
|
|
if resource_without_contract:
|
|
calendars_within_period_per_resource.update(
|
|
super(ResourceResource, resource_without_contract)._get_calendars_validity_within_period(start, end, default_company=default_company)
|
|
)
|
|
resource_with_contract = self - resource_without_contract
|
|
if not resource_with_contract:
|
|
return calendars_within_period_per_resource
|
|
timezones = {resource.tz for resource in resource_with_contract}
|
|
date_start = min(start.astimezone(timezone(tz)).date() for tz in timezones)
|
|
date_end = max(end.astimezone(timezone(tz)).date() for tz in timezones)
|
|
contracts = resource_with_contract.employee_id._get_contracts(
|
|
date_start, date_end, states=['open', 'draft', 'close']
|
|
).filtered(lambda c: c.state in ['open', 'close'] or c.kanban_state == 'done')
|
|
for contract in contracts:
|
|
tz = timezone(contract.employee_id.tz)
|
|
calendars_within_period_per_resource[contract.employee_id.resource_id.id][contract.resource_calendar_id] |= Intervals([(
|
|
tz.localize(datetime.combine(contract.date_start, datetime.min.time())) if contract.date_start > start.astimezone(tz).date() else start,
|
|
tz.localize(datetime.combine(contract.date_end, datetime.max.time())) if contract.date_end and contract.date_end < end.astimezone(tz).date() else end,
|
|
self.env['resource.calendar.attendance']
|
|
)])
|
|
return calendars_within_period_per_resource
|