diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py
index 403cfab7d61..7181e52e9b6 100644
--- a/addons/mail/mail_thread.py
+++ b/addons/mail/mail_thread.py
@@ -328,8 +328,8 @@ class mail_thread(osv.AbstractModel):
# Track initial values of tracked fields
tracked_fields = self._get_tracked_fields(cr, uid, values.keys(), context=context)
if tracked_fields:
- initial = self.read(cr, uid, ids, tracked_fields.keys(), context=context)
- initial_values = dict((item['id'], item) for item in initial)
+ records = self.browse(cr, uid, ids, context=context)
+ initial_values = dict((this.id, dict((key, getattr(this, key)) for key in tracked_fields.keys())) for this in records)
# Perform write, update followers
result = super(mail_thread, self).write(cr, uid, ids, values, context=context)
@@ -388,7 +388,8 @@ class mail_thread(osv.AbstractModel):
if not value:
return ''
if col_info['type'] == 'many2one':
- return value[1]
+ # return value[1]
+ return value.name_get()[0][1]
if col_info['type'] == 'selection':
return dict(col_info['selection'])[value]
return value
@@ -407,7 +408,9 @@ class mail_thread(osv.AbstractModel):
if not tracked_fields:
return True
- for record in self.read(cr, uid, ids, tracked_fields.keys(), context=context):
+ for browse_record in self.browse(cr, uid, ids, context=context):
+ record = dict((key, getattr(browse_record, key)) for key in tracked_fields.keys())
+ record['id'] = browse_record.id
initial = initial_values[record['id']]
changes = []
tracked_values = {}
@@ -433,7 +436,7 @@ class mail_thread(osv.AbstractModel):
if field not in changes:
continue
for subtype, method in track_info.items():
- if method(self, cr, uid, record, context):
+ if method(self, cr, uid, browse_record, context):
subtypes.append(subtype)
posted = False
diff --git a/addons/project/__openerp__.py b/addons/project/__openerp__.py
index c0a516cf555..634f79d12ac 100644
--- a/addons/project/__openerp__.py
+++ b/addons/project/__openerp__.py
@@ -40,7 +40,6 @@
],
'depends': [
'base_setup',
- 'base_status',
'product',
'analytic',
'board',
@@ -66,7 +65,6 @@ Dashboard / Reports for Project Management will include:
'data': [
'security/project_security.xml',
'wizard/project_task_delegate_view.xml',
- 'wizard/project_task_reevaluate_view.xml',
'security/ir.model.access.csv',
'project_data.xml',
'project_view.xml',
diff --git a/addons/project/board_project_view.xml b/addons/project/board_project_view.xml
index 931c661d211..3a14f7b6274 100644
--- a/addons/project/board_project_view.xml
+++ b/addons/project/board_project_view.xml
@@ -16,7 +16,6 @@
-
@@ -26,7 +25,7 @@
project.task
form
tree,form
- [('user_id','=',uid),('state','not in',('cancel','done'))]
+ [('user_id', '=', uid), ('stage_id.fold', '!=', True)]
diff --git a/addons/project/project.py b/addons/project/project.py
index cadc4d8afb8..738c512fe16 100644
--- a/addons/project/project.py
+++ b/addons/project/project.py
@@ -28,10 +28,8 @@ from openerp import tools
from openerp.osv import fields, osv
from openerp.tools.translate import _
-from openerp.addons.base_status.base_stage import base_stage
from openerp.addons.resource.faces import task as Task
-_TASK_STATE = [('draft', 'New'),('open', 'In Progress'),('pending', 'Pending'), ('done', 'Done'), ('cancelled', 'Cancelled')]
class project_task_type(osv.osv):
_name = 'project.task.type'
@@ -44,9 +42,6 @@ class project_task_type(osv.osv):
'case_default': fields.boolean('Default for New Projects',
help="If you check this field, this stage will be proposed by default on each new project. It will not assign this stage to existing projects."),
'project_ids': fields.many2many('project.project', 'project_task_type_rel', 'type_id', 'project_id', 'Projects'),
- 'state': fields.selection(_TASK_STATE, 'Related Status', required=True,
- help="The status of your document is automatically changed regarding the selected stage. " \
- "For example, if a stage is related to the status 'Close', when your document reaches this stage, it is automatically closed."),
'fold': fields.boolean('Folded by Default',
help="This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display."),
}
@@ -57,7 +52,6 @@ class project_task_type(osv.osv):
return proj
_defaults = {
'sequence': 1,
- 'state': 'open',
'fold': False,
'case_default': False,
'project_ids': _get_default_project_id
@@ -152,11 +146,13 @@ class project(osv.osv):
cr.execute("""
SELECT project_id, COALESCE(SUM(planned_hours), 0.0),
COALESCE(SUM(total_hours), 0.0), COALESCE(SUM(effective_hours), 0.0)
- FROM project_task WHERE project_id IN %s AND state <> 'cancelled'
+ FROM project_task
+ LEFT JOIN project_task_type ON project_task.stage_id = project_task_type.id
+ WHERE project_task.project_id IN %s AND project_task_type.fold = False
GROUP BY project_id
""", (tuple(child_parent.keys()),))
# aggregate results into res
- res = dict([(id, {'planned_hours':0.0,'total_hours':0.0,'effective_hours':0.0}) for id in ids])
+ res = dict([(id, {'planned_hours':0.0, 'total_hours':0.0, 'effective_hours':0.0}) for id in ids])
for id, planned, total, effective in cr.fetchall():
# add the values specific to id to all parent projects of id in the result
while id:
@@ -253,22 +249,22 @@ class project(osv.osv):
'planned_hours': fields.function(_progress_rate, multi="progress", string='Planned Time', help="Sum of planned hours of all tasks related to this project and its child projects.",
store = {
'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
- 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'state'], 20),
+ 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'stage_id'], 20),
}),
'effective_hours': fields.function(_progress_rate, multi="progress", string='Time Spent', help="Sum of spent hours of all tasks related to this project and its child projects.",
store = {
'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
- 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'state'], 20),
+ 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'stage_id'], 20),
}),
'total_hours': fields.function(_progress_rate, multi="progress", string='Total Time', help="Sum of total hours of all tasks related to this project and its child projects.",
store = {
'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
- 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'state'], 20),
+ 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'stage_id'], 20),
}),
'progress_rate': fields.function(_progress_rate, multi="progress", string='Progress', type='float', group_operator="avg", help="Percent of tasks closed according to the total of tasks todo.",
store = {
'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
- 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'state'], 20),
+ 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'stage_id'], 20),
}),
'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report", states={'close':[('readonly',True)]} ),
'type_ids': fields.many2many('project.task.type', 'project_task_type_rel', 'project_id', 'type_id', 'Tasks Stages', states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
@@ -327,22 +323,16 @@ class project(osv.osv):
return res
def set_done(self, cr, uid, ids, context=None):
- task_obj = self.pool.get('project.task')
- task_ids = task_obj.search(cr, uid, [('project_id', 'in', ids), ('state', 'not in', ('cancelled', 'done'))])
- task_obj.case_close(cr, uid, task_ids, context=context)
- return self.write(cr, uid, ids, {'state':'close'}, context=context)
+ return self.write(cr, uid, ids, {'state': 'close'}, context=context)
def set_cancel(self, cr, uid, ids, context=None):
- task_obj = self.pool.get('project.task')
- task_ids = task_obj.search(cr, uid, [('project_id', 'in', ids), ('state', '!=', 'done')])
- task_obj.case_cancel(cr, uid, task_ids, context=context)
- return self.write(cr, uid, ids, {'state':'cancelled'}, context=context)
+ return self.write(cr, uid, ids, {'state': 'cancelled'}, context=context)
def set_pending(self, cr, uid, ids, context=None):
- return self.write(cr, uid, ids, {'state':'pending'}, context=context)
+ return self.write(cr, uid, ids, {'state': 'pending'}, context=context)
def set_open(self, cr, uid, ids, context=None):
- return self.write(cr, uid, ids, {'state':'open'}, context=context)
+ return self.write(cr, uid, ids, {'state': 'open'}, context=context)
def reset_project(self, cr, uid, ids, context=None):
return self.setActive(cr, uid, ids, value=True, context=context)
@@ -459,7 +449,8 @@ class project(osv.osv):
if project.user_id and (project.user_id.id not in u_ids):
u_ids.append(project.user_id.id)
for task in project.tasks:
- if task.state in ('done','cancelled'):
+ # TDE: be sure about this, was if task.state in ('done','cancelled')
+ if task.stage_id and task.stage_id.fold:
continue
if task.user_id and (task.user_id.id not in u_ids):
u_ids.append(task.user_id.id)
@@ -525,7 +516,8 @@ def Project():
for project in projects:
project_gantt = getattr(projects_gantt, 'Project_%d' % (project.id,))
for task in project.tasks:
- if task.state in ('done', 'cancelled'):
+ # TDE CHECK: was if task.state in ('done', 'cancelled')
+ if task.stage_id and task.stage_id.fold:
continue
p = getattr(project_gantt, 'Task_%d' % (task.id,))
@@ -566,23 +558,20 @@ def Project():
vals.update(alias_model_id=model_ids[0])
return super(project, self).write(cr, uid, ids, vals, context=context)
-class task(base_stage, osv.osv):
+
+class task(osv.osv):
_name = "project.task"
_description = "Task"
_date_name = "date_start"
_inherit = ['mail.thread', 'ir.needaction_mixin']
_track = {
- 'state': {
- 'project.mt_task_new': lambda self, cr, uid, obj, ctx=None: obj['state'] in ['new', 'draft'],
- 'project.mt_task_started': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'open',
- 'project.mt_task_closed': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done',
- },
'stage_id': {
- 'project.mt_task_stage': lambda self, cr, uid, obj, ctx=None: obj['state'] not in ['new', 'draft', 'done', 'open'],
+ 'project.mt_task_new': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence == 1,
+ 'project.mt_task_stage': lambda self, cr, uid, obj, ctx=None: obj.stage_id.sequence != 1,
},
'kanban_state': { # kanban state: tracked, but only block subtype
- 'project.mt_task_blocked': lambda self, cr, uid, obj, ctx=None: obj['kanban_state'] == 'blocked',
+ 'project.mt_task_blocked': lambda self, cr, uid, obj, ctx=None: obj.kanban_state == 'blocked',
},
}
@@ -593,14 +582,15 @@ class task(base_stage, osv.osv):
def _get_default_stage_id(self, cr, uid, context=None):
""" Gives default stage_id """
project_id = self._get_default_project_id(cr, uid, context=context)
- return self.stage_find(cr, uid, [], project_id, [('state', '=', 'draft')], context=context)
+ return self.stage_find(cr, uid, [], project_id, [('sequence', '=', '1')], context=context)
def _resolve_project_id_from_context(self, cr, uid, context=None):
""" Returns ID of project based on the value of 'default_project_id'
context key, or None if it cannot be resolved to a single
project.
"""
- if context is None: context = {}
+ if context is None:
+ context = {}
if type(context.get('default_project_id')) in (int, long):
return context['default_project_id']
if isinstance(context.get('default_project_id'), basestring):
@@ -669,7 +659,8 @@ class task(base_stage, osv.osv):
res[task.id]['progress'] = 0.0
if (task.remaining_hours + hours.get(task.id, 0.0)):
res[task.id]['progress'] = round(min(100.0 * hours.get(task.id, 0.0) / res[task.id]['total_hours'], 99.99),2)
- if task.state in ('done','cancelled'):
+ # TDE CHECK: if task.state in ('done','cancelled'):
+ if task.stage_id and task.stage_id.fold:
res[task.id]['progress'] = 100.0
return res
@@ -690,6 +681,13 @@ class task(base_stage, osv.osv):
return {'value':{'partner_id':partner_id.id}}
return {}
+ def onchange_user_id(self, cr, uid, ids, user_id, context=None):
+ vals = {}
+ if user_id:
+ vals['date_start'] = time.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
+ vals['date_end'] = False
+ return {'value': vals}
+
def duplicate_task(self, cr, uid, map_ids, context=None):
for new in map_ids.values():
task = self.browse(cr, uid, new, context)
@@ -743,17 +741,12 @@ class task(base_stage, osv.osv):
'active': fields.function(_is_template, store=True, string='Not a Template Task', type='boolean', help="This field is computed automatically and have the same behavior than the boolean 'active' field: if the task is linked to a template or unactivated project, it will be hidden unless specifically asked."),
'name': fields.char('Task Summary', size=128, required=True, select=True),
'description': fields.text('Description'),
- 'priority': fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Important'), ('0','Very important')], 'Priority', select=True),
+ 'priority': fields.selection([('4', 'Very Low'), ('3', 'Low'), ('2', 'Medium'),
+ ('1', 'Important'), ('0', 'Very important')],
+ string='Priority', select=True),
'sequence': fields.integer('Sequence', select=True, help="Gives the sequence order when displaying a list of tasks."),
'stage_id': fields.many2one('project.task.type', 'Stage', track_visibility='onchange',
domain="[('project_ids', '=', project_id)]"),
- 'state': fields.related('stage_id', 'state', type="selection", store=True,
- selection=_TASK_STATE, string="Status", readonly=True,
- help='The status is set to \'Draft\', when a case is created.\
- If the case is in progress the status is set to \'Open\'.\
- When the case is over, the status is set to \'Done\'.\
- If the case needs to be reviewed then the status is \
- set to \'Pending\'.'),
'categ_ids': fields.many2many('project.category', string='Tags'),
'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready for next stage')], 'Kanban State',
track_visibility='onchange',
@@ -766,6 +759,7 @@ class task(base_stage, osv.osv):
'date_start': fields.datetime('Starting Date',select=True),
'date_end': fields.datetime('Ending Date',select=True),
'date_deadline': fields.date('Deadline',select=True),
+ 'date_last_stage_update': fields.datetime('Last Stage Update', select=True),
'project_id': fields.many2one('project.project', 'Project', ondelete='set null', select="1", track_visibility='onchange'),
'parent_ids': fields.many2many('project.task', 'project_task_parent_rel', 'task_id', 'parent_id', 'Parent Tasks'),
'child_ids': fields.many2many('project.task', 'project_task_parent_rel', 'parent_id', 'task_id', 'Delegated Tasks'),
@@ -782,7 +776,7 @@ class task(base_stage, osv.osv):
'project.task': (lambda self, cr, uid, ids, c={}: ids, ['work_ids', 'remaining_hours', 'planned_hours'], 10),
'project.task.work': (_get_task, ['hours'], 10),
}),
- 'progress': fields.function(_hours_get, string='Progress (%)', multi='hours', group_operator="avg", help="If the task has a progress of 99.99% you should close the task if it's finished or reevaluate the time",
+ 'progress': fields.function(_hours_get, string='Working Time Progress (%)', multi='hours', group_operator="avg", help="If the task has a progress of 99.99% you should close the task if it's finished or reevaluate the time",
store = {
'project.task': (lambda self, cr, uid, ids, c={}: ids, ['work_ids', 'remaining_hours', 'planned_hours','state'], 10),
'project.task.work': (_get_task, ['hours'], 10),
@@ -805,6 +799,7 @@ class task(base_stage, osv.osv):
_defaults = {
'stage_id': _get_default_stage_id,
'project_id': _get_default_project_id,
+ 'date_last_stage_update': lambda *a: fields.datetime.now(),
'kanban_state': 'normal',
'priority': '2',
'progress': 0,
@@ -931,10 +926,11 @@ class task(base_stage, osv.osv):
section_ids.append(task.project_id.id)
search_domain = []
if section_ids:
- search_domain = [('|')] * (len(section_ids)-1)
+ search_domain = [('|')] * (len(section_ids) - 1)
for section_id in section_ids:
search_domain.append(('project_ids', '=', section_id))
- search_domain += list(domain)
+ if domain:
+ search_domain += list(domain)
# perform search, return the first found
stage_ids = self.pool.get('project.task.type').search(cr, uid, search_domain, order=order, context=context)
if stage_ids:
@@ -948,82 +944,11 @@ class task(base_stage, osv.osv):
for task in tasks:
if task.child_ids:
for child in task.child_ids:
- if child.state in ['draft', 'open', 'pending']:
+ # TDE CHECK: was 'if child.state in ['draft', 'open', 'pending']''
+ if child.stage_id and not child.stage_id.fold:
raise osv.except_osv(_("Warning!"), _("Child task still open.\nPlease cancel or complete child task first."))
return True
- def action_close(self, cr, uid, ids, context=None):
- """ This action closes the task
- """
- task_id = len(ids) and ids[0] or False
- self._check_child_task(cr, uid, ids, context=context)
- if not task_id: return False
- return self.do_close(cr, uid, [task_id], context=context)
-
- def do_close(self, cr, uid, ids, context=None):
- """ Compatibility when changing to case_close. """
- return self.case_close(cr, uid, ids, context=context)
-
- def case_close(self, cr, uid, ids, context=None):
- """ Closes Task """
- if not isinstance(ids, list): ids = [ids]
- for task in self.browse(cr, uid, ids, context=context):
- vals = {}
- project = task.project_id
- for parent_id in task.parent_ids:
- if parent_id.state in ('pending','draft'):
- reopen = True
- for child in parent_id.child_ids:
- if child.id != task.id and child.state not in ('done','cancelled'):
- reopen = False
- if reopen:
- self.do_reopen(cr, uid, [parent_id.id], context=context)
- # close task
- vals['remaining_hours'] = 0.0
- if not task.date_end:
- vals['date_end'] = fields.datetime.now()
- self.case_set(cr, uid, [task.id], 'done', vals, context=context)
- return True
-
- def do_reopen(self, cr, uid, ids, context=None):
- for task in self.browse(cr, uid, ids, context=context):
- project = task.project_id
- self.case_set(cr, uid, [task.id], 'open', {}, context=context)
- return True
-
- def do_cancel(self, cr, uid, ids, context=None):
- """ Compatibility when changing to case_cancel. """
- return self.case_cancel(cr, uid, ids, context=context)
-
- def case_cancel(self, cr, uid, ids, context=None):
- tasks = self.browse(cr, uid, ids, context=context)
- self._check_child_task(cr, uid, ids, context=context)
- for task in tasks:
- self.case_set(cr, uid, [task.id], 'cancelled', {'remaining_hours': 0.0}, context=context)
- return True
-
- def do_open(self, cr, uid, ids, context=None):
- """ Compatibility when changing to case_open. """
- return self.case_open(cr, uid, ids, context=context)
-
- def case_open(self, cr, uid, ids, context=None):
- if not isinstance(ids,list): ids = [ids]
- return self.case_set(cr, uid, ids, 'open', {'date_start': fields.datetime.now()}, context=context)
-
- def do_draft(self, cr, uid, ids, context=None):
- """ Compatibility when changing to case_draft. """
- return self.case_draft(cr, uid, ids, context=context)
-
- def case_draft(self, cr, uid, ids, context=None):
- return self.case_set(cr, uid, ids, 'draft', {}, context=context)
-
- def do_pending(self, cr, uid, ids, context=None):
- """ Compatibility when changing to case_pending. """
- return self.case_pending(cr, uid, ids, context=context)
-
- def case_pending(self, cr, uid, ids, context=None):
- return self.case_set(cr, uid, ids, 'pending', {}, context=context)
-
def _delegate_task_attachments(self, cr, uid, task_id, delegated_task_id, context=None):
attachment = self.pool.get('ir.attachment')
attachment_ids = attachment.search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', task_id)], context=context)
@@ -1044,6 +969,7 @@ class task(base_stage, osv.osv):
delegated_task_id = self.copy(cr, uid, task.id, {
'name': delegate_data['name'],
'project_id': delegate_data['project_id'] and delegate_data['project_id'][0] or False,
+ 'stage_id': delegate_data.get('stage_id', [False])[0],
'user_id': delegate_data['user_id'] and delegate_data['user_id'][0] or False,
'planned_hours': delegate_data['planned_hours'] or 0.0,
'parent_ids': [(6, 0, [task.id])],
@@ -1058,16 +984,13 @@ class task(base_stage, osv.osv):
'planned_hours': delegate_data['planned_hours_me'] + (task.effective_hours or 0.0),
'name': newname,
}, context=context)
- if delegate_data['state'] == 'pending':
- self.do_pending(cr, uid, [task.id], context=context)
- elif delegate_data['state'] == 'done':
- self.do_close(cr, uid, [task.id], context=context)
delegated_tasks[task.id] = delegated_task_id
return delegated_tasks
def set_remaining_time(self, cr, uid, ids, remaining_time=1.0, context=None):
for task in self.browse(cr, uid, ids, context=context):
- if (task.state=='draft') or (task.planned_hours==0.0):
+ # TDE CHECK: was (task.state=='draft')
+ if (task.stage_id and task.stage_id.sequence == 1) or (task.planned_hours == 0.0):
self.write(cr, uid, [task.id], {'planned_hours': remaining_time}, context=context)
self.write(cr, uid, ids, {'remaining_hours': remaining_time}, context=context)
return True
@@ -1102,50 +1025,57 @@ class task(base_stage, osv.osv):
'planned_hours': task.planned_hours,
'kanban_state': task.kanban_state,
'type_id': task.stage_id.id,
- 'state': task.state,
'user_id': task.user_id.id
}, context=context)
return True
+ # ------------------------------------------------
+ # CRUD overrides
+ # ------------------------------------------------
+
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
+ # user_id change: update date_start
+ if vals.get('user_id'):
+ vals['date_start'] = fields.datetime.now()
+ # generate a default stage based on context / given project value
if not vals.get('stage_id'):
ctx = context.copy()
if vals.get('project_id'):
ctx['default_project_id'] = vals['project_id']
vals['stage_id'] = self._get_default_stage_id(cr, uid, context=ctx)
+
# context: no_log, because subtype already handle this
create_context = dict(context, mail_create_nolog=True)
task_id = super(task, self).create(cr, uid, vals, context=create_context)
self._store_history(cr, uid, [task_id], context=context)
return task_id
- # Overridden to reset the kanban_state to normal whenever
- # the stage (stage_id) of the task changes.
def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
- if vals.get('project_id'):
- project_id = self.pool.get('project.project').browse(cr, uid, vals.get('project_id'), context=context)
- if project_id:
- vals.setdefault('message_follower_ids', [])
- vals['message_follower_ids'] += [(6, 0,[follower.id]) for follower in project_id.message_follower_ids]
+ # stage change: update date_last_stage_update
+ if 'stage_id' in vals:
+ vals['date_last_stage_update'] = fields.datetime.now()
+ # user_id change: update date_start
+ if vals.get('user_id'):
+ vals['date_start'] = fields.datetime.now()
+
+ # Overridden to reset the kanban_state to normal whenever
+ # the stage (stage_id) of the task changes.
if vals and not 'kanban_state' in vals and 'stage_id' in vals:
new_stage = vals.get('stage_id')
vals_reset_kstate = dict(vals, kanban_state='normal')
for t in self.browse(cr, uid, ids, context=context):
- #TO FIX:Kanban view doesn't raise warning
- #stages = [stage.id for stage in t.project_id.type_ids]
- #if new_stage not in stages:
- #raise osv.except_osv(_('Warning!'), _('Stage is not defined in the project.'))
write_vals = vals_reset_kstate if t.stage_id != new_stage else vals
super(task, self).write(cr, uid, [t.id], write_vals, context=context)
result = True
else:
result = super(task, self).write(cr, uid, ids, vals, context=context)
- if ('stage_id' in vals) or ('remaining_hours' in vals) or ('user_id' in vals) or ('state' in vals) or ('kanban_state' in vals):
+
+ if any(item in vals for item in ['stage_id', 'remaining_hours', 'user_id', 'kanban_state']):
self._store_history(cr, uid, ids, context=context)
return result
@@ -1161,7 +1091,8 @@ class task(base_stage, osv.osv):
result = ""
ident = ' '*ident
for task in tasks:
- if task.state in ('done','cancelled'):
+ # TDE CHECK: if task.state in ('done','cancelled'):
+ if task.stage_id and task.stage_id.fold:
continue
result += '''
%sdef Task_%s():
@@ -1231,13 +1162,14 @@ class task(base_stage, osv.osv):
update_vals[field] = float(res.group(2).lower())
except (ValueError, TypeError):
pass
- elif match.lower() == 'state' \
- and res.group(2).lower() in ['cancel','close','draft','open','pending']:
- act = 'do_%s' % res.group(2).lower()
+ # elif match.lower() == 'state' \
+ # and res.group(2).lower() in ['cancel','close','draft','open','pending']:
+ # act = 'do_%s' % res.group(2).lower()
if act:
getattr(self,act)(cr, uid, ids, context=context)
return super(task,self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context)
+ # TDE NOTE: FIXME
def project_task_reevaluate(self, cr, uid, ids, context=None):
if self.pool.get('res.users').has_group(cr, uid, 'project.group_time_work_estimation_tasks'):
return {
diff --git a/addons/project/project_data.xml b/addons/project/project_data.xml
index 7bf1a623a2c..0f48dfe9a2b 100644
--- a/addons/project/project_data.xml
+++ b/addons/project/project_data.xml
@@ -30,51 +30,43 @@
1
Analysis
- draft
-
+
- 2
+ 10
Specification
- pending
- 2
+ 11
Design
- open
- 3
+ 12
Development
- open
- 4
+ 13
Testing
- open
- 5
+ 14
Merge
- open
- 100
+ 20
Done
- done
- 200
+ 30
Cancelled
- cancelled
@@ -86,11 +78,11 @@
Task created
-
- Task Started
+
+ Task Assigned
project.task
- Task started
+ Task Assigned
Task Blocked
@@ -98,12 +90,6 @@
Task blocked
-
- Task Done
- project.task
-
- Task closed
-
Stage Changed
project.task
@@ -118,11 +104,11 @@
project_id
-
- Task Started
+
+ Task Assigned
project.project
-
+
project_id
@@ -131,12 +117,6 @@
project_id
-
- Task Done
- project.project
-
- project_id
-
Task Stage Changed
project.project
diff --git a/addons/project/project_demo.xml b/addons/project/project_demo.xml
index b7e1524c2c8..b53c3bb359f 100644
--- a/addons/project/project_demo.xml
+++ b/addons/project/project_demo.xml
@@ -225,7 +225,7 @@
ref('project.project_category_04')])]"/>
-
+
@@ -237,7 +237,7 @@
6
-
+
@@ -248,7 +248,7 @@
Design Use Cases
-
+
@@ -282,7 +282,7 @@
Set target for all deparments
-
+
@@ -293,7 +293,7 @@
Integration of core components
-
+
@@ -315,7 +315,7 @@
-
+
42.0
@@ -325,7 +325,7 @@
Create new components
-
+
14.0
@@ -337,7 +337,7 @@
-
+
12.0
@@ -371,7 +371,7 @@
-
+
20
@@ -382,7 +382,7 @@
Data importation + Doc
-
+
20
diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml
index 33842a9a19c..8eafacd3bc0 100644
--- a/addons/project/project_view.xml
+++ b/addons/project/project_view.xml
@@ -18,23 +18,20 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -371,18 +368,6 @@
@@ -436,7 +420,7 @@
+ groups="project.group_delegate_task"/>
@@ -445,7 +429,6 @@
-
@@ -453,12 +436,11 @@
-
+
-
@@ -467,6 +449,7 @@
+
@@ -493,7 +476,6 @@
-
@@ -513,7 +495,7 @@
2
5
10
- !
+
@@ -562,7 +544,7 @@
project.task
-
+
@@ -575,7 +557,6 @@
-
@@ -706,7 +687,6 @@
-
@@ -723,7 +703,7 @@
-
+
diff --git a/addons/project/report/project_report.py b/addons/project/report/project_report.py
index 006aae1085c..a14ca10dfce 100644
--- a/addons/project/report/project_report.py
+++ b/addons/project/report/project_report.py
@@ -19,7 +19,7 @@
#
##############################################################################
-from openerp.osv import fields,osv
+from openerp.osv import fields, osv
from openerp import tools
class report_project_task_user(osv.osv):
@@ -31,10 +31,12 @@ class report_project_task_user(osv.osv):
'day': fields.char('Day', size=128, readonly=True),
'year': fields.char('Year', size=64, required=False, readonly=True),
'user_id': fields.many2one('res.users', 'Assigned To', readonly=True),
- 'date_start': fields.date('Starting Date',readonly=True),
+ 'date_start': fields.date('Assignation Date', readonly=True),
'no_of_days': fields.integer('# of Days', size=128, readonly=True),
'date_end': fields.date('Ending Date', readonly=True),
'date_deadline': fields.date('Deadline', readonly=True),
+ 'date_last_stage_update': fields.date('Last Stage Update', readonly=True),
+ 'month_last_stage_update': fields.date('Month of Last Stage Update', readonly=True),
'project_id': fields.many2one('project.project', 'Project', readonly=True),
'hours_planned': fields.float('Planned Hours', readonly=True),
'hours_effective': fields.float('Effective Hours', readonly=True),
@@ -44,16 +46,17 @@ class report_project_task_user(osv.osv):
'total_hours': fields.float('Total Hours', readonly=True),
'closing_days': fields.float('Days to Close', digits=(16,2), readonly=True, group_operator="avg",
help="Number of Days to close the task"),
- 'opening_days': fields.float('Days to Open', digits=(16,2), readonly=True, group_operator="avg",
+ 'opening_days': fields.float('Days to Assign', digits=(16,2), readonly=True, group_operator="avg",
help="Number of Days to Open the task"),
'delay_endings_days': fields.float('Overpassed Deadline', digits=(16,2), readonly=True),
'nbr': fields.integer('# of tasks', readonly=True),
- 'priority' : fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Urgent'),
-('0','Very urgent')], 'Priority', readonly=True),
+ 'priority': fields.selection([('4', 'Very Low'), ('3', 'Low'), ('2', 'Medium'), ('1', 'Urgent'), ('0', 'Very urgent')],
+ string='Priority', readonly=True),
'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'), ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')], 'Month', readonly=True),
'state': fields.selection([('draft', 'Draft'), ('open', 'In Progress'), ('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')],'Status', readonly=True),
'company_id': fields.many2one('res.company', 'Company', readonly=True),
'partner_id': fields.many2one('res.partner', 'Contact', readonly=True),
+ 'stage_id': fields.many2one('project.task.type', 'Stage'),
}
_order = 'name desc, project_id'
@@ -69,19 +72,20 @@ class report_project_task_user(osv.osv):
to_char(date_start, 'YYYY-MM-DD') as day,
date_trunc('day',t.date_start) as date_start,
date_trunc('day',t.date_end) as date_end,
+ date_trunc('day',t.date_last_stage_update) as date_last_stage_update,
+ to_char(date_last_stage_update, 'MM') as month_last_stage_update,
to_date(to_char(t.date_deadline, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_deadline,
-- sum(cast(to_char(date_trunc('day',t.date_end) - date_trunc('day',t.date_start),'DD') as int)) as no_of_days,
abs((extract('epoch' from (t.date_end-t.date_start)))/(3600*24)) as no_of_days,
t.user_id,
progress as progress,
t.project_id,
- t.state,
t.effective_hours as hours_effective,
t.priority,
t.name as name,
t.company_id,
t.partner_id,
- t.stage_id,
+ t.stage_id as stage_id,
remaining_hours as remaining_hours,
total_hours as total_hours,
t.delay_hours as hours_delay,
@@ -106,15 +110,13 @@ class report_project_task_user(osv.osv):
date_start,
date_end,
date_deadline,
+ date_last_stage_update,
+ month_last_stage_update,
t.user_id,
t.project_id,
- t.state,
t.priority,
name,
t.company_id,
t.partner_id,
- t.stage_id
-
+ stage_id
""")
-
-
diff --git a/addons/project/report/project_report_view.xml b/addons/project/report/project_report_view.xml
index d9650e165ce..38107ab9bcf 100644
--- a/addons/project/report/project_report_view.xml
+++ b/addons/project/report/project_report_view.xml
@@ -15,9 +15,10 @@
-
+
+
@@ -44,7 +45,7 @@
-
+
@@ -58,31 +59,32 @@
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/addons/project/test/task_process.yml b/addons/project/test/task_process.yml
index e37b2116714..471642610a1 100644
--- a/addons/project/test/task_process.yml
+++ b/addons/project/test/task_process.yml
@@ -1,14 +1,5 @@
-
- I put task in pending due to specification is not clear.
--
- !python {model: project.task}: |
- self.do_pending(cr, uid, [ref("project_task_1")])
- context.update({"active_id": ref("project_task_1")})
--
- I check state of task after put in pending.
--
- !assert {model: project.task, id: project_task_1, severity: error, string: task should be in pending state}:
- - state == "pending"
+ I open a delegation wizard.
-
!record {model: project.task.delegate, id: delegate_id}:
user_id: base.user_demo
diff --git a/addons/project/wizard/__init__.py b/addons/project/wizard/__init__.py
index 7a9a734b2bd..24017d9cbe8 100644
--- a/addons/project/wizard/__init__.py
+++ b/addons/project/wizard/__init__.py
@@ -20,7 +20,5 @@
##############################################################################
import project_task_delegate
-import project_task_reevaluate
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
diff --git a/addons/project/wizard/project_task_reevaluate.py b/addons/project/wizard/project_task_reevaluate.py
deleted file mode 100644
index eeb8cccc5d2..00000000000
--- a/addons/project/wizard/project_task_reevaluate.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL ().
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Affero General Public License as
-# published by the Free Software Foundation, either version 3 of the
-# License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU Affero General Public License for more details.
-#
-# You should have received a copy of the GNU Affero General Public License
-# along with this program. If not, see .
-#
-##############################################################################
-
-from lxml import etree
-from openerp.osv import fields, osv
-from openerp.tools.translate import _
-
-class project_task_reevaluate(osv.osv_memory):
- _name = 'project.task.reevaluate'
-
- def _get_remaining(self, cr, uid, context=None):
- if context is None:
- context = {}
- active_id = context.get('active_id', False)
- res = False
- if active_id:
- res = self.pool.get('project.task').browse(cr, uid, active_id, context=context).remaining_hours
- return res
-
- _columns = {
- 'remaining_hours' : fields.float('Remaining Hours', digits=(16,2), help="Put here the remaining hours required to close the task."),
- }
-
- _defaults = {
- 'remaining_hours': _get_remaining,
- }
-
- def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
- res = super(project_task_reevaluate, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu=submenu)
- users_pool = self.pool.get('res.users')
- time_mode = users_pool.browse(cr, uid, uid, context).company_id.project_time_mode_id
- time_mode_name = time_mode and time_mode.name or 'Hours'
- if time_mode_name in ['Hours','Hour']:
- return res
-
- eview = etree.fromstring(res['arch'])
-
- def _check_rec(eview):
- if eview.attrib.get('widget','') == 'float_time':
- eview.set('widget','float')
- for child in eview:
- _check_rec(child)
- return True
-
- _check_rec(eview)
-
- res['arch'] = etree.tostring(eview)
-
- for field in res['fields']:
- if 'Hours' in res['fields'][field]['string']:
- res['fields'][field]['string'] = res['fields'][field]['string'].replace('Hours',time_mode_name)
- return res
-
- def compute_hours(self, cr, uid, ids, context=None):
- if context is None:
- context = {}
- data = self.browse(cr, uid, ids, context=context)[0]
- task_id = context.get('active_id')
- if task_id:
- task_pool = self.pool.get('project.task')
- task_pool.write(cr, uid, task_id, {'remaining_hours': data.remaining_hours})
- if context.get('button_reactivate'):
- task_pool.do_reopen(cr, uid, [task_id], context=context)
- return {'type': 'ir.actions.act_window_close'}
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/project/wizard/project_task_reevaluate_view.xml b/addons/project/wizard/project_task_reevaluate_view.xml
deleted file mode 100644
index 6dfe58e8cfd..00000000000
--- a/addons/project/wizard/project_task_reevaluate_view.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-
-
-
-
-
- Re-evaluate Task
- project.task.reevaluate
-
-
-
-
-
-
- Re-evaluate Task
- ir.actions.act_window
- project.task.reevaluate
- form
- form
- new
-
-
-
diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py
index 33e4fcb409c..4c89c4def60 100644
--- a/addons/project_issue/project_issue.py
+++ b/addons/project_issue/project_issue.py
@@ -21,7 +21,6 @@
from openerp import SUPERUSER_ID
from openerp.addons.base_status.base_stage import base_stage
-from openerp.addons.project.project import _TASK_STATE
from openerp.addons.crm import crm
from datetime import datetime
from openerp.osv import fields, osv, orm
@@ -247,13 +246,6 @@ class project_issue(base_stage, osv.osv):
'partner_id': fields.many2one('res.partner', 'Contact', select=1),
'company_id': fields.many2one('res.company', 'Company'),
'description': fields.text('Private Note'),
- 'state': fields.related('stage_id', 'state', type="selection", store=True,
- selection=_TASK_STATE, string="Status", readonly=True,
- help='The status is set to \'Draft\', when a case is created.\
- If the case is in progress the status is set to \'Open\'.\
- When the case is over, the status is set to \'Done\'.\
- If the case needs to be reviewed then the status is \
- set to \'Pending\'.'),
'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready for next stage')], 'Kanban State',
track_visibility='onchange',
help="A Issue's kanban state indicates special situations affecting it:\n"