diff --git a/addons/project/project.py b/addons/project/project.py index 64d83538f0c..137ef3f8a3f 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -205,20 +205,37 @@ class project(osv.osv): if context is None: context = {} - proj = self.browse(cr, uid, id, context=context) default = default or {} context['active_test'] = False default['state'] = 'open' if not default.get('name', False): default['name'] = proj.name + _(' (copy)') + res = super(project, self).copy(cr, uid, id, default, context) + return res + + + def template_copy(self, cr, uid, id, default={}, context=None): + task_obj = self.pool.get('project.task') + proj = self.browse(cr, uid, id, context=context) + + default['tasks'] = [] #avoid to copy all the task automaticly + res = self.copy(cr, uid, id, default=default, context=context) + + #copy all the task manually + map_task_id = {} + for task in proj.tasks: + map_task_id[task.id] = task_obj.copy(cr, uid, task.id, {}, context=context) + + self.write(cr, uid, res, {'tasks':[(6,0, map_task_id.values())]}) + task_obj.duplicate_task(cr, uid, map_task_id, context=context) return res def duplicate_template(self, cr, uid, ids, context=None): if context is None: context = {} - project_obj = self.pool.get('project.project') + task_pool = self.pool.get('project.task') data_obj = self.pool.get('ir.model.data') result = [] for proj in self.browse(cr, uid, ids, context=context): @@ -231,7 +248,7 @@ class project(osv.osv): end_date = date(*time.strptime(proj.date,'%Y-%m-%d')[:3]) new_date_end = (datetime(*time.strptime(new_date_start,'%Y-%m-%d')[:3])+(end_date-start_date)).strftime('%Y-%m-%d') context.update({'copy':True}) - new_id = project_obj.copy(cr, uid, proj.id, default = { + new_id = self.template_copy(cr, uid, proj.id, default = { 'name': proj.name +_(' (copy)'), 'state':'open', 'date_start':new_date_start, @@ -332,7 +349,7 @@ class task(osv.osv): def onchange_planned(self, cr, uid, ids, planned = 0.0, effective = 0.0): return {'value':{'remaining_hours': planned - effective}} - + def onchange_project(self, cr, uid, id, project_id): if not project_id: return {} @@ -341,7 +358,7 @@ class task(osv.osv): if partner_id: return {'value':{'partner_id':partner_id.id}} return {} - + def _default_project(self, cr, uid, context=None): if context is None: context = {} @@ -349,6 +366,25 @@ class task(osv.osv): return int(context['project_id']) return False + def duplicate_task(self, cr, uid, map_ids, context=None): + for new in map_ids.values(): + task = self.browse(cr, uid, new, context) + child_ids = [ ch.id for ch in task.child_ids] + if task.child_ids: + for child in task.child_ids: + if child.id in map_ids.keys(): + child_ids.remove(child.id) + child_ids.append(map_ids[child.id]) + + parent_ids = [ ch.id for ch in task.parent_ids] + if task.parent_ids: + for parent in task.parent_ids: + if parent.id in map_ids.keys(): + parent_ids.remove(parent.id) + parent_ids.append(map_ids[parent.id]) + #FIXME why there is already the copy and the old one + self.write(cr, uid, new, {'parent_ids':[(6,0,set(parent_ids))], 'child_ids':[(6,0, set(child_ids))]}) + def copy_data(self, cr, uid, id, default={}, context=None): default = default or {} default.update({'work_ids':[], 'date_start': False, 'date_end': False, 'date_deadline': False}) @@ -360,7 +396,7 @@ class task(osv.osv): default['name'] = self.browse(cr, uid, id, context=context).name or '' if not context.get('copy',False): new_name = _("%s (copy)")%default.get('name','') - default.update({'name':new_name}) + default.update({'name':new_name}) return super(task, self).copy_data(cr, uid, id, default, context) def _check_dates(self, cr, uid, ids, context=None): @@ -448,27 +484,33 @@ class task(osv.osv): _order = "sequence,priority, date_start, name, id" def _check_recursion(self, cr, uid, ids, context=None): - obj_task = self.browse(cr, uid, ids[0], context=context) - parent_ids = [x.id for x in obj_task.parent_ids] - children_ids = [x.id for x in obj_task.child_ids] + for id in ids: + visited_branch = set() + visited_node = set() + res = self._check_cycle(cr, uid, id, visited_branch, visited_node, context=context) + if not res: + return False - if (obj_task.id in children_ids) or (obj_task.id in parent_ids): + return True + + def _check_cycle(self, cr, uid, id, visited_branch, visited_node, context=None): + if id in visited_branch: #Cycle return False - while(ids): - cr.execute('SELECT DISTINCT task_id '\ - 'FROM project_task_parent_rel '\ - 'WHERE parent_id IN %s', (tuple(ids),)) - child_ids = map(lambda x: x[0], cr.fetchall()) - c_ids = child_ids - if (list(set(parent_ids).intersection(set(c_ids)))) or (obj_task.id in c_ids): + if id in visited_node: #Already tested don't work one more time for nothing + return True + + visited_branch.add(id) + visited_node.add(id) + + #visit child using DFS + task = self.browse(cr, uid, id, context=context) + for child in task.child_ids: + res = self._check_cycle(cr, uid, child.id, visited_branch, visited_node, context=context) + if not res: return False - while len(c_ids): - s_ids = self.search(cr, uid, [('parent_ids', 'in', c_ids)]) - if (list(set(parent_ids).intersection(set(s_ids)))): - return False - c_ids = s_ids - ids = child_ids + + visited_branch.remove(id) return True def _check_dates(self, cr, uid, ids, context=None): @@ -478,9 +520,9 @@ class task(osv.osv): start = obj_task.date_start or False end = obj_task.date_end or False if start and end : - if start > end: + if start > end: return False - return True + return True _constraints = [ (_check_recursion, 'Error ! You cannot create recursive tasks.', ['parent_ids']),