diff --git a/addons/account/project/project_view.xml b/addons/account/project/project_view.xml index 789c76addcf..77e03c23700 100644 --- a/addons/account/project/project_view.xml +++ b/addons/account/project/project_view.xml @@ -243,7 +243,7 @@ - Analytic Entries + Analytic Items account.analytic.line form tree,form diff --git a/addons/outlook/plugin/openerp-outlook-plugin/tiny_xmlrpc.py b/addons/outlook/plugin/openerp-outlook-plugin/tiny_xmlrpc.py index 0ab5d08a6ec..0b4568fe8f4 100644 --- a/addons/outlook/plugin/openerp-outlook-plugin/tiny_xmlrpc.py +++ b/addons/outlook/plugin/openerp-outlook-plugin/tiny_xmlrpc.py @@ -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("<") diff --git a/addons/project/project.py b/addons/project/project.py index dda6819839a..b6664f9869b 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -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 = { diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 9539bd9acad..6ec47903ad2 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -29,20 +29,17 @@ - - - - - - + + + @@ -226,7 +223,7 @@
- + diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml index b40d0f42335..0c63698b209 100644 --- a/addons/project_issue/project_issue_view.xml +++ b/addons/project_issue/project_issue_view.xml @@ -333,7 +333,7 @@ form - + diff --git a/addons/project_long_term/__openerp__.py b/addons/project_long_term/__openerp__.py index 0d03b5f7315..25d06e3efe1 100644 --- a/addons/project_long_term/__openerp__.py +++ b/addons/project_long_term/__openerp__.py @@ -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", diff --git a/addons/project_long_term/i18n/de.po b/addons/project_long_term/i18n/de.po index 4c2b9281003..0d489ba8fbd 100644 --- a/addons/project_long_term/i18n/de.po +++ b/addons/project_long_term/i18n/de.po @@ -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 "" diff --git a/addons/project_long_term/i18n/en_US.po b/addons/project_long_term/i18n/en_US.po index 15a3bd4034b..3e64d59a5c1 100644 --- a/addons/project_long_term/i18n/en_US.po +++ b/addons/project_long_term/i18n/en_US.po @@ -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 diff --git a/addons/project_long_term/i18n/es.po b/addons/project_long_term/i18n/es.po index 829d67c1531..f28ffa8a67e 100644 --- a/addons/project_long_term/i18n/es.po +++ b/addons/project_long_term/i18n/es.po @@ -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 "" diff --git a/addons/project_long_term/i18n/fr.po b/addons/project_long_term/i18n/fr.po index 329e47626c6..57fcd6d1e47 100644 --- a/addons/project_long_term/i18n/fr.po +++ b/addons/project_long_term/i18n/fr.po @@ -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 "" diff --git a/addons/project_long_term/i18n/lv.po b/addons/project_long_term/i18n/lv.po index f7fc57d7f61..e01d607b031 100644 --- a/addons/project_long_term/i18n/lv.po +++ b/addons/project_long_term/i18n/lv.po @@ -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 "" diff --git a/addons/project_long_term/i18n/pl.po b/addons/project_long_term/i18n/pl.po index 7866d39f037..d2c668a3472 100644 --- a/addons/project_long_term/i18n/pl.po +++ b/addons/project_long_term/i18n/pl.po @@ -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 "" diff --git a/addons/project_long_term/i18n/project_long_term.pot b/addons/project_long_term/i18n/project_long_term.pot index cfacf155fc5..ec82f557b91 100644 --- a/addons/project_long_term/i18n/project_long_term.pot +++ b/addons/project_long_term/i18n/project_long_term.pot @@ -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 diff --git a/addons/project_long_term/i18n/pt.po b/addons/project_long_term/i18n/pt.po index fd6a5e08282..9c09ac02943 100644 --- a/addons/project_long_term/i18n/pt.po +++ b/addons/project_long_term/i18n/pt.po @@ -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 "" diff --git a/addons/project_long_term/project_long_term.py b/addons/project_long_term/project_long_term.py index 45d75b16b05..fc4151d855f 100644 --- a/addons/project_long_term/project_long_term.py +++ b/addons/project_long_term/project_long_term.py @@ -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: diff --git a/addons/project_long_term/project_long_term_demo.xml b/addons/project_long_term/project_long_term_demo.xml index 8a37f4cc444..961b0c62c9b 100644 --- a/addons/project_long_term/project_long_term_demo.xml +++ b/addons/project_long_term/project_long_term_demo.xml @@ -233,9 +233,9 @@ This Demo data file Human Resources, Phases and Resources,Tasks allocation and - + - + diff --git a/addons/project_long_term/project_long_term_view.xml b/addons/project_long_term/project_long_term_view.xml index ff4cfd8575b..8203cbfdf06 100644 --- a/addons/project_long_term/project_long_term_view.xml +++ b/addons/project_long_term/project_long_term_view.xml @@ -116,10 +116,10 @@ - + - + @@ -228,6 +228,7 @@ +