#
##############################################################################
-from crm import crm_case
+from base_status.base_stage import base_stage
from datetime import datetime, date
from lxml import etree
from osv import fields, osv
import time
from tools.translate import _
-_TASK_STATE = [('draft', 'New'),('open', 'In Progress'),('pending', 'Pending'), ('done', 'Done'), ('cancel', 'Cancelled')]
+_TASK_STATE = [('draft', 'New'),('open', 'In Progress'),('pending', 'Pending'), ('done', 'Done'), ('cancelled', 'Cancelled')]
class project_task_type(osv.osv):
_name = 'project.task.type'
help="This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display."),
}
_defaults = {
- 'state': 'draft',
'sequence': 1,
+ 'state': 'draft',
'fold': False,
}
_order = 'sequence'
res[task.project_id.id] += 1
return res
+ def _get_followers(self, cr, uid, ids, name, arg, context=None):
+ '''
+ Functional field that computes the users that are 'following' a thread.
+ '''
+ res = {}
+ for project in self.browse(cr, uid, ids, context=context):
+ l = set()
+ for message in project.message_ids:
+ l.add(message.user_id and message.user_id.id or False)
+ res[project.id] = list(filter(None, l))
+ return res
+
+ def _search_followers(self, cr, uid, obj, name, args, context=None):
+ project_obj = self.pool.get('project.project')
+ project_ids = project_obj.search(cr, uid, [('message_ids.user_id.id', 'in', args[0][2])], context=context)
+ return [('id', 'in', project_ids)]
+
_columns = {
'complete_name': fields.function(_complete_name, string="Project Name", type='char', size=250),
'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the project without removing it."),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of Projects."),
'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account', help="Link this project to an analytic account if you need financial management on projects. It enables you to connect projects with budgets, planning, cost and revenue analysis, timesheets on projects, etc.", ondelete="cascade", required=True),
'priority': fields.integer('Sequence', help="Gives the sequence order when displaying the list of projects"),
- 'warn_manager': fields.boolean('Notify Manager', help="If you check this field, the project manager will receive an email each time a task is completed by his team.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
-
'members': fields.many2many('res.users', 'project_user_rel', 'project_id', 'uid', 'Project Members',
help="Project's members are users who can have an access to the tasks related to this project.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
'tasks': fields.one2many('project.task', 'project_id', "Task Activities"),
'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'state'], 20),
}),
'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report", states={'close':[('readonly',True)]} ),
- 'warn_customer': fields.boolean('Warn Partner', help="If you check this, the user will have a popup when closing a task that propose a message to send by email to the customer.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
- 'warn_header': fields.text('Mail Header', help="Header added at the beginning of the email for the warning message sent to the customer when a task is closed.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
- 'warn_footer': fields.text('Mail Footer', help="Footer added at the beginning of the email for the warning message sent to the customer when a task is closed.", states={'close':[('readonly',True)], 'cancelled':[('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)]}),
- 'use_tasks': fields.boolean('Use Tasks', help="Check this field if this project is aimed at managing tasks"),
'task_count': fields.function(_task_count, type='integer', string="Open Tasks"),
'color': fields.integer('Color Index'),
- 'company_uom_id': fields.related('company_id', 'project_time_mode_id', type='many2one', relation='product.uom'),
+ 'privacy_visibility': fields.selection([('public','Public'), ('followers','Followers Only')], 'Privacy / Visibility', required=True),
+ 'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'), ('cancelled', 'Cancelled'),('pending','Pending'),('close','Closed')], 'Status', required=True,),
+ 'followers': fields.function(_get_followers, method=True, fnct_search=_search_followers,
+ type='many2many', relation='res.users', string='Followers'),
}
def dummy(self, cr, uid, ids, context):
return True
-
+
def _get_type_common(self, cr, uid, context):
ids = self.pool.get('project.task.type').search(cr, uid, [('case_default','=',1)], context=context)
return ids
_order = "sequence"
_defaults = {
'active': True,
+ 'type': 'contract',
+ 'state': 'open',
'priority': 1,
'sequence': 10,
'type_ids': _get_type_common,
- 'use_tasks': True,
+ 'privacy_visibility': 'public',
}
# TODO: Why not using a SQL contraints ?
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.write(cr, uid, task_ids, {'state': 'done', 'date_end':time.strftime('%Y-%m-%d %H:%M:%S'), 'remaining_hours': 0.0})
+ task_obj.case_close(cr, uid, task_ids, context=context)
self.write(cr, uid, ids, {'state':'close'}, context=context)
self.set_close_send_note(cr, uid, ids, context=context)
return True
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.write(cr, uid, task_ids, {'state': 'cancelled', 'date_end':time.strftime('%Y-%m-%d %H:%M:%S'), 'remaining_hours': 0.0})
+ task_obj.case_cancel(cr, uid, task_ids, context=context)
self.write(cr, uid, ids, {'state':'cancelled'}, context=context)
self.set_cancel_send_note(cr, uid, ids, context=context)
return True
# ------------------------------------------------
# OpenChatter methods and notifications
# ------------------------------------------------
-
- def get_needaction_user_ids(self, cr, uid, ids, context=None):
- result = dict.fromkeys(ids)
- for obj in self.browse(cr, uid, ids, context=context):
- result[obj.id] = []
- if obj.state == 'draft' and obj.user_id:
- result[obj.id] = [obj.user_id.id]
- return result
def message_get_subscribers(self, cr, uid, ids, context=None):
- sub_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context);
+ """ Override to add responsible user. """
+ user_ids = super(project, self).message_get_subscribers(cr, uid, ids, context=context)
for obj in self.browse(cr, uid, ids, context=context):
- if obj.user_id:
- sub_ids.append(obj.user_id.id)
- return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
+ if obj.user_id and not obj.user_id.id in user_ids:
+ user_ids.append(obj.user_id.id)
+ return user_ids
def create(self, cr, uid, vals, context=None):
obj_id = super(project, self).create(cr, uid, vals, context=context)
return self.message_append_note(cr, uid, ids, body=message, context=context)
-class task(crm_case, osv.osv):
+class task(base_stage, osv.osv):
_name = "project.task"
_description = "Task"
- _log_create = True
_date_name = "date_start"
_inherit = ['ir.needaction_mixin', 'mail.thread']
+ def _get_default_project_id(self, cr, uid, context=None):
+ """ Gives default section by checking if present in the context """
+ return (self._resolve_project_id_from_context(cr, uid, context=context) or False)
+
+ 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)
+
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
"""
if context is None: context = {}
if type(context.get('default_project_id')) in (int, long):
- project_id = context['default_project_id']
- return project_id
+ return context['default_project_id']
if isinstance(context.get('default_project_id'), basestring):
project_name = context['default_project_id']
project_ids = self.pool.get('project.project').name_search(cr, uid, name=project_name, context=context)
return project_ids[0][0]
return None
- def _read_group_type_id(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
+ def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
stage_obj = self.pool.get('project.task.type')
- project_id = self._resolve_project_id_from_context(cr, uid, context=context)
order = stage_obj._order
access_rights_uid = access_rights_uid or uid
- if read_group_order == 'type_id desc':
- # lame way to allow reverting search, should just work in the trivial case
+ # lame way to allow reverting search, should just work in the trivial case
+ if read_group_order == 'stage_id desc':
order = '%s desc' % order
+ # retrieve section_id from the context and write the domain
+ # - ('id', 'in', 'ids'): add columns that should be present
+ # - OR ('case_default', '=', True), ('fold', '=', False): add default columns that are not folded
+ # - OR ('project_ids', 'in', project_id), ('fold', '=', False) if project_id: add project columns that are not folded
+ search_domain = []
+ project_id = self._resolve_project_id_from_context(cr, uid, context=context)
if project_id:
- domain = ['|', ('id','in',ids), ('project_ids','in',project_id)]
- else:
- domain = ['|', ('id','in',ids), ('case_default','=',1)]
- stage_ids = stage_obj._search(cr, uid, domain, order=order, access_rights_uid=access_rights_uid, context=context)
+ search_domain += ['|', '&', ('project_ids', '=', project_id), ('fold', '=', False)]
+ search_domain += ['|', ('id', 'in', ids), '&', ('case_default', '=', True), ('fold', '=', False)]
+ stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
# restore order of the search
result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
return result
-
+
def _read_group_user_id(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
res_users = self.pool.get('res.users')
project_id = self._resolve_project_id_from_context(cr, uid, context=context)
return result
_group_by_full = {
- 'type_id': _read_group_type_id,
- 'user_id': _read_group_user_id
+ 'stage_id': _read_group_stage_ids,
+ 'user_id': _read_group_user_id,
}
def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
if not default.get('remaining_hours', False):
default['remaining_hours'] = float(self.read(cr, uid, id, ['planned_hours'])['planned_hours'])
default['active'] = True
- default['type_id'] = False
+ default['stage_id'] = False
if not default.get('name', False):
default['name'] = self.browse(cr, uid, id, context=context).name or ''
if not context.get('copy',False):
'description': fields.text('Description'),
'priority': fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Important'), ('0','Very important')], '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'),
+ 'stage_id': fields.many2one('project.task.type', 'Stage',
+ domain="['|', ('project_ids', '=', project_id), ('case_default', '=', True)]"),
'state': fields.related('stage_id', 'state', type="selection", store=True,
selection=_TASK_STATE, string="State", readonly=True,
help='The state is set to \'Draft\', when a case is created.\
}
_defaults = {
+ 'stage_id': _get_default_stage_id,
+ 'project_id': _get_default_project_id,
'state': 'draft',
'kanban_state': 'normal',
'priority': '2',
'sequence': 10,
'active': True,
'user_id': lambda obj, cr, uid, context: uid,
- 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'project.task', context=c)
+ 'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'project.task', context=c),
}
_order = "priority, sequence, date_start, name, id"
- def set_priority(self, cr, uid, ids, priority):
+ def set_priority(self, cr, uid, ids, priority, *args):
"""Set task priority
"""
return self.write(cr, uid, ids, {'priority' : priority})
#
def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
users_obj = self.pool.get('res.users')
-
+ if context is None: context = {}
# read uom as admin to avoid access rights issues, e.g. for portal/share users,
# this should be safe (no context passed to avoid side-effects)
obj_tm = users_obj.browse(cr, 1, uid, context=context).company_id.project_time_mode_id
def stage_find(self, cr, uid, cases, section_id, domain=[], order='sequence', context=None):
""" Override of the base.stage method
Parameter of the stage search taken from the lead:
- - type: stage type must be the same or 'both'
- section_id: if set, stages must belong to this section or
be a default stage; if not set, stages must be default
stages
"""
if isinstance(cases, (int, long)):
cases = self.browse(cr, uid, cases, context=context)
- domain = list(domain)
+ # collect all section_ids
+ section_ids = []
if section_id:
- domain += ['|', ('section_ids', '=', section_id)]
- domain.append(('case_default', '=', True))
- for lead in cases:
- domain += ['|', ('type', '=', lead.type), ('type', '=', 'both')]
- lead_section_id = lead.section_id.id if lead.section_id else None
- if lead_section_id:
- domain += ['|', ('section_ids', '=', lead_section_id), ('case_default', '=', True)]
- stage_ids = self.pool.get('crm.case.stage').search(cr, uid, domain, order=order, context=context)
+ section_ids.append(section_id)
+ for task in cases:
+ if task.project_id:
+ section_ids.append(task.project_id.id)
+ # OR all section_ids and OR with case_default
+ search_domain = []
+ if section_ids:
+ search_domain += [('|')] * len(section_ids)
+ for section_id in section_ids:
+ search_domain.append(('project_ids', '=', section_id))
+ search_domain.append(('case_default', '=', True))
+ # AND with the domain in parameter
+ 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:
return stage_ids[0]
return False
return True
def action_close(self, cr, uid, ids, context=None):
- """ This action closes the task, then opens the wizard to send an
- email to the partner or the project manager.
+ """ 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
- task = self.browse(cr, uid, task_id, context=context)
- project = task.project_id
- res = self.case_close(cr, uid, [task_id], context=context)
- if project.warn_manager or project.warn_customer:
- return {
- 'name': _('Send Email after close task'),
- 'view_type': 'form',
- 'view_mode': 'form',
- 'res_model': 'mail.compose.message',
- 'type': 'ir.actions.act_window',
- 'target': 'new',
- 'nodestroy': True,
- 'context': {'active_id': task.id,
- 'active_model': 'project.task'}
- }
- return res
+ 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. """
def case_close(self, cr, uid, ids, context=None):
""" Closes Task """
- request = self.pool.get('res.request')
if not isinstance(ids, list): ids = [ids]
for task in self.browse(cr, uid, ids, context=context):
vals = {}
project = task.project_id
- if project:
- # Send request to project manager
- if project.warn_manager and project.user_id and (project.user_id.id != uid):
- request.create(cr, uid, {
- 'name': _("Task '%s' closed") % task.name,
- 'state': 'waiting',
- 'act_from': uid,
- 'act_to': project.user_id.id,
- 'ref_partner_id': task.partner_id.id,
- 'ref_doc1': 'project.task,%d'% (task.id,),
- 'ref_doc2': 'project.project,%d'% (project.id,),
- }, context=context)
for parent_id in task.parent_ids:
if parent_id.state in ('pending','draft'):
reopen = True
# close task
vals['remaining_hours'] = 0.0
if not task.date_end:
- vals['date_end']: fields.datetime.now()
+ vals['date_end'] = fields.datetime.now()
self.case_set(cr, uid, [task.id], 'done', vals, context=context)
- self.case_close_send_note(cr, uid, ids, context=context)
+ self.case_close_send_note(cr, uid, [task.id], context=context)
return True
def do_reopen(self, cr, uid, ids, context=None):
- request = self.pool.get('res.request')
-
for task in self.browse(cr, uid, ids, context=context):
project = task.project_id
- if project and project.warn_manager and project.user_id.id and (project.user_id.id != uid):
- request.create(cr, uid, {
- 'name': _("Task '%s' set in progress") % task.name,
- 'state': 'waiting',
- 'act_from': uid,
- 'act_to': project.user_id.id,
- 'ref_partner_id': task.partner_id.id,
- 'ref_doc1': 'project.task,%d' % task.id,
- 'ref_doc2': 'project.project,%d' % project.id,
- }, context=context)
-
- self.write(cr, uid, [task.id], {'state': 'open'}, context=context)
- self.do_open_send_note(cr, uid, [task.id], context)
+ self.case_set(cr, uid, [task.id], 'open', {}, context=context)
+ self.case_open_send_note(cr, uid, [task.id], 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(cr, uid, ids, context=None):
- request = self.pool.get('res.request')
+ 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:
- project = task.project_id
- if project.warn_manager and project.user_id and (project.user_id.id != uid):
- request.create(cr, uid, {
- 'name': _("Task '%s' cancelled") % task.name,
- 'state': 'waiting',
- 'act_from': uid,
- 'act_to': project.user_id.id,
- 'ref_partner_id': task.partner_id.id,
- 'ref_doc1': 'project.task,%d' % task.id,
- 'ref_doc2': 'project.project,%d' % project.id,
- }, context=context)
- # cancel task
- self.case_set(cr, uid, [task.id], 'cancel', {'remaining_hours': 0.0}, context=context)
+ self.case_set(cr, uid, [task.id], 'cancelled', {'remaining_hours': 0.0}, context=context)
self.case_cancel_send_note(cr, uid, [task.id], context=context)
return True
""" Compatibility when changing to case_draft. """
return self.case_draft(cr, uid, ids, context=context)
- def case_draft(cr, uid, ids, context=None):
+ def case_draft(self, cr, uid, ids, context=None):
self.case_set(cr, uid, ids, 'draft', {}, context=context)
self.case_draft_send_note(cr, uid, ids, context=context)
return True
def set_kanban_state_blocked(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'kanban_state': 'blocked'}, context=context)
+ return False
def set_kanban_state_normal(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'kanban_state': 'normal'}, context=context)
+ return False
def set_kanban_state_done(self, cr, uid, ids, context=None):
self.write(cr, uid, ids, {'kanban_state': 'done'}, context=context)
-
- def _change_type(self, cr, uid, ids, next, context=None):
- """
- go to the next stage
- if next is False, go to previous stage
- """
- for task in self.browse(cr, uid, ids):
- if task.project_id.type_ids:
- typeid = task.type_id.id
- types_seq={}
- for type in task.project_id.type_ids :
- types_seq[type.id] = type.sequence
- if next:
- types = sorted(types_seq.items(), lambda x, y: cmp(x[1], y[1]))
- else:
- types = sorted(types_seq.items(), lambda x, y: cmp(y[1], x[1]))
- sorted_types = [x[0] for x in types]
- if not typeid:
- self.write(cr, uid, task.id, {'type_id': sorted_types[0]})
- elif typeid and typeid in sorted_types and sorted_types.index(typeid) != len(sorted_types)-1:
- index = sorted_types.index(typeid)
- self.write(cr, uid, task.id, {'type_id': sorted_types[index+1]})
- self.state_change_send_note(cr, uid, [task.id], context)
- return True
-
- def next_type(self, cr, uid, ids, context=None):
- return self._change_type(cr, uid, ids, True, context=context)
-
- def prev_type(self, cr, uid, ids, context=None):
- return self._change_type(cr, uid, ids, False, context=context)
+ return False
def _store_history(self, cr, uid, ids, context=None):
for task in self.browse(cr, uid, ids, context=context):
'remaining_hours': task.remaining_hours,
'planned_hours': task.planned_hours,
'kanban_state': task.kanban_state,
- 'type_id': task.type_id.id,
+ 'type_id': task.stage_id.id,
'state': task.state,
'user_id': task.user_id.id
return task_id
# Overridden to reset the kanban_state to normal whenever
- # the stage (type_id) of the task changes.
+ # 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 and not 'kanban_state' in vals and 'type_id' in vals:
- new_stage = vals.get('type_id')
+ 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.type_id != new_stage else vals
+ #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 ('type_id' in vals) or ('remaining_hours' in vals) or ('user_id' in vals) or ('state' in vals) or ('kanban_state' in vals):
+ if ('stage_id' in vals) or ('remaining_hours' in vals) or ('user_id' in vals) or ('state' in vals) or ('kanban_state' in vals):
self._store_history(cr, uid, ids, context=context)
- self.state_change_send_note(cr, uid, ids, context)
return result
def unlink(self, cr, uid, ids, context=None):
# ---------------------------------------------------
# OpenChatter methods and notifications
# ---------------------------------------------------
-
+
+ def case_get_note_msg_prefix(self, cr, uid, id, context=None):
+ """ Override of default prefix for notifications. """
+ return 'Task'
+
def get_needaction_user_ids(self, cr, uid, ids, context=None):
- result = dict.fromkeys(ids, [])
+ """ Returns the user_ids that have to perform an action.
+ Add to the previous results given by super the document responsible
+ when in draft mode.
+ :return: dict { record_id: [user_ids], }
+ """
+ result = super(task, self).get_needaction_user_ids(cr, uid, ids, context=context)
for obj in self.browse(cr, uid, ids, context=context):
if obj.state == 'draft' and obj.user_id:
- result[obj.id] = [obj.user_id.id]
+ result[obj.id].append(obj.user_id.id)
return result
def message_get_subscribers(self, cr, uid, ids, context=None):
- sub_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context);
+ """ Override to add responsible user and project manager. """
+ user_ids = super(task, self).message_get_subscribers(cr, uid, ids, context=context)
for obj in self.browse(cr, uid, ids, context=context):
- if obj.user_id:
- sub_ids.append(obj.user_id.id)
- if obj.manager_id:
- sub_ids.append(obj.manager_id.id)
- return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
+ if obj.user_id and not obj.user_id.id in user_ids:
+ user_ids.append(obj.user_id.id)
+ if obj.manager_id and not obj.manager_id.id in user_ids:
+ user_ids.append(obj.manager_id.id)
+ return user_ids
+
+ def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
+ """ Override of the (void) default notification method. """
+ stage_name = self.pool.get('project.task.type').name_get(cr, uid, [stage_id], context=context)[0][1]
+ return self.message_append_note(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context)
def create_send_note(self, cr, uid, ids, context=None):
return self.message_append_note(cr, uid, ids, body=_("Task has been <b>created</b>."), context=context)
self.message_append_note(cr, uid, [task.id], body=msg, context=context)
return True
- def state_change_send_note(self, cr, uid, ids, context=None):
- for task in self.browse(cr, uid, ids, context=context):
- msg = _('Stage changed to <b>%s</b>') % (task.type_id.name)
- self.message_append_note(cr, uid, [task.id], body=msg, context=context)
- return True
-
-task()
class project_work(osv.osv):
_name = "project.task.work"
for work in self.browse(cr, uid, ids):
cr.execute('update project_task set remaining_hours=remaining_hours + %s where id=%s', (work.hours, work.task_id.id))
return super(project_work,self).unlink(cr, uid, ids,*args, **kwargs)
-project_work()
-class account_analytic_account(osv.osv):
+class account_analytic_account(osv.osv):
_inherit = 'account.analytic.account'
_description = 'Analytic Account'
+ _columns = {
+ 'use_tasks': fields.boolean('Tasks Management',help="If check,this contract will be available in the project menu and you will be able to manage tasks or track issues"),
+ 'company_uom_id': fields.related('company_id', 'project_time_mode_id', type='many2one', relation='product.uom'),
+ }
+
+ def _trigger_project_creation(self, cr, uid, vals, context=None):
+ '''
+ This function is used to decide if a project needs to be automatically created or not when an analytic account is created. It returns True if it needs to be so, False otherwise.
+ '''
+ return vals.get('use_tasks')
+
+ def project_create(self, cr, uid, analytic_account_id, vals, context=None):
+ '''
+ This function is called at the time of analytic account creation and is used to create a project automatically linked to it if the conditions are meet.
+ '''
+ project_pool = self.pool.get('project.project')
+ project_id = project_pool.search(cr, uid, [('analytic_account_id','=', analytic_account_id)])
+ if not project_id and self._trigger_project_creation(cr, uid, vals, context=context):
+ project_values = {
+ 'name': vals.get('name'),
+ 'analytic_account_id': analytic_account_id,
+ }
+ return project_pool.create(cr, uid, project_values, context=context)
+ return False
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
if vals.get('child_ids', False) and context.get('analytic_project_copy', False):
vals['child_ids'] = []
- return super(account_analytic_account, self).create(cr, uid, vals, context=context)
+ analytic_account_id = super(account_analytic_account, self).create(cr, uid, vals, context=context)
+ self.project_create(cr, uid, analytic_account_id, vals, context=context)
+ return analytic_account_id
+
+ def write(self, cr, uid, ids, vals, context=None):
+ name = vals.get('name')
+ for account in self.browse(cr, uid, ids, context=context):
+ if not name:
+ vals['name'] = account.name
+ self.project_create(cr, uid, account.id, vals, context=context)
+ return super(account_analytic_account, self).write(cr, uid, ids, vals, context=context)
def unlink(self, cr, uid, ids, *args, **kwargs):
project_obj = self.pool.get('project.project')
raise osv.except_osv(_('Warning !'), _('Please delete the project linked with this account first.'))
return super(account_analytic_account, self).unlink(cr, uid, ids, *args, **kwargs)
-account_analytic_account()
#
# Tasks History, used for cumulative flow charts (Lean/Agile)
_defaults = {
'date': fields.date.context_today,
}
-project_task_history()
+
class project_task_history_cumulative(osv.osv):
_name = 'project.task.history.cumulative'
) as history
)
""")
-project_task_history_cumulative()
+