[IMP] Project_long_term: Refactoring Scheduling Process.
[odoo/odoo.git] / addons / project_long_term / project_long_term.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 from datetime import datetime, timedelta
23 from dateutil.relativedelta import relativedelta
24 from tools.translate import _
25 from osv import fields, osv
26 from resource.faces import task as Task 
27 import operator
28 from new import classobj
29 import types
30 import new
31
32 class project_phase(osv.osv):
33     _name = "project.phase"
34     _description = "Project Phase"
35
36     def _check_recursion(self, cr, uid, ids, context=None):
37          if context is None:
38             context = {}
39
40          data_phase = self.browse(cr, uid, ids[0], context=context)
41          prev_ids = data_phase.previous_phase_ids
42          next_ids = data_phase.next_phase_ids
43          # it should neither be in prev_ids nor in next_ids
44          if (data_phase in prev_ids) or (data_phase in next_ids):
45              return False
46          ids = [id for id in prev_ids if id in next_ids]
47          # both prev_ids and next_ids must be unique
48          if ids:
49              return False
50          # unrelated project
51          prev_ids = [rec.id for rec in prev_ids]
52          next_ids = [rec.id for rec in next_ids]
53          # iter prev_ids
54          while prev_ids:
55              cr.execute('SELECT distinct prv_phase_id FROM project_phase_rel WHERE next_phase_id IN %s', (tuple(prev_ids),))
56              prv_phase_ids = filter(None, map(lambda x: x[0], cr.fetchall()))
57              if data_phase.id in prv_phase_ids:
58                  return False
59              ids = [id for id in prv_phase_ids if id in next_ids]
60              if ids:
61                  return False
62              prev_ids = prv_phase_ids
63          # iter next_ids
64          while next_ids:
65              cr.execute('SELECT distinct next_phase_id FROM project_phase_rel WHERE prv_phase_id IN %s', (tuple(next_ids),))
66              next_phase_ids = filter(None, map(lambda x: x[0], cr.fetchall()))
67              if data_phase.id in next_phase_ids:
68                  return False
69              ids = [id for id in next_phase_ids if id in prev_ids]
70              if ids:
71                  return False
72              next_ids = next_phase_ids
73          return True
74
75     def _check_dates(self, cr, uid, ids, context=None):
76          for phase in self.read(cr, uid, ids, ['date_start', 'date_end'], context=context):
77              if phase['date_start'] and phase['date_end'] and phase['date_start'] > phase['date_end']:
78                  return False
79          return True
80
81     def _check_constraint_start(self, cr, uid, ids, context=None):
82          phase = self.read(cr, uid, ids[0], ['date_start', 'constraint_date_start'], context=context)
83          if phase['date_start'] and phase['constraint_date_start'] and phase['date_start'] < phase['constraint_date_start']:
84              return False
85          return True
86
87     def _check_constraint_end(self, cr, uid, ids, context=None):
88          phase = self.read(cr, uid, ids[0], ['date_end', 'constraint_date_end'], context=context)
89          if phase['date_end'] and phase['constraint_date_end'] and phase['date_end'] > phase['constraint_date_end']:
90              return False
91          return True
92
93     def _get_default_uom_id(self, cr, uid):
94        model_data_obj = self.pool.get('ir.model.data')
95        model_data_id = model_data_obj._get_id(cr, uid, 'product', 'uom_hour')
96        return model_data_obj.read(cr, uid, [model_data_id], ['res_id'])[0]['res_id']
97
98     def _compute(self, cr, uid, ids, field_name, arg, context=None):
99         res = {}
100         if not ids:
101             return res
102         for phase in self.browse(cr, uid, ids, context=context):
103             tot = 0.0
104             for task in phase.task_ids:
105                 tot += task.planned_hours
106             res[phase.id] = tot
107         return res
108
109     _columns = {
110         'name': fields.char("Name", size=64, required=True),
111         '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)]}),
112         '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)]}),
113         'constraint_date_start': fields.date('Minimum Start Date', help='force the phase to start after this date', states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
114         'constraint_date_end': fields.date('Deadline', help='force the phase to finish before this date', states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
115         'project_id': fields.many2one('project.project', 'Project', required=True),
116         'next_phase_ids': fields.many2many('project.phase', 'project_phase_rel', 'prv_phase_id', 'next_phase_id', 'Next Phases', states={'cancelled':[('readonly',True)]}),
117         'previous_phase_ids': fields.many2many('project.phase', 'project_phase_rel', 'next_phase_id', 'prv_phase_id', 'Previous Phases', states={'cancelled':[('readonly',True)]}),
118         'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of phases."),
119         'duration': fields.float('Duration', required=True, help="By default in days", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
120         'product_uom': fields.many2one('product.uom', 'Duration UoM', required=True, help="UoM (Unit of Measure) is the unit of measurement for Duration", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
121         'task_ids': fields.one2many('project.task', 'phase_id', "Project Tasks", states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
122         'resource_ids': fields.one2many('project.resource.allocation', 'phase_id', "Project Resources",states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
123         'responsible_id': fields.many2one('res.users', 'Responsible', states={'done':[('readonly',True)], 'cancelled':[('readonly',True)]}),
124         'state': fields.selection([('draft', 'Draft'), ('open', 'In Progress'), ('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')], 'State', readonly=True, required=True,
125                                   help='If the phase is created the state \'Draft\'.\n If the phase is started, the state becomes \'In Progress\'.\n If review is needed the phase is in \'Pending\' state.\
126                                   \n If the phase is over, the states is set to \'Done\'.'),
127         'total_hours': fields.function(_compute, method=True, string='Total Hours'),
128      }
129     _defaults = {
130         'responsible_id': lambda obj,cr,uid,context: uid,
131         'state': 'draft',
132         'sequence': 10,
133         'product_uom': lambda self,cr,uid,c: self.pool.get('product.uom').search(cr, uid, [('name', '=', _('Day'))], context=c)[0]
134     }
135     _order = "project_id, date_start, sequence, name"
136     _constraints = [
137         (_check_recursion,'Loops in phases not allowed',['next_phase_ids', 'previous_phase_ids']),
138         (_check_dates, 'Phase start-date must be lower than phase end-date.', ['date_start', 'date_end']),
139     ]
140
141     def onchange_project(self, cr, uid, ids, project, context=None):
142         result = {}
143         result['date_start'] = False
144         project_obj = self.pool.get('project.project')
145         if project:
146             project_id = project_obj.browse(cr, uid, project, context=context)
147             result['date_start'] = project_id.date_start
148         return {'value': result}
149
150
151     def _check_date_start(self, cr, uid, phase, date_end, context=None):
152        """
153        Check And Compute date_end of phase if change in date_start < older time.
154        """
155        uom_obj = self.pool.get('product.uom')
156        resource_obj = self.pool.get('resource.resource')
157        cal_obj = self.pool.get('resource.calendar')
158        calendar_id = phase.project_id.resource_calendar_id and phase.project_id.resource_calendar_id.id or False
159        resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)])
160        if resource_id:
161             res = resource_obj.read(cr, uid, resource_id, ['calendar_id'], context=context)[0]
162             cal_id = res.get('calendar_id', False) and res.get('calendar_id')[0] or False
163             if cal_id:
164                 calendar_id = cal_id
165        default_uom_id = self._get_default_uom_id(cr, uid)
166        avg_hours = uom_obj._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
167        work_times = cal_obj.interval_min_get(cr, uid, calendar_id, date_end, avg_hours or 0.0, resource_id and resource_id[0] or False)
168        dt_start = work_times[0][0].strftime('%Y-%m-%d')
169        self.write(cr, uid, [phase.id], {'date_start': dt_start, 'date_end': date_end.strftime('%Y-%m-%d')}, context=context)
170
171     def _check_date_end(self, cr, uid, phase, date_start, context=None):
172        """
173        Check And Compute date_end of phase if change in date_end > older time.
174        """
175        uom_obj = self.pool.get('product.uom')
176        resource_obj = self.pool.get('resource.resource')
177        cal_obj = self.pool.get('resource.calendar')
178        calendar_id = phase.project_id.resource_calendar_id and phase.project_id.resource_calendar_id.id or False
179        resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)], context=context)
180        if resource_id:
181             res = resource_obj.read(cr, uid, resource_id, ['calendar_id'], context=context)[0]
182             cal_id = res.get('calendar_id', False) and res.get('calendar_id')[0] or False
183             if cal_id:
184                 calendar_id = cal_id
185        default_uom_id = self._get_default_uom_id(cr, uid)
186        avg_hours = uom_obj._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
187        work_times = cal_obj.interval_get(cr, uid, calendar_id, date_start, avg_hours or 0.0, resource_id and resource_id[0] or False)
188        dt_end = work_times[-1][1].strftime('%Y-%m-%d')
189        self.write(cr, uid, [phase.id], {'date_start': date_start.strftime('%Y-%m-%d'), 'date_end': dt_end}, context=context)
190
191     def copy(self, cr, uid, id, default=None, context=None):
192         if default is None:
193             default = {}
194         if not default.get('name', False):
195             default['name'] = self.browse(cr, uid, id, context=context).name + _(' (copy)')
196         return super(project_phase, self).copy(cr, uid, id, default, context)
197
198     def set_draft(self, cr, uid, ids, *args):
199         self.write(cr, uid, ids, {'state': 'draft'})
200         return True
201
202     def set_open(self, cr, uid, ids, *args):
203         self.write(cr, uid, ids, {'state': 'open'})
204         return True
205
206     def set_pending(self, cr, uid, ids, *args):
207         self.write(cr, uid, ids, {'state': 'pending'})
208         return True
209
210     def set_cancel(self, cr, uid, ids, *args):
211         self.write(cr, uid, ids, {'state': 'cancelled'})
212         return True
213
214     def set_done(self, cr, uid, ids, *args):
215         self.write(cr, uid, ids, {'state': 'done'})
216         return True
217
218     def generate_phase(self, cr, uid, ids, f, parent=False, context=None):
219         if context is None:
220             context = {}
221         phase_ids = []
222         resource_pool = self.pool.get('resource.resource')
223         data_pool = self.pool.get('ir.model.data')
224         resource_allocation_pool = self.pool.get('project.resource.allocation')
225         uom_pool = self.pool.get('product.uom')
226         task_pool = self.pool.get('project.task')
227         data_model, day_uom_id = data_pool.get_object_reference(cr, uid, 'product', 'uom_day')
228         for phase in self.browse(cr, uid, ids, context=context):
229             avg_days = uom_pool._compute_qty(cr, uid, phase.product_uom.id, phase.duration, day_uom_id)
230             duration = str(avg_days) + 'd'
231             # Create a new project for each phase
232             str_resource = ('%s,'*len(phase.resource_ids))[:-1]
233             str_vals = str_resource % tuple(map(lambda x: 'Resource_%s'%x.resource_id.id, phase.resource_ids))
234             # Phases Defination for the Project
235             s = '''
236     def Phase_%s():
237         title = \"%s\"
238         effort = \'%s\'
239         resource = %s
240 '''%(phase.id, phase.name, duration, str_vals or False)
241             if parent:
242                 start = 'up.Phase_%s.end' % (parent.id)
243                 s += '''
244         start = %s
245 '''%(start)
246             else:
247                 start = phase.project_id.date_start or phase.date_start
248                 s += '''
249         start = \"%s\"
250 '''%(start)
251                 #start = datetime.strftime((datetime.strptime(start, "%Y-%m-%d")), "%Y-%m-%d")
252             
253             
254             phase_ids.append(phase.id)
255             parent = False
256             task_ids = []
257             todo_task_ids = task_pool.search(cr, uid, [('id', 'in', map(lambda x : x.id, phase.task_ids)),
258                                               ('state', 'in', ['draft', 'open', 'pending'])
259                                               ], order='sequence')
260             for task in task_pool.browse(cr, uid, todo_task_ids, context=context):
261                 s += task_pool.generate_task(cr, uid, task.id, parent=parent, flag=False, context=context)
262                 if not parent:
263                     parent = task
264                 task_ids.append(task.id)
265             
266             f += s + '\n'
267             # Recursive call till all the next phases scheduled
268             for next_phase in phase.next_phase_ids:
269                 if next_phase.state in ['draft', 'open', 'pending']:
270                     rf, rphase_ids = self.generate_phase(cr, uid, [next_phase.id], f = '', parent=phase, context=context)
271                     f += rf +'\n'
272                     phase_ids += rphase_ids
273                 else:   
274                     continue
275         return f, phase_ids
276
277     def schedule_tasks(self, cr, uid, ids, context=None):
278         """
279         Schedule tasks base on faces lib
280         """
281         if context is None:
282             context = {}
283         if type(ids) in (long, int,):
284             ids = [ids]
285         task_pool = self.pool.get('project.task')
286         resource_pool = self.pool.get('resource.resource')
287         data_pool = self.pool.get('ir.model.data')
288         resource_allocation_pool = self.pool.get('project.resource.allocation')
289
290         for phase in self.browse(cr, uid, ids, context=context):
291             project = phase.project_id
292             calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
293             start_date = project.date_start
294             #Creating resources using the member of the Project
295             u_ids = [i.id for i in project.members]
296             resource_objs = resource_pool.generate_resources(cr, uid, u_ids, calendar_id, context=context)
297             start_date = datetime.strftime((datetime.strptime(start_date, "%Y-%m-%d")), "%Y-%m-%d")
298             func_str = ''
299             start = start_date
300             minimum_time_unit = 1
301             # default values
302             working_hours_per_day = 24
303             working_days_per_week = 7
304             working_days_per_month = 30
305             working_days_per_year = 365
306             
307             vacation = []
308             if calendar_id:
309                 working_hours_per_day = 8 #TODO: it should be come from calendars
310                 working_days_per_week = 5
311                 working_days_per_month = 20
312                 working_days_per_year = 200
313                 vacation = tuple(resource_pool.compute_vacation(cr, uid, calendar_id, context=context))
314
315             working_days = resource_pool.compute_working_calendar(cr, uid, calendar_id, context=context)
316             
317             cls_str = ''
318             # Creating Resources for the Project
319             for key, vals in resource_objs.items():
320                 cls_str +='''
321     class Resource_%s(Resource):
322         title = \"%s\"
323         vacation = %s
324         efficiency = %s
325 '''%(key,  vals.get('name',False), vals.get('vacation', False), vals.get('efficiency', False))
326     
327             # Create a new project for each phase
328             func_str += '''
329 def Phase_%d():
330     from resource.faces import Resource
331     title = \"%s\"
332     start = \'%s\'
333     minimum_time_unit = %s
334     working_hours_per_day = %s
335     working_days_per_week = %s
336     working_days_per_month = %s
337     working_days_per_year = %s
338     vacation = %s
339     working_days =  %s
340 '''%(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 )
341             func_str += cls_str
342             parent = False
343             task_ids = []
344             todo_task_ids = task_pool.search(cr, uid, [('id', 'in', map(lambda x : x.id, phase.task_ids)),
345                                               ('state', 'in', ['draft', 'open', 'pending'])
346                                               ], order='sequence')
347             for task in task_pool.browse(cr, uid, todo_task_ids, context=context):
348                 func_str += task_pool.generate_task(cr, uid, task.id, parent=parent, flag=True, context=context)
349                 if not parent:
350                     parent = task
351                 task_ids.append(task.id)
352
353             #Temp File to test the Code for the Allocation
354 #            fn = '/home/hmo/Desktop/plt.py'
355 #            fp = open(fn, 'w')
356 #            fp.writelines(func_str)
357 #            fp.close()
358     
359             # Allocating Memory for the required Project and Pahses and Resources
360             exec(func_str)
361             Phase = eval('Phase_%d' % phase.id)
362             phase = Task.BalancedProject(Phase)
363         
364             for task_id in task_ids:
365                 task = eval("phase.Task_%d" % task_id)
366                 start_date = task.start.to_datetime()
367                 end_date = task.end.to_datetime()
368                 
369                 task_pool.write(cr, uid, [task_id], {
370                                       'date_start': start_date.strftime('%Y-%m-%d'),
371                                       'date_end': end_date.strftime('%Y-%m-%d')
372                                     }, context=context)
373         return True
374 project_phase()
375
376 class project_resource_allocation(osv.osv):
377     _name = 'project.resource.allocation'
378     _description = 'Project Resource Allocation'
379     _rec_name = 'resource_id'
380
381     def get_name(self, cr, uid, ids, field_name, arg, context=None):
382         res = {}
383         for allocation in self.browse(cr, uid, ids, context=context):
384             name = allocation.phase_id.name
385             name += ' (%s%%)' %(allocation.useability)
386             res[allocation.id] = name
387         return res
388     _columns = {
389         'name': fields.function(get_name, method=True, type='char', size=256),
390         'resource_id': fields.many2one('resource.resource', 'Resource', required=True),
391         'phase_id': fields.many2one('project.phase', 'Project Phase', ondelete='cascade', required=True),
392         'project_id': fields.related('phase_id', 'project_id', type='many2one', relation="project.project", string='Project', store=True),
393         'user_id': fields.related('resource_id', 'user_id', type='many2one', relation="res.users", string='User'),
394         'date_start': fields.date('Start Date', help="Starting Date"),
395         'date_end': fields.date('End Date', help="Ending Date"),
396         'useability': fields.float('Availability', help="Availability of this resource for this project phase in percentage (=50%)"),
397     }
398     _defaults = {
399         'useability': 100,
400     }
401
402 project_resource_allocation()
403
404 class project(osv.osv):
405     _inherit = "project.project"
406     _columns = {
407         'phase_ids': fields.one2many('project.phase', 'project_id', "Project Phases"),
408         'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report", states={'close':[('readonly',True)]} ),
409     }
410     def generate_members(self, cr, uid, ids, context=None):
411         """
412         Return a list of  Resource Class objects for the resources allocated to the phase.
413         """
414         res = {}
415         resource_pool = self.pool.get('resource.resource')
416         for project in self.browse(cr, uid, ids, context=context):
417             user_ids = map(lambda x:x.id, project.members)
418             calendar_id  = project.resource_calendar_id and project.resource_calendar_id.id or False
419             resource_objs = resource_pool.generate_resources(cr, uid, user_ids, calendar_id, context=context)
420             res[project.id] = resource_objs
421         return res
422
423     def schedule_phases(self, cr, uid, ids, context=None):
424         """
425         Schedule phase base on faces lib
426         """
427         if context is None:
428             context = {}
429         if type(ids) in (long, int,):
430             ids = [ids]
431         phase_pool = self.pool.get('project.phase')
432         task_pool = self.pool.get('project.task')        
433         resource_pool = self.pool.get('resource.resource')
434         data_pool = self.pool.get('ir.model.data')
435         resource_allocation_pool = self.pool.get('project.resource.allocation')
436         uom_pool = self.pool.get('product.uom')
437         data_model, day_uom_id = data_pool.get_object_reference(cr, uid, 'product', 'uom_day')
438
439         for project in self.browse(cr, uid, ids, context=context):
440             root_phase_ids = phase_pool.search(cr, uid, [('project_id', '=', project.id),
441                                                   ('state', 'in', ['draft', 'open', 'pending']),
442                                                   ('previous_phase_ids', '=', False)
443                                                   ])
444             calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
445             start_date = project.date_start
446             #if start_date:
447             #    start_date = datetime.strftime((datetime.strptime(start_date, "%Y-%m-%d")), "%Y-%m-%d")
448             #Creating resources using the member of the Project
449             u_ids = [i.id for i in project.members]
450             resource_objs = resource_pool.generate_resources(cr, uid, u_ids, calendar_id, context=context)
451             func_str = ''
452             start = start_date
453             minimum_time_unit = 1
454             # default values
455             working_hours_per_day = 24
456             working_days_per_week = 7
457             working_days_per_month = 30
458             working_days_per_year = 365
459             
460             vacation = []
461             if calendar_id:
462                 working_hours_per_day = 8 #TODO: it should be come from calendars
463                 working_days_per_week = 5
464                 working_days_per_month = 20
465                 working_days_per_year = 200
466                 vacation = tuple(resource_pool.compute_vacation(cr, uid, calendar_id, context=context))
467
468             working_days = resource_pool.compute_working_calendar(cr, uid, calendar_id, context=context)
469             
470             cls_str = ''
471             # Creating Resources for the Project
472             for key, vals in resource_objs.items():
473                 cls_str +='''
474     class Resource_%s(Resource):
475         title = \"%s\"
476         vacation = %s
477         efficiency = %s
478 '''%(key,  vals.get('name',False), vals.get('vacation', False), vals.get('efficiency', False))
479         
480             # Create a new project for each phase
481             func_str += '''
482 def Project_%d():
483     from resource.faces import Resource
484     title = \"%s\"
485     start = \'%s\'
486     minimum_time_unit = %s
487     working_hours_per_day = %s
488     working_days_per_week = %s
489     working_days_per_month = %s
490     working_days_per_year = %s
491     vacation = %s
492     working_days =  %s
493 '''%(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 )
494             func_str += cls_str
495             phase_ids = []
496             for root_phase in phase_pool.browse(cr, uid, root_phase_ids, context=context):
497                 phases, child_phase_ids = phase_pool.generate_phase(cr, uid, [root_phase.id], '', context=context)
498                 func_str += phases
499                 phase_ids += child_phase_ids
500             #Temp File to test the Code for the Allocation
501             fn = '/home/tiny/Desktop/plt.py'
502             fp = open(fn, 'w')
503             fp.writelines(func_str)
504             fp.close()
505         
506             # Allocating Memory for the required Project and Pahses and Resources
507             exec(func_str)
508             Project = eval('Project_%d' % project.id)
509             project = Task.BalancedProject(Project)
510
511             for phase_id in phase_ids:
512                 act_phase = phase_pool.browse(cr, uid, phase_id, context=context)
513                 print "============",act_phase
514                 phase = eval("project.Phase_%d" % phase_id)
515                 start_date = phase.start.to_datetime()
516                 end_date = phase.end.to_datetime()
517                 if act_phase.task_ids:
518                     for task in act_phase.task_ids:
519                         vals = {}
520                         #Getting values of the Tasks
521                         temp = eval("phase.Task_%s"%task.id)
522                         vals.update({'date_start' : temp.start.strftime('%Y-%m-%d %H:%M:%S')})
523 #                        vals.update({'date_end' : temp.end.strftime('%Y-%m-%d %H:%M:%S')})
524                         vals.update({'planned_hours' : str(temp._Task__calc_duration().strftime('%H.%M'))})
525                         vals.update({'date_deadline' : str(temp._Task__calc_end().strftime('%Y-%m-%d %H:%M:%S'))})
526                         task_pool.write(cr, uid, task.id, vals, context=context)
527                         print ">>>>>",dir(temp)                        
528                         print ">>>><<<<<<<<<<<<<<<<>",temp.booked_resource
529                         
530                 # Recalculate date_start and date_end
531                 # according to constraints on date start and date end on phase
532
533                 #if phase.constraint_date_start and str(s_date) < phase.constraint_date_start:
534                 #    start_date = datetime.strptime(phase.constraint_date_start, '%Y-%m-%d')
535                 #else:
536                 #    start_date = s_date
537                 #if phase.constraint_date_end and str(e_date) > phase.constraint_date_end:
538                 #    end_date= datetime.strptime(phase.constraint_date_end, '%Y-%m-%d')
539                 #    date_start = phase.constraint_date_end
540                 #else:
541                 #    end_date = e_date
542                 #    date_start = end_date
543
544                 # Write the calculated dates back
545                 #ctx = context.copy()
546                 #ctx.update({'scheduler': True})
547                 phase_pool.write(cr, uid, [phase_id], {
548                                       'date_start': start_date.strftime('%Y-%m-%d'),
549                                       'date_end': end_date.strftime('%Y-%m-%d')
550                                     }, context=context)
551         return True            
552
553     def schedule_tasks(self, cr, uid, ids, context=None):
554         """
555         Schedule task base on faces lib
556         """
557         if context is None:
558             context = {}
559         if type(ids) in (long, int,):
560             ids = [ids]
561         task_pool = self.pool.get('project.task')
562         resource_pool = self.pool.get('resource.resource')
563         data_pool = self.pool.get('ir.model.data')
564         resource_allocation_pool = self.pool.get('project.resource.allocation')
565         uom_pool = self.pool.get('product.uom')
566         data_model, day_uom_id = data_pool.get_object_reference(cr, uid, 'product', 'uom_day')
567
568         for project in self.browse(cr, uid, ids, context=context):
569             calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
570             start_date = project.date_start
571             #Creating resources using the member of the Project
572             u_ids = [i.id for i in project.members]
573             resource_objs = resource_pool.generate_resources(cr, uid, u_ids, calendar_id, context=context)
574             start_date = datetime.strftime((datetime.strptime(start_date, "%Y-%m-%d")), "%Y-%m-%d")
575             func_str = ''
576             start = start_date
577             minimum_time_unit = 1
578             # default values
579             working_hours_per_day = 24
580             working_days_per_week = 7
581             working_days_per_month = 30
582             working_days_per_year = 365
583             
584             vacation = []
585             if calendar_id:
586                 working_hours_per_day = 8 #TODO: it should be come from calendars
587                 working_days_per_week = 5
588                 working_days_per_month = 20
589                 working_days_per_year = 200
590                 vacation = tuple(resource_pool.compute_vacation(cr, uid, calendar_id, context=context))
591
592             working_days = resource_pool.compute_working_calendar(cr, uid, calendar_id, context=context)
593             
594             cls_str = ''
595             # Creating Resources for the Project
596             for key, vals in resource_objs.items():
597                 cls_str +='''
598     class Resource_%s(Resource):
599         title = \"%s\"
600         vacation = %s
601         efficiency = %s
602 '''%(key,  vals.get('name',False), vals.get('vacation', False), vals.get('efficiency', False))
603     
604             # Create a new project for each phase
605             func_str += '''
606 def Project_%d():
607     from resource.faces import Resource
608     title = \"%s\"
609     start = \'%s\'
610     minimum_time_unit = %s
611     working_hours_per_day = %s
612     working_days_per_week = %s
613     working_days_per_month = %s
614     working_days_per_year = %s
615     vacation = %s
616     working_days =  %s
617 '''%(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 )
618             func_str += cls_str
619             parent = False
620             task_ids = []
621             todo_task_ids = task_pool.search(cr, uid, [('project_id', '=', project.id),
622                                               ('state', 'in', ['draft', 'open', 'pending'])
623                                               ], order='sequence')
624             for task in task_pool.browse(cr, uid, todo_task_ids, context=context):
625                 func_str += task_pool.generate_task(cr, uid, task.id, parent=parent, flag=True,context=context)
626                 if not parent:
627                     parent = task
628                 task_ids.append(task.id)
629
630             #Temp File to test the Code for the Allocation
631             fn = '/home/tiny/Desktop/plt_1.py'
632             fp = open(fn, 'w')
633             fp.writelines(func_str)
634             fp.close()
635     
636             # Allocating Memory for the required Project and Pahses and Resources
637             exec(func_str)
638             Project = eval('Project_%d' % project.id)
639             project = Task.BalancedProject(Project)
640             for task_id in task_ids:
641                 task = eval("project.Task_%d" % task_id)
642                 start_date = task.start.to_datetime()
643                 end_date = task.end.to_datetime()
644                 
645                 task_pool.write(cr, uid, [task_id], {
646                                       'date_start': start_date.strftime('%Y-%m-%d'),
647                                       'date_end': end_date.strftime('%Y-%m-%d')
648                                     }, context=context)
649         return True
650
651 project()
652
653 class resource_resource(osv.osv):
654     _inherit = "resource.resource"
655     def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False):
656         if context is None:
657             context = {}
658         if context.get('project_id',False):
659             project_pool = self.pool.get('project.project')
660             project_rec = project_pool.browse(cr, uid, context['project_id'], context=context)
661             user_ids = [user_id.id for user_id in project_rec.members]
662             args.append(('user_id','in',user_ids))
663         return super(resource_resource, self).search(cr, uid, args, offset, limit, order, context, count)
664
665 resource_resource()
666
667 class project_task(osv.osv):
668     _inherit = "project.task"
669     _columns = {
670         'phase_id': fields.many2one('project.phase', 'Project Phase'),
671     }
672     _defaults = {
673         'user_id' : False
674     }
675
676     def generate_task(self, cr, uid, task_id, parent=False, flag=False, context=None):
677         if context is None:
678             context = {}
679         resource_pool = self.pool.get('resource.resource')
680         resource_allocation_pool = self.pool.get('project.resource.allocation')
681         task = self.browse(cr, uid, task_id, context=context)
682         duration = str(task.planned_hours )+ 'H'
683         resource = False
684         if task.user_id:
685             resource_ids = self.search(cr, uid, [('user_id', '=', task.user_id.id),('resource_type','=','user')], context=context)
686             print "\n\n==========", task.user_id.name, resource_ids
687             if len(resource_ids):
688                 resource = 'Resource_%s'%resource_ids[0]
689         # Phases Defination for the Project 
690         if not flag:
691             s = '''
692         def Task_%s():
693             title = \"%s\"
694             effort = \'%s\'
695             resource = %s
696 '''%(task.id, task.name, duration, resource)
697             if parent:
698                 start = 'up.Task_%s.end' % (parent.id)
699                 s += '''
700             start = %s
701 '''%(start)
702             #start = datetime.strftime((datetime.strptime(start, "%Y-%m-%d")), "%Y-%m-%d")
703         else:
704             s = '''
705     def Task_%s():
706         title = \"%s\"
707         effort = \'%s\'
708         resource = %s
709 '''%(task.id, task.name, duration, resource)
710             if parent:
711                 start = 'up.Task_%s.end' % (parent.id)
712                 s += '''
713         start = %s
714 '''%(start)
715         s += '\n'
716         return s
717 project_task()
718 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: