[IMP] project_long_term: Improve the module and bug fixes and overall cleaning
bzr revid: mra@tinyerp.com-20100311071006-u6usdghqls7t4inl
This commit is contained in:
@@ -32,7 +32,7 @@
|
||||
"init_xml" : [],
|
||||
"demo_xml" : ["project_demo.xml"],
|
||||
"update_xml": [
|
||||
#"security/ir.model.access.csv",
|
||||
"security/ir.model.access.csv",
|
||||
"project_wizard.xml" ,
|
||||
"project_view.xml",
|
||||
"project_workflow.xml"
|
||||
|
||||
@@ -18,24 +18,23 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from lxml import etree
|
||||
import mx.DateTime
|
||||
import time
|
||||
|
||||
from tools.translate import _
|
||||
from osv import fields, osv
|
||||
|
||||
|
||||
class project_phase(osv.osv):
|
||||
_name = "project.phase"
|
||||
_description = "Project Phase"
|
||||
|
||||
def _check_recursion(self, cr, uid, ids):
|
||||
obj_self = self.browse(cr, uid, ids[0])
|
||||
prev_ids = obj_self.previous_phase_ids
|
||||
next_ids = obj_self.next_phase_ids
|
||||
def _check_recursion(self, cr, uid, ids, context={}):
|
||||
data_phase = self.browse(cr, uid, ids[0], context=context)
|
||||
prev_ids = data_phase.previous_phase_ids
|
||||
next_ids = data_phase.next_phase_ids
|
||||
# it should nither be in prev_ids nor in next_ids
|
||||
if (obj_self in prev_ids) or (obj_self in next_ids):
|
||||
if (data_phase in prev_ids) or (data_phase in next_ids):
|
||||
return False
|
||||
ids = [id for id in prev_ids if id in next_ids]
|
||||
# both prev_ids and next_ids must be unique
|
||||
@@ -48,7 +47,7 @@ class project_phase(osv.osv):
|
||||
while prev_ids:
|
||||
cr.execute('select distinct prv_phase_id from project_phase_rel where next_phase_id in ('+','.join(map(str, prev_ids))+')')
|
||||
prv_phase_ids = filter(None, map(lambda x: x[0], cr.fetchall()))
|
||||
if obj_self.id in prv_phase_ids:
|
||||
if data_phase.id in prv_phase_ids:
|
||||
return False
|
||||
ids = [id for id in prv_phase_ids if id in next_ids]
|
||||
if ids:
|
||||
@@ -58,7 +57,7 @@ class project_phase(osv.osv):
|
||||
while next_ids:
|
||||
cr.execute('select distinct next_phase_id from project_phase_rel where prv_phase_id in ('+','.join(map(str, next_ids))+')')
|
||||
next_phase_ids = filter(None, map(lambda x: x[0], cr.fetchall()))
|
||||
if obj_self.id in next_phase_ids:
|
||||
if data_phase.id in next_phase_ids:
|
||||
return False
|
||||
ids = [id for id in next_phase_ids if id in prev_ids]
|
||||
if ids:
|
||||
@@ -66,33 +65,30 @@ class project_phase(osv.osv):
|
||||
next_ids = next_phase_ids
|
||||
return True
|
||||
|
||||
def _check_dates(self, cr, uid, ids):
|
||||
phase = self.read(cr, uid, ids[0], ['date_start', 'date_end'])
|
||||
if phase['date_start'] and phase['date_end']:
|
||||
if phase['date_start'] > phase['date_end']:
|
||||
return False
|
||||
def _check_dates(self, cr, uid, ids, context={}):
|
||||
phase = self.read(cr, uid, ids[0], ['date_start', 'date_end'], context=context)
|
||||
if phase['date_start'] and phase['date_end'] and phase['date_start'] > phase['date_end']:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_constraint_start(self, cr, uid, ids):
|
||||
phase = self.read(cr, uid, ids[0], ['date_start', 'constraint_date_start'])
|
||||
if phase['date_start'] and phase['constraint_date_start']:
|
||||
if phase['date_start'] < phase['constraint_date_start']:
|
||||
return False
|
||||
def _check_constraint_start(self, cr, uid, ids, context={}):
|
||||
phase = self.read(cr, uid, ids[0], ['date_start', 'constraint_date_start'], context=context)
|
||||
if phase['date_start'] and phase['constraint_date_start'] and phase['date_start'] < phase['constraint_date_start']:
|
||||
return False
|
||||
return True
|
||||
|
||||
def _check_constraint_end(self, cr, uid, ids):
|
||||
phase = self.read(cr, uid, ids[0], ['date_end', 'constraint_date_end'])
|
||||
if phase['date_end'] and phase['constraint_date_end']:
|
||||
if phase['date_end'] > phase['constraint_date_end']:
|
||||
return False
|
||||
def _check_constraint_end(self, cr, uid, ids, context={}):
|
||||
phase = self.read(cr, uid, ids[0], ['date_end', 'constraint_date_end'], context=context)
|
||||
if phase['date_end'] and phase['constraint_date_end'] and phase['date_end'] > phase['constraint_date_end']:
|
||||
return False
|
||||
return True
|
||||
|
||||
_columns = {
|
||||
'name': fields.char("Phase Name", size=64, required=True),
|
||||
'date_start': fields.datetime('Starting Date'),
|
||||
'date_end': fields.datetime('End Date'),
|
||||
'constraint_date_start': fields.datetime('Constraint Starting Date'),
|
||||
'constraint_date_end': fields.datetime('Constraint End Date'),
|
||||
'constraint_date_start': fields.datetime('Start Date'),
|
||||
'constraint_date_end': fields.datetime('End Date'),
|
||||
'project_id': fields.many2one('project.project', 'Project', required=True),
|
||||
'next_phase_ids': fields.many2many('project.phase', 'project_phase_rel', 'prv_phase_id', 'next_phase_id', 'Next Phases'),
|
||||
'previous_phase_ids': fields.many2many('project.phase', 'project_phase_rel', 'next_phase_id', 'prv_phase_id', 'Previous Phases'),
|
||||
@@ -120,73 +116,66 @@ class project_phase(osv.osv):
|
||||
(_check_constraint_end, 'Error! Phase must end-before Constraint End Date.', ['date_end', 'constraint_date_end']),
|
||||
]
|
||||
|
||||
def onchange_project(self, cr, uid, ids, project):
|
||||
def onchange_project(self, cr, uid, ids, project, context={}):
|
||||
result = {}
|
||||
if project:
|
||||
project_obj = self.pool.get('project.project')
|
||||
project_id = project_obj.browse(cr, uid, project)
|
||||
if project_id.date_start:
|
||||
result['date_start'] = mx.DateTime.strptime(project_id.date_start, "%Y-%m-%d").strftime('%Y-%m-%d %H:%M:%S')
|
||||
return {'value': result}
|
||||
project_id = self.pool.get('project.project').browse(cr, uid, project, context=context)
|
||||
if project_id.date_start:
|
||||
result['date_start'] = mx.DateTime.strptime(project_id.date_start, "%Y-%m-%d").strftime('%Y-%m-%d %H:%M:%S')
|
||||
return {'value': result}
|
||||
return {'value': {'date_start': []}}
|
||||
|
||||
def _check_date_start(self, cr, uid, phase, date_end, context=None):
|
||||
def _check_date_start(self, cr, uid, phase, date_end, context={}):
|
||||
"""
|
||||
Check And Compute date_end of phase if change in date_start < older time.
|
||||
"""
|
||||
resource_calendar_obj = self.pool.get('resource.calendar')
|
||||
uom_obj = self.pool.get('product.uom')
|
||||
resource_obj = self.pool.get('resource.resource')
|
||||
calendar_id = phase.project_id.resource_calendar_id.id
|
||||
calendar_id = phase.project_id.resource_calendar_id and phase.project_id.resource_calendar_id.id or False
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)])
|
||||
if resource_id:
|
||||
cal_id = resource_obj.browse(cr, uid, resource_id[0]).calendar_id.id
|
||||
cal_id = resource_obj.browse(cr, uid, resource_id[0], context=context).calendar_id.id
|
||||
if cal_id:
|
||||
calendar_id = cal_id
|
||||
default_uom_id = uom_obj.search(cr, uid, [('name', '=', 'Hour')])[0]
|
||||
default_uom_id = uom_obj.search(cr, uid, [('name', '=', 'Hour')], context=context)[0]
|
||||
avg_hours = uom_obj._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
|
||||
work_times = resource_calendar_obj.interval_min_get(cr, uid, calendar_id or False, date_end, avg_hours or 0.0, resource_id or False)
|
||||
work_times = self.pool.get('resource.calendar').interval_min_get(cr, uid, calendar_id, date_end, avg_hours or 0.0, resource_id[0] or False)
|
||||
dt_start = work_times[0][0].strftime('%Y-%m-%d %H:%M:%S')
|
||||
self.write(cr, uid, [phase.id], {'date_start': dt_start, 'date_end': date_end.strftime('%Y-%m-%d %H:%M:%S')})
|
||||
self.write(cr, uid, [phase.id], {'date_start': dt_start, 'date_end': date_end.strftime('%Y-%m-%d %H:%M:%S')}, context=context)
|
||||
|
||||
def _check_date_end(self, cr, uid, phase, date_start, context=None):
|
||||
def _check_date_end(self, cr, uid, phase, date_start, context={}):
|
||||
"""
|
||||
Check And Compute date_end of phase if change in date_end > older time.
|
||||
"""
|
||||
resource_calendar_obj = self.pool.get('resource.calendar')
|
||||
uom_obj = self.pool.get('product.uom')
|
||||
resource_obj = self.pool.get('resource.resource')
|
||||
calendar_id = phase.project_id.resource_calendar_id.id
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)])
|
||||
calendar_id = phase.project_id.resource_calendar_id and phase.project_id.resource_calendar_id.id or False
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)], context=context)
|
||||
if resource_id:
|
||||
cal_id = resource_obj.browse(cr, uid, resource_id[0]).calendar_id.id
|
||||
cal_id = resource_obj.browse(cr, uid, resource_id[0], context=context).calendar_id.id
|
||||
if cal_id:
|
||||
calendar_id = cal_id
|
||||
default_uom_id = uom_obj.search(cr, uid, [('name', '=', 'Hour')])[0]
|
||||
default_uom_id = uom_obj.search(cr, uid, [('name', '=', 'Hour')], context=context)[0]
|
||||
avg_hours = uom_obj._compute_qty(cr, uid, phase.product_uom.id, phase.duration, default_uom_id)
|
||||
work_times = resource_calendar_obj.interval_get(cr, uid, calendar_id or False, date_start, avg_hours or 0.0, resource_id or False)
|
||||
work_times = self.pool.get('resource.calendar').interval_get(cr, uid, calendar_id, date_start, avg_hours or 0.0, resource_id[0] or False)
|
||||
dt_end = work_times[-1][1].strftime('%Y-%m-%d %H:%M:%S')
|
||||
self.write(cr, uid, [phase.id], {'date_start': date_start.strftime('%Y-%m-%d %H:%M:%S'), 'date_end': dt_end})
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
self.write(cr, uid, [phase.id], {'date_start': date_start.strftime('%Y-%m-%d %H:%M:%S'), 'date_end': dt_end}, context=context)
|
||||
|
||||
def write(self, cr, uid, ids, vals, context={}):
|
||||
if not context:
|
||||
context = {}
|
||||
|
||||
if context.get('scheduler',False):
|
||||
return super(project_phase, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
# Consider calendar and efficiency if the phase is performed by a resource
|
||||
# otherwise consider the project's working calendar
|
||||
phase = self.browse(cr, uid, ids[0])
|
||||
phase = self.browse(cr, uid, ids[0], context=context)
|
||||
resource_calendar_obj = self.pool.get('resource.calendar')
|
||||
resource_obj = self.pool.get('resource.resource')
|
||||
uom_obj = self.pool.get('product.uom')
|
||||
resource_id = False
|
||||
calendar_id = phase.project_id.resource_calendar_id.id
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)])
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)],context=context)
|
||||
if resource_id:
|
||||
cal_id = resource_obj.browse(cr, uid, resource_id[0]).calendar_id.id
|
||||
cal_id = resource_obj.browse(cr, uid, resource_id[0], context=context).calendar_id.id
|
||||
if cal_id:
|
||||
calendar_id = cal_id
|
||||
default_uom_id = uom_obj.search(cr, uid, [('name', '=', 'Hour')])[0]
|
||||
@@ -194,25 +183,18 @@ class project_phase(osv.osv):
|
||||
|
||||
# Change the date_start and date_end
|
||||
# for previous and next phases respectively based on valid condition
|
||||
if vals.get('date_start'):
|
||||
if vals['date_start'] < phase.date_start:
|
||||
if vals.get('date_start', False) and vals['date_start'] < phase.date_start:
|
||||
dt_start = mx.DateTime.strptime(vals['date_start'],'%Y-%m-%d %H:%M:%S')
|
||||
work_times = resource_calendar_obj.interval_get(cr, uid, calendar_id or False, dt_start, avg_hours or 0.0, resource_id or False)
|
||||
work_times = resource_calendar_obj.interval_get(cr, uid, calendar_id or False, dt_start, avg_hours or 0.0, resource_id[0] or False)
|
||||
vals['date_end'] = work_times[-1][1].strftime('%Y-%m-%d %H:%M:%S')
|
||||
super(project_phase, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
for prv_phase in phase.previous_phase_ids:
|
||||
self._check_date_start(cr, uid, prv_phase, dt_start)
|
||||
if vals.get('date_end'):
|
||||
if vals['date_end'] > phase.date_end:
|
||||
self._check_date_start(cr, uid, prv_phase, dt_start, context=context)
|
||||
if vals.get('date_end', False) and vals['date_end'] > phase.date_end:
|
||||
dt_end = mx.DateTime.strptime(vals['date_end'],'%Y-%m-%d %H:%M:%S')
|
||||
work_times = resource_calendar_obj.interval_min_get(cr, uid, calendar_id or False, dt_end, avg_hours or 0.0, resource_id or False)
|
||||
work_times = resource_calendar_obj.interval_min_get(cr, uid, calendar_id or False, dt_end, avg_hours or 0.0, resource_id[0] or False)
|
||||
vals['date_start'] = work_times[0][0].strftime('%Y-%m-%d %H:%M:%S')
|
||||
super(project_phase, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
for next_phase in phase.next_phase_ids:
|
||||
self._check_date_end(cr, uid, next_phase, dt_end)
|
||||
|
||||
self._check_date_end(cr, uid, next_phase, dt_end, context=context)
|
||||
return super(project_phase, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
def set_draft(self, cr, uid, ids, *args):
|
||||
@@ -289,12 +271,12 @@ class task(osv.osv):
|
||||
hrs = planned / (float(occupation_rate) * resource.time_efficiency)
|
||||
if resource.calendar_id.id:
|
||||
calendar_id = resource.calendar_id.id
|
||||
work_times = resource_calendar.interval_get(cr, uid, calendar_id or False, dt_start, hrs or 0.0, resource_id or False)
|
||||
work_times = resource_calendar.interval_get(cr, uid, calendar_id or False, dt_start, hrs or 0.0, resource.id or False)
|
||||
result['date_end'] = work_times[-1][1].strftime('%Y-%m-%d %H:%M:%S')
|
||||
result['remaining_hours'] = planned - effective
|
||||
return {'value' : result}
|
||||
|
||||
def _check_date_start(self, cr, uid, task, date_end, context=None):
|
||||
def _check_date_start(self, cr, uid, task, date_end, context={}):
|
||||
"""
|
||||
Check And Compute date_end of task if change in date_start < older time.
|
||||
"""
|
||||
@@ -302,9 +284,9 @@ class task(osv.osv):
|
||||
resource_obj = self.pool.get('resource.resource')
|
||||
calendar_id = task.project_id.resource_calendar_id.id
|
||||
hours = task.planned_hours / task.occupation_rate
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', task.user_id.id)])
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', task.user_id.id)], context=context)
|
||||
if resource_id:
|
||||
resource = resource_obj.browse(cr, uid, resource_id[0])
|
||||
resource = resource_obj.browse(cr, uid, resource_id[0], context=context)
|
||||
if resource.calendar_id.id:
|
||||
calendar_id = resource.calendar_id.id
|
||||
hours = task.planned_hours / (float(task.occupation_rate) * resource.time_efficiency)
|
||||
@@ -312,30 +294,28 @@ class task(osv.osv):
|
||||
dt_start = work_times[0][0].strftime('%Y-%m-%d %H:%M:%S')
|
||||
self.write(cr, uid, [task.id], {'date_start' : dt_start,'date_end' : date_end.strftime('%Y-%m-%d %H:%M:%S')})
|
||||
|
||||
def _check_date_end(self, cr, uid, task, date_start, context=None):
|
||||
def _check_date_end(self, cr, uid, task, date_start, context={}):
|
||||
"""
|
||||
Check And Compute date_end of task if change in date_end > older time.
|
||||
"""
|
||||
|
||||
resource_calendar_obj = self.pool.get('resource.calendar')
|
||||
resource_obj = self.pool.get('resource.resource')
|
||||
calendar_id = task.project_id.resource_calendar_id.id
|
||||
hours = task.planned_hours / task.occupation_rate
|
||||
resource_id = resource_obj.search(cr,uid,[('user_id', '=', task.user_id.id)])
|
||||
resource_id = resource_obj.search(cr,uid,[('user_id', '=', task.user_id.id)], context=context)
|
||||
if resource_id:
|
||||
resource = resource_obj.browse(cr, uid, resource_id[0])
|
||||
resource = resource_obj.browse(cr, uid, resource_id[0], context=context)
|
||||
if resource.calendar_id.id:
|
||||
calendar_id = resource.calendar_id.id
|
||||
hours = task.planned_hours / (float(task.occupation_rate) * resource.time_efficiency)
|
||||
work_times = resource_calendar_obj.interval_get(cr, uid, calendar_id or False, date_start, hours or 0.0, resource_id or False)
|
||||
work_times = resource_calendar_obj.interval_get(cr, uid, calendar_id or False, date_start, hours or 0.0, resource.id or False)
|
||||
dt_end = work_times[-1][1].strftime('%Y-%m-%d %H:%M:%S')
|
||||
self.write(cr, uid, [task.id], {'date_start' : date_start.strftime('%Y-%m-%d %H:%M:%S'),'date_end' : dt_end})
|
||||
self.write(cr, uid, [task.id], {'date_start': date_start.strftime('%Y-%m-%d %H:%M:%S'),'date_end' : dt_end}, context=context)
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
def write(self, cr, uid, ids, vals, context={}):
|
||||
|
||||
if not context:
|
||||
context = {}
|
||||
|
||||
if context.get('scheduler',False):
|
||||
return super(task, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
@@ -349,36 +329,31 @@ class task(osv.osv):
|
||||
resource_obj = self.pool.get('resource.resource')
|
||||
calendar_id = task_rec.project_id.resource_calendar_id.id
|
||||
hrs = task_rec.planned_hours / task_rec.occupation_rate
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', task_rec.user_id.id)])
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', task_rec.user_id.id)], context=context)
|
||||
if resource_id:
|
||||
resource = resource_obj.browse(cr, uid, resource_id[0])
|
||||
resource = resource_obj.browse(cr, uid, resource_id[0], context=context)
|
||||
if resource.calendar_id.id:
|
||||
calendar_id = resource.calendar_id.id
|
||||
hrs = task_rec.planned_hours / (float(task_rec.occupation_rate) * resource.time_efficiency)
|
||||
|
||||
# Change the date_start and date_end
|
||||
# for previous and next tasks respectively based on valid condition
|
||||
if vals.get('date_start'):
|
||||
if vals['date_start'] < task_rec.date_start:
|
||||
dt_start = mx.DateTime.strptime(vals['date_start'], '%Y-%m-%d %H:%M:%S')
|
||||
work_times = resource_calendar_obj.interval_get(cr, uid, calendar_id or False, dt_start, hrs or 0.0, resource_id or False)
|
||||
vals['date_end'] = work_times[-1][1].strftime('%Y-%m-%d %H:%M:%S')
|
||||
super(task, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
for prv_task in task_rec.parent_ids:
|
||||
self._check_date_start(cr, uid, prv_task, dt_start)
|
||||
if vals.get('date_end'):
|
||||
if vals['date_end'] > task_rec.date_end:
|
||||
dt_end = mx.DateTime.strptime(vals['date_end'], '%Y-%m-%d %H:%M:%S')
|
||||
work_times = resource_calendar_obj.interval_min_get(cr, uid, calendar_id or False, dt_end, hrs or 0.0, resource_id or False)
|
||||
vals['date_start'] = work_times[0][0].strftime('%Y-%m-%d %H:%M:%S')
|
||||
super(task, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
for next_task in task_rec.child_ids:
|
||||
self._check_date_end(cr, uid, next_task, dt_end)
|
||||
if vals.get('date_start', False) and vals['date_start'] < task_rec.date_start:
|
||||
dt_start = mx.DateTime.strptime(vals['date_start'], '%Y-%m-%d %H:%M:%S')
|
||||
work_times = resource_calendar_obj.interval_get(cr, uid, calendar_id or False, dt_start, hrs or 0.0, resource.id or False)
|
||||
vals['date_end'] = work_times[-1][1].strftime('%Y-%m-%d %H:%M:%S')
|
||||
super(task, self).write(cr, uid, ids, vals, context=context)
|
||||
for prv_task in task_rec.parent_ids:
|
||||
self._check_date_start(cr, uid, prv_task, dt_start)
|
||||
if vals.get('date_end', False) and vals['date_end'] > task_rec.date_end:
|
||||
dt_end = mx.DateTime.strptime(vals['date_end'], '%Y-%m-%d %H:%M:%S')
|
||||
work_times = resource_calendar_obj.interval_min_get(cr, uid, calendar_id or False, dt_end, hrs or 0.0, resource.id or False)
|
||||
vals['date_start'] = work_times[0][0].strftime('%Y-%m-%d %H:%M:%S')
|
||||
super(task, self).write(cr, uid, ids, vals, context=context)
|
||||
for next_task in task_rec.child_ids:
|
||||
self._check_date_end(cr, uid, next_task, dt_end)
|
||||
|
||||
return super(task, self).write(cr, uid, ids, vals, context=context)
|
||||
|
||||
task()
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data noupdate="1">
|
||||
|
||||
|
||||
<record forcecreate="True" id="ir_cron_scheduler_action" model="ir.cron">
|
||||
|
||||
|
||||
<!-- <record forcecreate="True" id="ir_cron_scheduler_action" model="ir.cron">
|
||||
<field name="name">Calculate Project Task Length Scheduler</field>
|
||||
<field eval="False" name="active"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
@@ -13,11 +13,11 @@
|
||||
<field eval="False" name="doall"/>
|
||||
<field eval="True" name="active"/>
|
||||
<field eval="'project.project'" name="model"/>
|
||||
<field eval="'compute_date'" name="function"/>
|
||||
<field eval="'compute_date'" name="function"/>-->
|
||||
<!--<field model="project.project" search="[]" name="args"/>
|
||||
<field eval="select id from project.project" name="args"/>-->
|
||||
</record>
|
||||
|
||||
<!-- </record>-->
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
|
||||
@@ -68,9 +68,9 @@
|
||||
<field name="state" select="1"/>
|
||||
<button string="Draft" name="set_draft" states="open" icon="gtk-indent"/>
|
||||
<button string="Start Phase" name="set_open" states="pending,draft" icon="gtk-execute"/>
|
||||
<button string="Cancel" name="set_cancel" states="draft,open,pending" icon="gtk-cancel"/>
|
||||
<button string="Done" name="set_done" states="draft,pending,open" icon="gtk-jump-to"/>
|
||||
<button string="Pending" name="set_pending" states="open" icon="gtk-media-pause"/>
|
||||
<button string="Cancel" name="set_cancel" states="draft,open,pending" icon="gtk-cancel"/>
|
||||
</group>
|
||||
</page>
|
||||
<page string="Other Info">
|
||||
@@ -149,9 +149,10 @@
|
||||
<field name="project_id" on_change="onchange_project(project_id)"/>
|
||||
<field name="responsible_id"/>
|
||||
<field name="date_start"/>
|
||||
<field name="date_end"/>
|
||||
<!--<field name="date_end"/>
|
||||
<field name="duration"/>
|
||||
<field name="product_uom"/>
|
||||
<field name="product_uom"/>-->
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -190,17 +191,23 @@
|
||||
<field name="arch" type="xml">
|
||||
<search string="Project Phases">
|
||||
<group colspan="4" col="20">
|
||||
<filter string="Current" domain="[('state','in',('open','draft'))]" help="Draft, Open and Pending Phases" icon="terp-project" default="1"/>
|
||||
<filter string="In Progress" domain="[('state','=','open')]" help="Open Phases" icon="terp-project"/>
|
||||
<filter string="Pending" domain="[('state','=','pending')]" help="Pending Phases" icon="terp-project"/>
|
||||
<field name="name"/>
|
||||
<field name="project_id" widget="selection"/>
|
||||
<field name="responsible_id">
|
||||
<field name="project_id" widget="selection">
|
||||
<filter domain="[('project_id.user_id','=',uid)]" help="My Projects" icon="terp-project"/>
|
||||
</field>
|
||||
<field name="responsible_id" widget="selection">
|
||||
<filter domain="[('responsible_id','=',uid)]" help="My Phase" icon="gtk-execute" default="1"/>
|
||||
</field>
|
||||
<field name="date_start"/>
|
||||
<field name="date_end"/>
|
||||
<!--<field name="date_end"/>-->
|
||||
</group>
|
||||
<newline/>
|
||||
<group expand="1" string="Group By..." colspan="4" col="20">
|
||||
<filter string="Project" icon="terp-project" domain="[]" context="{'group_by':'project_id'}"/>
|
||||
<filter string="State" icon="terp-project" domain="[]" context="{'group_by':'state'}"/>
|
||||
<filter string="User" icon="terp-project" domain="[]" context="{'group_by':'responsible_id'}"/>
|
||||
<filter string="Start Date" icon="terp-project" domain="[]" context="{'group_by':'date_start'}"/>
|
||||
</group>
|
||||
@@ -209,7 +216,7 @@
|
||||
</record>
|
||||
|
||||
<record id="act_project_phase" model="ir.actions.act_window">
|
||||
<field name="name">Project phases</field>
|
||||
<field name="name">Project Phases</field>
|
||||
<field name="res_model">project.phase</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form,calendar,gantt</field>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<wizard id="wizard_compute_phase" menu="False" model="project.phase" name="wizard.compute.phases" string="Compute Phase Scheduling"/>
|
||||
<wizard id="wizard_schedule_task" menu="False" model="project.phase" name="phase.schedule.tasks" string="Schedule Tasks"/>
|
||||
<wizard id="wizard_compute_task" model="project.task" menu="False" name="wizard.compute.tasks" string="Compute Task Scheduling"/>
|
||||
|
||||
<menuitem icon="terp-project" id="base.menu_main" name="Project Management" sequence="1"/>
|
||||
<menuitem id="base.menu_pm_planning" name="Planning" parent="base.menu_main" sequence="3"/>
|
||||
<menuitem
|
||||
|
||||
@@ -18,14 +18,13 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
import datetime
|
||||
from resource.faces import *
|
||||
from new import classobj
|
||||
|
||||
import wizard
|
||||
import pooler
|
||||
from tools.translate import _
|
||||
import datetime
|
||||
|
||||
from resource.faces import *
|
||||
from new import classobj
|
||||
|
||||
import working_calendar as wkcal
|
||||
|
||||
@@ -60,12 +59,13 @@ class wizard_compute_phases(wizard.interface):
|
||||
leaves = []
|
||||
time_efficiency = 1.0
|
||||
resource_id = resource_obj.search(cr, uid, [('user_id', '=', phase.responsible_id.id)])
|
||||
|
||||
if resource_id:
|
||||
# Create a new resource object with
|
||||
# all the attributes of the Resource Class
|
||||
resource = resource_obj.browse(cr, uid, resource_id, context=context)[0]
|
||||
time_efficiency = resource.time_efficiency
|
||||
leaves = wkcal.compute_leaves(cr, uid, calendar_id or False , resource_id, resource.calendar_id.id)
|
||||
leaves = wkcal.compute_leaves(cr, uid, calendar_id , resource.id, resource.calendar_id.id)
|
||||
phase_resource_obj = classobj(str(phase.responsible_id.name), (Resource,),
|
||||
{'__doc__': phase.responsible_id.name,
|
||||
'__name__': phase.responsible_id.name,
|
||||
@@ -105,18 +105,19 @@ class wizard_compute_phases(wizard.interface):
|
||||
end_date = e_date
|
||||
date_start = end_date
|
||||
# Write the calculated dates back
|
||||
ctx = context.copy()
|
||||
ctx.update({'scheduler': True})
|
||||
phase_obj.write(cr, uid, [phase.id], {'date_start': start_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'date_end': end_date.strftime('%Y-%m-%d %H:%M:%S')},
|
||||
context={'scheduler': True
|
||||
})
|
||||
context=ctx)
|
||||
# Recursive call till all the next phases scheduled
|
||||
for phase in phase.next_phase_ids:
|
||||
if phase.state in ['draft','open','pending']:
|
||||
self._phase_schedule(cr, uid, phase, date_start, phase.project_id.resource_calendar_id.id or False)
|
||||
self._phase_schedule(cr, uid, phase, date_start, phase.project_id.resource_calendar_id.id or False, context=context)
|
||||
else:
|
||||
continue
|
||||
|
||||
def _compute_date(self, cr, uid, data, context):
|
||||
def _compute_date(self, cr, uid, data, context={}):
|
||||
"""
|
||||
Compute the phases for scheduling.
|
||||
"""
|
||||
@@ -132,7 +133,7 @@ class wizard_compute_phases(wizard.interface):
|
||||
else: # Else take all the draft,open,pending states phases
|
||||
phase_ids = phase_obj.search(cr, uid,[('state', 'in', ['draft', 'open', 'pending']),
|
||||
('previous_phase_ids', '=', False)
|
||||
])
|
||||
], context=context)
|
||||
phase_ids.sort()
|
||||
phase_objs = phase_obj.browse(cr, uid, phase_ids, context=context)
|
||||
for phase in phase_objs:
|
||||
@@ -141,7 +142,7 @@ class wizard_compute_phases(wizard.interface):
|
||||
start_date = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
start_dt = datetime.datetime.strftime((datetime.datetime.strptime(start_date, "%Y-%m-%d")), "%Y-%m-%d %H:%M")
|
||||
calendar_id = phase.project_id.resource_calendar_id.id
|
||||
self._phase_schedule(cr, uid, phase, start_dt, calendar_id or False)
|
||||
self._phase_schedule(cr, uid, phase, start_dt, calendar_id or False, context=context)
|
||||
return {}
|
||||
|
||||
def _open_phases_list(self, cr, uid, data, context):
|
||||
@@ -162,7 +163,7 @@ class wizard_compute_phases(wizard.interface):
|
||||
states = {
|
||||
'init': {
|
||||
'actions': [],
|
||||
'result': {'type':'form', 'arch':compute_form, 'fields':compute_fields, 'state':[
|
||||
'result': {'type': 'form', 'arch': compute_form, 'fields': compute_fields, 'state':[
|
||||
('end', 'Cancel'),
|
||||
('compute', 'Compute')
|
||||
|
||||
@@ -170,7 +171,7 @@ class wizard_compute_phases(wizard.interface):
|
||||
},
|
||||
'compute': {
|
||||
'actions': [_compute_date],
|
||||
'result': {'type': 'action', 'action':_open_phases_list, 'state':'end'},
|
||||
'result': {'type': 'action', 'action': _open_phases_list, 'state': 'end'},
|
||||
},
|
||||
}
|
||||
wizard_compute_phases('wizard.compute.phases')
|
||||
|
||||
@@ -18,18 +18,17 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import wizard
|
||||
import pooler
|
||||
from tools.translate import _
|
||||
import datetime
|
||||
|
||||
from resource.faces import *
|
||||
from new import classobj
|
||||
import operator
|
||||
|
||||
import working_calendar as wkcal
|
||||
|
||||
import wizard
|
||||
import pooler
|
||||
from tools.translate import _
|
||||
|
||||
compute_form = """<?xml version="1.0" ?>
|
||||
<form string="Compute Scheduling of Tasks">
|
||||
<field name="project_id" colspan="4"/>
|
||||
@@ -105,7 +104,7 @@ class wizard_compute_tasks(wizard.interface):
|
||||
try:
|
||||
resource = reduce(operator.or_, resources)
|
||||
except:
|
||||
raise wizard.except_wizard(_('MemberError'), _('Project must have members assigned !'))
|
||||
raise wizard.except_wizard(_('Error'), _('Project must have members assigned !'))
|
||||
minimum_time_unit = 1
|
||||
if calendar_id: # If project has calendar
|
||||
working_days = wkcal.compute_working_calendar(cr, uid, calendar_id)
|
||||
@@ -131,29 +130,30 @@ class wizard_compute_tasks(wizard.interface):
|
||||
s_date = t.start.to_datetime()
|
||||
e_date = t.end.to_datetime()
|
||||
if loop_no == 0:
|
||||
project_obj.write(cr, uid, [project_id], {'date' : e_date})
|
||||
project_obj.write(cr, uid, [project_id], {'date' : e_date}, context=context)
|
||||
else:
|
||||
ctx = context.copy()
|
||||
ctx.update({'scheduler': True})
|
||||
user_id = user_obj.search(cr, uid, [('name', '=', t.booked_resource[0].__name__)])
|
||||
task_pool.write(cr, uid, [task_objs[loop_no-1].id], {'date_start': s_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'date_deadline': e_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'user_id': user_id[0]},
|
||||
context={'scheduler': True
|
||||
})
|
||||
context=ctx)
|
||||
loop_no +=1
|
||||
return {}
|
||||
|
||||
states = {
|
||||
'init': {
|
||||
'actions': [],
|
||||
'result': {'type':'form', 'arch':compute_form, 'fields':compute_fields, 'state':[
|
||||
'result': {'type': 'form', 'arch': compute_form, 'fields': compute_fields, 'state': [
|
||||
('end', 'Cancel'),
|
||||
('compute', 'Compute')
|
||||
]},
|
||||
},
|
||||
'compute': {
|
||||
'actions': [_compute_date],
|
||||
'result': {'type':'form','arch':success_msg,'fields':{}, 'state':[('end', 'Ok')]},
|
||||
'result': {'type': 'form','arch': success_msg,'fields': {}, 'state': [('end', 'Ok')]},
|
||||
}
|
||||
}
|
||||
wizard_compute_tasks('wizard.compute.tasks')
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -18,18 +18,17 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import wizard
|
||||
import pooler
|
||||
from tools.translate import _
|
||||
import datetime
|
||||
|
||||
from resource.faces import *
|
||||
from new import classobj
|
||||
import operator
|
||||
|
||||
import working_calendar as wkcal
|
||||
|
||||
import wizard
|
||||
import pooler
|
||||
from tools.translate import _
|
||||
|
||||
success_msg = """<?xml version="1.0" ?>
|
||||
<form string="Compute Scheduling of Tasks">
|
||||
<label string="Task Scheduling completed successfully."/>
|
||||
@@ -48,7 +47,8 @@ class wizard_schedule_task(wizard.interface):
|
||||
resource_eff = res.time_efficiency
|
||||
resource_cal = res.calendar_id.id
|
||||
if resource_cal:
|
||||
leaves = wkcal.compute_leaves(cr, uid, phase.project_id.resource_calendar_id.id or False, res.id, resource_cal)
|
||||
cal_id = phase.project_id.resource_calendar_id and phase.project_id.resource_calendar_id.id or False
|
||||
leaves = wkcal.compute_leaves(cr, uid, cal_id, res.id, resource_cal, context=context)
|
||||
resource_objs.append(classobj(str(res.user_id.name), (Resource,),
|
||||
{'__doc__': res.user_id.name,
|
||||
'__name__': res.user_id.name,
|
||||
@@ -57,7 +57,7 @@ class wizard_schedule_task(wizard.interface):
|
||||
}))
|
||||
return resource_objs
|
||||
|
||||
def _compute_date(self, cr, uid, data, context):
|
||||
def _compute_date(self, cr, uid, data, context={}):
|
||||
"""
|
||||
Schedule the tasks according to resource available and priority.
|
||||
"""
|
||||
@@ -101,7 +101,7 @@ class wizard_schedule_task(wizard.interface):
|
||||
try:
|
||||
resource = reduce(operator.or_, resources)
|
||||
except:
|
||||
raise wizard.except_wizard(_('ResourceError'), _('Phase must have resources assigned !'))
|
||||
raise wizard.except_wizard(_('Error'), _('Phase must have resources assigned !'))
|
||||
minimum_time_unit = 1
|
||||
if calendar_id: # If project has working calendar
|
||||
working_days = wkcal.compute_working_calendar(cr, uid, calendar_id)
|
||||
@@ -127,12 +127,13 @@ class wizard_schedule_task(wizard.interface):
|
||||
s_date = t.start.to_datetime()
|
||||
e_date = t.end.to_datetime()
|
||||
if loop_no > 0:
|
||||
ctx = context.copy()
|
||||
ctx.update({'scheduler': True})
|
||||
user_id = user_obj.search(cr, uid, [('name', '=', t.booked_resource[0].__name__)])
|
||||
task_obj.write(cr, uid, [tasks[loop_no-1].id], {'date_start': s_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'date_deadline': e_date.strftime('%Y-%m-%d %H:%M:%S'),
|
||||
'user_id': user_id[0]},
|
||||
context={'scheduler': True
|
||||
})
|
||||
context=ctx)
|
||||
loop_no +=1
|
||||
return {}
|
||||
states = {
|
||||
|
||||
@@ -18,9 +18,9 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
import datetime
|
||||
|
||||
import pooler
|
||||
import datetime
|
||||
|
||||
def convert_timeformat(cr, uid, time_string, context={}):
|
||||
""" Convert input time string: 8.5 to output time string 8:30."""
|
||||
@@ -32,7 +32,7 @@ def convert_timeformat(cr, uid, time_string, context={}):
|
||||
converted_string = hour_part + ':' + str(round_mins)[0:2]
|
||||
return converted_string
|
||||
|
||||
def compute_leaves(cr, uid, calendar_id, resource_id=False, resource_calendar=False):
|
||||
def compute_leaves(cr, uid, calendar_id, resource_id=False, resource_calendar=False, context={}):
|
||||
|
||||
"""Compute the leaves from the working calendar of the resource.
|
||||
|
||||
@@ -49,12 +49,12 @@ def compute_leaves(cr, uid, calendar_id, resource_id=False, resource_calendar=Fa
|
||||
leave_ids = resource_calendar_leaves_obj.search(cr, uid, ['|', ('calendar_id', '=', calendar_id),
|
||||
('calendar_id', '=', resource_calendar),
|
||||
('resource_id', '=', resource_id)
|
||||
])
|
||||
], context=context)
|
||||
else:
|
||||
leave_ids = resource_calendar_leaves_obj.search(cr, uid, [('calendar_id', '=', calendar_id),
|
||||
('resource_id', '=', False)
|
||||
])
|
||||
leaves = resource_calendar_leaves_obj.read(cr, uid, leave_ids, ['date_from', 'date_to'])
|
||||
], context=context)
|
||||
leaves = resource_calendar_leaves_obj.read(cr, uid, leave_ids, ['date_from', 'date_to'], context=context)
|
||||
for i in range(len(leaves)):
|
||||
dt_start = datetime.datetime.strptime(leaves[i]['date_from'], '%Y-%m-%d %H:%M:%S')
|
||||
dt_end = datetime.datetime.strptime(leaves[i]['date_to'], '%Y-%m-%d %H:%M:%S')
|
||||
@@ -63,7 +63,7 @@ def compute_leaves(cr, uid, calendar_id, resource_id=False, resource_calendar=Fa
|
||||
leave_list.sort()
|
||||
return leave_list
|
||||
|
||||
def compute_working_calendar(cr, uid, calendar_id):
|
||||
def compute_working_calendar(cr, uid, calendar_id, context={}):
|
||||
|
||||
"""Change the format of working calendar from 'Openerp' format to bring it into 'Faces' format.
|
||||
|
||||
@@ -80,8 +80,8 @@ def compute_working_calendar(cr, uid, calendar_id):
|
||||
wk_time = {}
|
||||
wktime_list = []
|
||||
wktime_cal = []
|
||||
week_ids = resource_week_obj.search(cr, uid, [('calendar_id', '=', calendar_id)])
|
||||
weeks = resource_week_obj.read(cr, uid, week_ids, ['dayofweek', 'hour_from', 'hour_to'])
|
||||
week_ids = resource_week_obj.search(cr, uid, [('calendar_id', '=', calendar_id)], context=context)
|
||||
weeks = resource_week_obj.read(cr, uid, week_ids, ['dayofweek', 'hour_from', 'hour_to'], context=context)
|
||||
# Convert time formats into appropriate format required
|
||||
# and create a list like [('mon', '8:00-12:00'), ('mon', '13:00-18:00')]
|
||||
for week in weeks:
|
||||
|
||||
Reference in New Issue
Block a user