[FIX] project_long_term : Scheduling, test yaml, few fix

bzr revid: tfr@openerp.com-20110110094856-bxaohl1jmc6u8re0
This commit is contained in:
Thibault Francois 2011-01-10 10:48:56 +01:00
commit b0e1baae57
29 changed files with 1470 additions and 246 deletions

View File

@ -243,7 +243,7 @@
<!-- Entries by Line -->
<record id="action_account_tree1" model="ir.actions.act_window">
<field name="name">Analytic Entries</field>
<field name="name">Analytic Items</field>
<field name="res_model">account.analytic.line</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>

View File

@ -288,6 +288,7 @@ class XMLRpcConn(object):
headers[line[:split_here]] = line[split_here:]
temp1 = headers.get('Message-ID')
temp2 = headers.get('Message-Id')
referances = headers.get('References')
if temp1 == None: message_id = temp2
if temp2 == None: message_id = temp1
startCut = message_id.find("<")

View File

@ -126,9 +126,11 @@ class project(osv.osv):
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the project without removing it."),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of Projects."),
'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account', help="Link this project to an analytic account if you need financial management on projects. It enables you to connect projects with budgets, planning, cost and revenue analysis, timesheets on projects, etc.", ondelete="cascade", required=True),
'priority': fields.integer('Sequence', help="Gives the sequence order when displaying a list of task"),
'priority': fields.integer('Sequence', help="Gives the sequence order when displaying the list of projects"),
'warn_manager': fields.boolean('Warn Manager', help="If you check this field, the project manager will receive a request each time a task is completed by his team.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'members': fields.many2many('res.users', 'project_user_rel', 'project_id', 'uid', 'Project Members', help="Project's member. Not used in any computation, just for information purpose.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'members': fields.many2many('res.users', 'project_user_rel', 'project_id', 'uid', 'Project Members', help="Project's member. Not used in any computation, just for information purpose, but a user has to be member of a project to add a the to this project.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'parent_id': fields.many2one('project.project', 'Parent Project'),
'tasks': fields.one2many('project.task', 'project_id', "Project tasks"),
'planned_hours': fields.function(_progress_rate, multi="progress", method=True, string='Planned Time', help="Sum of planned hours of all tasks related to this project and its child projects.",
store = {

View File

@ -29,20 +29,17 @@
</group>
<notebook colspan="4">
<page string="Administration">
<group col="2" colspan="2" groups="base.group_extended">
<separator colspan="2" string="Scheduling"/>
<field name="priority"/>
<field name="active"/>
</group>
<group col="2" colspan="2">
<separator colspan="2" string="Performance"/>
<field name="planned_hours" widget="float_time"/>
<field name="effective_hours" widget="float_time" />
</group>
<group col="2" colspan="2" name="misc">
<separator colspan="2" string="Miscelleanous"/>
<separator colspan="4" string="Miscelleanous"/>
<field name="company_id" select="1" groups="base.group_multi_company" widget="selection" required="1"/>
<field name="warn_manager"/>
<field name="priority"/>
<field name="active"/>
</group>
<newline/>
<separator colspan="4"/>
@ -226,7 +223,7 @@
<form string="Task edition">
<group colspan="6" col="6">
<field name="name" select="1"/>
<field name="project_id" select="1" on_change="onchange_project(project_id)" domain="[('user_id','=',uid)]"/>
<field name="project_id" select="1" on_change="onchange_project(project_id)" domain="['|',('user_id','=',uid),('members','=',uid)]" />
<field name="total_hours" widget="float_time"/>
<field name="date_deadline" attrs="{'readonly':[('state','in',['done', 'cancelled'])]}"/>
<field name="user_id" select="1" attrs="{'readonly':[('state','in',['done', 'cancelled'])]}"/>

View File

@ -333,7 +333,7 @@
<field name="type">form</field>
<field name="inherit_id" ref="project.edit_project"/>
<field name="arch" type="xml">
<field name="priority" position="before">
<field name="priority" position="after">
<field name="resource_calendar_id"/>
</field>

View File

@ -40,9 +40,15 @@ Features.
"init_xml": [],
"demo_xml": ["project_long_term_demo.xml"],
"test": [
'test/project_schedule_consecutive_day.yml',
'test/project_schedule_without_wroking_hour.yml',
'test/schedule_project_phases.yml',
'test/phase_constraint.yml',
'test/test_schedule_phases_case2.yml',
'test/test_schedule_phases_case1.yml',
'test/schedule_project_tasks.yml',
'test/schedule_phase_tasks.yml'
'test/schedule_phase_tasks.yml',
'test/test_schedule_tasks_case1.yml'
],
"update_xml": [
"security/ir.model.access.csv",

View File

@ -546,6 +546,12 @@ msgstr "Berechne Aufgabenterminierung für spezifiziertes Projekt."
msgid "Phase"
msgstr "Phase"
#. module: project_long_term
#: help:project.phase,date_start:0
msgid ""
"It's computed by the scheduler according the project date or the end date of the previous phase."
msgstr "Beginn der Phase"
#. module: project_long_term
#: help:project.phase,state:0
msgid ""

View File

@ -451,8 +451,8 @@ msgstr "Phase"
#. module: project_long_term
#: help:project.phase,date_start:0
#: field:project.resource.allocation,phase_id_date_start:0
msgid "It's computed according to the phases order : the start date of the 1st phase is set by you while the other start dates depend on the end date of their previous phases"
msgstr "It's computed according to the phases order : the start date of the 1st phase is set by you while the other start dates depend on the end date of their previous phases"
msgid "It's computed by the scheduler according the project date or the end date of the previous phase."
msgstr "It's computed by the scheduler according the project date or the end date of the previous phase."
#. module: project_long_term
#: help:project.phase,state:0

View File

@ -522,6 +522,12 @@ msgstr ""
msgid "Phase"
msgstr "Fase"
#. module: project_long_term
#: help:project.phase,date_start:0
msgid ""
"It's computed by the scheduler according the project date or the end date of the previous phase."
msgstr ""
#. module: project_long_term
#: help:project.phase,state:0
msgid ""

View File

@ -555,6 +555,12 @@ msgstr "Calculer l'ordonnancement des tâches pour un projet spécifié."
msgid "Phase"
msgstr "Phase"
#. module: project_long_term
#: help:project.phase,date_start:0
msgid ""
"It's computed by the scheduler according the project date or the end date of the previous phase."
msgstr "Date de début de la phase"
#. module: project_long_term
#: help:project.phase,state:0
msgid ""

View File

@ -519,6 +519,12 @@ msgstr "Aprēķināt uzdevumu grafiku norādītajām projektam."
msgid "Phase"
msgstr "Fāze"
#. module: project_long_term
#: help:project.phase,date_start:0
msgid ""
"It's computed by the scheduler according the project date or the end date of the previous phase."
msgstr ""
#. module: project_long_term
#: help:project.phase,state:0
msgid ""

View File

@ -520,6 +520,12 @@ msgstr "Oblicz planowanie zadań dla projektu"
msgid "Phase"
msgstr "Faza"
#. module: project_long_term
#: help:project.phase,date_start:0
msgid ""
"It's computed by the scheduler according the project date or the end date of the previous phase."
msgstr ""
#. module: project_long_term
#: help:project.phase,state:0
msgid ""

View File

@ -369,7 +369,7 @@ msgstr ""
#. module: project_long_term
#: help:project.phase,date_start:0
msgid "It's computed according to the phases order : the start date of the 1st phase is set by you while the other start dates depend on the end date of their previous phases"
msgid "It's computed by the scheduler according the project date or the end date of the previous phase."
msgstr ""
#. module: project_long_term

View File

@ -519,6 +519,12 @@ msgstr ""
msgid "Phase"
msgstr "Fase"
#. module: project_long_term
#: help:project.phase,date_start:0
msgid ""
"It's computed by the scheduler according the project date or the end date of the previous phase."
msgstr "Data de início da fase"
#. module: project_long_term
#: help:project.phase,state:0
msgid ""

View File

@ -23,8 +23,11 @@ from datetime import datetime, timedelta
from dateutil.relativedelta import relativedelta
from tools.translate import _
from osv import fields, osv
from resource.faces import task as Task
from resource.faces import task as Task
import operator
from new import classobj
import types
import new
class project_phase(osv.osv):
_name = "project.phase"
@ -105,7 +108,7 @@ class project_phase(osv.osv):
_columns = {
'name': fields.char("Name", size=64, required=True),
'date_start': fields.date('Start Date', help="It's computed according to the phases order : the start date of the 1st phase is set by you while the other start dates depend on the end date of their previous phases", states={'done':[('readonly',True)], 'cancelled':[('readonly',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)]}),
@ -212,126 +215,177 @@ class project_phase(osv.osv):
self.write(cr, uid, ids, {'state': 'done'})
return True
def generate_resources(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 phase in self.browse(cr, uid, ids, context=context):
user_ids = map(lambda x:x.resource_id.user_id.id, phase.resource_ids)
project = phase.project_id
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[phase.id] = resource_objs
return res
def generate_schedule(self, cr, uid, ids, start_date=False, calendar_id=False, context=None):
"""
Schedule phase with the start date till all the next phases are completed.
@param: start_date (datetime.datetime) : start date for the phase. It would be either Start date of phase or start date of project or system current date
@param: calendar_id : working calendar of the project
"""
def generate_phase(self, cr, uid, ids, f, parent=False, context=None):
if context is None:
context = {}
phase_ids = []
resource_pool = self.pool.get('resource.resource')
data_pool = self.pool.get('ir.model.data')
resource_allocation_pool = self.pool.get('project.resource.allocation')
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):
if not phase.responsible_id:
raise osv.except_osv(_('No responsible person assigned !'),_("You must assign a responsible person for phase '%s' !") % (phase.name,))
if not start_date:
start_date = phase.project_id.date_start or phase.date_start or datetime.now().strftime("%Y-%m-%d")
start_date = datetime.strftime((datetime.strptime(start_date, "%Y-%m-%d")), "%Y-%m-%d")
phase_resource_obj = self.generate_resources(cr, uid, [phase.id], context=context)[phase.id]
avg_days = uom_pool._compute_qty(cr, uid, phase.product_uom.id, phase.duration, day_uom_id)
if not phase_resource_obj: #TOCHECK: why need this ?
avg_days = avg_days - 1
duration = str(avg_days) + 'd'
# Create a new project for each phase
def Project():
# If project has working calendar then that
# else the default one would be considered
start = start_date
minimum_time_unit = 1
resource = phase_resource_obj
working_hours_per_day = 24
vacation = []
if calendar_id:
working_hours_per_day = 8 #TODO: it should be come from calendars
vacation = tuple(resource_pool.compute_vacation(cr, uid, calendar_id))
working_days = resource_pool.compute_working_calendar(cr, uid, calendar_id, context=context)
def phase():
effort = duration
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))
project = Task.BalancedProject(Project)
# Phases Defination for the Project
s = '''
def Phase_%s():
title = \"%s\"
effort = \'%s\'
resource = %s
'''%(phase.id, phase.name, duration, str_vals or False)
s_date = project.phase.start.to_datetime()
e_date = project.phase.end.to_datetime()
# Recalculate date_start and date_end
# according to constraints on date start and date end on phase
if phase.constraint_date_start and str(s_date) < phase.constraint_date_start:
start_date = ''
end_date = ''
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:
start_date = s_date
if phase.constraint_date_end and str(e_date) > phase.constraint_date_end:
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')
date_start = phase.constraint_date_end
else:
end_date = e_date
date_start = end_date
# Write the calculated dates back
ctx = context.copy()
ctx.update({'scheduler': True})
self.write(cr, uid, [phase.id], {
'date_start': start_date.strftime('%Y-%m-%d'),
'date_end': end_date.strftime('%Y-%m-%d')
}, context=ctx)
# write dates into Resources Allocation
for resource in phase.resource_ids:
resource_allocation_pool.write(cr, uid, [resource.id], {
'date_start': start_date.strftime('%Y-%m-%d'),
'date_end': end_date.strftime('%Y-%m-%d')
}, context=ctx)
s += '''
end = \"%s\"
'''%(datetime.strftime(end_date, "%Y-%m-%d"))
#start = datetime.strftime((datetime.strptime(start, "%Y-%m-%d")), "%Y-%m-%d")
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']:
id_cal = next_phase.project_id.resource_calendar_id and next_phase.project_id.resource_calendar_id.id or False
self.generate_schedule(cr, uid, [next_phase.id], date_start+timedelta(days=1), id_cal, context=context)
else:
continue
return True
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 the tasks according to resource available and priority.
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')
resources_list = self.generate_resources(cr, uid, ids, context=context)
return_msg = {}
for phase in self.browse(cr, uid, ids, context=context):
start_date = phase.date_start
if not start_date and phase.project_id.date_start:
start_date = phase.project_id.date_start
if not start_date:
start_date = datetime.now().strftime("%Y-%m-%d")
resources = resources_list.get(phase.id, [])
calendar_id = phase.project_id.resource_calendar_id.id
task_ids = map(lambda x : x.id, (filter(lambda x : x.state in ['draft'] , phase.task_ids))) #reassign only task not yet started
if task_ids:
task_pool.generate_schedule(cr, uid, task_ids, resources, calendar_id, start_date, context=context)
data_pool = self.pool.get('ir.model.data')
resource_allocation_pool = self.pool.get('project.resource.allocation')
if not task_ids:
warning_msg = _("No tasks to compute for Phase '%s'.") % (phase.name)
if "warning" not in return_msg:
return_msg["warning"] = warning_msg
else:
return_msg["warning"] = return_msg["warning"] + "\n" + warning_msg
return return_msg
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
# Allocating Memory for the required Project and Pahses and Resources
exec(func_str)
Phase = eval('Phase_%d' % phase.id)
phase = Task.BalancedProject(Phase)
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'),
'date_end': end_date.strftime('%Y-%m-%d')
}, context=context)
return True
project_phase()
class project_resource_allocation(osv.osv):
@ -383,53 +437,224 @@ class project(osv.osv):
def schedule_phases(self, cr, uid, ids, context=None):
"""
Schedule the phases.
Schedule phase base on faces lib
"""
if context is None:
context = {}
if type(ids) in (long, int,):
ids = [ids]
phase_pool = self.pool.get('project.phase')
task_pool = self.pool.get('project.task')
resource_pool = self.pool.get('resource.resource')
data_pool = self.pool.get('ir.model.data')
resource_allocation_pool = self.pool.get('project.resource.allocation')
uom_pool = self.pool.get('product.uom')
data_model, day_uom_id = data_pool.get_object_reference(cr, uid, 'product', 'uom_day')
for project in self.browse(cr, uid, ids, context=context):
phase_ids = phase_pool.search(cr, uid, [('project_id', '=', project.id),
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 = False
phase_pool.generate_schedule(cr, uid, phase_ids, start_date, calendar_id, context=context)
return True
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]
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
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 )
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
# Allocating Memory for the required Project and Pahses and Resources
exec(func_str)
Project = eval('Project_%d' % project.id)
project = Task.BalancedProject(Project)
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)
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
#TODO: DO Resource allocation and compute availability
def compute_allocation(self, rc, uid, ids, start_date, end_date, context=None):
if context == None:
contex = {}
allocation = {}
return allocation
def schedule_tasks(self, cr, uid, ids, context=None):
"""
Schedule the tasks according to resource available and priority.
Schedule task base on faces lib
"""
if context is None:
context = {}
if type(ids) in (long, int,):
ids = [ids]
user_pool = self.pool.get('res.users')
task_pool = self.pool.get('project.task')
resource_pool = self.pool.get('resource.resource')
resources_list = self.generate_members(cr, uid, ids, context=context)
return_msg = {}
data_pool = self.pool.get('ir.model.data')
resource_allocation_pool = self.pool.get('project.resource.allocation')
uom_pool = self.pool.get('product.uom')
data_model, day_uom_id = data_pool.get_object_reference(cr, uid, 'product', 'uom_day')
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
if not start_date:
start_date = datetime.now().strftime("%Y-%m-%d")
resources = resources_list.get(project.id, [])
calendar_id = project.resource_calendar_id.id
task_ids = task_pool.search(cr, uid, [('project_id', '=', project.id),
#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 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
if task_ids:
task_pool.generate_schedule(cr, uid, task_ids, resources, calendar_id, start_date, context=context)
else:
warning_msg = _("No tasks to compute for Project '%s'.") % (project.name)
if "warning" not in return_msg:
return_msg["warning"] = warning_msg
else:
return_msg["warning"] = return_msg["warning"] + "\n" + warning_msg
return return_msg
# Allocating Memory for the required Project and Pahses and Resources
exec(func_str)
Project = eval('Project_%d' % project.id)
project = Task.BalancedProject(Project)
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'),
'date_end': end_date.strftime('%Y-%m-%d')
}, context=context)
return True
project()
@ -452,82 +677,39 @@ class project_task(osv.osv):
_columns = {
'phase_id': fields.many2one('project.phase', 'Project Phase'),
}
_defaults = {
'user_id' : False
}
def generate_schedule(self, cr, uid, ids, resources, calendar_id, start_date, context=None):
"""
Schedule the tasks according to resource available and priority.
"""
resource_pool = self.pool.get('resource.resource')
if not ids:
return False
def generate_task(self, cr, uid, task_id, parent=False, flag=False, context=None):
if context is None:
context = {}
user_pool = self.pool.get('res.users')
project_pool = self.pool.get('project.project')
priority_dict = {'0': 1000, '1': 800, '2': 500, '3': 300, '4': 100}
# Create dynamic no of tasks with the resource specified
def create_tasks(task_number, eff, priorty=500, obj=False):
def task():
"""
task is a dynamic method!
"""
effort = eff
if obj:
resource = obj
priority = priorty
task.__doc__ = "TaskNO%d" %task_number
task.__name__ = "task%d" %task_number
return task
# Create a 'Faces' project with all the tasks and resources
def Project():
title = "Project"
start = datetime.strftime(datetime.strptime(start_date, "%Y-%m-%d"), "%Y-%m-%d %H:%M")
try:
resource = reduce(operator.or_, resources)
except:
raise osv.except_osv(_('Error'), _('Resources should be allocated to your phases and Members should be assigned to your Project!'))
minimum_time_unit = 1
working_hours_per_day = 24
vacation = []
if calendar_id:
working_hours_per_day = 8 #TODO: it should be come from calendars
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)
# Dynamic creation of tasks
task_number = 0
for openobect_task in self.browse(cr, uid, ids, context=context):
hours = str(openobect_task.planned_hours )+ 'H'
if openobect_task.priority in priority_dict.keys():
priorty = priority_dict[openobect_task.priority]
real_resource = False
if openobect_task.user_id:
for task_resource in resources:
if task_resource.__name__ == task_resource:
real_resource = task_resource
break
task = create_tasks(task_number, hours, priorty, real_resource)
task_number += 1
face_projects = Task.BalancedProject(Project)
loop_no = 0
# Write back the computed dates
for face_project in face_projects:
s_date = face_project.start.to_datetime()
e_date = face_project.end.to_datetime()
if loop_no > 0:
ctx = context.copy()
ctx.update({'scheduler': True})
user_id = user_pool.search(cr, uid, [('name', '=', face_project.booked_resource[0].__name__)])
self.write(cr, uid, [ids[loop_no-1]], {
'date_start': s_date.strftime('%Y-%m-%d %H:%M:%S'),
'date_end': e_date.strftime('%Y-%m-%d %H:%M:%S'),
'user_id': user_id[0]
}, context=ctx)
loop_no += 1
return True
phase_pool = self.pool.get('project.phase')
resource_pool = self.pool.get('resource.resource')
resource_allocation_pool = self.pool.get('project.resource.allocation')
task = self.browse(cr, uid, task_id, context=context)
duration = str(task.planned_hours )+ 'H'
str_resource = False
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)
#start = datetime.strftime((datetime.strptime(start, "%Y-%m-%d")), "%Y-%m-%d")
else:
s = '''
def Task_%s():
title = \"%s\"
effort = \'%s\'
resource = %s
'''%(task.id, task.name, duration, str_resource)
s += '\n'
return s
project_task()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -233,9 +233,9 @@ This Demo data file Human Resources, Phases and Resources,Tasks allocation and
</record>
<!-- run scheduling of phase -->
<function model="project.project" name="schedule_phases" eval="(ref('project.project_integrate_openerp'),)"/>
<!--<function model="project.project" name="schedule_phases" eval="(ref('project.project_integrate_openerp'),)"/>-->
<!-- run scheduling of tasks -->
<function model="project.project" name="schedule_tasks" eval="(ref('project.project_integrate_openerp'),)"/>
<!--<function model="project.project" name="schedule_tasks" eval="(ref('project.project_integrate_openerp'),)"/>-->
</data>
</openerp>

View File

@ -116,10 +116,10 @@
<field name="responsible_id"/>
</group>
<group colspan="6" col="6">
<field name="date_start"/>
<field name="date_start" readonly="1"/>
<field name="duration"/>
<field name="product_uom" nolabel="1" domain="[('category_id.name', '=', 'Working Time')]"/>
<field name="date_end"/>
<field name="date_end" readonly="1"/>
</group>
</group>
<notebook colspan="4">
@ -228,6 +228,7 @@
<field name="date_end"/>
<field name="duration"/>
<field name="state"/>
<button name="%(action_project_schedule_tasks)d" string="Schedule Tasks" type="action" icon="gtk-jump-to"/>
</tree>
</field>
</record>
@ -322,7 +323,7 @@
<field name="type">form</field>
<field name="inherit_id" ref="project.edit_project"/>
<field name="arch" type="xml">
<field name="priority" position="before">
<field name="effective_hours" position="after">
<field name="resource_calendar_id"/>
</field>

View File

@ -0,0 +1,179 @@
-
I have a project 'Develop yaml Project Module'.
-
!record {model: project.project, id: project_project_developyamlproject0}:
name: "Develop Yaml Project Module"
date_start: !eval time.strftime('%Y-%m-%d')
-
I have set Working Time from Monday to Friday from 9am to 17pm.
-
!record {model: resource.calendar, id: resource_calendar_hoursweeks0}:
name: "from Monday to Friday, from 9am to 17pm"
-
I have set Day1 Working Time for Monday to Friday from 9am to 17pm working hour
-
!record {model: resource.calendar.attendance, id: resource_calendar_atendanceday1}:
name: "Day1"
hour_from : 09.00
hour_to : 17.00
dayofweek : "0"
calendar_id : resource_calendar_hoursweeks0
-
I have set Day2 Working Time for Monday to Friday from 9am to 17pm working hour
-
!record {model: resource.calendar.attendance, id: resource_calendar_atendanceday1}:
name: "Day2"
hour_from : 09.00
hour_to : 17.00
dayofweek : "1"
calendar_id : resource_calendar_hoursweeks0
-
I have set Day3 Working Time for Monday to Friday from 9am to 17pm working hour
-
!record {model: resource.calendar.attendance, id: resource_calendar_atendanceday1}:
name: "Day3"
hour_from : 09.00
hour_to : 17.00
dayofweek : "2"
calendar_id : resource_calendar_hoursweeks0
-
I have set Day4 Working Time for Monday to Friday from 9am to 17pm working hour
-
!record {model: resource.calendar.attendance, id: resource_calendar_atendanceday1}:
name: "Day4"
hour_from : 09.00
hour_to : 17.00
dayofweek : "3"
calendar_id : resource_calendar_hoursweeks0
-
I have set Day5 Working Time for Monday to Friday from 9am to 17pm working hour
-
!record {model: resource.calendar.attendance, id: resource_calendar_atendanceday1}:
name: "Day5"
hour_from : 09.00
hour_to : 17.00
dayofweek : "4"
calendar_id : resource_calendar_hoursweeks0
-
Now Set working period to Project 'Develop yaml Project Module'
-
!python {model: project.project}: |
self.write(cr, uid, [ref("project_project_developyamlproject0")], {'resource_calendar_id': ref("resource_calendar_hoursweeks0")})
-
Creating 3 consecutive 5-days phases.
For that Creating a Analyst Human resource to analyst Project Yaml.
-
!record {model: resource.resource, id: resource_resource_analyst1}:
calendar_id: project_long_term.resource_calendar_hoursweeks0
name: Analyst
resource_type: user
time_efficiency: 1.0
user_id: base.user_admin
-
Creating Developer Human resource who develop Yaml
-
!record {model: resource.resource, id: resource_resource_develop0}:
calendar_id: project_long_term.resource_calendar_hoursweeks0
name: Developer
resource_type: user
time_efficiency: 1.0
user_id: base.user_admin
-
Creating a Tester Human resource for testing Yaml
-
!record {model: resource.resource, id: resource_resource_tester1}:
calendar_id: project_long_term.resource_calendar_hoursweeks0
name: tester
resource_type: user
time_efficiency: 1.0
user_id: base.user_admin
-
Create 3 a project phase and NO one depends on other one.
so,they should start on the same day.
Creating First 'Analysis Flow for Yaml' Project Phase.
-
!record {model: project.phase, id: project_phase_analysisflowforyaml0}:
date_start: !eval time.strftime('%Y-%m-%d')
duration: 6.0
name: "Analysis Flow for Yaml"
product_uom: product.uom_day
project_id: project_project_developyamlproject0
responsible_id: project.res_users_analyst
state: draft
-
I update the Constrain Date Start adn Date End for phase Analysis Flow for Yaml
-
!python {model : project.phase }: |
import datetime
from dateutil.relativedelta import *
start = datetime.date.today() - relativedelta(days=2)
end = datetime.date.today() + relativedelta(days=6)
self.write(cr,uid, [(ref("project_phase_analysisflowforyaml0"))], {'constraint_date_start': start, 'constraint_date_end':end})
-
Create project phase 'Develop yaml' with constraint date.
-
!record {model: project.phase, id: project_phase_developyaml0}:
duration: 6.0
name: Develop Yaml
product_uom: product.uom_day
project_id: project_project_developyamlproject0
previous_phase_ids:
- project_phase_analysisflowforyaml0
-
I update the Constrain Date Start adn Date End for phase Develop yaml
-
!python {model : project.phase }: |
import datetime
from dateutil.relativedelta import *
start = datetime.date.today() - relativedelta(days=5)
end = datetime.date.today() + relativedelta(days=10)
self.write(cr,uid, [(ref("project_phase_developyaml0"))], {'constraint_date_start': start, 'constraint_date_end':end})
-
Create project phase 'Test Yaml'
-
!record {model: project.phase, id: project_phase_testyaml0}:
duration: 6.0
name: Testing Yaml
product_uom: product.uom_day
project_id: project_project_developyamlproject0
previous_phase_ids:
- project_phase_developyaml0
constraint_date_start :
constraint_date_end :
-
Compute Schedule of phases For One project
-
!record {model: project.compute.phases, id: project_compute_phases_0}:
project_id: project_project_developyamlproject0
target_project: one
-
Schedule project phases using "Compute Phase Scheduling"
-
!python {model: project.project}: |
self.schedule_phases(cr, uid, [ref("project_project_developyamlproject0")])
-
After scheduling,check that
Phase "Analysis Flow for Yaml" and "Test Yaml" Phase should start on the project start date while "Develop yaml" Phase should respect the constraint.
-
!python {model: project.project}: |
proj=self.browse(cr, uid, [ref("project_project_developyamlproject0")])[0]
for phase in proj.phase_ids:
for phase1 in proj.phase_ids:
if phase1.id != phase.id and phase.constraint_date_start == phase1.date_start:
raise AssertionError("Phases not scheduled")

View File

@ -0,0 +1,126 @@
-
Create a project 'Develop yaml Project Module'.
-
!record {model: project.project, id: project_project_developyamlproject123}:
name: "Develop Yaml Project Module"
date_start: !eval time.strftime('%Y-%m-%d')
-
I have set Working Time from Monday to Friday from 9am to 17pm.
-
!record {model: resource.calendar, id: resource_calendar_hoursweekstest2}:
name: "from Monday to Friday, from 9am to 17pm"
-
I have set Day1 Working Time for Monday to Friday from 9am to 17pm working hour
-
!record {model: resource.calendar.attendance, id: resource_calendar_atendanceday1}:
name: "Day1"
hour_from : 09.00
hour_to : 17.00
dayofweek : "0"
calendar_id : resource_calendar_hoursweekstest2
-
I have set Day2 Working Time for Monday to Friday from 9am to 17pm working hour
-
!record {model: resource.calendar.attendance, id: resource_calendar_atendanceday2}:
name: "Day2"
hour_from : 09.00
hour_to : 17.00
dayofweek : "1"
calendar_id : resource_calendar_hoursweekstest2
-
I have set Day3 Working Time for Monday to Friday from 9am to 17pm working hour
-
!record {model: resource.calendar.attendance, id: resource_calendar_atendanceday3}:
name: "Day3"
hour_from : 09.00
hour_to : 17.00
dayofweek : "2"
calendar_id : resource_calendar_hoursweekstest2
-
I have set Day4 Working Time for Monday to Friday from 9am to 17pm working hour
-
!record {model: resource.calendar.attendance, id: resource_calendar_atendanceday4}:
name: "Day4"
hour_from : 09.00
hour_to : 17.00
dayofweek : "3"
calendar_id : resource_calendar_hoursweekstest2
-
I have set Day5 Working Time for Monday to Friday from 9am to 17pm working hour
-
!record {model: resource.calendar.attendance, id: resource_calendar_atendanceday5}:
name: "Day5"
hour_from : 09.00
hour_to : 17.00
dayofweek : "4"
calendar_id : resource_calendar_hoursweekstest2
-
Now Set working period to Project 'Develop yaml Project Module'
-
!python {model: project.project}: |
self.write(cr, uid, [ref("project_project_developyamlproject123")], {'resource_calendar_id': ref("resource_calendar_hoursweekstest2")})
-
Create 3 a project phase.
First 'Analysis Flow for Yaml'Project Phase
-
!record {model: project.phase, id: project_phase_analysisflowforyaml0}:
date_start: !eval time.strftime('%Y-%m-%d')
duration: 5.0
name: "Analysis Flow for Yaml"
product_uom: product.uom_day
project_id: project_project_developyamlproject123
state: draft
-
Create project phase 'Develop yaml'
-
!record {model: project.phase, id: project_phase_establishingprojectfeasibility0}:
duration: 5.0
name: Develop Yaml
product_uom: product.uom_day
project_id: project_project_developyamlproject123
previous_phase_ids:
- project_phase_analysisflowforyaml0
-
Create project phase 'Test Yaml'
-
!record {model: project.phase, id: project_phase_preparationofengineeringdesigns0}:
duration: 5.0
name: Testing Yaml
product_uom: product.uom_day
project_id: project_project_developyamlproject123
previous_phase_ids:
- project_phase_establishingprojectfeasibility0
-
Compute Schedule of phases For One project
-
!record {model: project.compute.phases, id: project_compute_phases_0}:
project_id: project_project_developyamlproject123
target_project: one
-
Schedule project phases using Compute Phase Scheduling
-
!python {model: project.project}: |
self.schedule_phases(cr, uid, [ref("project_project_developyamlproject123")])
-
After scheduling, Check that phases scheduled, check that either of phase's start_date, end_date not null.
-
!python {model: project.project}: |
proj=self.browse(cr, uid, [ref("project_project_developyamlproject123")])[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")

View File

@ -0,0 +1,77 @@
-
Create a project 'Develop yaml Implementation Module'.
-
!record {model: project.project, id: project_project_developyamlproject0}:
name: "Develop Yaml Project Module"
date_start: !eval time.strftime('%Y-%m-%d')
-
Create 4 Project phase.
First Project Phase 'Analysis Flow for Yaml'
-
!record {model: project.phase, id: project_phase_analysisflowforyaml0}:
date_start: !eval time.strftime('%Y-%m-%d')
duration: 6.0
product_uom: product.uom_day
name: "Analysis Flow for Yaml"
project_id: project_project_developyamlproject0
responsible_id: project.res_users_analyst
state: draft
-
Create project phase 'Develop yaml'
-
!record {model: project.phase, id: project_phase_establishingprojectfeasibility0}:
duration: 6.0
name: "Develop yaml"
product_uom: product.uom_day
project_id: project_project_developyamlproject0
previous_phase_ids:
- project_phase_analysisflowforyaml0
state: draft
-
Create project phase 'Test Yaml'
-
!record {model: project.phase, id: project_phase_preparationofengineeringdesigns0}:
duration: 6.0
name: Testing Yaml
product_uom: product.uom_day
project_id: project_project_developyamlproject0
previous_phase_ids:
- project_phase_establishingprojectfeasibility0
state: draft
-
Create project phase 'Implement Yaml'
-
!record {model: project.phase, id: project_phase_implementingreadycase0}:
duration: 6.0
name: Testing Yaml
product_uom: product.uom_day
project_id: project_project_developyamlproject0
previous_phase_ids:
- project_phase_preparationofengineeringdesigns0
state: draft
-
Compute Schedule of phases For One project
-
!record {model: project.compute.phases, id: project_compute_phases_0}:
project_id: project_project_developyamlproject0
target_project: one
-
Schedule project phases using Compute Phase Scheduling
-
!python {model: project.project}: |
self.schedule_phases(cr, uid, [ref("project_project_developyamlproject0")])
-
After scheduling, Check that phases scheduled, check that either of phase's start_date, end_date not null.
-
!python {model: project.project}: |
proj=self.browse(cr, uid, [ref("project_project_developyamlproject0")])[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")

View File

@ -23,12 +23,8 @@
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'

View File

@ -4,7 +4,14 @@
!record {model: project.project, id: project_project_worldbanksproject0}:
name: "World Bank's Project"
priority: 4
members:
- project.res_users_analyst
- project.res_users_project_manager
- project.res_users_technical_leader
- project.res_users_developer
- project.res_users_designer
- project.res_users_tester
-
Create a project phase 'Defining Client's Basic Idea of Project'
-
@ -14,8 +21,7 @@
name: "Defining Client's Basic Idea of Project"
product_uom: product.uom_day
project_id: project_project_worldbanksproject0
-
Create project phase 'Establishing Project Feasibility'
-
@ -25,7 +31,49 @@
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'
-
!record {model: project.task, id: project_task_t0}:
name: Develop GUI for Server Configuration
planned_hours: 20.0
state: draft
phase_id: project_phase_definingclientsbasicideaofproject0
project_id: project_project_worldbanksproject0
-
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_definingclientsbasicideaofproject0
project_id: project_project_worldbanksproject0
-
Create project phase 'Preparation of Engineering Designs'
-
@ -36,7 +84,43 @@
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'
-
!record {model: project.task, id: project_task_t2}:
name: Develop GUI for Server Configuration
planned_hours: 20.0
remaining_hours: 20.0
state: draft
phase_id: project_phase_preparationofengineeringdesigns0
project_id: project_project_worldbanksproject0
-
Create the phase task 'Develop GUI for Client Module Configuration'
-
!record {model: project.task, id: project_task_t3}:
name: Develop GUI for Modules Configuration
planned_hours: 25.0
remaining_hours: 25.0
state: draft
phase_id: project_phase_preparationofengineeringdesigns0
project_id: project_project_worldbanksproject0
-
Create project phase 'Procurement of Works and Goods'
-
@ -46,14 +130,13 @@
name: Procurement of Works and Goods
product_uom: product.uom_hour
project_id: project_project_worldbanksproject0
-
Create project phase 'Project Construction'
-
!record {model: project.phase, id: project_phase_projectconstruction0}:
date_start: '2010-06-02'
duration: 4320.0
duration: 20.0
name: Project Construction
product_uom: product.uom_hour
project_id: project_project_worldbanksproject0

View File

@ -0,0 +1,65 @@
-
In order to test scheduling of project phases, I create two different phases and
test it with two different dates for scheduling.
-
I create a project 'Development and Testing'.
-
!record {model: project.project, id: project_project_project0}:
date_start: '2010-12-30'
balance: 0.0
credit: 0.0
currency_id: base.EUR
debit: 0.0
effective_hours: 0.0
members:
- base.user_admin
name: Development and Testing
planned_hours: 0.0
progress_rate: 0.0
quantity: 0.0
quantity_max: 0.0
state: open
type_ids:
- project.project_tt_specification
- project.project_tt_development
- project.project_tt_testing
- project.project_tt_merge
-
I create first phase of the project.
-
!record {model: project.phase, id: project_phase_firstphase0}:
duration: 2.0
name: First Phase
product_uom: product.uom_day
project_id: project_project_project0
state: draft
-
I create second phase of the project.
-
!record {model: project.phase, id: project_phase_secondphase0}:
duration: 3.0
name: Second Phase
previous_phase_ids:
- project_phase_firstphase0
product_uom: product.uom_day
project_id: project_project_project0
state: draft
-
Now I create a record to compute the phase of project.
-
!record {model: project.compute.phases, id: project_compute_phases0}:
target_project: 'one'
project_id: project_project_project0
-
I schedule the phases.
-
!python {model: project.compute.phases}: |
self.check_selection(cr, uid, [ref("project_compute_phases0")])
-
I check the starting and ending dates of both phases.
-
!python {model: project.phase}: |
first_phase = self.browse(cr, uid, ref('project_phase_firstphase0'))
assert (first_phase.date_start == '2010-12-30' and first_phase.date_end == '2010-12-31'),'Dates are wrong!'
second_phase = self.browse(cr, uid, ref('project_phase_secondphase0'))
assert (second_phase.date_start == '2011-01-01' and second_phase.date_end == '2011-01-03'),'Dates are wrong!'

View File

@ -0,0 +1,193 @@
-
In order to test scheduling of project phases, I create two different
working periods and resources. And schedule the phases.
-
I create first working period 'Working Time A'.
-
!record {model: resource.calendar, id: resource_calendar_workingtimea0}:
attendance_ids:
- dayofweek: '0'
hour_from: 10.0
hour_to: 16.0
name: Monday
- dayofweek: '1'
hour_from: 10.0
hour_to: 16.0
name: Tuesday
- dayofweek: '2'
hour_from: 10.0
hour_to: 16.0
name: Wednesday
- dayofweek: '3'
hour_from: 10.0
hour_to: 16.0
name: Thursday
name: Working Time A
-
Creating a resource.calendar.attendance record
-
!record {model: resource.calendar.attendance, id: resource_calendar_attendance_monday0}:
calendar_id: resource_calendar_workingtimea0
dayofweek: '0'
hour_from: 10.0
hour_to: 16.0
name: Monday
-
Creating a resource.calendar.attendance record
-
!record {model: resource.calendar.attendance, id: resource_calendar_attendance_tuesday0}:
calendar_id: resource_calendar_workingtimea0
dayofweek: '1'
hour_from: 10.0
hour_to: 16.0
name: Tuesday
-
Creating a resource.calendar.attendance record
-
!record {model: resource.calendar.attendance, id: resource_calendar_attendance_wednesday0}:
calendar_id: resource_calendar_workingtimea0
dayofweek: '2'
hour_from: 10.0
hour_to: 16.0
name: Wednesday
-
Creating a resource.calendar.attendance record
-
!record {model: resource.calendar.attendance, id: resource_calendar_attendance_thursday0}:
calendar_id: resource_calendar_workingtimea0
dayofweek: '3'
hour_from: 10.0
hour_to: 16.0
name: Thursday
-
I create second working period 'Working Time B'.
-
!record {model: resource.calendar, id: resource_calendar_workingtimeb0}:
attendance_ids:
- dayofweek: '4'
hour_from: 9.0
hour_to: 18.0
name: Friday
name: Working Time B
-
Creating a resource.calendar.attendance record
-
!record {model: resource.calendar.attendance, id: resource_calendar_attendance_friday0}:
calendar_id: resource_calendar_workingtimeb0
dayofweek: '4'
hour_from: 9.0
hour_to: 18.0
name: Friday
-
I create first resource say 'Resource X' without assigning working period.
-
!record {model: resource.resource, id: resource_resource_resourcex0}:
name: Resource X
resource_type: user
time_efficiency: 1.0
user_id: base.user_admin
-
I create first resource say 'Resource Y' with working period 'Working Time B'.
-
!record {model: resource.resource, id: resource_resource_resourcey0}:
calendar_id: resource_calendar_workingtimeb0
name: Resource Y
resource_type: user
time_efficiency: 1.0
user_id: base.user_admin
-
I create a project 'Development and Testing' and assign working period 'Working Time A'.
-
!record {model: project.project, id: project_project_project0}:
balance: 0.0
credit: 0.0
currency_id: base.EUR
debit: 0.0
effective_hours: 0.0
members:
- base.user_admin
name: Development and Testing
planned_hours: 20.0
progress_rate: 0.0
quantity: 0.0
quantity_max: 0.0
resource_calendar_id: resource_calendar_workingtimea0
state: open
tasks:
- delay_hours: 0.0
effective_hours: 0.0
name: Task 1
planned_hours: 10.0
remaining_hours: 10.0
state: draft
total_hours: 10.0
- delay_hours: 0.0
effective_hours: 0.0
name: Task 2
planned_hours: 10.0
remaining_hours: 10.0
state: draft
total_hours: 10.0
total_hours: 10.0
members:
- project.res_users_analyst
- project.res_users_project_manager
- project.res_users_technical_leader
- project.res_users_developer
- project.res_users_designer
- project.res_users_tester
type_ids:
- project.project_tt_specification
- project.project_tt_development
- project.project_tt_testing
- project.project_tt_merge
-
I create a phase 'Initial Phase' for project 'Development and Testing'.
-
!record {model: project.phase, id: project_phase_phase0}:
date_start: '2011-01-03'
duration: 0.0
name: Initial Phase
product_uom: product.uom_day
project_id: project_project_project0
state: draft
-
I create first task 'Developing module' and assign 10h.
-
!record {model: project.task, id: project_task_task0}:
delay_hours: 0.0
effective_hours: 0.0
name: Developing module
planned_hours: 10.0
project_id: project_project_project0
remaining_hours: 10.0
state: draft
total_hours: 10.0
phase_id: project_phase_phase0
-
I create first task 'Testing module' and assign 10h.
-
!record {model: project.task, id: project_task_task1}:
delay_hours: 0.0
effective_hours: 0.0
name: Testing module
planned_hours: 10.0
project_id: project_project_project0
remaining_hours: 10.0
state: draft
total_hours: 10.0
phase_id: project_phase_phase0
-
Now I create a record to compute the phase of project.
-
!record {model: project.compute.phases, id: project_compute_phases0}:
target_project: 'one'
project_id: project_project_project0
-
I schedule the phases.
-
!python {model: project.compute.phases}: |
self.check_selection(cr, uid, [ref("project_compute_phases0")])

View File

@ -0,0 +1,263 @@
-
In order to test scheduling of tasks, I create four tasks with different sequences and schedule them.
-
I create address for first user.
-
!record {model: res.partner.address, id: res_partner_address_user0}:
name: User 1
-
I create address for second user.
-
!record {model: res.partner.address, id: res_partner_address_user1}:
name: User 2
-
I create first user.
-
!record {model: res.users, id: res_users_user0}:
address_id: res_partner_address_user0
company_id: base.main_company
context_lang: en_US
groups_id:
- base.group_partner_manager
- base.group_user
login: user1
name: User 1
password: user1
-
I create second user.
-
!record {model: res.users, id: res_users_user1}:
address_id: res_partner_address_user1
company_id: base.main_company
context_lang: en_US
groups_id:
- base.group_partner_manager
- base.group_user
login: user2
name: User 2
password: user2
-
I create a Project.
-
!record {model: project.project, id: project_project_projecta0}:
balance: 0.0
credit: 0.0
currency_id: base.EUR
debit: 0.0
effective_hours: 0.0
name: Project A
members:
- res_users_user0
- res_users_user1
phase_ids:
- date_start: '2011-01-06'
duration: 2.0
name: Phase A
product_uom: product.uom_day
state: draft
task_ids:
- delay_hours: 0.0
effective_hours: 0.0
name: Task D
planned_hours: 5.0
remaining_hours: 5.0
state: draft
total_hours: 5.0
- delay_hours: 0.0
effective_hours: 0.0
name: Task C
planned_hours: 5.0
remaining_hours: 5.0
sequence: 11
state: draft
total_hours: 5.0
- delay_hours: 0.0
effective_hours: 0.0
name: Task B
planned_hours: 5.0
remaining_hours: 5.0
sequence: 13
state: draft
total_hours: 5.0
- delay_hours: 0.0
effective_hours: 0.0
name: Task A
planned_hours: 5.0
remaining_hours: 5.0
sequence: 14
state: draft
total_hours: 5.0
planned_hours: 20.0
progress_rate: 0.0
quantity: 0.0
quantity_max: 0.0
state: open
tasks:
- delay_hours: 0.0
effective_hours: 0.0
name: Task D
planned_hours: 5.0
remaining_hours: 5.0
state: draft
total_hours: 5.0
- delay_hours: 0.0
effective_hours: 0.0
name: Task C
planned_hours: 5.0
remaining_hours: 5.0
sequence: 11
state: draft
total_hours: 5.0
- delay_hours: 0.0
effective_hours: 0.0
name: Task B
planned_hours: 5.0
remaining_hours: 5.0
sequence: 13
state: draft
total_hours: 5.0
- delay_hours: 0.0
effective_hours: 0.0
name: Task A
planned_hours: 5.0
remaining_hours: 5.0
sequence: 14
state: draft
total_hours: 5.0
total_hours: 15.0
-
I create first task with highest sequence.
-
!record {model: project.task, id: project_task_taska0}:
delay_hours: 0.0
effective_hours: 0.0
name: Task A
planned_hours: 5.0
project_id: project_project_projecta0
remaining_hours: 5.0
sequence: 14
state: draft
total_hours: 5.0
-
I create second task.
-
!record {model: project.task, id: project_task_taskb0}:
delay_hours: 0.0
effective_hours: 0.0
name: Task B
planned_hours: 5.0
project_id: project_project_projecta0
remaining_hours: 5.0
sequence: 13
state: draft
total_hours: 5.0
-
I create third task.
-
!record {model: project.task, id: project_task_taskc0}:
delay_hours: 0.0
effective_hours: 0.0
name: Task C
planned_hours: 5.0
project_id: project_project_projecta0
remaining_hours: 5.0
sequence: 11
state: draft
total_hours: 5.0
-
I create fourth task with lowest sequence.
-
!record {model: project.task, id: project_task_taskd0}:
delay_hours: 0.0
effective_hours: 0.0
name: Task D
planned_hours: 5.0
project_id: project_project_projecta0
remaining_hours: 5.0
state: draft
total_hours: 5.0
-
Now I create a resource for first user.
-
!record {model: resource.resource, id: resource_resource_user0}:
name: User 1
resource_type: user
time_efficiency: 1.0
user_id: res_users_user0
-
I create a resource for second user.
-
!record {model: resource.resource, id: resource_resource_user1}:
name: User 2
resource_type: user
time_efficiency: 1.0
user_id: res_users_user1
-
Now I create one phase for the project.
-
!record {model: project.phase, id: project_phase_phasea0}:
date_start: '2011-01-06'
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
effective_hours: 0.0
name: Task D
planned_hours: 5.0
project_id: project_project_projecta0
remaining_hours: 5.0
state: draft
total_hours: 5.0
- delay_hours: 0.0
effective_hours: 0.0
name: Task C
planned_hours: 5.0
project_id: project_project_projecta0
remaining_hours: 5.0
sequence: 11
state: draft
total_hours: 5.0
- delay_hours: 0.0
effective_hours: 0.0
name: Task B
planned_hours: 5.0
project_id: project_project_projecta0
remaining_hours: 5.0
sequence: 13
state: draft
total_hours: 5.0
- delay_hours: 0.0
effective_hours: 0.0
name: Task A
planned_hours: 5.0
project_id: project_project_projecta0
remaining_hours: 5.0
sequence: 14
state: draft
total_hours: 5.0
-
Now I create a record to schedule the tasks of project.
-
!record {model: project.compute.tasks, id: project_compute_tasks0}:
project_id: project_project_projecta0
-
I schedule the tasks.
-
!python {model: project.compute.tasks}: |
self.compute_date(cr, uid, [ref("project_compute_tasks0")])
-
I check that whether the tasks now allocated to respected resources or not.
-
!python {model: project.task}: |
task_ids = self.search(cr, uid, [('project_id','=',ref('project_project_projecta0'))])
for task in self.browse(cr, uid, task_ids):
if (not task.user_id) or (not task.date_start) or (not task.date_end):
raise AssertionError("Tasks are not scheduled.")

View File

@ -28,16 +28,7 @@ from tools.translate import _
class project_project(osv.osv):
_inherit = 'project.project'
def onchange_partner_id(self, cr, uid, ids, part=False, context=None):
result = super(project_project, self).onchange_partner_id(cr, uid, ids, part, context=context)
if result.get('value', False):
try:
d = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'hr_timesheet_invoice', 'timesheet_invoice_factor1')
if d:
result['value']['to_invoice'] = d[1]
except ValueError, e:
pass
return result
project_project()
class project_work(osv.osv):
@ -231,4 +222,16 @@ class res_partner(osv.osv):
context=context)
res_partner()
class account_analytic_line(osv.osv):
_inherit = "account.analytic.line"
def on_change_account_id(self, cr, uid, ids, account_id):
res = {}
if not account_id:
return res
res.setdefault('value',{})
acc = self.pool.get('account.analytic.account').browse(cr, uid, account_id)
st = acc.to_invoice.id
res['value']['to_invoice'] = st or False
return res
account_analytic_line()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -11,7 +11,7 @@
<group colspan="4" col="4">
<separator colspan="4" string="Invoicing Data"/>
<field name="pricelist_id" domain="[('type','=','sale')]" widget="selection"/>
<field name="to_invoice" widget="selection" string="Invoice Task Work"/>
<field name="to_invoice" widget="selection" string="Invoice Task Work" attrs="{'required':[('partner_id','!=',False)]}"/>
<field name="amount_max" groups="base.group_extended"/>
<field name="amount_invoiced" groups="base.group_extended"/>
</group>
@ -61,7 +61,7 @@
<field name="inherit_id" ref="account.view_account_analytic_line_form"/>
<field name="arch" type="xml">
<field name="account_id" position="replace">
<field name="account_id" string="Project"/>
<field name="account_id" string="Analytic account/project" on_change="on_change_account_id(account_id)"/>
</field>
</field>
</record>

View File

@ -250,7 +250,7 @@ class resource_resource(osv.osv):
"""
Return a list of Resource Class objects for the resources allocated to the phase.
"""
resource_objs = []
resource_objs = {}
user_pool = self.pool.get('res.users')
for user in user_pool.browse(cr, uid, user_ids, context=context):
resource_ids = self.search(cr, uid, [('user_id', '=', user.id)], context=context)
@ -264,12 +264,18 @@ class resource_resource(osv.osv):
resource_cal = resource.calendar_id.id
if resource_cal:
leaves = self.compute_vacation(cr, uid, calendar_id, resource.id, resource_cal, context=context)
resource_objs.append(classobj(str(user.name), (Resource,),{
'__doc__': user.name,
'__name__': user.name,
'vacation': tuple(leaves),
'efficiency': resource_eff,
}))
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,
# }))
return resource_objs
def compute_vacation(self, cr, uid, calendar_id, resource_id=False, resource_calendar=False, context=None):
@ -322,6 +328,7 @@ class resource_resource(osv.osv):
# and create a list like [('mon', '8:00-12:00'), ('mon', '13:00-18:00')]
for week in weeks:
res_str = ""
day = None
if week_days.has_key(week['dayofweek']):
day = week_days[week['dayofweek']]
wk_days[week['dayofweek']] = week_days[week['dayofweek']]
@ -348,6 +355,13 @@ class resource_resource(osv.osv):
wktime_cal.append((non_working[:-1], time_range))
return wktime_cal
#TODO: Write optimized alogrothem for resource availability. : Method Yet not implemented
def check_availability(self, cr, uid, ids, start, end, context=None):
if context == None:
contex = {}
allocation = {}
return allocation
resource_resource()
class resource_calendar_leaves(osv.osv):

View File

@ -225,7 +225,7 @@
</group>
<group colspan="2" col="2">
<field name="date_from"/>
<field name="calendar_id" string="Working Period"/>
<field name="calendar_id" string="Working Period" readonly='1'/>
<field name="company_id" widget="selection" groups="base.group_multi_company"/>
</group>
<group colspan="2" col="2">