diff --git a/addons/project_long_term/__openerp__.py b/addons/project_long_term/__openerp__.py
index ef57f53e5f1..31522d63ea2 100644
--- a/addons/project_long_term/__openerp__.py
+++ b/addons/project_long_term/__openerp__.py
@@ -49,13 +49,11 @@ Features
'test/test_schedule_phases_case2.yml',
'test/project_schedule_consecutive_day.yml',
'test/project_schedule_without_wroking_hour.yml',
- 'test/schedule_phase_tasks.yml',
'test/phase_constraint.yml',
'test/test_schedule_tasks_case1.yml',
],
"update_xml": [
"security/ir.model.access.csv",
- "wizard/project_schedule_tasks_view.xml",
"project_long_term_view.xml",
"project_long_term_workflow.xml",
"wizard/project_compute_phases_view.xml",
diff --git a/addons/project_long_term/project_long_term.py b/addons/project_long_term/project_long_term.py
index 7cd6697b67a..77fa3f38c02 100644
--- a/addons/project_long_term/project_long_term.py
+++ b/addons/project_long_term/project_long_term.py
@@ -22,7 +22,7 @@
from datetime import datetime
from tools.translate import _
from osv import fields, osv
-from resource.faces import task as Task
+from resource.faces import task as Task
from operator import itemgetter
class project_phase(osv.osv):
@@ -74,40 +74,43 @@ class project_phase(osv.osv):
return False
return True
- def _check_constraint_start(self, cr, uid, ids, context=None):
- phase = self.read(cr, uid, ids[0], ['date_start', 'constraint_date_start'], context=context)
- if phase['date_start'] and phase['constraint_date_start'] and phase['date_start'] < phase['constraint_date_start']:
- return False
- return True
-
- def _check_constraint_end(self, cr, uid, ids, context=None):
- phase = self.read(cr, uid, ids[0], ['date_end', 'constraint_date_end'], context=context)
- if phase['date_end'] and phase['constraint_date_end'] and phase['date_end'] > phase['constraint_date_end']:
- return False
- return True
-
def _get_default_uom_id(self, cr, uid):
model_data_obj = self.pool.get('ir.model.data')
model_data_id = model_data_obj._get_id(cr, uid, 'product', 'uom_hour')
return model_data_obj.read(cr, uid, [model_data_id], ['res_id'])[0]['res_id']
- def _compute(self, cr, uid, ids, field_name, arg, context=None):
+ def _compute_progress(self, cr, uid, ids, field_name, arg, context=None):
res = {}
if not ids:
return res
for phase in self.browse(cr, uid, ids, context=context):
- tot = 0.0
+ if phase.state=='done':
+ res[phase.id] = 100.0
+ continue
+ elif phase.state=="cancelled":
+ res[phase.id] = 0.0
+ continue
+ elif not phase.task_ids:
+ res[phase.id] = 0.0
+ continue
+
+ tot = done = 0.0
for task in phase.task_ids:
- tot += task.planned_hours
- res[phase.id] = tot
+ tot += task.total_hours
+ done += min(task.effective_hours, task.total_hours)
+
+ if not tot:
+ res[phase.id] = 0.0
+ else:
+ res[phase.id] = round(100.0 * done / tot, 2)
return res
_columns = {
'name': fields.char("Name", size=64, required=True),
- 'date_start': fields.date('Start Date', help="It's computed by the scheduler according the project date or the end date of the previous phase.", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
- 'date_end': fields.date('End Date', help=" It's computed by the scheduler according to the start date and the duration.", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
- 'constraint_date_start': fields.date('Minimum Start Date', help='force the phase to start after this date', states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
- 'constraint_date_end': fields.date('Deadline', help='force the phase to finish before this date', states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
+ 'date_start': fields.datetime('Start Date', help="It's computed by the scheduler according the project date or the end date of the previous phase.", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
+ 'date_end': fields.datetime('End Date', help=" It's computed by the scheduler according to the start date and the duration.", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
+ 'constraint_date_start': fields.datetime('Minimum Start Date', help='force the phase to start after this date', states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
+ 'constraint_date_end': fields.datetime('Deadline', help='force the phase to finish before this date', states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'project_id': fields.many2one('project.project', 'Project', required=True),
'next_phase_ids': fields.many2many('project.phase', 'project_phase_rel', 'prv_phase_id', 'next_phase_id', 'Next Phases', states={'cancelled':[('readonly',True)]}),
'previous_phase_ids': fields.many2many('project.phase', 'project_phase_rel', 'next_phase_id', 'prv_phase_id', 'Previous Phases', states={'cancelled':[('readonly',True)]}),
@@ -115,74 +118,27 @@ class project_phase(osv.osv):
'duration': fields.float('Duration', required=True, help="By default in days", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'product_uom': fields.many2one('product.uom', 'Duration UoM', required=True, help="UoM (Unit of Measure) is the unit of measurement for Duration", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'task_ids': fields.one2many('project.task', 'phase_id', "Project Tasks", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
- 'resource_ids': fields.one2many('project.resource.allocation', 'phase_id', "Project Resources",states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
- 'responsible_id': fields.many2one('res.users', 'Responsible', states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
+ 'user_force_ids': fields.many2many('res.users', string='Force Assigned Users'),
+ 'user_ids': fields.one2many('project.user.allocation', 'phase_id', "Assigned Users",states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]},
+ help="The ressources on the project can be computed automatically by the scheduler"),
'state': fields.selection([('draft', 'New'), ('open', 'In Progress'), ('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State', readonly=True, required=True,
help='If the phase is created the state \'Draft\'.\n If the phase is started, the state becomes \'In Progress\'.\n If review is needed the phase is in \'Pending\' state.\
\n If the phase is over, the states is set to \'Done\'.'),
- 'total_hours': fields.function(_compute, string='Total Hours'),
+ 'progress': fields.function(_compute_progress, string='Progress', help="Computed based on related tasks"),
}
_defaults = {
- 'responsible_id': lambda obj,cr,uid,context: uid,
'state': 'draft',
'sequence': 10,
'product_uom': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('Day'))], context=c)[0]
}
- _order = "project_id, date_start, sequence, name"
+ _order = "project_id, date_start, sequence"
_constraints = [
(_check_recursion,'Loops in phases not allowed',['next_phase_ids', 'previous_phase_ids']),
(_check_dates, 'Phase start-date must be lower than phase end-date.', ['date_start', 'date_end']),
]
def onchange_project(self, cr, uid, ids, project, context=None):
- result = {}
- result['date_start'] = False
- project_obj = self.pool.get('project.project')
- if project:
- project_id = project_obj.browse(cr, uid, project, context=context)
- result['date_start'] = project_id.date_start
- return {'value': result}
-
-
- def _check_date_start(self, cr, uid, phase, date_end, context=None):
- """
- Check And Compute date_end of phase if change in date_start < older time.
- """
- uom_obj = self.pool.get('product.uom')
- resource_obj = self.pool.get('resource.resource')
- cal_obj = self.pool.get('resource.calendar')
- calendar_id = phase.project_id.resource_calendar_id and phase.project_id.resource_calendar_id.id or False
- resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)])
- if resource_id:
- res = resource_obj.read(cr, uid, resource_id, ['calendar_id'], context=context)[0]
- cal_id = res.get('calendar_id', False) and res.get('calendar_id')[0] or False
- if cal_id:
- calendar_id = cal_id
- default_uom_id = self._get_default_uom_id(cr, uid)
- avg_hours = uom_obj._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
- work_times = cal_obj.interval_min_get(cr, uid, calendar_id, date_end, avg_hours or 0.0, resource_id and resource_id[0] or False)
- dt_start = work_times[0][0].strftime('%Y-%m-%d')
- self.write(cr, uid, [phase.id], {'date_start': dt_start, 'date_end': date_end.strftime('%Y-%m-%d')}, context=context)
-
- def _check_date_end(self, cr, uid, phase, date_start, context=None):
- """
- Check And Compute date_end of phase if change in date_end > older time.
- """
- uom_obj = self.pool.get('product.uom')
- resource_obj = self.pool.get('resource.resource')
- cal_obj = self.pool.get('resource.calendar')
- calendar_id = phase.project_id.resource_calendar_id and phase.project_id.resource_calendar_id.id or False
- resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)], context=context)
- if resource_id:
- res = resource_obj.read(cr, uid, resource_id, ['calendar_id'], context=context)[0]
- cal_id = res.get('calendar_id', False) and res.get('calendar_id')[0] or False
- if cal_id:
- calendar_id = cal_id
- default_uom_id = self._get_default_uom_id(cr, uid)
- avg_hours = uom_obj._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
- work_times = cal_obj.interval_get(cr, uid, calendar_id, date_start, avg_hours or 0.0, resource_id and resource_id[0] or False)
- dt_end = work_times[-1][1].strftime('%Y-%m-%d')
- self.write(cr, uid, [phase.id], {'date_start': date_start.strftime('%Y-%m-%d'), 'date_end': dt_end}, context=context)
+ return {}
def copy(self, cr, uid, id, default=None, context=None):
if default is None:
@@ -211,208 +167,58 @@ class project_phase(osv.osv):
self.write(cr, uid, ids, {'state': 'done'})
return True
- def generate_phase(self, cr, uid, ids, f, parent=False, context=None):
- if context is None:
- context = {}
- phase_ids = []
- data_pool = self.pool.get('ir.model.data')
- uom_pool = self.pool.get('product.uom')
- task_pool = self.pool.get('project.task')
- data_model, day_uom_id = data_pool.get_object_reference(cr, uid, 'product', 'uom_day')
- for phase in self.browse(cr, uid, ids, context=context):
- avg_days = uom_pool._compute_qty(cr, uid, phase.product_uom.id, phase.duration, day_uom_id)
- duration = str(avg_days) + 'd'
- # Create a new project for each phase
- str_resource = ('%s & '*len(phase.resource_ids))[:-2]
- str_vals = str_resource % tuple(map(lambda x: 'Resource_%s'%x.resource_id.id, phase.resource_ids))
+ def generate_phase(self, cr, uid, phases, context=None):
+ context = context or {}
+ result = ""
- # Phases Defination for the Project
- s = '''
+ task_pool = self.pool.get('project.task')
+ for phase in phases:
+ if phase.state in ('done','cancelled'):
+ continue
+ duration_uom = {
+ 'days': 'd', 'day': 'd', 'd':'d',
+ 'months': 'm', 'month':'month', 'm':'m',
+ 'weeks': 'w', 'week': 'w', 'w':'w',
+ 'hours': 'H', 'hour': 'H', 'h':'H',
+ }.get(phase.product_uom.name.lower(), "h")
+ duration = str(phase.duration) + duration_uom
+ result += '''
def Phase_%s():
title = \"%s\"
- effort = \'%s\'
- resource = %s
-'''%(phase.id, phase.name, duration, str_vals or False)
-
- # Recalculate date_start and date_end
- # according to constraints on date start and date end on phase
- start_date = ''
- end_date = ''
+ effort = \"%s\"''' % (phase.id, phase.name, duration)
+ start = []
if phase.constraint_date_start:
- start_date = datetime.strptime(phase.constraint_date_start, '%Y-%m-%d')
- s += '''
- start = \"%s\"
-'''%(datetime.strftime(start_date, "%Y-%m-%d"))
- else:
- if parent:
- start = 'up.Phase_%s.end' % (parent.id)
- s += '''
- start = %s
-'''%(start)
- else:
- start = phase.project_id.date_start or phase.date_start
- s += '''
- start = \"%s\"
-'''%(start)
-
- if phase.constraint_date_end :
- end_date= datetime.strptime(phase.constraint_date_end, '%Y-%m-%d')
- s += '''
- end = \"%s\"
-'''%(datetime.strftime(end_date, "%Y-%m-%d"))
+ start.append('datetime.datetime.strptime("'+str(phase.constraint_date_start)+'", "%Y-%m-%d %H:%M:%S")')
+ for previous_phase in phase.previous_phase_ids:
+ start.append("up.Phase_%s.end" % (previous_phase.id,))
+ if start:
+ result += '''
+ start = max(%s)
+''' % (','.join(start))
+ if phase.user_force_ids:
+ result += '''
+ resource = %s
+''' % '|'.join(map(lambda x: 'User_'+str(x.id), phase.user_force_ids))
- #start = datetime.strftime((datetime.strptime(start, "%Y-%m-%d")), "%Y-%m-%d")
+ result += task_pool._generate_task(cr, uid, phase.task_ids, ident=8, context=context)
+ result += "\n"
- phase_ids.append(phase.id)
- parent = False
- task_ids = []
- todo_task_ids = task_pool.search(cr, uid, [('id', 'in', map(lambda x : x.id, phase.task_ids)),
- ('state', 'in', ['draft', 'open', 'pending'])
- ], order='sequence')
- if todo_task_ids :
- for task in task_pool.browse(cr, uid, todo_task_ids, context=context):
- s += task_pool.generate_task(cr, uid, task.id, parent=parent, flag=False, context=context)
- if not parent:
- parent = task
- task_ids.append(task.id)
-
- f += s + '\n'
- # Recursive call till all the next phases scheduled
- for next_phase in phase.next_phase_ids:
- if next_phase.state in ['draft', 'open', 'pending']:
- rf, rphase_ids = self.generate_phase(cr, uid, [next_phase.id], f = '', parent=phase, context=context)
- f += rf +'\n'
- phase_ids += rphase_ids
- else:
- continue
- return f, phase_ids
-
- def schedule_tasks(self, cr, uid, ids, context=None):
- """
- Schedule tasks base on faces lib
- """
- if context is None:
- context = {}
- if type(ids) in (long, int,):
- ids = [ids]
- task_pool = self.pool.get('project.task')
- resource_pool = self.pool.get('resource.resource')
- for phase in self.browse(cr, uid, ids, context=context):
- project = phase.project_id
- calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
- start_date = project.date_start
- #Creating resources using the member of the Project
- u_ids = [i.id for i in project.members]
- resource_objs = resource_pool.generate_resources(cr, uid, u_ids, calendar_id, context=context)
- start_date = datetime.strftime((datetime.strptime(start_date, "%Y-%m-%d")), "%Y-%m-%d")
- func_str = ''
- start = start_date
- minimum_time_unit = 1
- # default values
- working_hours_per_day = 24
- working_days_per_week = 7
- working_days_per_month = 30
- working_days_per_year = 365
-
- vacation = []
- if calendar_id:
- working_hours_per_day = 8 #TODO: it should be come from calendars
- working_days_per_week = 5
- working_days_per_month = 20
- working_days_per_year = 200
- vacation = tuple(resource_pool.compute_vacation(cr, uid, calendar_id, context=context))
-
- working_days = resource_pool.compute_working_calendar(cr, uid, calendar_id, context=context)
-
- cls_str = ''
- # Creating Resources for the Project
- for key, vals in resource_objs.items():
- cls_str +='''
- class Resource_%s(Resource):
- title = \"%s\"
- vacation = %s
- efficiency = %s
-'''%(key, vals.get('name',False), vals.get('vacation', False), vals.get('efficiency', False))
-
- # Create a new project for each phase
- func_str += '''
-def Phase_%d():
- from resource.faces import Resource
- title = \"%s\"
- start = \'%s\'
- minimum_time_unit = %s
- working_hours_per_day = %s
- working_days_per_week = %s
- working_days_per_month = %s
- working_days_per_year = %s
- vacation = %s
- working_days = %s
-'''%(phase.id, phase.name, start, minimum_time_unit, working_hours_per_day, working_days_per_week, working_days_per_month, working_days_per_year, vacation, working_days )
-
- parent = False
- task_ids = []
- todo_task_ids = task_pool.search(cr, uid, [('id', 'in', map(lambda x : x.id, phase.task_ids)),
- ('state', 'in', ['draft', 'open', 'pending'])
- ], order='sequence')
- for task in task_pool.browse(cr, uid, todo_task_ids, context=context):
- func_str += task_pool.generate_task(cr, uid, task.id, parent=parent, flag=True, context=context)
- if not parent:
- parent = task
- task_ids.append(task.id)
- func_str += cls_str
- phase_id = phase.id
- #check known constraints before running Face algorithm in order to have the error translated
- if not phase.project_id.date_start:
- raise osv.except_osv(_('Error !'),_('Task Scheduling is not possible.\nProject should have the Start date for scheduling.'))
- # Allocating Memory for the required Project and Pahses and Resources
- exec(func_str)
- Phase = eval('Phase_%d' % phase.id)
- phase = None
- try:
- phase = Task.BalancedProject(Phase)
- except Exception, e:
- raise osv.except_osv(_('Error !'),e)
-
- for task_id in task_ids:
- task = eval("phase.Task_%d" % task_id)
- start_date = task.start.to_datetime()
- end_date = task.end.to_datetime()
-
- task_pool.write(cr, uid, [task_id], {
- 'date_start': start_date.strftime('%Y-%m-%d %H:%M:%S'),
- 'date_end': end_date.strftime('%Y-%m-%d %H:%M:%S')
- }, context=context)
- return True
+ return result
project_phase()
-class project_resource_allocation(osv.osv):
- _name = 'project.resource.allocation'
- _description = 'Project Resource Allocation'
- _rec_name = 'resource_id'
-
- def get_name(self, cr, uid, ids, field_name, arg, context=None):
- res = {}
- for allocation in self.browse(cr, uid, ids, context=context):
- name = allocation.phase_id.name
- name += ' (%s%%)' %(allocation.useability)
- res[allocation.id] = name
- return res
+class project_user_allocation(osv.osv):
+ _name = 'project.user.allocation'
+ _description = 'Phase User Allocation'
+ _rec_name = 'user_id'
_columns = {
- 'name': fields.function(get_name, type='char', size=256),
- 'resource_id': fields.many2one('resource.resource', 'Resource', required=True),
+ 'user_id': fields.many2one('res.users', 'User', required=True),
'phase_id': fields.many2one('project.phase', 'Project Phase', ondelete='cascade', required=True),
'project_id': fields.related('phase_id', 'project_id', type='many2one', relation="project.project", string='Project', store=True),
- 'user_id': fields.related('resource_id', 'user_id', type='many2one', relation="res.users", string='User'),
- 'date_start': fields.date('Start Date', help="Starting Date"),
- 'date_end': fields.date('End Date', help="Ending Date"),
- 'useability': fields.float('Availability', help="Availability of this resource for this project phase in percentage (=50%)"),
+ 'date_start': fields.datetime('Start Date', help="Starting Date"),
+ 'date_end': fields.datetime('End Date', help="Ending Date"),
}
- _defaults = {
- 'useability': 100,
- }
-
-project_resource_allocation()
+project_user_allocation()
class project(osv.osv):
_inherit = "project.project"
@@ -420,161 +226,104 @@ class project(osv.osv):
'phase_ids': fields.one2many('project.phase', 'project_id', "Project Phases"),
'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report", states={'close':[('readonly',True)]} ),
}
- def generate_members(self, cr, uid, ids, context=None):
- """
- Return a list of Resource Class objects for the resources allocated to the phase.
- """
- res = {}
- resource_pool = self.pool.get('resource.resource')
- for project in self.browse(cr, uid, ids, context=context):
- user_ids = map(lambda x:x.id, project.members)
- calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
- resource_objs = resource_pool.generate_resources(cr, uid, user_ids, calendar_id, context=context)
- res[project.id] = resource_objs
- return res
-
- def schedule_phases(self, cr, uid, ids, context=None):
- """
- Schedule phase base on faces lib
- """
- if context is None:
- context = {}
+ def _schedule_header(self, cr, uid, ids, context=None):
+ context = context or {}
if type(ids) in (long, int,):
ids = [ids]
- phase_pool = self.pool.get('project.phase')
- task_pool = self.pool.get('project.task')
+ projects = self.browse(cr, uid, ids, context=context)
+
+ for project in projects:
+ if not project.members:
+ raise osv.except_osv(_('Warning !'),_("You must assign members on the project '%s' !") % (project.name,))
+
resource_pool = self.pool.get('resource.resource')
- data_pool = self.pool.get('ir.model.data')
- resource_allocation_pool = self.pool.get('project.resource.allocation')
- data_model, day_uom_id = data_pool.get_object_reference(cr, uid, 'product', 'uom_day')
-
- #Checking the Valid Phase resource allocation from project member
- for project in self.browse(cr, uid, ids, context=context):
- flag = False
- res_missing = []
- members_ids = []
- if project.members:
- members_ids = [user.id for user in project.members]
- for phase in project.phase_ids:
- if phase.resource_ids:
- res_ids = [ re.id for re in phase.resource_ids]
- for res in resource_allocation_pool.browse(cr, uid, res_ids, context=context):
- if res.resource_id.user_id.id not in members_ids:
- res_missing += [res.resource_id.name]
- flag = True
- if flag:
- raise osv.except_osv(_('Warning !'),_("Resource(s) %s is(are) not member(s) of the project '%s' .") % (",".join(res_missing), project.name))
+ result = "from resource.faces import *\n"
+ result += "import datetime\n"
for project in self.browse(cr, uid, ids, context=context):
- root_phase_ids = phase_pool.search(cr, uid, [('project_id', '=', project.id),
- ('state', 'in', ['draft', 'open', 'pending']),
- ('previous_phase_ids', '=', False)
- ])
- calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
- start_date = project.date_start
- #if start_date:
- # start_date = datetime.strftime((datetime.strptime(start_date, "%Y-%m-%d")), "%Y-%m-%d")
- #Creating resources using the member of the Project
u_ids = [i.id for i in project.members]
+ for task in project.tasks:
+ if task.state in ('done','cancelled'):
+ continue
+ if task.user_id and (task.user_id.id not in u_ids):
+ u_ids.append(task.user_id.id)
+ calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
resource_objs = resource_pool.generate_resources(cr, uid, u_ids, calendar_id, context=context)
- func_str = ''
- start = start_date
- minimum_time_unit = 1
- # default values
- working_hours_per_day = 24
- working_days_per_week = 7
- working_days_per_month = 30
- working_days_per_year = 365
-
- vacation = []
- if calendar_id:
- working_hours_per_day = 8 #TODO: it should be come from calendars
- working_days_per_week = 5
- working_days_per_month = 20
- working_days_per_year = 200
- vacation = tuple(resource_pool.compute_vacation(cr, uid, calendar_id, context=context))
-
- working_days = resource_pool.compute_working_calendar(cr, uid, calendar_id, context=context)
-
- cls_str = ''
- # Creating Resources for the Project
for key, vals in resource_objs.items():
- cls_str +='''
- class Resource_%s(Resource):
- title = \"%s\"
- vacation = %s
- efficiency = %s
-'''%(key, vals.get('name',False), vals.get('vacation', False), vals.get('efficiency', False))
-
- # Create a new project for each phase
- func_str += '''
-def Project_%d():
- from resource.faces import Resource
+ result +='''
+class User_%s(Resource):
+ title = \"%s\"
+ efficiency = %s
+''' % (key, vals.get('name',False), vals.get('efficiency', False))
+
+ result += '''
+def Project():
+ '''
+ return result
+
+ def _schedule_project(self, cr, uid, project, context=None):
+ resource_pool = self.pool.get('resource.resource')
+ calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
+ working_days = resource_pool.compute_working_calendar(cr, uid, calendar_id, context=context)
+ # TODO: check if we need working_..., default values are ok.
+ result = """
+ def Project_%d():
title = \"%s\"
start = \'%s\'
- minimum_time_unit = %s
- working_hours_per_day = %s
- working_days_per_week = %s
- working_days_per_month = %s
- working_days_per_year = %s
+ working_days = %s
+ resource = %s
+""" % (
+ project.id, project.name,
+ project.date_start, working_days,
+ '|'.join(['User_'+str(x.id) for x in project.members])
+ )
+ vacation = calendar_id and tuple(resource_pool.compute_vacation(cr, uid, calendar_id, context=context)) or False
+ if vacation:
+ result+= """
vacation = %s
- working_days = %s
-'''%(project.id, project.name, start, minimum_time_unit, working_hours_per_day, working_days_per_week, working_days_per_month, working_days_per_year, vacation, working_days )
-
- func_str += cls_str
- phase_ids = []
- for root_phase in phase_pool.browse(cr, uid, root_phase_ids, context=context):
- phases, child_phase_ids = phase_pool.generate_phase(cr, uid, [root_phase.id], '', context=context)
- func_str += phases
- phase_ids += child_phase_ids
-
- project_id = project.id
- if not project.date_start:
- raise osv.except_osv(_('Error !'),_('Task Scheduling is not possible.\nProject should have the Start date for scheduling.'))
- # Allocating Memory for the required Project and Phases and Resources
- exec(func_str)
- Project = eval('Project_%d' % project.id)
- project = None
- try:
- project = Task.BalancedProject(Project)
- except Exception, e:
- raise osv.except_osv(_('Error !'), e)
-
- for phase_id in phase_ids:
- act_phase = phase_pool.browse(cr, uid, phase_id, context=context)
- resources = act_phase.resource_ids
- phase = eval("project.Phase_%d" % phase_id)
- start_date = phase.start.to_datetime()
- end_date = phase.end.to_datetime()
-
- if resources:
- for res in resources:
- vals = {}
- vals.update({'date_start' : start_date })
- vals.update({'date_end' : end_date})
- resource_allocation_pool.write(cr, uid, res.id, vals, context=context)
- if act_phase.task_ids:
- for task in act_phase.task_ids:
- vals = {}
- #Getting values of the Tasks
- temp = eval("phase.Task_%s"%task.id)
- if temp.booked_resource:
- res_name = temp.booked_resource[0].title
- res_id = resource_pool.search(cr, uid,[('name','=',res_name)], context = context)
- if res_id:
- res = resource_pool.browse(cr, uid, res_id[0], context = context)
- vals.update({'user_id' : res.user_id.id})
-
- vals.update({'date_start' : temp.start.strftime('%Y-%m-%d %H:%M:%S')})
- vals.update({'date_end' : temp.end.strftime('%Y-%m-%d %H:%M:%S')})
- task_pool.write(cr, uid, task.id, vals, context=context)
+""" % ( vacation, )
+ return result
- phase_pool.write(cr, uid, [phase_id], {
- 'date_start': start_date.strftime('%Y-%m-%d'),
- 'date_end': end_date.strftime('%Y-%m-%d')
- }, context=context)
- return True
+ def schedule_phases(self, cr, uid, ids, context=None):
+ context = context or {}
+ if type(ids) in (long, int,):
+ ids = [ids]
+ projects = self.browse(cr, uid, ids, context=context)
+ result = self._schedule_header(cr, uid, ids, context=context)
+ for project in projects:
+ result += self._schedule_project(cr, uid, project, context=context)
+ result += self.pool.get('project.phase').generate_phase(cr, uid, project.phase_ids, context=context)
+
+ local_dict = {}
+ exec result in local_dict
+ projects_gantt = Task.BalancedProject(local_dict['Project'])
+
+ for project in projects:
+ project_gantt = getattr(projects_gantt, 'Project_%d' % (project.id,))
+ for phase in project.phase_ids:
+ if phase.state in ('done','cancelled'):
+ continue
+ # Maybe it's better to update than unlink/create if it already exists ?
+ p = getattr(project_gantt, 'Phase_%d' % (phase.id,))
+
+ self.pool.get('project.user.allocation').unlink(cr, uid,
+ [x.id for x in phase.user_ids],
+ context=context
+ )
+
+ for r in p.booked_resource:
+ self.pool.get('project.user.allocation').create(cr, uid, {
+ 'user_id': int(r.name[5:]),
+ 'phase_id': phase.id,
+ 'date_start': p.start.strftime('%Y-%m-%d %H:%M:%S'),
+ 'date_end': p.end.strftime('%Y-%m-%d %H:%M:%S')
+ }, context=context)
+ self.pool.get('project.phase').write(cr, uid, [phase.id], {
+ 'date_start': p.start.strftime('%Y-%m-%d %H:%M:%S'),
+ 'date_end': p.end.strftime('%Y-%m-%d %H:%M:%S')
+ }, context=context)
+ return True
#TODO: DO Resource allocation and compute availability
def compute_allocation(self, rc, uid, ids, start_date, end_date, context=None):
@@ -584,186 +333,68 @@ def Project_%d():
return allocation
def schedule_tasks(self, cr, uid, ids, context=None):
- """
- Schedule task base on faces lib
- """
- if context is None:
- context = {}
+ context = context or {}
if type(ids) in (long, int,):
ids = [ids]
- task_pool = self.pool.get('project.task')
- resource_pool = self.pool.get('resource.resource')
- data_pool = self.pool.get('ir.model.data')
- data_model, day_uom_id = data_pool.get_object_reference(cr, uid, 'product', 'uom_day')
+ projects = self.browse(cr, uid, ids, context=context)
+ result = self._schedule_header(cr, uid, ids, context=context)
+ for project in projects:
+ result += self._schedule_project(cr, uid, project, context=context)
+ result += self.pool.get('project.task')._generate_task(cr, uid, project.tasks, ident=4, context=context)
- for project in self.browse(cr, uid, ids, context=context):
- calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
- start_date = project.date_start
-
- #Checking the Valid Phase resource allocation from project member
- flag = False
- res_missing = []
- members_ids = []
- if project.members:
- members_ids = [user.id for user in project.members]
- for phase in project.phase_ids:
- if phase.resource_ids:
- res_ids = [ re.id for re in phase.resource_ids]
- for res in self.pool.get('project.resource.allocation').browse(cr, uid, res_ids, context=context):
- if res.resource_id.user_id.id not in members_ids:
- res_missing += [res.resource_id.name]
- flag = True
- if flag:
- raise osv.except_osv(_('Warning !'),_("Resource(s) %s is(are) not member(s) of the project '%s' .") % (",".join(res_missing), project.name))
- #Creating resources using the member of the Project
- u_ids = [i.id for i in project.members]
- resource_objs = resource_pool.generate_resources(cr, uid, u_ids, calendar_id, context=context)
- try:
- start_date = datetime.strftime((datetime.strptime(start_date, "%Y-%m-%d")), "%Y-%m-%d")
- except:
- raise osv.except_osv(_('Error !'),_('Task Scheduling is not possible.\nProject should have the Start date for scheduling.'))
- func_str = ''
- start = start_date
- minimum_time_unit = 1
- # default values
- working_hours_per_day = 24
- working_days_per_week = 7
- working_days_per_month = 30
- working_days_per_year = 365
-
- vacation = []
- if calendar_id:
- working_hours_per_day = 8 #TODO: it should be come from calendars
- working_days_per_week = 5
- working_days_per_month = 20
- working_days_per_year = 200
- vacation = tuple(resource_pool.compute_vacation(cr, uid, calendar_id, context=context))
+ local_dict = {}
+ exec result in local_dict
+ projects_gantt = Task.BalancedProject(local_dict['Project'])
- working_days = resource_pool.compute_working_calendar(cr, uid, calendar_id, context=context)
-
- cls_str = ''
- # Creating Resources for the Project
- for key, vals in resource_objs.items():
- cls_str +='''
- class Resource_%s(Resource):
- title = \"%s\"
- vacation = %s
- efficiency = %s
-'''%(key, vals.get('name',False), vals.get('vacation', False), vals.get('efficiency', False))
-
- # Create a new project for each phase
- func_str += '''
-def Project_%d():
- from resource.faces import Resource
- title = \"%s\"
- start = \'%s\'
- minimum_time_unit = %s
- working_hours_per_day = %s
- working_days_per_week = %s
- working_days_per_month = %s
- working_days_per_year = %s
- vacation = %s
- working_days = %s
-'''%(project.id, project.name, start, minimum_time_unit, working_hours_per_day, working_days_per_week, working_days_per_month, working_days_per_year, vacation, working_days )
-
- parent = False
- task_ids = []
- todo_task_ids = task_pool.search(cr, uid, [('project_id', '=', project.id),
- ('state', 'in', ['draft', 'open', 'pending'])
- ], order='sequence')
- if todo_task_ids:
- for task in task_pool.browse(cr, uid, todo_task_ids, context=context):
- func_str += task_pool.generate_task(cr, uid, task.id, parent=parent, flag=True,context=context)
- if not parent:
- parent = task
- task_ids.append(task.id)
- func_str += cls_str
+ for project in projects:
+ project_gantt = getattr(projects_gantt, 'Project_%d' % (project.id,))
+ for task in project.tasks:
+ if task.state in ('done','cancelled'):
+ continue
- if not project.date_start:# or not project.members:
- raise osv.except_osv(_('Error !'),_('Task Scheduling is not possible.\nProject should have the Start date for scheduling.'))
- # Allocating Memory for the required Project and Phases and Resources
- exec(func_str)
- Project = eval('Project_%d' % project.id)
- project = None
- try:
- project = Task.BalancedProject(Project)
- except Exception, e:
- raise osv.except_osv(_('Error !'), e)
-
- for task_id in task_ids:
- task = eval("project.Task_%d" % task_id)
- start_date = task.start.to_datetime()
- end_date = task.end.to_datetime()
-
- task_pool.write(cr, uid, [task_id], {
- 'date_start': start_date.strftime('%Y-%m-%d %H:%M:%S'),
- 'date_end': end_date.strftime('%Y-%m-%d %H:%M:%S')
- }, context=context)
+ p = getattr(project_gantt, 'Task_%d' % (task.id,))
+
+ self.pool.get('project.task').write(cr, uid, [task.id], {
+ 'date_start': p.start.strftime('%Y-%m-%d %H:%M:%S'),
+ 'date_end': p.end.strftime('%Y-%m-%d %H:%M:%S')
+ }, context=context)
+ if (not task.user_id) and (p.booked_resource):
+ self.pool.get('project.task').write(cr, uid, [task.id], {
+ 'user_id': int(p.booked_resource[0].name[5:]),
+ }, context=context)
return True
-
project()
-class resource_resource(osv.osv):
- _inherit = "resource.resource"
- def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
- if context is None:
- context = {}
- if context.get('project_id',False):
- project_pool = self.pool.get('project.project')
- project_rec = project_pool.browse(cr, uid, context['project_id'], context=context)
- user_ids = [user_id.id for user_id in project_rec.members]
- args.append(('user_id','in',user_ids))
- return super(resource_resource, self).search(cr, uid, args, offset, limit, order, context, count)
-
-resource_resource()
-
class project_task(osv.osv):
_inherit = "project.task"
_columns = {
'phase_id': fields.many2one('project.phase', 'Project Phase'),
}
- _defaults = {
- 'user_id' : False
- }
+ def _generate_task(self, cr, uid, tasks, ident=4, context=None):
+ context = context or {}
+ result = ""
+ ident = ' '*ident
+ for task in tasks:
+ if task.state in ('done','cancelled'):
+ continue
+ result += '''
+%sdef Task_%s():
+%s title = \"%s\"
+%s todo = \"%.2fH\"
+%s effort = \"%.2fH\"''' % (ident,task.id, ident,task.name, ident,task.remaining_hours, ident,task.total_hours)
+ start = []
+ for t2 in task.parent_ids:
+ start.append("up.Task_%s.end" % (t2.id,))
+ if start:
+ result += '''
+%s start = max(%s)
+''' % (ident,','.join(start))
- def generate_task(self, cr, uid, task_id, parent=False, flag=False, context=None):
- if context is None:
- context = {}
- task = self.browse(cr, uid, task_id, context=context)
- duration = str(task.planned_hours)+ 'H'
- str_resource = False
- parent = task.parent_ids
- if task.phase_id.resource_ids:
- str_resource = ('%s | '*len(task.phase_id.resource_ids))[:-2]
- str_resource = str_resource % tuple(map(lambda x: 'Resource_%s'%x.resource_id.id, task.phase_id.resource_ids))
- # Task Defination for the Phase of the Project
- if not flag:
- s = '''
- def Task_%s():
- title = \"%s\"
- effort = \'%s\'
- resource = %s
-'''%(task.id, task.name, duration, str_resource)
- if task.child_ids:
- seq = [[child.planned_hours, child.id] for child in task.child_ids]
- seq.sort(key=itemgetter(0))
- s +='''
- start = up.Task_%s.end
- '''%(seq[-1][1])
- else:
- s = '''
- def Task_%s():
- title = \"%s\"
- effort = \'%s\'
- resource = %s
-'''%(task.id, task.name, duration, str_resource)
- if task.child_ids:
- seq = [[child.planned_hours, child.id] for child in task.child_ids]
- seq.sort(key=itemgetter(0))
- s +='''
- start = up.Task_%s.end
-'''%(seq[-1][1])
- s += '\n'
- return s
+ if task.user_id:
+ result += '''
+%s resource = %s
+''' % (ident, 'User_'+str(task.user_id.id))
+
+ result += "\n"
+ return result
project_task()
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/project_long_term/project_long_term_demo.xml b/addons/project_long_term/project_long_term_demo.xml
index 961b0c62c9b..252bcdd0e9d 100644
--- a/addons/project_long_term/project_long_term_demo.xml
+++ b/addons/project_long_term/project_long_term_demo.xml
@@ -1,55 +1,9 @@
-
-
-
-
- Analyst
-
-
-
-
-
- Project Manager
-
-
-
-
-
- Technical Leader
-
-
-
-
-
- Developer
-
-
-
-
-
- Designer
-
-
-
-
-
- Tester
-
-
-
-
-
- Project Finacial Manager
-
-
-
-
-
-
+
@@ -58,7 +12,6 @@ This Demo data file Human Resources, Phases and Resources,Tasks allocation and
30
-
@@ -68,7 +21,6 @@ This Demo data file Human Resources, Phases and Resources,Tasks allocation and
20
-
@@ -78,7 +30,6 @@ This Demo data file Human Resources, Phases and Resources,Tasks allocation and
20
-
@@ -88,7 +39,6 @@ This Demo data file Human Resources, Phases and Resources,Tasks allocation and
90
-
@@ -98,7 +48,6 @@ This Demo data file Human Resources, Phases and Resources,Tasks allocation and
30
-
@@ -108,56 +57,9 @@ This Demo data file Human Resources, Phases and Resources,Tasks allocation and
10
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/addons/project_long_term/project_long_term_view.xml b/addons/project_long_term/project_long_term_view.xml
index 49b306ebfd5..b5af969f554 100644
--- a/addons/project_long_term/project_long_term_view.xml
+++ b/addons/project_long_term/project_long_term_view.xml
@@ -5,29 +5,29 @@
# ------------------------------------------------------
- # Project Resource Allocation
+ # Project User Allocation
# ------------------------------------------------------
-
- project.resource.allocation.gantt
- project.resource.allocation
+
+ project.user.allocation.gantt
+ project.user.allocationgantt
-
+
-
- project.resource.allocation.calendar
- project.resource.allocation
+
+ project.user.allocation.calendar
+ project.user.allocationcalendar
-
+
@@ -35,53 +35,51 @@
-
- project.resource.allocation.form
- project.resource.allocation
+
+ project.user.allocation.form
+ project.user.allocationform
-
-
- project.resource.allocation.list
- project.resource.allocation
+
+ project.user.allocation.list
+ project.user.allocationtree
-
-
+
+
-
-
- project.resource.allocation.search
- project.resource.allocation
+
+ project.user.allocation.search
+ project.user.allocationsearch
-
+
-
+
-
+
@@ -90,12 +88,12 @@
- Resources Allocation
- project.resource.allocation
+ Users Allocation
+ project.user.allocationformgantt,tree,form,calendar{}
-
+
@@ -110,32 +108,27 @@
@@ -241,7 +205,6 @@
-
@@ -275,14 +238,11 @@
-
-
-
@@ -300,7 +260,7 @@
gantt,tree,form,calendar{"search_default_current": 1}
- A project can be split into the different phases. For each phase, you can define your resources allocation, describe different tasks and link your phase to previous and next phases, add date constraints for the automated scheduling. Use the long term planning in order to planify your available human resources, convert your phases into a series of tasks when you start working on the project.
+ A project can be split into the different phases. For each phase, you can define your users allocation, describe different tasks and link your phase to previous and next phases, add date constraints for the automated scheduling. Use the long term planning in order to planify your available users, convert your phases into a series of tasks when you start working on the project.
@@ -317,7 +277,7 @@
# Project
# ------------------------------------------------------
-
+ Project Resource Calendar Viewproject.projectform
@@ -402,17 +362,6 @@
view_type="form"
/>
-
-
# ------------------------------------------------------
# Menu Items
# ------------------------------------------------------
@@ -428,16 +377,16 @@
+ name="Users Allocations" parent="base.menu_project_long_term" sequence="2"/>
-
+ name="Users" parent="project.menu_definitions" sequence="3"/>
-
-
-
+
+
+
diff --git a/addons/project_long_term/security/ir.model.access.csv b/addons/project_long_term/security/ir.model.access.csv
index 6d4961f2cfe..4382d27834e 100644
--- a/addons/project_long_term/security/ir.model.access.csv
+++ b/addons/project_long_term/security/ir.model.access.csv
@@ -1,9 +1,9 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_project_phase","project.phase","model_project_phase","project.group_project_user",1,1,1,0
-"access_project_resource_allocation","project.resource.allocation","model_project_resource_allocation","project.group_project_user",1,0,0,0
+"access_project_user_allocation","project.user.allocation","model_project_user_allocation","project.group_project_user",1,0,0,0
"access_project_phase_manager","project.phase manager","model_project_phase","project.group_project_manager",1,1,1,1
-"access_project_resource_allocation_manager","project.resource.allocation manager","model_project_resource_allocation","project.group_project_manager",1,1,1,1
-"access_resource_resource_user","resource.resource user","resource.model_resource_resource","project.group_project_user",1,0,0,0
-"access_resource_calendar_leaves_user","resource.calendar.leaves user","resource.model_resource_calendar_leaves","project.group_project_user",1,1,1,1
-"access_resource_resource_manager","resource.resource manager","resource.model_resource_resource","project.group_project_manager",1,1,1,1
-"access_project_resource_allocation_manager","project.resource.allocation.manager","model_project_resource_allocation","project.group_project_manager",1,1,1,1
+"access_project_user_allocation_manager","project.user.allocation manager","model_project_user_allocation","project.group_project_manager",1,1,1,1
+"access_resource_resource_user","user.user user","resource.model_resource_resource","project.group_project_user",1,0,0,0
+"access_resource_calendar_leaves_user","user.calendar.leaves user","resource.model_resource_calendar_leaves","project.group_project_user",1,1,1,1
+"access_resource_resource_manager","user.user manager","resource.model_resource_resource","project.group_project_manager",1,1,1,1
+"access_project_user_allocation_manager","project.user.allocation.manager","model_project_user_allocation","project.group_project_manager",1,1,1,1
diff --git a/addons/project_long_term/test/phase_constraint.yml b/addons/project_long_term/test/phase_constraint.yml
index ad1a3db095e..f4fa09e6e38 100644
--- a/addons/project_long_term/test/phase_constraint.yml
+++ b/addons/project_long_term/test/phase_constraint.yml
@@ -5,6 +5,8 @@
name: "Develop Yaml Project Module"
date_start: !eval time.strftime('%Y-%m-%d')
resource_calendar_id: resource.timesheet_group1
+ members:
+ - base.user_admin
-
And I create three human ressources to work on this project, an analyst.
-
@@ -43,7 +45,6 @@
name: "Analysis"
product_uom: product.uom_day
project_id: project_project_developyamlproject0
- responsible_id: project.res_users_analyst
state: draft
-
diff --git a/addons/project_long_term/test/project_schedule_consecutive_day.yml b/addons/project_long_term/test/project_schedule_consecutive_day.yml
index 10cfc7b6b9c..cffe06a9ff9 100644
--- a/addons/project_long_term/test/project_schedule_consecutive_day.yml
+++ b/addons/project_long_term/test/project_schedule_consecutive_day.yml
@@ -4,6 +4,8 @@
!record {model: project.project, id: project_project_developyamlproject_2}:
name: "Develop Yaml Project Module"
date_start: !eval time.strftime('%Y-%m-%d')
+ members:
+ - base.user_admin
-
I have set Working Time from Monday to Friday from 9am to 17pm.
@@ -107,7 +109,6 @@
!record {model: project.compute.phases, id: project_compute_phases_0}:
project_id: project_project_developyamlproject_2
target_project: one
-
-
Schedule project phases using Compute Phase Scheduling
-
@@ -120,7 +121,7 @@
!python {model: project.project}: |
proj=self.browse(cr, uid, [ref("project_project_developyamlproject_2")])[0]
for phase in proj.phase_ids:
- if (not phase.responsible_id) or (not phase.date_start) or (not phase.date_end):
- raise AssertionError("Phases not scheduled")
-
+ if (not phase.date_start) or (not phase.date_end):
+ raise AssertionError("Phases not scheduled")
+
diff --git a/addons/project_long_term/test/project_schedule_without_wroking_hour.yml b/addons/project_long_term/test/project_schedule_without_wroking_hour.yml
index 61a8539585a..4fe7687425b 100644
--- a/addons/project_long_term/test/project_schedule_without_wroking_hour.yml
+++ b/addons/project_long_term/test/project_schedule_without_wroking_hour.yml
@@ -15,7 +15,6 @@
product_uom: product.uom_day
name: "Analysis Flow for Yaml"
project_id: project_project_developyamlproject_2
- responsible_id: project.res_users_analyst
state: draft
-
@@ -71,7 +70,7 @@
!python {model: project.project}: |
proj=self.browse(cr, uid, [ref("project_project_developyamlproject_2")])[0]
for phase in proj.phase_ids:
- if (not phase.responsible_id) or (not phase.date_start) or (not phase.date_end):
+ if (not phase.date_start) or (not phase.date_end):
raise AssertionError("Phases not scheduled")
diff --git a/addons/project_long_term/test/schedule_phase_tasks.yml b/addons/project_long_term/test/schedule_phase_tasks.yml
deleted file mode 100644
index be22f778cee..00000000000
--- a/addons/project_long_term/test/schedule_phase_tasks.yml
+++ /dev/null
@@ -1,90 +0,0 @@
--
- Create project 'Develop an outlook-openerp synchronization plugin'
--
- !record {model: project.project, id: project_project_project4}:
- name: Develop an outlook-openerp synchronization plugin
- members:
- - project.res_users_project_manager
- - project.res_users_technical_leader
- - project.res_users_developer
- - project.res_users_designer
- - project.res_users_tester
- - project.res_users_analyst
- - project.res_users_finacial_manager
--
- Create phase 'Develop GUI' in Outlook
--
- !record {model: project.phase, id: project_phase_phase0}:
- date_start: !eval time.strftime("%Y-%m-%d")
- duration: 200.0
- name: Develop GUI in Outlook
- product_uom: product.uom_day
- project_id: project_project_project4
- state: draft
- resource_ids:
- - resource_id: project_long_term.resource_analyst
- - resource_id: project_long_term.resource_developer
- - resource_id: project_long_term.resource_designer
- - resource_id: project_long_term.resource_tester
- - resource_id: project_long_term.resource_project_manager
-
--
- Create the phase task 'Develop GUI for Server Configuration'
--
- !record {model: project.task, id: project_task_t0}:
- name: Develop GUI for Server Configuration
- planned_hours: 20.0
- remaining_hours: 20.0
- state: draft
- phase_id: project_phase_phase0
- project_id: project_project_project4
-
-
--
- Create the phase task 'Develop GUI for Modules Configuration'
--
- !record {model: project.task, id: project_task_t1}:
- name: Develop GUI for Modules Configuration
- planned_hours: 25.0
- remaining_hours: 25.0
- state: draft
- phase_id: project_phase_phase0
- project_id: project_project_project4
-
--
- Create the phase task 'Develop GUI for OpenERP Synchronisation'
--
- !record {model: project.task, id: project_task_t2}:
- name: Develop GUI for OpenERP Synchronisation
- planned_hours: 30.0
- remaining_hours: 30.0
- state: draft
- phase_id: project_phase_phase0
- project_id: project_project_project4
-
--
- Create the phase task 'Design required GUI/Menus'
--
- !record {model: project.task, id: project_task_3}:
- name: Design required GUI/Menus
- planned_hours: 25.0
- remaining_hours: 25.0
- state: draft
- phase_id: project_phase_phase0
- project_id: project_project_project4
-
--
- Schedule phase tasks
--
- !python {model: project.phase}: |
- res = self.schedule_tasks(cr, uid, [ref("project_phase_phase0")])
-
--
- Check if tasks scheduled, check that either of task's start_date, end_date and user_id is not null
--
- !python {model: project.phase}: |
- phase = self.browse(cr, uid, [ref("project_phase_phase0")])[0]
- for task in phase.task_ids:
- if (not task.date_start) or (not task.date_end):
- raise AssertionError("Phase Tasks not scheduled: %d uid=%r start=%r end=%r" % \
- (task.id, task.user_id, task.date_start, task.date_end))
diff --git a/addons/project_long_term/test/schedule_project_phases.yml b/addons/project_long_term/test/schedule_project_phases.yml
index 4bc6549bf35..550787fe867 100644
--- a/addons/project_long_term/test/schedule_project_phases.yml
+++ b/addons/project_long_term/test/schedule_project_phases.yml
@@ -31,28 +31,6 @@
name: Establishing Project Feasibility
product_uom: product.uom_day
project_id: project_project_worldbanksproject0
-
--
- Resource1
--
- !record {model: project.resource.allocation, id: res_phase1}:
- resource_id: project_long_term.resource_project_manager
- phase_id: project_phase_definingclientsbasicideaofproject0
-
--
- Resource2
--
- !record {model: project.resource.allocation, id: res_phase2}:
- resource_id: project_long_term.resource_analyst
- phase_id: project_phase_definingclientsbasicideaofproject0
-
--
- Resource3
--
- !record {model: project.resource.allocation, id: res_phase3}:
- resource_id: project_long_term.resource_technical_leader
- phase_id: project_phase_definingclientsbasicideaofproject0
-
-
Create the phase task 'Develop GUI for Server Configuration'
-
@@ -84,20 +62,6 @@
product_uom: product.uom_hour
project_id: project_project_worldbanksproject0
--
- Resource4
--
- !record {model: project.resource.allocation, id: res_phase4}:
- resource_id: project_long_term.resource_developer
- phase_id: project_phase_preparationofengineeringdesigns0
-
--
- Resource5
--
- !record {model: project.resource.allocation, id: res_phase5}:
- resource_id: project_long_term.resource_designer
- phase_id: project_phase_preparationofengineeringdesigns0
-
-
Create the phase task 'Develop GUI for Client Configuration'
-
@@ -210,7 +174,7 @@
!python {model: project.project}: |
prj=self.browse(cr, uid, [ref("project_project_worldbanksproject0")])[0]
for phase in prj.phase_ids:
- if (not phase.responsible_id) or (not phase.date_start) or (not phase.date_end):
- raise AssertionError("Tasks not scheduled")
+ if (not phase.date_start) or (not phase.date_end):
+ raise AssertionError("Tasks not scheduled")
diff --git a/addons/project_long_term/test/schedule_project_tasks.yml b/addons/project_long_term/test/schedule_project_tasks.yml
index 825b7c404d9..17db7ef8038 100644
--- a/addons/project_long_term/test/schedule_project_tasks.yml
+++ b/addons/project_long_term/test/schedule_project_tasks.yml
@@ -11,26 +11,17 @@
- project.res_users_tester
- project.res_users_analyst
- project.res_users_finacial_manager
-
+
-
Create phase 'Develop GUI' in thunderbird
-
!record {model: project.phase, id: project_phase_phase1}:
- date_start: !eval time.strftime('%Y-%m-%d')
+ date_start: !eval time.strftime('%Y-%m-%d %H:%M:%S')
duration: 200.0
name: Develop GUI in thunderbird
product_uom: product.uom_day
project_id: project_project_project1
state: draft
- resource_ids:
- - resource_id: project_long_term.resource_analyst
- useability: 80.0
- - resource_id: project_long_term.resource_developer
- useability: 30.0
- - resource_id: project_long_term.resource_designer
- useability: 30.0
-
-
-
Create the phase task 'Develop GUI for Server Configuration'
-
@@ -76,8 +67,7 @@
phase_id: project_phase_phase1
project_id: project_project_project1
user_id: project.res_users_designer
-
-
+
-
Schedule tasks
-
@@ -88,7 +78,9 @@
Check if tasks scheduled, check that either of task's start_date, end_date and user_id is not null
-
!python {model: project.project}: |
- prj = self.browse(cr, uid, [ref("project_project_project1")])[0]
- for task in prj.tasks:
- if (not task.user_id) or (not task.date_start) or (not task.date_end):
- raise AssertionError("Project Tasks not scheduled")
+ prj = self.browse(cr, uid, [ref("project_project_project1")])[0]
+ for task in prj.tasks:
+ if task.state in ('done','cancelled'):
+ continue
+ if (not task.user_id) or (not task.date_start) or (not task.date_end):
+ raise AssertionError("Project tasks not scheduled!")
diff --git a/addons/project_long_term/test/test_schedule_phases_case1.yml b/addons/project_long_term/test/test_schedule_phases_case1.yml
index ba091a29916..ef8d0410439 100644
--- a/addons/project_long_term/test/test_schedule_phases_case1.yml
+++ b/addons/project_long_term/test/test_schedule_phases_case1.yml
@@ -63,10 +63,12 @@
import datetime
from dateutil.relativedelta import *
start = (datetime.date.today()).strftime('%Y-%m-%d')
- end = (datetime.date.today() + relativedelta(days=2)).strftime('%Y-%m-%d')
+ end = (datetime.date.today() + relativedelta(days=1)).strftime('%Y-%m-%d 17:00:00')
first_phase = self.browse(cr, uid, ref('project_phase_firstphase0_case1'))
- assert (first_phase.date_start == start and first_phase.date_end == end),'Dates are wrong!'
+ assert (first_phase.date_start[:10] == start),'Start dates are wrong: %s != %s !' % (first_phase.date_start, start)
+ assert (first_phase.date_end >= end),'End dates are wrong: %s <%s!' % (first_phase.date_end, end)
+
second_phase = self.browse(cr, uid, ref('project_phase_secondphase0_case2'))
start = first_phase.date_end
end = (datetime.date.today() + relativedelta(days=5)).strftime('%Y-%m-%d')
- assert (second_phase.date_start == start and second_phase.date_end == end),'Dates are wrong!'
+ assert (second_phase.date_start >= start and second_phase.date_end >= end),'Dates are wrong on second phase!'
diff --git a/addons/project_long_term/test/test_schedule_phases_case2.yml b/addons/project_long_term/test/test_schedule_phases_case2.yml
index 67a7332cf5b..35173a897a3 100644
--- a/addons/project_long_term/test/test_schedule_phases_case2.yml
+++ b/addons/project_long_term/test/test_schedule_phases_case2.yml
@@ -147,7 +147,7 @@
I create a phase 'Initial Phase' for project 'Development and Testing'.
-
!record {model: project.phase, id: project_phase_phase0}:
- date_start: '2011-01-03'
+ date_start: '2011-01-03 08:00:00'
duration: 0.0
name: Initial Phase
product_uom: product.uom_day
diff --git a/addons/project_long_term/test/test_schedule_tasks_case1.yml b/addons/project_long_term/test/test_schedule_tasks_case1.yml
index d40b61d6b3a..3bb0ea87c43 100644
--- a/addons/project_long_term/test/test_schedule_tasks_case1.yml
+++ b/addons/project_long_term/test/test_schedule_tasks_case1.yml
@@ -38,7 +38,7 @@
- res_users_user0
- res_users_user1
phase_ids:
- - date_start: '2011-01-06'
+ - date_start: '2011-01-06 08:00:00'
duration: 2.0
name: Phase A
product_uom: product.uom_day
@@ -184,16 +184,11 @@
Now I create one phase for the project.
-
!record {model: project.phase, id: project_phase_phasea0}:
- date_start: '2011-01-06'
+ date_start: '2011-01-06 08:00:00'
duration: 2.0
name: Phase A
product_uom: product.uom_day
project_id: project_project_projecta0
- resource_ids:
- - project_id: project_project_projecta0
- resource_id: resource_resource_user0
- - project_id: project_project_projecta0
- resource_id: resource_resource_user1
state: draft
task_ids:
- delay_hours: 0.0
diff --git a/addons/project_long_term/wizard/__init__.py b/addons/project_long_term/wizard/__init__.py
index c1e6d838111..4536f4a3cbb 100644
--- a/addons/project_long_term/wizard/__init__.py
+++ b/addons/project_long_term/wizard/__init__.py
@@ -21,6 +21,5 @@
import project_compute_phases
import project_compute_tasks
-import project_schedule_tasks
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/project_long_term/wizard/project_compute_phases.py b/addons/project_long_term/wizard/project_compute_phases.py
index 7e84f0681cc..57a1b3b6ed3 100644
--- a/addons/project_long_term/wizard/project_compute_phases.py
+++ b/addons/project_long_term/wizard/project_compute_phases.py
@@ -25,21 +25,19 @@ class project_compute_phases(osv.osv_memory):
_name = 'project.compute.phases'
_description = 'Project Compute Phases'
_columns = {
- 'target_project': fields.selection([('all', 'Compute All Projects'),
- ('one', 'Compute a Single Project'),
- ], 'Schedule', required = True),
-
+ 'target_project': fields.selection([
+ ('all', 'Compute All My Projects'),
+ ('one', 'Compute a Single Project'),
+ ], 'Schedule', required=True),
'project_id': fields.many2one('project.project', 'Project')
}
-
_defaults = {
- 'target_project': 'all'
+ 'target_project': 'one'
}
def check_selection(self, cr, uid, ids, context=None):
return self.compute_date(cr, uid, ids, context=context)
-
def compute_date(self, cr, uid, ids, context=None):
"""
Compute the phases for scheduling.
@@ -47,12 +45,12 @@ class project_compute_phases(osv.osv_memory):
project_pool = self.pool.get('project.project')
data = self.read(cr, uid, ids, [], context=context)[0]
if not data['project_id'] and data['target_project'] == 'one':
- raise osv.except_osv(_('Error!'), _('Please Specify Project to be schedule'))
+ raise osv.except_osv(_('Error!'), _('Please specify a project to schedule.'))
- if data['project_id']: # If project mentioned find its phases
+ if data['target_project'] == 'one':
project_ids = [data['project_id'][0]]
- else: # Else take all the draft,open,pending states phases
- project_ids = project_pool.search(cr, uid, [], context=context)
+ else:
+ project_ids = project_pool.search(cr, uid, [('user_id','=',uid)], context=context)
project_pool.schedule_phases(cr, uid, project_ids, context=context)
return self._open_phases_list(cr, uid, data, context=context)
@@ -70,8 +68,7 @@ class project_compute_phases(osv.osv_memory):
result = act_obj.read(cr, uid, [id], context=context)[0]
result['target'] = 'current'
project_id = data.get('project_id') and data.get('project_id')[0] or False
- result['context'] = {"search_default_project_id":project_id, "default_project_id":project_id, "search_default_responsible_id":uid, "search_default_current": 1}
+ result['context'] = {"search_default_project_id":project_id, "default_project_id":project_id, "search_default_current": 1}
return result
project_compute_phases()
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/project_long_term/wizard/project_compute_phases_view.xml b/addons/project_long_term/wizard/project_compute_phases_view.xml
index 69b7d94eabd..c70e46583a7 100644
--- a/addons/project_long_term/wizard/project_compute_phases_view.xml
+++ b/addons/project_long_term/wizard/project_compute_phases_view.xml
@@ -2,42 +2,42 @@
-
+ Compute Scheduling of Phasesproject.compute.phasesform
-
+
-
+
-
-
+
+
-
-
+
+
-
+
-
- Compute Phase Scheduling
- ir.actions.act_window
- project.compute.phases
- form
- form
-
- new
- To schedule phases of all or a specified project. It then open a gantt view.
-
-
+
+ Compute Phase Scheduling
+ ir.actions.act_window
+ project.compute.phases
+ form
+ form
+
+ new
+ To schedule phases of all or a specified project. It then open a gantt view.
+
+
-
+
diff --git a/addons/project_long_term/wizard/project_compute_tasks.py b/addons/project_long_term/wizard/project_compute_tasks.py
index a74cd07ddc6..24240f90a54 100644
--- a/addons/project_long_term/wizard/project_compute_tasks.py
+++ b/addons/project_long_term/wizard/project_compute_tasks.py
@@ -30,7 +30,7 @@ class project_compute_tasks(osv.osv_memory):
def compute_date(self, cr, uid, ids, context=None):
"""
- Schedule the tasks according to resource available and priority.
+ Schedule the tasks according to users and priority.
"""
project_pool = self.pool.get('project.project')
task_pool = self.pool.get('project.task')
diff --git a/addons/project_long_term/wizard/project_compute_tasks_view.xml b/addons/project_long_term/wizard/project_compute_tasks_view.xml
index e679110acdc..74ce7ab88e5 100644
--- a/addons/project_long_term/wizard/project_compute_tasks_view.xml
+++ b/addons/project_long_term/wizard/project_compute_tasks_view.xml
@@ -2,39 +2,39 @@
-
+ Compute Task Schedulingproject.compute.tasksform
-
-
-
-
+
+
+
+
-
-
-
-
+
+
+
+
-
+
-
- Compute Task Scheduling
- ir.actions.act_window
- project.compute.tasks
- form
- form
-
- new
-
+
+ Compute Task Scheduling
+ ir.actions.act_window
+ project.compute.tasks
+ form
+ form
+
+ new
+
-
+
diff --git a/addons/project_long_term/wizard/project_schedule_tasks.py b/addons/project_long_term/wizard/project_schedule_tasks.py
deleted file mode 100644
index d446ef20183..00000000000
--- a/addons/project_long_term/wizard/project_schedule_tasks.py
+++ /dev/null
@@ -1,51 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2009 Tiny SPRL ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
-
-from osv import fields, osv
-
-class project_schedule_task(osv.osv_memory):
- _name = "project.schedule.tasks"
- _description = 'project.schedule.tasks'
- _columns = {
- 'msg': fields.char('Message', size=64)
- }
- _defaults = {
- 'msg': 'Task Scheduling Completed Successfully'
- }
-
- def default_get(self, cr, uid, fields_list, context=None):
- res = super(project_schedule_task, self).default_get(cr, uid, fields_list, context)
- self.compute_date(cr, uid, context=context)
- return res
-
- def compute_date(self, cr, uid, context=None):
- """
- Schedule the tasks according to resource available and priority.
- """
- phase_pool = self.pool.get('project.phase')
- if context is None:
- context = {}
- if not 'active_id' in context:
- return {'type': 'ir.actions.act_window_close'}
- return phase_pool.schedule_tasks(cr, uid, [context['active_id']], context=context)
-project_schedule_task()
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/project_long_term/wizard/project_schedule_tasks_view.xml b/addons/project_long_term/wizard/project_schedule_tasks_view.xml
deleted file mode 100644
index f8e2f2d828b..00000000000
--- a/addons/project_long_term/wizard/project_schedule_tasks_view.xml
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
- Schedule Tasks
- project.schedule.tasks
- form
-
-
-
-
-
-
-
-
-
-
-
-
-
- Schedule Tasks
- ir.actions.act_window
- project.schedule.tasks
- form
- form
-
- {'record_id': active_id}
- new
-
-
-
-
diff --git a/addons/resource/resource.py b/addons/resource/resource.py
index 932e9cd4855..f90a402946b 100644
--- a/addons/resource/resource.py
+++ b/addons/resource/resource.py
@@ -265,28 +265,20 @@ class resource_resource(osv.osv):
resource_objs = {}
user_pool = self.pool.get('res.users')
for user in user_pool.browse(cr, uid, user_ids, context=context):
+ resource_objs[user.id] = {
+ 'name' : user.name,
+ 'vacation': [],
+ 'efficiency': 1.0,
+ }
+
resource_ids = self.search(cr, uid, [('user_id', '=', user.id)], context=context)
- #assert len(resource_ids) < 1, "User should not has more than one resources"
- leaves = []
- resource_eff = 1.0
if resource_ids:
for resource in self.browse(cr, uid, resource_ids, context=context):
- resource_eff = resource.time_efficiency
+ resource_objs[user.id]['efficiency'] = resource.time_efficiency
resource_cal = resource.calendar_id.id
if resource_cal:
leaves = self.compute_vacation(cr, uid, calendar_id, resource.id, resource_cal, context=context)
- temp = {
- 'name' : resource.name,
- 'vacation': tuple(leaves),
- 'efficiency': resource_eff,
- }
- resource_objs[resource.id] = temp
-# resource_objs.append(classobj(str(user.name), (Resource,),{
-# '__doc__': user.name,
-# '__name__': user.name,
-# 'vacation': tuple(leaves),
-# 'efficiency': resource_eff,
-# }))
+ resource_objs[user.id]['vacation'] += list(leaves)
return resource_objs
def compute_vacation(self, cr, uid, calendar_id, resource_id=False, resource_calendar=False, context=None):
@@ -323,8 +315,8 @@ class resource_resource(osv.osv):
"""
if not calendar_id:
# Calendar is not specified: working days: 24/7
- return [('fri', '1:0-12:0','12:0-24:0'), ('thu', '1:0-12:0','12:0-24:0'), ('wed', '1:0-12:0','12:0-24:0'),
- ('mon', '1:0-12:0','12:0-24:0'), ('tue', '1:0-12:0','12:0-24:0'), ('sat', '1:0-12:0','12:0-24:0'), ('sun', '1:0-12:0','12:0-24:0')]
+ return [('fri', '8:0-12:0','13:0-17:0'), ('thu', '8:0-12:0','13:0-17:0'), ('wed', '8:0-12:0','13:0-17:0'),
+ ('mon', '8:0-12:0','13:0-17:0'), ('tue', '8:0-12:0','13:0-17:0')]
resource_attendance_pool = self.pool.get('resource.calendar.attendance')
time_range = "8:00-8:00"
non_working = ""