diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py index 643908c1098..f0f181c47da 100644 --- a/addons/crm/__openerp__.py +++ b/addons/crm/__openerp__.py @@ -129,7 +129,8 @@ Creates a dashboard for CRM that includes: 'test/process/segmentation.yml', 'test/ui/crm_demo.yml', 'test/ui/duplicate_lead.yml', - 'test/ui/delete_lead.yml' + 'test/ui/delete_lead.yml', + 'test/process/crm_message_subtype.yml', ], 'installable': True, 'application': True, diff --git a/addons/crm/crm_data.xml b/addons/crm/crm_data.xml index c5671385e56..e5f067c1075 100644 --- a/addons/crm/crm_data.xml +++ b/addons/crm/crm_data.xml @@ -56,7 +56,7 @@ - + Module CRM has been installed From the top menu Sales, you can: trace leads and opportunities, get accurate forecast on your sales pipeline, plan meetings and phonecalls, get realtime statistics and efficiently organize the communication with your prospects. diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 146417a7abb..0ecc7d9bee2 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -278,6 +278,9 @@ class crm_lead(base_stage, osv.osv): def create(self, cr, uid, vals, context=None): obj_id = super(crm_lead, self).create(cr, uid, vals, context) + record = self.browse(cr, uid, obj_id, context) + if record.user_id: + self.message_subscribe(cr, uid, [obj_id], [record.user_id.id], context = context) self.create_send_note(cr, uid, [obj_id], context=context) return obj_id @@ -867,7 +870,7 @@ class crm_lead(base_stage, osv.osv): 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('crm.case.stage').name_get(cr, uid, [stage_id], context=context)[0][1] - return self.message_append_note(cr, uid, ids, body= _("Stage changed to %s.") % (stage_name), context=context) + return self.message_append_note(cr, uid, ids, body= _("Stage changed to %s.") % (stage_name), subtype="stage change",context=context) def case_get_note_msg_prefix(self, cr, uid, lead, context=None): if isinstance(lead, (int, long)): @@ -877,16 +880,16 @@ class crm_lead(base_stage, osv.osv): def create_send_note(self, cr, uid, ids, context=None): for id in ids: message = _("%s has been created.")% (self.case_get_note_msg_prefix(cr, uid, id, context=context)) - self.message_append_note(cr, uid, [id], body=message, context=context) + self.message_append_note(cr, uid, [id], body=message, subtype="new", context=context) return True def case_mark_lost_send_note(self, cr, uid, ids, context=None): message = _("Opportunity has been lost.") - return self.message_append_note(cr, uid, ids, body=message, context=context) + return self.message_append_note(cr, uid, ids, body=message,subtype="lost", context=context) def case_mark_won_send_note(self, cr, uid, ids, context=None): message = _("Opportunity has been won.") - return self.message_append_note(cr, uid, ids, body=message, context=context) + return self.message_append_note(cr, uid, ids, body=message, subtype="won", context=context) def schedule_phonecall_send_note(self, cr, uid, ids, phonecall_id, action, context=None): phonecall = self.pool.get('crm.phonecall').browse(cr, uid, [phonecall_id], context=context)[0] diff --git a/addons/crm/crm_lead_data.xml b/addons/crm/crm_lead_data.xml index 04f91933f66..ce9951defc6 100644 --- a/addons/crm/crm_lead_data.xml +++ b/addons/crm/crm_lead_data.xml @@ -154,5 +154,38 @@ + + + new + + + + + won + + + + lost + + + + stage change + + + + + other + + + + + email + + + + comment + + + diff --git a/addons/crm/test/process/crm_message_subtype.yml b/addons/crm/test/process/crm_message_subtype.yml new file mode 100644 index 00000000000..705d3cc2fc4 --- /dev/null +++ b/addons/crm/test/process/crm_message_subtype.yml @@ -0,0 +1,48 @@ +- + I have add the sub_type name other with default false +- + !record {model: mail.message.subtype, id: mail_subtype_lead_won }: + name: won + model_ids: + - crm.model_crm_lead + default: False +- + I have add the sub_type name email with default true +- + !record {model: mail.message.subtype, id: mail_subtype_lead_email }: + name: email + model_ids: + - crm.model_crm_lead + default: True +- + I have add the sub_type name comment with default true +- + !record {model: mail.message.subtype, id: mail_subtype_lead_comment }: + name: comment + model_ids: + - crm.model_crm_lead + default: True +- + I have add the subtypes as won in feeds +- + !python {model: mail.followers}: | + ids = self.search(cr, uid, [('res_model', '=', 'crm.lead'),('res_id', '=', ref('crm_case_1'))]) + self.write(cr, uid, ids, {'subtype_ids': [(6,0,[ref('mail_subtype_lead_won')])]}) +- + I have change the lead into mark won +- + !python {model: crm.lead}: | + self.convert_opportunity(cr, uid ,[ref("crm_case_1")], ref("base.res_partner_4")) + self.case_mark_won(cr, uid,[ref("crm_case_1")] ) +- + I have check the lead into mark won +- + !assert {model: crm.lead, id: crm_case_1 ,string: 'The record is not in won state'}: + - state == 'done' +- + I have check the subtype as won in feeds +- + !python {model: mail.followers}: | + followers_ids =self.search(cr, uid, [('res_model', '=','crm.lead'),('res_id', '=', ref('crm_case_1')),('subtype_ids', 'in',[ref('mail_subtype_lead_won')])]) + if len(followers_ids): + assert followers_ids, 'lead is in won' diff --git a/addons/mail/__init__.py b/addons/mail/__init__.py index 289b9080fe7..3f0b618b795 100644 --- a/addons/mail/__init__.py +++ b/addons/mail/__init__.py @@ -19,6 +19,7 @@ # ############################################################################## +import mail_message_subtype import mail_alias import mail_message import mail_followers diff --git a/addons/mail/__openerp__.py b/addons/mail/__openerp__.py index 0e1caabcf65..f1caed309b9 100644 --- a/addons/mail/__openerp__.py +++ b/addons/mail/__openerp__.py @@ -62,6 +62,7 @@ The main features of the module are: 'depends': ['base', 'base_tools', 'base_setup'], 'data': [ 'wizard/mail_compose_message_view.xml', + 'mail_message_subtype.xml', 'res_config_view.xml', 'mail_message_view.xml', 'mail_followers_view.xml', diff --git a/addons/mail/data/mail_group_data.xml b/addons/mail/data/mail_group_data.xml index 2152706d056..400e8ffc061 100644 --- a/addons/mail/data/mail_group_data.xml +++ b/addons/mail/data/mail_group_data.xml @@ -14,7 +14,7 @@ - + Welcome to OpenERP! Your homepage is a summary of messages you received and key information about documents you follow. diff --git a/addons/mail/mail_followers.py b/addons/mail/mail_followers.py index fcdc78d5f0a..3686dd7c4ed 100644 --- a/addons/mail/mail_followers.py +++ b/addons/mail/mail_followers.py @@ -45,6 +45,10 @@ class mail_followers(osv.Model): help='Id of the followed resource'), 'user_id': fields.many2one('res.users', string='Related User', ondelete='cascade', required=True, select=1), + 'subtype_ids': fields.many2many('mail.message.subtype', + 'mail_message_subtyp_rel', + 'subscription_id', 'subtype_id', 'Subtype', + help = "linking some subscription to several subtype for projet/task"), } class mail_notification(osv.Model): diff --git a/addons/mail/mail_followers_view.xml b/addons/mail/mail_followers_view.xml index 62e93430675..6c2a6eee6a2 100644 --- a/addons/mail/mail_followers_view.xml +++ b/addons/mail/mail_followers_view.xml @@ -17,6 +17,28 @@ + + mail.followers.form + mail.followers + form + +
+ + + + + + + + + + + + +
+
+
+ mail.notification.tree diff --git a/addons/mail/mail_message.py b/addons/mail/mail_message.py index 7801a9b42d9..a50d091f7ff 100644 --- a/addons/mail/mail_message.py +++ b/addons/mail/mail_message.py @@ -234,6 +234,7 @@ class mail_message(osv.Model): select=True, ondelete='set null', help="Parent message, used for displaying as threads with hierarchy"), 'child_ids': fields.one2many('mail.message', 'parent_id', 'Child Messages'), + 'subtype_id': fields.many2one('mail.message.subtype', 'Subtype'), } _defaults = { @@ -278,7 +279,7 @@ class mail_message(osv.Model): def create(self, cr, uid, values, context=None): self.check(cr, uid, [], mode='create', context=context, values=values) return super(mail_message, self).create(cr, uid, values, context) - + def read(self, cr, uid, ids, fields_to_read=None, context=None, load='_classic_read'): self.check(cr, uid, ids, 'read', context=context) return super(mail_message, self).read(cr, uid, ids, fields_to_read, context, load) diff --git a/addons/mail/mail_message_subtype.py b/addons/mail/mail_message_subtype.py new file mode 100644 index 00000000000..0c4f5e7baa6 --- /dev/null +++ b/addons/mail/mail_message_subtype.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# Copyright (C) 2009-today OpenERP SA () +# +# 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 osv import osv +from osv import fields + +class mail_message_subtype(osv.osv): + + _name = 'mail.message.subtype' + _description = 'mail_message_subtype' + _columns = { + 'name': fields.char(' Message Subtype ', size = 128, + required = True, select = 1, + help = 'Subtype Of Message'), + 'model_ids': fields.many2many('ir.model', + 'mail_message_subtyp_message_rel', + 'message_subtype_id', 'model_id', 'Model', + help = "link some subtypes to several models, for projet/task"), + 'default': fields.boolean('Default', help = "When subscribing to the document, users will receive by default messages related to this subtype unless they uncheck this subtype"), + } + _defaults = { + 'default': True, + } + _sql_constraints = [ + ('name_uniq', 'unique (name)', 'The name of the message subtype must be unique !') + ] diff --git a/addons/mail/mail_message_subtype.xml b/addons/mail/mail_message_subtype.xml new file mode 100644 index 00000000000..8de3224470f --- /dev/null +++ b/addons/mail/mail_message_subtype.xml @@ -0,0 +1,56 @@ + + + + + + + + mail.message.subtype.tree + mail.message.subtype + tree + 10 + + + + + + + + + + + mail.message.subtype.form + mail.message.subtype + form + +
+ + + + + + + + + + + + +
+
+
+ + + Subtypes + mail.message.subtype + form + tree,form + + + + +
+
diff --git a/addons/mail/mail_message_view.xml b/addons/mail/mail_message_view.xml index 0aa5261d7e7..502a3e68e62 100644 --- a/addons/mail/mail_message_view.xml +++ b/addons/mail/mail_message_view.xml @@ -145,6 +145,7 @@ + diff --git a/addons/mail/mail_thread.py b/addons/mail/mail_thread.py index 56c7e6b7088..ddace0cf9b9 100644 --- a/addons/mail/mail_thread.py +++ b/addons/mail/mail_thread.py @@ -260,7 +260,7 @@ class mail_thread(osv.Model): # mail.message wrappers and tools #------------------------------------------------------ - def message_create(self, cr, uid, thread_id, vals, context=None): + def message_create(self, cr, uid, thread_id, vals, context = None): """ OpenChatter: wrapper of mail.message create method - creates the mail.message - automatically subscribe the message writer @@ -268,7 +268,19 @@ class mail_thread(osv.Model): """ if context is None: context = {} - + + notification_obj = self.pool.get('mail.notification') + followers_obj = self.pool.get('mail.followers') + subtype_obj = self.pool.get('mail.message.subtype') + subtype_name = vals.get('subtype') + subtype = False + body = vals.get('body_html', '') if vals.get('content_subtype') == 'html' else vals.get('body_text', '') + if subtype_name: + subtypes = subtype_obj.name_search(cr, uid, subtype_name) + if len(subtypes): + subtype = subtype_obj.browse(cr, uid, subtypes[0][0]) + if self._name in [model.model for model in subtype.model_ids]: + vals['subtype_id']=subtype.id # create message msg_id = self.pool.get('mail.message').create(cr, uid, vals, context=context) @@ -280,31 +292,39 @@ class mail_thread(osv.Model): self.message_subscribe(cr, uid, [thread_id], [vals.get('user_id')], context=context) # Set as unread if writer is not the document responsible - self.message_create_set_unread(cr, uid, [thread_id], context=context) + self.message_create_set_unread(cr, uid, [thread_id], context = context) # special: if install mode, do not push demo data if context.get('install_mode', False): return msg_id # get users that will get a notification pushed - notification_obj = self.pool.get('mail.notification') - user_to_push_ids = self.message_get_user_ids_to_notify(cr, uid, [thread_id], vals, context=context) - for id in user_to_push_ids: - notification_obj.create(cr, uid, {'user_id': id, 'message_id': msg_id}, context=context) + subtype_id = subtype and subtype.id or False + user_to_push_ids = self.message_get_user_ids_to_notify(cr, uid, [thread_id], vals, subtype_id, context = context) + + for user_id in user_to_push_ids: + notification_obj.create(cr, uid, {'user_id': user_id, 'message_id': msg_id}, context = context) # create the email to send self.message_create_notify_by_email(cr, uid, vals, user_to_push_ids, context=context) return msg_id - def message_get_user_ids_to_notify(self, cr, uid, thread_ids, new_msg_vals, context=None): + def message_get_user_ids_to_notify(self, cr, uid, thread_ids, new_msg_vals, subtype_id=None, context=None): # get body body = new_msg_vals.get('body_html', '') if new_msg_vals.get('content_subtype') == 'html' else new_msg_vals.get('body_text', '') # get subscribers subscr_obj = self.pool.get('mail.followers') subscr_ids = subscr_obj.search(cr, uid, ['&', ('res_model', '=', self._name), ('res_id', 'in', thread_ids)], context=context) - notif_user_ids = [sub['user_id'][0] for sub in subscr_obj.read(cr, uid, subscr_ids, ['user_id'], context=context)] + notif_user_ids = [] + # check with subtype + for subscription in subscr_obj.browse(cr, uid, subscr_ids, context=context): + if subtype_id: + if subtype_id in [subtype.id for subtype in subscription.subtype_ids]: + notif_user_ids.append(subscription.user_id.id) + else: + notif_user_ids.append(subscription.user_id.id) # add users requested to perform an action (need_action mechanism) if hasattr(self, 'get_needaction_user_ids') and self._columns.get('user_id'): @@ -336,12 +356,12 @@ class mail_thread(osv.Model): return ret_dict def message_append(self, cr, uid, threads, subject, body_text=None, body_html=None, - type='email', email_date=None, parent_id=False, + type = 'email', email_date = None, parent_id = False, content_subtype='plain', state=None, partner_ids=None, email_from=False, email_to=False, email_cc=None, email_bcc=None, reply_to=None, headers=None, message_id=False, references=None, - attachments=None, original=None, context=None): + attachments=None, original=None, subtype = "other", context=None): """ Creates a new mail.message through message_create. The new message is attached to the current mail.thread, containing all the details passed as parameters. All attachments will be attached to the @@ -391,6 +411,8 @@ class mail_thread(osv.Model): :param dict context: if a ``thread_model`` value is present in the context, its value will be used to determine the model of the thread to update (instead of the current model). + :param subtype: subtype of message: 'email', 'comment', 'notification'; + other by default """ if context is None: context = {} @@ -433,9 +455,11 @@ class mail_thread(osv.Model): if partner_ids is None: partner_ids = [] mail_partner_ids = [(6, 0, partner_ids)] - + if type in ['email', 'comment']: + subtype = type data = { 'subject': subject, + 'subtype': subtype, 'body_text': body_text or thread._model._columns.get('description') and thread.description or '', 'body_html': body_html or '', 'parent_id': parent_id, @@ -465,7 +489,8 @@ class mail_thread(osv.Model): 'references': references, 'headers': headers, 'reply_to': reply_to, - 'original': original, }) + 'original': original, + }) new_msg_ids.append(self.message_create(cr, uid, thread.id, data, context=context)) return new_msg_ids @@ -507,6 +532,7 @@ class mail_thread(osv.Model): attachments = msg_dict.get('attachments'), headers = msg_dict.get('headers'), original = msg_dict.get('original'), + subtype = msg_dict.get('subtype','other'), context = context) #------------------------------------------------------ @@ -990,16 +1016,16 @@ class mail_thread(osv.Model): self.message_append_note(cr, uid, [id], 'res.log', message, context=context) def message_append_note(self, cr, uid, ids, subject=None, body=None, parent_id=False, - type='notification', content_subtype='html', context=None): + type='notification', content_subtype='html', subtype='other', context=None): if content_subtype == 'html': body_html = body body_text = body else: body_html = body body_text = body - return self.message_append(cr, uid, ids, subject, body_html, body_text, - type, parent_id=parent_id, - content_subtype=content_subtype, context=context) + return self.message_append(cr, uid, ids, subject, body_text, body_html, + type, parent_id = parent_id, + content_subtype=content_subtype, subtype=subtype, context=context) #------------------------------------------------------ # Subscription mechanism @@ -1011,7 +1037,7 @@ class mail_thread(osv.Model): """ return [] - def message_subscribe(self, cr, uid, ids, user_ids = None, context=None): + def message_subscribe(self, cr, uid, ids, user_ids = None, subtype_ids = None, context = None): """ Subscribe the user (or user_ids) to the current document. :param user_ids: a list of user_ids; if not set, subscribe @@ -1020,7 +1046,10 @@ class mail_thread(osv.Model): """ to_subscribe_uids = [uid] if user_ids is None else user_ids write_res = self.write(cr, uid, ids, {'message_follower_ids': self.message_subscribe_get_command(cr, uid, to_subscribe_uids, context)}, context=context) - return [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] + follower_ids = [follower.id for thread in self.browse(cr, uid, ids, context=context) for follower in thread.message_follower_ids] + if subtype_ids: + self.pool.get('mail.followers').write(cr, uid, follower_ids, {'subtype_ids': [(6, 0, subtype_ids)]}, context=context) + return follower_ids def message_subscribe_get_command(self, cr, uid, follower_ids, context=None): """ Generate the many2many command to add followers. """ @@ -1158,10 +1187,25 @@ class mail_thread(osv.Model): for obj in self.browse(cr, uid, ids, context=context): if not obj.message_state and self._columns.get('user_id') and obj.user_id and obj.user_id.id == uid: self.message_mark_as_read(cr, uid, [obj.id], context=context) - + def message_mark_as_read(self, cr, uid, ids, context=None): """ Set as read. """ return self.write(cr, uid, ids, {'message_state': True}, context=context) - + def message_subscribe_udpate_subtypes(self, cr, uid, ids, user_id, subtype_ids): + subscription_obj = self.pool.get('mail.followers') + subscription_ids = subscription_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', 'in', ids)]) + return subscription_obj.write(cr, uid, subscription_ids, {'subtype_ids': [6, 0 , subtype_ids]}, context = context) #overright or add new one + + def message_subscription_remove_subtype(self, cr, uid, ids, user_id, subtype_id): + subscription_obj = self.pool.get('mail.followers') + subscription_ids = subscription_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', 'in', ids)]) + return subscription_obj.write(cr, uid, subscription_ids, {'subtype_ids': [3, subtype_id]}, context = context) # remove link + + def message_subscription_remove_subtype_name(self, cr, uid, ids, user_id, subtype_name): + subtype_obj = self.pool.get('mail.message.subtype') + subtype_ids = subtype_obj.search(cr, uid, [('name', '=', subtype_name), ('model_ids', '=', self._name)]) + if not subtype_ids: + return False + self.message_subscription_remove_subtype(cr, uid, ids, user_id, subtype_ids) # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: