[MERGE] Merged branch about task and issues followers being retrieved from the related project, with matching subtypes.

This implementation is not the best-one, because subscribing to a projet
does not subscribe the user to previous task; only future tasks (or issues).
However, the subtype model is quite experimental and is likely to be
improved in the next version. Therefore the current implementation
is kept.

bzr revid: tde@openerp.com-20121102162111-kg2q4u5gwu0z28kv
This commit is contained in:
Thibault Delavallée 2012-11-02 17:21:11 +01:00
commit 17a907fff4
7 changed files with 97 additions and 42 deletions

View File

@ -28,7 +28,7 @@
</record>
<record id="mt_comment" model="mail.message.subtype">
<field name="name">comment</field>
<field name="name">Discussions</field>
</record>
</data>
</openerp>

View File

@ -31,14 +31,15 @@ class mail_message_subtype(osv.osv):
_description = 'mail_message_subtype'
_columns = {
'name': fields.char('Message Type', required=True, translate=True,
help='Message subtype, gives a more precise type on the message, '\
help='Message subtype gives a more precise type on the message, '\
'especially for system notifications. For example, it can be '\
'a notification related to a new record (New), or to a stage '\
'change in a process (Stage change). Message subtypes allow to '\
'precisely tune the notifications the user want to receive on its wall.'),
'res_model': fields.char('Model', help="link subtype to model"),
'res_model': fields.char('Model',
help="Related model of the subtype. If False, this subtype exists for all models."),
'default': fields.boolean('Default',
help="When subscribing to the document, this subtype will be checked by default."),
help="Checked by default when subscribing."),
}
_defaults = {
'default': True,

View File

@ -334,8 +334,8 @@ class test_mail(TestMailMockups):
# ----------------------------------------
subtype_data = group_pigs._get_subscription_data(None, None)[group_pigs.id]['message_subtype_data']
self.assertEqual(set(subtype_data.keys()), set(['comment', 'mt_mg_def', 'mt_all_def', 'mt_mg_nodef', 'mt_all_nodef']), 'mail.group available subtypes incorrect')
self.assertFalse(subtype_data['comment']['followed'], 'Admin should not follow comments in pigs')
self.assertEqual(set(subtype_data.keys()), set(['Discussions', 'mt_mg_def', 'mt_all_def', 'mt_mg_nodef', 'mt_all_nodef']), 'mail.group available subtypes incorrect')
self.assertFalse(subtype_data['Discussions']['followed'], 'Admin should not follow Discussions in pigs')
self.assertTrue(subtype_data['mt_mg_nodef']['followed'], 'Admin should follow mt_mg_nodef in pigs')
self.assertTrue(subtype_data['mt_all_nodef']['followed'], 'Admin should follow mt_all_nodef in pigs')

View File

@ -1129,13 +1129,36 @@ class task(base_stage, osv.osv):
}, context=context)
return True
def _subscribe_project_followers_to_task(self, cr, uid, task_id, context=None):
""" TDE note: not the best way to do this, we could override _get_followers
of task, and perform a better mapping of subtypes than a mapping
based on names.
However we will keep this implementation, maybe to be refactored
in 7.1 of future versions. """
# task followers are project followers, with matching subtypes
task_record = self.browse(cr, uid, task_id, context=context)
subtype_obj = self.pool.get('mail.message.subtype')
follower_obj = self.pool.get('mail.followers')
if task_record.project_id:
# create mapping
task_subtype_ids = subtype_obj.search(cr, uid, ['|', ('res_model', '=', False), ('res_model', '=', self._name)], context=context)
task_subtypes = subtype_obj.browse(cr, uid, task_subtype_ids, context=context)
# fetch subscriptions
follower_ids = follower_obj.search(cr, uid, [('res_model', '=', 'project.project'), ('res_id', '=', task_record.project_id.id)], context=context)
# copy followers
for follower in follower_obj.browse(cr, uid, follower_ids, context=context):
if not follower.subtype_ids:
continue
project_subtype_names = [project_subtype.name for project_subtype in follower.subtype_ids]
task_subtype_ids = [task_subtype.id for task_subtype in task_subtypes if task_subtype.name in project_subtype_names]
self.message_subscribe(cr, uid, [task_id], [follower.partner_id.id],
subtype_ids=task_subtype_ids, context=context)
def create(self, cr, uid, vals, context=None):
task_id = super(task, self).create(cr, uid, vals, context=context)
task_record = self.browse(cr, uid, task_id, context=context)
if task_record.project_id:
project_follower_ids = [follower.id for follower in task_record.project_id.message_follower_ids]
self.message_subscribe(cr, uid, [task_id], project_follower_ids,
context=context)
# subscribe project followers to the task
self._subscribe_project_followers_to_task(cr, uid, task_id, context=context)
self._store_history(cr, uid, [task_id], context=context)
self.create_send_note(cr, uid, [task_id], context=context)
return task_id
@ -1145,9 +1168,6 @@ class task(base_stage, osv.osv):
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)
vals['message_follower_ids'] = [(4, follower.id) for follower in project_id.message_follower_ids]
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')
@ -1157,13 +1177,18 @@ class task(base_stage, osv.osv):
#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)
super(task, self).write(cr, uid, [t.id], write_vals, context=context)
self.stage_set_send_note(cr, uid, [t.id], new_stage, context=context)
result = True
else:
result = super(task,self).write(cr, uid, ids, vals, context=context)
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):
self._store_history(cr, uid, ids, context=context)
# subscribe new project followers to the task
if vals.get('project_id'):
for id in ids:
self._subscribe_project_followers_to_task(cr, uid, id, context=context)
return result
def unlink(self, cr, uid, ids, context=None):

View File

@ -86,37 +86,37 @@
<!-- mail: subtypes -->
<record id="mt_project_new" model="mail.message.subtype">
<field name="name">New Task</field>
<field name="name">New</field>
<field name="res_model">project.project</field>
<field name="default" eval="False"/>
</record>
<record id="mt_project_closed" model="mail.message.subtype">
<field name="name">Task Closed</field>
<field name="name">Closed</field>
<field name="res_model">project.project</field>
</record>
<record id="mt_project_canceled" model="mail.message.subtype">
<field name="name">Task Canceled</field>
<field name="res_model">project.task</field>
<field name="name">Canceled</field>
<field name="res_model">project.project</field>
</record>
<record id="mt_project_stage" model="mail.message.subtype">
<field name="name">Task Stage Changed</field>
<field name="res_model">project.task</field>
<field name="name">Stage Changed</field>
<field name="res_model">project.project</field>
</record>
<record id="mt_task_new" model="mail.message.subtype">
<field name="name">New Task</field>
<field name="name">New</field>
<field name="res_model">project.task</field>
</record>
<record id="mt_task_closed" model="mail.message.subtype">
<field name="name">Task Closed</field>
<field name="name">Closed</field>
<field name="res_model">project.task</field>
</record>
<record id="mt_task_canceled" model="mail.message.subtype">
<field name="name">Task canceled</field>
<field name="name">Canceled</field>
<field name="res_model">project.task</field>
</record>
<record id="mt_task_change" model="mail.message.subtype">
<field name="name">Task Stage Changed</field>
<field name="name">Stage Changed</field>
<field name="res_model">project.task</field>
</record>

View File

@ -359,22 +359,49 @@ class project_issue(base_stage, osv.osv):
return super(project_issue, self).copy(cr, uid, id, default=default,
context=context)
def _subscribe_project_followers_to_issue(self, cr, uid, task_id, context=None):
""" TDE note: not the best way to do this, we could override _get_followers
of issue, and perform a better mapping of subtypes than a mapping
based on names.
However we will keep this implementation, maybe to be refactored
in 7.1 of future versions. """
# task followers are project followers, with matching subtypes
task_record = self.browse(cr, uid, task_id, context=context)
subtype_obj = self.pool.get('mail.message.subtype')
follower_obj = self.pool.get('mail.followers')
if task_record.project_id:
# create mapping
task_subtype_ids = subtype_obj.search(cr, uid, ['|', ('res_model', '=', False), ('res_model', '=', self._name)], context=context)
task_subtypes = subtype_obj.browse(cr, uid, task_subtype_ids, context=context)
# fetch subscriptions
follower_ids = follower_obj.search(cr, uid, [('res_model', '=', 'project.project'), ('res_id', '=', task_record.project_id.id)], context=context)
# copy followers
for follower in follower_obj.browse(cr, uid, follower_ids, context=context):
if not follower.subtype_ids:
continue
project_subtype_names = [project_subtype.name for project_subtype in follower.subtype_ids]
task_subtype_ids = [task_subtype.id for task_subtype in task_subtypes if task_subtype.name in project_subtype_names]
self.message_subscribe(cr, uid, [task_id], [follower.partner_id.id],
subtype_ids=task_subtype_ids, context=context)
def write(self, cr, uid, ids, vals, context=None):
#Update last action date every time the user change the stage, the state or send a new email
logged_fields = ['stage_id', 'state', 'message_ids']
if any([field in vals for field in logged_fields]):
vals['date_action_last'] = time.strftime('%Y-%m-%d %H:%M:%S')
# subscribe new project followers to the issue
if vals.get('project_id'):
project_id = self.pool.get('project.project').browse(cr, uid, vals.get('project_id'), context=context)
vals['message_follower_ids'] = [(4, follower.id) for follower in project_id.message_follower_ids]
for id in ids:
self._subscribe_project_followers_to_issue(cr, uid, id, context=context)
return super(project_issue, self).write(cr, uid, ids, vals, context)
def onchange_task_id(self, cr, uid, ids, task_id, context=None):
if not task_id:
return {'value':{}}
return {'value': {}}
task = self.pool.get('project.task').browse(cr, uid, task_id, context=context)
return {'value':{'user_id': task.user_id.id,}}
return {'value': {'user_id': task.user_id.id, }}
def case_reset(self, cr, uid, ids, context=None):
"""Resets case as draft
@ -385,11 +412,11 @@ class project_issue(base_stage, osv.osv):
def create(self, cr, uid, vals, context=None):
obj_id = super(project_issue, self).create(cr, uid, vals, context=context)
project_id = self.browse(cr, uid, obj_id, context=context).project_id
if project_id:
followers = [follower.id for follower in project_id.message_follower_ids]
self.message_subscribe(cr, uid, [obj_id], followers, context=context)
# subscribe project follower to the issue
self._subscribe_project_followers_to_issue(cr, uid, obj_id, context=context)
self.create_send_note(cr, uid, [obj_id], context=context)
return obj_id
# -------------------------------------------------------

View File

@ -44,17 +44,19 @@ Access all issues from the top Project menu, and access the issues of a specific
<!-- Mail subtypes -->
<record id="mail.mt_issue_new" model="mail.message.subtype">
<field name="name">created</field>
<field name="name">New</field>
<field name="res_model">project.issue</field>
<field name="default" eval="False"/>
</record>
<record id="mail.mt_issue_new" model="mail.message.subtype">
<field name="name">stage change</field>
<field name="res_model">project.issue</field>
<field name="default" eval="False"/>
</record>
<record id="mail.mt_issue_closed" model="mail.message.subtype">
<field name="name">closed</field>
<field name="name">Closed</field>
<field name="res_model">project.issue</field>
</record>
<record id="mail.mt_issue_canceled" model="mail.message.subtype">
<field name="name">Canceled</field>
<field name="res_model">project.issue</field>
</record>
<record id="mail.mt_issue_change" model="mail.message.subtype">
<field name="name">Stage Changed</field>
<field name="res_model">project.issue</field>
</record>