Files
tryton/work.py
T

719 lines
26 KiB
Python
Raw Normal View History

2009-06-25 17:37:37 +02:00
#This file is part of Tryton. The COPYRIGHT file at the top level of
#this repository contains the full copyright notices and license terms.
2010-08-16 19:28:32 +02:00
import datetime
from collections import deque, defaultdict
from heapq import heappop, heappush
2012-09-24 22:18:07 +02:00
from trytond.model import ModelSQL, fields
2012-01-02 18:02:54 +01:00
from trytond.wizard import Wizard, StateTransition
2011-08-21 11:20:49 +02:00
from trytond.pyson import Eval
2010-08-16 19:28:32 +02:00
from trytond.transaction import Transaction
2012-09-24 22:18:07 +02:00
from trytond.pool import Pool, PoolMeta
__all__ = ['Work', 'PredecessorSuccessor', 'Leveling']
__metaclass__ = PoolMeta
2010-08-16 19:28:32 +02:00
2009-06-25 17:37:37 +02:00
def intfloor(x):
return int(round(x, 4))
2012-09-24 22:18:07 +02:00
class Work:
__name__ = 'project.work'
2009-06-25 17:37:37 +02:00
predecessors = fields.Many2Many('project.predecessor_successor',
2011-08-04 16:57:37 +02:00
'successor', 'predecessor', 'Predecessors',
domain=[
('parent', '=', Eval('parent')),
('id', '!=', Eval('id')),
],
depends=['parent', 'id'])
2009-06-25 17:37:37 +02:00
successors = fields.Many2Many('project.predecessor_successor',
2011-08-04 16:57:37 +02:00
'predecessor', 'successor', 'Successors',
domain=[
('parent', '=', Eval('parent')),
('id', '!=', Eval('id')),
],
depends=['parent', 'id'])
2009-06-25 17:37:37 +02:00
leveling_delay = fields.Float("Leveling Delay")
back_leveling_delay = fields.Float("Leveling Delay")
allocations = fields.One2Many('project.allocation', 'work', 'Allocations',
2011-08-21 11:20:49 +02:00
states={
'invisible': Eval('type') != 'task',
2009-06-25 17:37:37 +02:00
}, depends=['type'])
2010-03-11 16:52:42 +01:00
duration = fields.Function(fields.Float('Duration'), 'get_function_fields')
2009-06-25 17:37:37 +02:00
early_start_time = fields.DateTime("Early Start Time", readonly=True)
late_start_time = fields.DateTime("Late Start Time", readonly=True)
early_finish_time = fields.DateTime("Early Finish Time", readonly=True)
late_finish_time = fields.DateTime("Late Finish Time", readonly=True)
actual_start_time = fields.DateTime("Actual Start Time")
actual_finish_time = fields.DateTime("Actual Finish Time")
constraint_start_time = fields.DateTime("Constraint Start Time")
constraint_finish_time = fields.DateTime("Constraint Finish Time")
2010-03-11 16:52:42 +01:00
early_start_date = fields.Function(fields.Date('Early Start'),
'get_function_fields')
late_start_date = fields.Function(fields.Date('Late Start'),
'get_function_fields')
early_finish_date = fields.Function(fields.Date('Early Finish'),
'get_function_fields')
late_finish_date = fields.Function(fields.Date('Late Finish'),
'get_function_fields')
actual_start_date = fields.Function(fields.Date('Actual Start'),
'get_function_fields', setter='set_function_fields')
actual_finish_date = fields.Function(fields.Date('Actual Finish'),
'get_function_fields', setter='set_function_fields')
constraint_start_date = fields.Function(fields.Date('Constraint Start',
depends=['type']), 'get_function_fields',
setter='set_function_fields')
constraint_finish_date = fields.Function(fields.Date('Constraint Finish',
depends=['type']), 'get_function_fields',
setter='set_function_fields')
requests = fields.Function(fields.One2Many('res.request', None,
'Requests'), 'get_function_fields', setter='set_function_fields')
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
@classmethod
def __setup__(cls):
super(Work, cls).__setup__()
@classmethod
def validate(cls, works):
super(Work, cls).validate(works)
cls.check_recursion(works)
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
@classmethod
def check_recursion(cls, records, parent='successors'):
return super(Work, cls).check_recursion(records, parent=parent)
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
@classmethod
def get_function_fields(cls, works, names):
2009-06-25 17:37:37 +02:00
'''
Function to compute function fields
'''
2012-09-24 22:18:07 +02:00
RequestReference = Pool().get('res.request.reference')
2010-09-03 21:03:55 +02:00
cursor = Transaction().cursor
2010-08-16 19:28:32 +02:00
2009-06-25 17:37:37 +02:00
res = {}
2012-09-24 22:18:07 +02:00
ids = [w.id for w in works]
2009-06-25 17:37:37 +02:00
if 'requests' in names:
requests = dict((i, []) for i in ids)
2009-06-25 17:37:37 +02:00
for i in range(0, len(ids), cursor.IN_MAX):
sub_ids = ids[i:i + cursor.IN_MAX]
2012-09-24 22:18:07 +02:00
req_refs = RequestReference.search([
('reference', 'in', [
'project.work,%s' % i for i in sub_ids
]
),
2010-08-16 19:28:32 +02:00
])
for req_ref in req_refs:
_, work_id = req_ref.reference.split(',')
requests[int(work_id)].append(req_ref.request.id)
2009-06-25 17:37:37 +02:00
res['requests'] = requests
if 'duration' in names:
2012-09-24 22:18:07 +02:00
all_works = cls.search([
2009-06-25 17:37:37 +02:00
('parent', 'child_of', ids),
2012-09-24 22:18:07 +02:00
('active', '=', True)]) + works
2012-10-17 16:30:38 +02:00
all_works = set(all_works)
2009-06-25 17:37:37 +02:00
durations = {}
id2work = {}
leafs = set()
2012-09-24 22:18:07 +02:00
for work in all_works:
2009-06-25 17:37:37 +02:00
id2work[work.id] = work
if not work.children:
leafs.add(work.id)
total_allocation = 0
if not work.allocations:
durations[work.id] = work.effort
continue
for allocation in work.allocations:
total_allocation += allocation.percentage
durations[work.id] = work.effort / (total_allocation / 100.0)
while leafs:
for work_id in leafs:
work = id2work[work_id]
2012-10-17 16:30:38 +02:00
all_works.remove(work)
2009-06-25 17:37:37 +02:00
if not work.active:
continue
if work.parent and work.parent.id in durations:
durations[work.parent.id] += durations[work_id]
2012-10-17 16:30:38 +02:00
next_leafs = set(w.id for w in all_works)
for work in all_works:
if not work.parent:
continue
if work.parent.id in next_leafs and work.parent in works:
next_leafs.remove(work.parent.id)
leafs = next_leafs
2009-06-25 17:37:37 +02:00
res['duration'] = durations
fun_fields = ('early_start_date', 'early_finish_date',
'late_start_date', 'late_finish_date',
'actual_start_date', 'actual_finish_date',
'constraint_start_date', 'constraint_finish_date')
db_fields = ('early_start_time', 'early_finish_time',
'late_start_time', 'late_finish_time',
'actual_start_time', 'actual_finish_time',
'constraint_start_time', 'constraint_finish_time')
for fun_field, db_field in zip(fun_fields, db_fields):
if fun_field in names:
values = {}
for work in works:
2012-09-24 22:18:07 +02:00
values[work.id] = getattr(work, db_field) \
and getattr(work, db_field).date() or None
2009-06-25 17:37:37 +02:00
res[fun_field] = values
return res
2012-09-24 22:18:07 +02:00
@classmethod
def set_function_fields(cls, works, name, value):
pool = Pool()
Request = pool.get('res.request')
RequestReference = pool.get('res.request.reference')
if name == 'requests':
2010-03-11 16:52:42 +01:00
currents = dict((req.id, req) for work in works for req in
work.requests)
if not value:
return
for v in value:
to_unlink = []
to_link = []
operator = v[0]
2010-03-11 16:52:42 +01:00
target_ids = len(v) > 1 and v[1] or []
2011-09-28 13:17:03 +02:00
if operator == 'create':
2013-01-06 22:18:13 +01:00
request, = Request.create(v[1])
to_link.append(request.id)
2011-09-28 13:17:03 +02:00
elif operator == 'write':
2012-09-24 22:18:07 +02:00
Request.write([Request(v[1])], v[2])
2011-09-28 13:17:03 +02:00
elif operator == 'delete':
2012-09-24 22:18:07 +02:00
Request.delete([Request(v[1])])
2011-09-28 13:17:03 +02:00
elif operator == 'delete_all':
target_ids = []
2012-09-24 22:18:07 +02:00
for work in works:
refs = RequestReference.search([
('reference', '=', str(work)),
2011-09-28 13:17:03 +02:00
])
target_ids.extend(ref.request.id for ref in refs)
2012-09-24 22:18:07 +02:00
Request.delete(Request.browse(target_ids))
2011-09-28 13:17:03 +02:00
elif operator == 'unlink':
to_unlink.extend((i for i in target_ids if i in currents))
elif operator == 'add':
to_link.extend((i for i in target_ids if i not in
currents))
elif operator == 'unlink_all':
to_unlink.extend(currents)
elif operator == 'set':
2010-03-11 16:52:42 +01:00
to_link.extend((i for i in target_ids
if i not in currents))
to_unlink.extend((i for i in currents
if i not in target_ids))
else:
raise Exception('Operation not supported')
2012-09-24 22:18:07 +02:00
req_refs = []
for i in to_unlink:
request = currents[i]
for ref in request.references:
2012-09-24 22:18:07 +02:00
if ref.reference in works:
req_refs.append(ref)
RequestReference.delete(req_refs)
2013-01-06 22:18:13 +01:00
to_create = []
for i in to_link:
2012-09-24 22:18:07 +02:00
for work in works:
2013-01-06 22:18:13 +01:00
to_create.append({
2010-03-11 16:52:42 +01:00
'request': i,
2012-09-24 22:18:07 +02:00
'reference': str(work),
2010-08-16 19:28:32 +02:00
})
2013-01-06 22:18:13 +01:00
if to_create:
RequestReference.create(to_create)
return
2009-06-25 17:37:37 +02:00
fun_fields = ('actual_start_date', 'actual_finish_date',
'constraint_start_date', 'constraint_finish_date')
db_fields = ('actual_start_time', 'actual_finish_time',
'constraint_start_time', 'constraint_finish_time')
for fun_field, db_field in zip(fun_fields, db_fields):
if fun_field == name:
2012-09-24 22:18:07 +02:00
cls.write(works, {
2013-02-24 15:35:20 +01:00
db_field: (value
and datetime.datetime.combine(value,
datetime.time())
or None),
2010-08-16 19:28:32 +02:00
})
2009-06-25 17:37:37 +02:00
break
2012-09-24 22:18:07 +02:00
@classmethod
def add_minutes(cls, company, date, minutes):
2009-06-25 17:37:37 +02:00
minutes = int(round(minutes))
minutes = date.minute + minutes
hours = minutes // 60
if hours:
2012-09-24 22:18:07 +02:00
date = cls.add_hours(company, date, hours)
2009-06-25 17:37:37 +02:00
minutes = minutes % 60
2010-02-04 18:26:38 +01:00
date = datetime.datetime(
2009-06-25 17:37:37 +02:00
date.year,
date.month,
date.day,
date.hour,
minutes,
date.second)
return date
2012-09-24 22:18:07 +02:00
@classmethod
def add_hours(cls, company, date, hours):
2009-06-25 17:37:37 +02:00
while hours:
2012-04-03 20:07:29 +02:00
if hours != intfloor(hours):
2009-06-25 17:37:37 +02:00
minutes = (hours - intfloor(hours)) * 60
2012-09-24 22:18:07 +02:00
date = cls.add_minutes(company, date, minutes)
2009-06-25 17:37:37 +02:00
hours = intfloor(hours)
hours = date.hour + hours
days = hours // company.hours_per_work_day
if days:
2012-09-24 22:18:07 +02:00
date = cls.add_days(company, date, days)
2009-06-25 17:37:37 +02:00
hours = hours % company.hours_per_work_day
2010-02-04 18:26:38 +01:00
date = datetime.datetime(
2009-06-25 17:37:37 +02:00
date.year,
date.month,
date.day,
intfloor(hours),
date.minute,
date.second)
hours = hours - intfloor(hours)
return date
2012-09-24 22:18:07 +02:00
@classmethod
def add_days(cls, company, date, days):
2009-06-25 17:37:37 +02:00
day_per_week = company.hours_per_work_week / company.hours_per_work_day
while days:
2012-04-03 20:07:29 +02:00
if days != intfloor(days):
2009-06-25 17:37:37 +02:00
hours = (days - intfloor(days)) * company.hours_per_work_day
2012-09-24 22:18:07 +02:00
date = cls.add_hours(company, date, hours)
2009-06-25 17:37:37 +02:00
days = intfloor(days)
days = date.weekday() + days
weeks = days // day_per_week
days = days % day_per_week
if weeks:
2012-09-24 22:18:07 +02:00
date = cls.add_weeks(company, date, weeks)
2009-06-25 17:37:37 +02:00
2012-04-03 20:07:29 +02:00
date += datetime.timedelta(days=-date.weekday() + intfloor(days))
2009-06-25 17:37:37 +02:00
days = days - intfloor(days)
return date
2012-09-24 22:18:07 +02:00
@classmethod
def add_weeks(cls, company, date, weeks):
2009-06-25 17:37:37 +02:00
day_per_week = company.hours_per_work_week / company.hours_per_work_day
2012-04-03 20:07:29 +02:00
if weeks != intfloor(weeks):
2009-06-25 17:37:37 +02:00
days = (weeks - intfloor(weeks)) * day_per_week
if days:
2012-09-24 22:18:07 +02:00
date = cls.add_days(company, date, days)
2009-06-25 17:37:37 +02:00
2012-04-03 20:07:29 +02:00
date += datetime.timedelta(days=7 * intfloor(weeks))
2009-06-25 17:37:37 +02:00
return date
2012-09-24 22:18:07 +02:00
def compute_dates(self):
2009-06-25 17:37:37 +02:00
values = {}
get_early_finish = lambda work: values.get(work, {}).get(
2012-09-24 22:18:07 +02:00
'early_finish_time', work.early_finish_time)
2009-06-25 17:37:37 +02:00
get_late_start = lambda work: values.get(work, {}).get(
2012-09-24 22:18:07 +02:00
'late_start_time', work.late_start_time)
2012-04-03 20:07:29 +02:00
maxdate = lambda x, y: x and y and max(x, y) or x or y
mindate = lambda x, y: x and y and min(x, y) or x or y
2009-06-25 17:37:37 +02:00
# propagate constraint_start_time
2013-02-24 15:35:20 +01:00
constraint_start = reduce(maxdate, (pred.early_finish_time
2012-09-24 22:18:07 +02:00
for pred in self.predecessors), None)
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
if constraint_start is None and self.parent:
constraint_start = self.parent.early_start_time
2009-06-25 17:37:37 +02:00
constraint_start = maxdate(constraint_start,
2012-09-24 22:18:07 +02:00
self.constraint_start_time)
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
works = deque([(self, constraint_start)])
2009-06-25 17:37:37 +02:00
work2children = {}
parent = None
while works or parent:
if parent:
work = parent
parent = None
# Compute early_finish
if work.children:
early_finish_time = reduce(
maxdate, map(get_early_finish, work.children), None)
else:
early_finish_time = None
if values[work]['early_start_time']:
2010-08-16 19:28:32 +02:00
early_finish_time = self.add_hours(work.company,
values[work]['early_start_time'],
work.duration)
2009-06-25 17:37:37 +02:00
values[work]['early_finish_time'] = early_finish_time
# Propagate constraint_start on successors
for w in work.successors:
works.append((w, early_finish_time))
if not work.parent:
continue
# housecleaning work2children
if work.parent not in work2children:
work2children[work.parent] = set()
work2children[work.parent].update(work.successors)
if work in work2children[work.parent]:
work2children[work.parent].remove(work)
# if no sibling continue to walk up the tree
if not work2children.get(work.parent):
if not work.parent in values:
values[work.parent] = {}
parent = work.parent
continue
work, constraint_start = works.popleft()
# take constraint define on the work into account
constraint_start = maxdate(constraint_start,
work.constraint_start_time)
if constraint_start:
2010-08-16 19:28:32 +02:00
early_start = self.add_hours(work.company, constraint_start,
work.leveling_delay)
2009-06-25 17:37:37 +02:00
else:
early_start = None
# update values
if not work in values:
values[work] = {}
values[work]['early_start_time'] = early_start
# Loop on children if they exist
if work.children and work not in work2children:
work2children[work] = set(work.children)
# Propagate constraint_start on children
for w in work.children:
if w.predecessors:
continue
works.append((w, early_start))
else:
parent = work
# propagate constraint_finish_time
2013-02-24 15:35:20 +01:00
constraint_finish = reduce(mindate, (succ.late_start_time
2012-09-24 22:18:07 +02:00
for succ in self.successors), None)
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
if constraint_finish is None and self.parent:
constraint_finish = self.parent.late_finish_time
2009-06-25 17:37:37 +02:00
constraint_finish = mindate(constraint_finish,
2012-09-24 22:18:07 +02:00
self.constraint_finish_time)
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
works = deque([(self, constraint_finish)])
2009-06-25 17:37:37 +02:00
work2children = {}
parent = None
while works or parent:
if parent:
work = parent
parent = None
# Compute late_start
if work.children:
reduce(mindate, map(get_late_start, work.children), None)
else:
late_start_time = None
if values[work]['late_finish_time']:
2010-08-16 19:28:32 +02:00
late_start_time = self.add_hours(work.company,
values[work]['late_finish_time'],
-work.duration)
2009-06-25 17:37:37 +02:00
values[work]['late_start_time'] = late_start_time
# Propagate constraint_finish on predecessors
for w in work.predecessors:
works.append((w, late_start_time))
if not work.parent:
continue
# housecleaning work2children
if work.parent not in work2children:
work2children[work.parent] = set()
work2children[work.parent].update(work.predecessors)
if work in work2children[work.parent]:
work2children[work.parent].remove(work)
# if no sibling continue to walk up the tree
if not work2children.get(work.parent):
if not work.parent in values:
values[work.parent] = {}
parent = work.parent
continue
work, constraint_finish = works.popleft()
# take constraint define on the work into account
constraint_finish = mindate(constraint_finish,
work.constraint_finish_time)
if constraint_finish:
2010-08-16 19:28:32 +02:00
late_finish = self.add_hours(work.company, constraint_finish,
-work.back_leveling_delay)
2009-06-25 17:37:37 +02:00
else:
late_finish = None
# update values
if not work in values:
values[work] = {}
values[work]['late_finish_time'] = late_finish
# Loop on children if they exist
if work.children and work not in work2children:
work2children[work] = set(work.children)
# Propagate constraint_start on children
for w in work.children:
if w.successors:
continue
works.append((w, late_finish))
else:
parent = work
# write values
write_fields = ('early_start_time', 'early_finish_time',
'late_start_time', 'late_finish_time')
for work, val in values.iteritems():
write_cond = False
for field in write_fields:
2012-09-24 22:18:07 +02:00
if field in val and getattr(work, field) != val[field]:
2009-06-25 17:37:37 +02:00
write_cond = True
break
if write_cond:
2012-09-24 22:18:07 +02:00
self.write([work], val)
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
def reset_leveling(self):
2009-06-25 17:37:37 +02:00
get_key = lambda w: (set(p.id for p in w.predecessors),
set(s.id for s in w.successors))
2012-09-24 22:18:07 +02:00
parent_id = self.parent and self.parent.id or None
siblings = self.search([
2009-06-25 17:37:37 +02:00
('parent', '=', parent_id)
2010-08-16 19:28:32 +02:00
])
2009-06-25 17:37:37 +02:00
to_clean = []
2012-09-24 22:18:07 +02:00
ref_key = get_key(self)
2009-06-25 17:37:37 +02:00
for sibling in siblings:
if sibling.leveling_delay == sibling.back_leveling_delay == 0:
continue
if get_key(sibling) == ref_key:
2012-09-24 22:18:07 +02:00
to_clean.append(sibling)
2009-06-25 17:37:37 +02:00
if to_clean:
2010-08-16 19:28:32 +02:00
self.write(to_clean, {
2009-06-25 17:37:37 +02:00
'leveling_delay': 0,
'back_leveling_delay': 0,
2010-08-16 19:28:32 +02:00
})
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
def create_leveling(self):
2009-06-25 17:37:37 +02:00
# define some helper functions
get_key = lambda w: (set(p.id for p in w.predecessors),
set(s.id for s in w.successors))
2012-04-03 20:07:29 +02:00
over_alloc = lambda current_alloc, work: (
reduce(lambda res, alloc: (res
or (current_alloc[alloc.employee.id]
+ alloc.percentage) > 100),
work.allocations,
False))
2009-06-25 17:37:37 +02:00
def sum_allocs(current_alloc, work):
res = defaultdict(float)
for alloc in work.allocations:
empl = alloc.employee.id
res[empl] = current_alloc[empl] + alloc.percentage
return res
def compute_delays(siblings):
# time_line is a list [[end_delay, allocations], ...], this
# mean that allocations is valid between the preceding end_delay
# (or 0 if it doesn't exist) and the current end_delay.
timeline = []
for sibling in siblings:
delay = 0
ignored = []
overloaded = []
item = None
while timeline:
# item is [end_delay, allocations]
item = heappop(timeline)
if over_alloc(item[1], sibling):
ignored.extend(overloaded)
ignored.append(item)
delay = item[0]
continue
elif item[1] >= delay + sibling.duration:
overloaded.append(item)
else:
#Succes!
break
heappush(timeline,
[delay + sibling.duration,
sum_allocs(defaultdict(float), sibling),
sibling.id])
for i in ignored:
heappush(timeline, i)
for i in overloaded:
i[1] = sum_allocs(i[1], sibling)
heappush(timeline, i)
yield sibling, delay
2012-09-24 22:18:07 +02:00
parent = self.parent and self.parent.id or None
siblings = self.search([
('parent', '=', parent.id)
2010-08-16 19:28:32 +02:00
])
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
refkey = get_key(self)
siblings = [s for s in siblings if get_key(s) == refkey]
2009-06-25 17:37:37 +02:00
for sibling, delay in compute_delays(siblings):
2012-09-24 22:18:07 +02:00
self.write([sibling], {
2009-06-25 17:37:37 +02:00
'leveling_delay': delay,
2010-08-16 19:28:32 +02:00
})
2009-06-25 17:37:37 +02:00
siblings.reverse()
for sibling, delay in compute_delays(siblings):
2012-09-24 22:18:07 +02:00
self.write([sibling], {
2009-06-25 17:37:37 +02:00
'back_leveling_delay': delay,
2010-08-16 19:28:32 +02:00
})
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
if parent:
parent.compute_dates()
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
@classmethod
def write(cls, works, values):
super(Work, cls).write(works, values)
2009-06-25 17:37:37 +02:00
if 'effort' in values:
2012-09-24 22:18:07 +02:00
for work in works:
work.reset_leveling()
2009-06-25 17:37:37 +02:00
fields = ('constraint_start_time', 'constraint_finish_time',
'effort')
2012-04-03 20:07:29 +02:00
if reduce(lambda x, y: x or y in values, fields, False):
2012-09-24 22:18:07 +02:00
for work in works:
work.compute_dates()
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
@classmethod
2013-01-06 22:18:13 +01:00
def create(cls, vlist):
works = super(Work, cls).create(vlist)
for work in works:
work.reset_leveling()
work.compute_dates()
return works
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
@classmethod
def delete(cls, works):
2009-06-25 17:37:37 +02:00
to_update = set()
for work in works:
2012-09-24 22:18:07 +02:00
if work.parent and work.parent not in works:
to_update.add(work.parent)
to_update.update(c for c in work.parent.children
if c not in works)
super(Work, cls).delete(works)
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
for work in to_update:
work.reset_leveling()
work.compute_dates()
2009-06-25 17:37:37 +02:00
class PredecessorSuccessor(ModelSQL):
'Predecessor - Successor'
2012-09-24 22:18:07 +02:00
__name__ = 'project.predecessor_successor'
2009-06-25 17:37:37 +02:00
predecessor = fields.Many2One('project.work', 'Predecessor',
2012-03-05 10:53:05 +01:00
ondelete='CASCADE', required=True, select=True)
2009-06-25 17:37:37 +02:00
successor = fields.Many2One('project.work', 'Successor',
2012-03-05 10:53:05 +01:00
ondelete='CASCADE', required=True, select=True)
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
@classmethod
def write(cls, pred_succs, values):
Work = Pool().get('project.work')
super(PredecessorSuccessor, cls).write(pred_succs, values)
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
works = Work.browse(values.itervalues())
for work in works:
work.reset_leveling()
for work in works:
work.compute_dates()
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
@classmethod
def delete(cls, pred_succs):
works = set()
parents = set()
2009-06-25 17:37:37 +02:00
for pred_succ in pred_succs:
2012-09-24 22:18:07 +02:00
works.update((pred_succ.predecessor,
pred_succ.successor))
2009-06-25 17:37:37 +02:00
if pred_succ.predecessor.parent:
2012-09-24 22:18:07 +02:00
parents.add(pred_succ.predecessor.parent)
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
super(PredecessorSuccessor, cls).delete(pred_succs)
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
for work in works:
work.reset_leveling()
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
for parent in parents:
parent.compute_dates()
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
@classmethod
2013-01-06 22:18:13 +01:00
def create(cls, vlist):
pred_succs = super(PredecessorSuccessor, cls).create(vlist)
2009-06-25 17:37:37 +02:00
2013-01-06 22:18:13 +01:00
for pred_succ in pred_succs:
pred_succ.predecessor.reset_leveling()
pred_succ.successor.reset_leveling()
2009-06-25 17:37:37 +02:00
2013-01-06 22:18:13 +01:00
if pred_succ.predecessor.parent:
pred_succ.predecessor.parent.compute_dates()
return pred_succs
2009-06-25 17:37:37 +02:00
class Leveling(Wizard):
'Tasks Leveling'
2012-09-24 22:18:07 +02:00
__name__ = 'project_plan.work.leveling'
2012-01-02 18:02:54 +01:00
start_state = 'leveling'
leveling = StateTransition()
2009-06-25 17:37:37 +02:00
2012-09-24 22:18:07 +02:00
def transition_leveling(self):
Work = Pool().get('project.work')
Work(Transaction().context['active_id']).create_leveling()
2012-01-02 18:02:54 +01:00
return 'end'