[FIX] Propagated ir.needaction class name change.
[odoo/odoo.git] / addons / crm / crm_lead.py
index 9510041..00f477a 100644 (file)
@@ -40,7 +40,7 @@ class crm_lead(crm_case, osv.osv):
     _name = "crm.lead"
     _description = "Lead/Opportunity"
     _order = "priority,date_action,id desc"
-    _inherit = ['mail.thread','res.partner.address']
+    _inherit = ['ir.needaction_mixin', 'mail.thread','res.partner']
 
     def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
         access_rights_uid = access_rights_uid or uid
@@ -60,14 +60,6 @@ class crm_lead(crm_case, osv.osv):
         'stage_id': _read_group_stage_ids
     }
 
-    # overridden because res.partner.address has an inconvenient name_get,
-    # especially if base_contact is installed.
-    def name_get(self, cr, user, ids, context=None):
-        if isinstance(ids, (int, long)):
-            ids = [ids]
-        return [(r['id'], tools.ustr(r[self._rec_name]))
-                    for r in self.read(cr, user, ids, [self._rec_name], context)]
-
     def _compute_day(self, cr, uid, ids, fields, args, context=None):
         """
         @param cr: the current row, from the database cursor,
@@ -147,7 +139,6 @@ class crm_lead(crm_case, osv.osv):
         return res
 
     _columns = {
-        # Overridden from res.partner.address:
         'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null',
             select=True, help="Optional linked partner, usually after conversion of the lead"),
 
@@ -170,11 +161,11 @@ class crm_lead(crm_case, osv.osv):
             domain="['|',('section_id','=',section_id),('section_id','=',False)]", help="From which campaign (seminar, marketing campaign, mass mailing, ...) did this contact come from?"),
         'channel_id': fields.many2one('crm.case.channel', 'Channel', help="Communication channel (mail, direct, phone, ...)"),
         'contact_name': fields.char('Contact Name', size=64),
-        'partner_name': fields.char("Customer Name", size=64,help='The name of the future partner that will be created while converting the into opportunity', select=1),
+        'partner_name': fields.char("Customer Name", size=64,help='The name of the future partner company that will be created while converting the lead into opportunity', select=1),
         'optin': fields.boolean('Opt-In', help="If opt-in is checked, this contact has accepted to receive emails."),
         'optout': fields.boolean('Opt-Out', help="If opt-out is checked, this contact has refused to receive emails or unsubscribed to a campaign."),
         'type':fields.selection([ ('lead','Lead'), ('opportunity','Opportunity'), ],'Type', help="Type is used to separate Leads and Opportunities"),
-        'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
+        'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True),
         'date_closed': fields.datetime('Closed', readonly=True),
         'stage_id': fields.many2one('crm.case.stage', 'Stage', domain="[('section_ids', '=', section_id)]"),
         'user_id': fields.many2one('res.users', 'Salesman'),
@@ -192,21 +183,19 @@ class crm_lead(crm_case, osv.osv):
         'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
         'subjects': fields.function(_get_email_subject, fnct_search=_history_search, string='Subject of Email', type='char', size=64),
 
-
         # Only used for type opportunity
-        'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', domain="[('partner_id','=',partner_id)]"), 
         'probability': fields.float('Probability (%)',group_operator="avg"),
         'planned_revenue': fields.float('Expected Revenue'),
         'ref': fields.reference('Reference', selection=crm._links_get, size=128),
         'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128),
         'phone': fields.char("Phone", size=64),
         'date_deadline': fields.date('Expected Closing'),
-        'date_action': fields.date('Next Action Date'),
+        'date_action': fields.date('Next Action Date', select=True),
         'title_action': fields.char('Next Action', size=64),
         'stage_id': fields.many2one('crm.case.stage', 'Stage', domain="[('section_ids', '=', section_id)]"),
         'color': fields.integer('Color Index'),
-        'partner_address_name': fields.related('partner_address_id', 'name', type='char', string='Partner Contact Name', readonly=True),
-        'partner_address_email': fields.related('partner_address_id', 'email', type='char', string='Partner Contact Email', readonly=True),
+        'partner_address_name': fields.related('partner_id', 'name', type='char', string='Partner Contact Name', readonly=True),
+        'partner_address_email': fields.related('partner_id', 'email', type='char', string='Partner Contact Email', readonly=True),
         'company_currency': fields.related('company_id', 'currency_id', 'symbol', type='char', string='Company Currency', readonly=True),
         'user_email': fields.related('user_id', 'user_email', type='char', string='User Email', readonly=True),
         'user_login': fields.related('user_id', 'login', type='char', string='User Login', readonly=True),
@@ -225,14 +214,19 @@ class crm_lead(crm_case, osv.osv):
         'color': 0,
     }
 
-    def onchange_partner_address_id(self, cr, uid, ids, add, email=False):
-        """This function returns value of partner email based on Partner Address
-        """
-        if not add:
-            return {'value': {'email_from': False, 'country_id': False}}
-        address = self.pool.get('res.partner.address').browse(cr, uid, add)
-        return {'value': {'email_from': address.email, 'phone': address.phone, 'country_id': address.country_id.id}}
-
+    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):
+            # salesman must perform an action when in draft mode
+            if obj.state == 'draft' and obj.user_id:
+                result[obj.id] = [obj.user_id.id]
+        return result
+    
+    def create(self, cr, uid, vals, context=None):
+        obj_id = super(crm_lead, self).create(cr, uid, vals, context)
+        self.create_send_note(cr, uid, [obj_id], context=context)
+        return obj_id
+    
     def on_change_optin(self, cr, uid, ids, optin):
         return {'value':{'optin':optin,'optout':False}}
 
@@ -266,75 +260,58 @@ class crm_lead(crm_case, osv.osv):
     def stage_find_won(self, cr, uid, section_id):
         return self.stage_find_percent(cr, uid, 100.0, section_id)
 
-    def case_open(self, cr, uid, ids, *args):
-        for l in self.browse(cr, uid, ids):
-            # When coming from draft override date and stage otherwise just set state
-            if l.state == 'draft':
-                if l.type == 'lead':
-                    message = _("The lead '%s' has been opened.") % l.name
-                elif l.type == 'opportunity':
-                    message = _("The opportunity '%s' has been opened.") % l.name
-                else:
-                    message = _("The case '%s' has been opened.") % l.name
-                self.log(cr, uid, l.id, message)
+    def case_open(self, cr, uid, ids, context=None):
+        for lead in self.browse(cr, uid, ids, context=context):
+            if lead.state == 'draft':
                 value = {'date_open': time.strftime('%Y-%m-%d %H:%M:%S')}
-                self.write(cr, uid, [l.id], value)
-                if l.type == 'opportunity' and not l.stage_id:
-                    stage_id = self.stage_find(cr, uid, l.section_id.id or False, [('sequence','>',0)])
+                self.write(cr, uid, [lead.id], value)
+                if lead.type == 'opportunity' and not lead.stage_id:
+                    stage_id = self.stage_find(cr, uid, lead.section_id.id or False, [('sequence','>',0)])
                     if stage_id:
-                        self.stage_set(cr, uid, [l.id], stage_id)
-        res = super(crm_lead, self).case_open(cr, uid, ids, *args)
+                        self.stage_set(cr, uid, [lead.id], stage_id)
+        res = super(crm_lead, self).case_open(cr, uid, ids, context)
         return res
 
-    def case_close(self, cr, uid, ids, *args):
-        res = super(crm_lead, self).case_close(cr, uid, ids, *args)
+    def case_close(self, cr, uid, ids, context=None):
+        res = super(crm_lead, self).case_close(cr, uid, ids, context)
         self.write(cr, uid, ids, {'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')})
-        for case in self.browse(cr, uid, ids):
-            if case.type == 'lead':
-                message = _("The lead '%s' has been closed.") % case.name
-            else:
-                message = _("The case '%s' has been closed.") % case.name
-            self.log(cr, uid, case.id, message)
         return res
 
-    def case_cancel(self, cr, uid, ids, *args):
+    def case_cancel(self, cr, uid, ids, context=None):
         """Overrides cancel for crm_case for setting probability
         """
-        res = super(crm_lead, self).case_cancel(cr, uid, ids, args)
+        res = super(crm_lead, self).case_cancel(cr, uid, ids, context)
         self.write(cr, uid, ids, {'probability' : 0.0})
         return res
 
-    def case_reset(self, cr, uid, ids, *args):
+    def case_reset(self, cr, uid, ids, context=None):
         """Overrides reset as draft in order to set the stage field as empty
         """
-        res = super(crm_lead, self).case_reset(cr, uid, ids, *args)
+        res = super(crm_lead, self).case_reset(cr, uid, ids, context)
         self.write(cr, uid, ids, {'stage_id': False, 'probability': 0.0})
         return res
 
-    def case_mark_lost(self, cr, uid, ids, *args):
+    def case_mark_lost(self, cr, uid, ids, context=None):
         """Mark the case as lost: state = done and probability = 0%
         """
-        res = super(crm_lead, self).case_close(cr, uid, ids, *args)
+        res = super(crm_lead, self).case_close(cr, uid, ids, context)
         self.write(cr, uid, ids, {'probability' : 0.0})
-        for l in self.browse(cr, uid, ids):
-            stage_id = self.stage_find_lost(cr, uid, l.section_id.id or False)
+        for lead in self.browse(cr, uid, ids):
+            stage_id = self.stage_find_lost(cr, uid, lead.section_id.id or False)
             if stage_id:
-                self.stage_set(cr, uid, [l.id], stage_id)
-            message = _("The opportunity '%s' has been marked as lost.") % l.name
-            self.log(cr, uid, l.id, message)
+                self.stage_set(cr, uid, [lead.id], stage_id)
         return res
 
-    def case_mark_won(self, cr, uid, ids, *args):
+    def case_mark_won(self, cr, uid, ids, context=None):
         """Mark the case as lost: state = done and probability = 0%
         """
-        res = super(crm_lead, self).case_close(cr, uid, ids, *args)
+        res = super(crm_lead, self).case_close(cr, uid, ids, context=None)
         self.write(cr, uid, ids, {'probability' : 100.0})
-        for l in self.browse(cr, uid, ids):
-            stage_id = self.stage_find_won(cr, uid, l.section_id.id or False)
+        for lead in self.browse(cr, uid, ids):
+            stage_id = self.stage_find_won(cr, uid, lead.section_id.id or False)
             if stage_id:
-                self.stage_set(cr, uid, [l.id], stage_id)
-            message = _("The opportunity '%s' has been been won.") % l.name
-            self.log(cr, uid, l.id, message)
+                self.stage_set(cr, uid, [lead.id], stage_id)
+            self.case_mark_won_send_note(cr, uid, [lead.id], context=context)
         return res
 
     def set_priority(self, cr, uid, ids, priority):
@@ -342,17 +319,17 @@ class crm_lead(crm_case, osv.osv):
         """
         return self.write(cr, uid, ids, {'priority' : priority})
 
-    def set_high_priority(self, cr, uid, ids, *args):
+    def set_high_priority(self, cr, uid, ids, context=None):
         """Set lead priority to high
         """
         return self.set_priority(cr, uid, ids, '1')
 
-    def set_normal_priority(self, cr, uid, ids, *args):
+    def set_normal_priority(self, cr, uid, ids, context=None):
         """Set lead priority to normal
         """
         return self.set_priority(cr, uid, ids, '3')
 
-    
+
     def _merge_data(self, cr, uid, ids, oldest, fields, context=None):
         # prepare opportunity data into dictionary for merging
         opportunities = self.browse(cr, uid, ids, context=context)
@@ -367,9 +344,9 @@ class crm_lead(crm_case, osv.osv):
         def _get_first_not_null_id(attr):
             res = _get_first_not_null(attr)
             return res and res.id or False
-    
+
         def _concat_all(attr):
-            return ', '.join([getattr(opportunity, attr) or '' for opportunity in opportunities if hasattr(opportunity, attr)])
+            return ', '.join(filter(lambda x: x, [getattr(opportunity, attr) or '' for opportunity in opportunities if hasattr(opportunity, attr)]))
 
         data = {}
         for field_name in fields:
@@ -378,7 +355,7 @@ class crm_lead(crm_case, osv.osv):
                 continue
             field = field_info.column
             if field._type in ('many2many', 'one2many'):
-                continue  
+                continue
             elif field._type == 'many2one':
                 data[field_name] = _get_first_not_null_id(field_name)  # !!
             elif field._type == 'text':
@@ -422,7 +399,7 @@ class crm_lead(crm_case, osv.osv):
             else:
                 value = lead[field_name]
 
-            body.append("%s: %s\n" % (field.string, value or ''))
+            body.append("%s: %s" % (field.string, value or ''))
         return "\n".join(body + ['---'])
 
     def _merge_notification(self, cr, uid, opportunity_id, opportunities, context=None):
@@ -437,18 +414,18 @@ class crm_lead(crm_case, osv.osv):
             subject.append(opportunity.name)
             title = "%s : %s" % (merge_message, opportunity.name)
             details.append(self._mail_body_text(cr, uid, opportunity, fields, title=title, context=context))
-            
+
         subject = subject[0] + ", ".join(subject[1:])
         details = "\n\n".join(details)
-        return self.message_append(cr, uid, [opportunity_id], subject, body_text=details, context=context)
-        
+        return self.message_append_note(cr, uid, [opportunity_id], subject=subject, body=details)
+
     def _merge_opportunity_history(self, cr, uid, opportunity_id, opportunities, context=None):
         message = self.pool.get('mail.message')
         for opportunity in opportunities:
             for history in opportunity.message_ids:
                 message.write(cr, uid, history.id, {
-                        'res_id': opportunity_id, 
-                        'subject' : _("From %s : %s") % (opportunity.name, history.subject) 
+                        'res_id': opportunity_id,
+                        'subject' : _("From %s : %s") % (opportunity.name, history.subject)
                 }, context=context)
 
         return True
@@ -474,8 +451,8 @@ class crm_lead(crm_case, osv.osv):
                         )
                         attachment.write(values)
                         count+=1
-                    
-        return True    
+
+        return True
 
     def merge_opportunity(self, cr, uid, ids, context=None):
         """
@@ -483,12 +460,12 @@ class crm_lead(crm_case, osv.osv):
             :param ids: list of opportunities ids to merge
         """
         if context is None: context = {}
-        
+
         #TOCHECK: where pass lead_ids in context?
         lead_ids = context and context.get('lead_ids', []) or []
 
         if len(ids) <= 1:
-            raise osv.except_osv(_('Warning !'),_('Please select more than one opportunities.'))
+            raise osv.except_osv(_('Warning !'),_('Please select more than one opportunity from the list view.'))
 
         ctx_opportunities = self.browse(cr, uid, lead_ids, context=context)
         opportunities = self.browse(cr, uid, ids, context=context)
@@ -501,11 +478,10 @@ class crm_lead(crm_case, osv.osv):
             first_opportunity = opportunities_list[0]
             tail_opportunities = opportunities_list[1:]
 
-        fields = ['partner_id', 'title', 'name', 'categ_id', 'channel_id', 'city', 'company_id', 'contact_name', 'country_id', 
-            'partner_address_id', 'type_id', 'user_id', 'section_id', 'state_id', 'description', 'email', 'fax', 'mobile',
+        fields = ['partner_id', 'title', 'name', 'categ_id', 'channel_id', 'city', 'company_id', 'contact_name', 'country_id', 'type_id', 'user_id', 'section_id', 'state_id', 'description', 'email', 'fax', 'mobile',
             'partner_name', 'phone', 'probability', 'planned_revenue', 'street', 'street2', 'zip', 'create_date', 'date_action_last',
             'date_action_next', 'email_from', 'email_cc', 'partner_name']
-        
+
         data = self._merge_data(cr, uid, ids, oldest, fields, context=context)
 
         # merge data into first opportunity
@@ -513,8 +489,8 @@ class crm_lead(crm_case, osv.osv):
 
         #copy message and attachements into the first opportunity
         self._merge_opportunity_history(cr, uid, first_opportunity.id, tail_opportunities, context=context)
-        self._merge_opportunity_attachments(cr, uid, first_opportunity.id, tail_opportunities, context=context)        
-        
+        self._merge_opportunity_attachments(cr, uid, first_opportunity.id, tail_opportunities, context=context)
+
         #Notification about loss of information
         self._merge_notification(cr, uid, first_opportunity, opportunities, context=context)
         #delete tail opportunities
@@ -534,7 +510,7 @@ class crm_lead(crm_case, osv.osv):
         if section_id:
             stage_ids = crm_stage.search(cr, uid, [('sequence','>=',1), ('section_ids','=', section_id)])
         else:
-            stage_ids = crm_stage.search(cr, uid, [('sequence','>=',1)])            
+            stage_ids = crm_stage.search(cr, uid, [('sequence','>=',1)])
         stage_id = stage_ids and stage_ids[0] or False
         return {
                 'planned_revenue': lead.planned_revenue,
@@ -545,14 +521,8 @@ class crm_lead(crm_case, osv.osv):
                 'type': 'opportunity',
                 'stage_id': stage_id or False,
                 'date_action': time.strftime('%Y-%m-%d %H:%M:%S'),
-                'partner_address_id': contact_id
+                'date_open': time.strftime('%Y-%m-%d %H:%M:%S'),
         }
-    def _convert_opportunity_notification(self, cr, uid, lead, context=None):
-        success_message = _("Lead '%s' has been converted to an opportunity.") % lead.name
-        self.message_append(cr, uid, [lead.id], success_message, body_text=success_message, context=context)
-        self.log(cr, uid, lead.id, success_message)
-        self._send_mail_to_salesman(cr, uid, lead, context=context)
-        return True
 
     def convert_opportunity(self, cr, uid, ids, partner_id, user_ids=False, section_id=False, context=None):
         partner = self.pool.get('res.partner')
@@ -565,11 +535,11 @@ class crm_lead(crm_case, osv.osv):
                 continue
             if user_ids or section_id:
                 self.allocate_salesman(cr, uid, [lead.id], user_ids, section_id, context=context)
-            
+
             vals = self._convert_opportunity_data(cr, uid, lead, customer, section_id, context=context)
             self.write(cr, uid, [lead.id], vals, context=context)
-            
-            self._convert_opportunity_notification(cr, uid, lead, context=context)
+
+            self.convert_opportunity_send_note(cr, uid, lead, context=context)
             #TOCHECK: why need to change partner details in all messages of lead ?
             if lead.partner_id:
                 msg_ids = [ x.id for x in lead.message_ids]
@@ -578,15 +548,42 @@ class crm_lead(crm_case, osv.osv):
                     }, context=context)
         return True
 
-    def _lead_create_partner(self, cr, uid, lead, context=None):
+    def _lead_create_contact(self, cr, uid, lead, name, is_company, parent_id=False, context=None):
         partner = self.pool.get('res.partner')
-        partner_id = partner.create(cr, uid, {
-                    'name': lead.partner_name or lead.contact_name or lead.name,
-                    'user_id': lead.user_id.id,
-                    'comment': lead.description,
-                    'section_id': lead.section_id.id or False,
-                    'address': []
-        })
+        vals = { 'name': name,
+            'user_id': lead.user_id.id,
+            'comment': lead.description,
+            'section_id': lead.section_id.id or False,
+            'parent_id': parent_id,
+            'phone': lead.phone,
+            'mobile': lead.mobile,
+            'email': lead.email_from and to_email(lead.email_from)[0],
+            'fax': lead.fax,
+            'title': lead.title and lead.title.id or False,
+            'function': lead.function,
+            'street': lead.street,
+            'street2': lead.street2,
+            'zip': lead.zip,
+            'city': lead.city,
+            'country_id': lead.country_id and lead.country_id.id or False,
+            'state_id': lead.state_id and lead.state_id.id or False,
+            'is_company': is_company,
+            'type': 'contact'
+        }
+        partner = partner.create(cr, uid,vals, context)
+        return partner
+
+    def _create_lead_partner(self, cr, uid, lead, context=None):
+        partner_id =  False
+        if lead.partner_name and lead.contact_name:
+            partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context)
+            self._lead_create_contact(cr, uid, lead, lead.contact_name, False, partner_id, context=context)
+        elif lead.partner_name and not lead.contact_name:
+            partner_id = self._lead_create_contact(cr, uid, lead, lead.partner_name, True, context=context)
+        elif not lead.partner_name and lead.contact_name:
+            partner_id = self._lead_create_contact(cr, uid, lead, lead.contact_name, False, context=context)
+        else:
+            partner_id = self._lead_create_contact(cr, uid, lead, lead.name, False, context=context)
         return partner_id
 
     def _lead_set_partner(self, cr, uid, lead, partner_id, context=None):
@@ -595,29 +592,10 @@ class crm_lead(crm_case, osv.osv):
         if partner_id:
             res_partner.write(cr, uid, partner_id, {'section_id': lead.section_id.id or False})
             contact_id = res_partner.address_get(cr, uid, [partner_id])['default']
-            res = lead.write({'partner_id' : partner_id, 'partner_address_id': contact_id}, context=context)
-            
+            res = lead.write({'partner_id' : partner_id, }, context=context)
+            self._lead_set_partner_send_note(cr, uid, [lead.id], context)
         return res
 
-    def _lead_create_partner_address(self, cr, uid, lead, partner_id, context=None):
-        address = self.pool.get('res.partner.address')
-        return address.create(cr, uid, {
-                    'partner_id': partner_id,
-                    'name': lead.contact_name,
-                    'phone': lead.phone,
-                    'mobile': lead.mobile,
-                    'email': lead.email_from and to_email(lead.email_from)[0],
-                    'fax': lead.fax,
-                    'title': lead.title and lead.title.id or False,
-                    'function': lead.function,
-                    'street': lead.street,
-                    'street2': lead.street2,
-                    'zip': lead.zip,
-                    'city': lead.city,
-                    'country_id': lead.country_id and lead.country_id.id or False,
-                    'state_id': lead.state_id and lead.state_id.id or False,
-                })
-
     def convert_partner(self, cr, uid, ids, action='create', partner_id=False, context=None):
         """
         This function convert partner based on action.
@@ -628,10 +606,9 @@ class crm_lead(crm_case, osv.osv):
             context = {}
         partner_ids = {}
         for lead in self.browse(cr, uid, ids, context=context):
-            if action == 'create': 
+            if action == 'create':
                 if not partner_id:
-                    partner_id = self._lead_create_partner(cr, uid, lead, context=context)
-                self._lead_create_partner_address(cr, uid, lead, partner_id, context=context)
+                    partner_id = self._create_lead_partner(cr, uid, lead, context)
             self._lead_set_partner(cr, uid, lead, partner_id, context=context)
             partner_ids[lead.id] = partner_id
         return partner_ids
@@ -641,12 +618,12 @@ class crm_lead(crm_case, osv.osv):
         Send mail to salesman with updated Lead details.
         @ lead: browse record of 'crm.lead' object.
         """
-        #TOFIX: mail template should be used here instead of fix subject, body text. 
+        #TOFIX: mail template should be used here instead of fix subject, body text.
         message = self.pool.get('mail.message')
         email_to = lead.user_id and lead.user_id.user_email
         if not email_to:
             return False
-        
+
         email_from = lead.section_id and lead.section_id.user_id and lead.section_id.user_id.user_email or email_to
         partner = lead.partner_id and lead.partner_id.name or lead.partner_name
         subject = "lead %s converted into opportunity" % lead.name
@@ -692,17 +669,16 @@ class crm_lead(crm_case, osv.osv):
                     'date' : schedule_time,
                     'section_id' : section_id or False,
                     'partner_id': lead.partner_id and lead.partner_id.id or False,
-                    'partner_address_id': lead.partner_address_id and lead.partner_address_id.id or False,
-                    'partner_phone' : phone or lead.phone or (lead.partner_address_id and lead.partner_address_id.phone or False),
-                    'partner_mobile' : lead.partner_address_id and lead.partner_address_id.mobile or False,
+                    'partner_phone' : phone or lead.phone or (lead.partner_id and lead.partner_id.phone or False),
+                    'partner_mobile' : lead.partner_id and lead.partner_id.mobile or False,
                     'priority': lead.priority,
             }
-            
             new_id = phonecall.create(cr, uid, vals, context=context)
-            phonecall.case_open(cr, uid, [new_id])
+            phonecall.case_open(cr, uid, [new_id], context=context)
             if action == 'log':
-                phonecall.case_close(cr, uid, [new_id])
+                phonecall.case_close(cr, uid, [new_id], context=context)
             phonecall_dict[lead.id] = new_id
+            self.schedule_phonecall_send_note(cr, uid, [lead.id], new_id, action, context=context)
         return phonecall_dict
 
 
@@ -748,10 +724,11 @@ class crm_lead(crm_case, osv.osv):
         self.write(cr, uid, [res_id], vals, context)
         return res_id
 
-    def message_update(self, cr, uid, ids, msg, vals={}, default_act='pending', context=None):
+    def message_update(self, cr, uid, ids, msg, vals=None, default_act='pending', context=None):
         if isinstance(ids, (str, int, long)):
             ids = [ids]
-
+        if vals == None:
+            vals = {}
         super(crm_lead, self).message_update(cr, uid, ids, msg, context=context)
 
         if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES):
@@ -776,7 +753,10 @@ class crm_lead(crm_case, osv.osv):
         for case in self.browse(cr, uid, ids, context=context):
             values = dict(vals)
             if case.state in CRM_LEAD_PENDING_STATES:
-                values.update(state=crm.AVAILABLE_STATES[1][0]) #re-open
+                #re-open
+                values.update(state=crm.AVAILABLE_STATES[1][0])
+                if not case.date_open:
+                    values['date_open'] = time.strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
             res = self.write(cr, uid, [case.id], values, context=context)
         return res
 
@@ -798,10 +778,10 @@ class crm_lead(crm_case, osv.osv):
             context.update({
                 'default_opportunity_id': opp.id,
                 'default_partner_id': opp.partner_id and opp.partner_id.id or False,
-                'default_user_id': uid, 
+                'default_user_id': uid,
                 'default_section_id': opp.section_id and opp.section_id.id or False,
                 'default_email_from': opp.email_from,
-                'default_state': 'open',  
+                'default_state': 'open',
                 'default_name': opp.name
             })
             value = {
@@ -821,9 +801,10 @@ class crm_lead(crm_case, osv.osv):
 
     def unlink(self, cr, uid, ids, context=None):
         for lead in self.browse(cr, uid, ids, context):
-            if (not lead.section_id.allow_unlink) and (lead.state <> 'draft'):
-                raise osv.except_osv(_('Warning !'),
-                    _('You can not delete this lead. You should better cancel it.'))
+            if (not lead.section_id.allow_unlink) and (lead.state != 'draft'):
+                raise osv.except_osv(_('Error'),
+                    _("You cannot delete lead '%s'; it must be in state 'Draft' to be deleted. " \
+                      "You should better cancel it, instead of deleting it.") % lead.name)
         return super(crm_lead, self).unlink(cr, uid, ids, context)
 
 
@@ -834,18 +815,64 @@ class crm_lead(crm_case, osv.osv):
         if 'date_closed' in vals:
             return super(crm_lead,self).write(cr, uid, ids, vals, context=context)
 
-        if 'stage_id' in vals and vals['stage_id']:
-            stage_obj = self.pool.get('crm.case.stage').browse(cr, uid, vals['stage_id'], context=context)
-            text = _("Changed Stage to: %s") % stage_obj.name
-            self.message_append(cr, uid, ids, text, body_text=text, context=context)
-            message=''
+        if vals.get('stage_id'):
+            stage = self.pool.get('crm.case.stage').browse(cr, uid, vals['stage_id'], context=context)
+            # change probability of lead(s) if required by stage
+            if not vals.get('probability') and stage.on_change:
+                vals['probability'] = stage.probability
             for case in self.browse(cr, uid, ids, context=context):
-                if case.type == 'lead' or  context.get('stage_type',False)=='lead':
-                    message = _("The stage of lead '%s' has been changed to '%s'.") % (case.name, stage_obj.name)
-                elif case.type == 'opportunity':
-                    message = _("The stage of opportunity '%s' has been changed to '%s'.") % (case.name, stage_obj.name)
-                self.log(cr, uid, case.id, message)
+                message = _("Stage changed to <b>%s</b>.") % (stage.name)
+                case.message_append_note(body=message)
         return super(crm_lead,self).write(cr, uid, ids, vals, context)
+    
+    # ----------------------------------------
+    # OpenChatter methods and notifications
+    # ----------------------------------------
+
+    def message_get_subscribers(self, cr, uid, ids, context=None):
+        sub_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context)
+        # add salesman to the subscribers
+        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)
+    
+    def case_get_note_msg_prefix(self, cr, uid, lead, context=None):
+        if isinstance(lead, (int, long)):
+            lead = self.browse(cr, uid, [lead], context=context)[0]
+        return ('Opportunity' if lead.type == 'opportunity' else 'Lead')
+    
+    def create_send_note(self, cr, uid, ids, context=None):
+        for id in ids:
+            message = _("%s has been <b>created</b>.")% (self.case_get_note_msg_prefix(cr, uid, id, context=context))
+            self.message_append_note(cr, uid, [id], body=message, context=context)
+        return True
+
+    def case_mark_lost_send_note(self, cr, uid, ids, context=None):
+        message = _("Opportunity has been <b>lost</b>.")
+        return self.message_append_note(cr, uid, ids, body=message, context=context)
+
+    def case_mark_won_send_note(self, cr, uid, ids, context=None):
+        message = _("Opportunity has been <b>won</b>.")
+        return self.message_append_note(cr, uid, ids, body=message, 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]
+        if action == 'log': prefix = 'Logged'
+        else: prefix = 'Scheduled'
+        message = _("<b>%s a call</b> for the <em>%s</em>.") % (prefix, phonecall.date)
+        return self. message_append_note(cr, uid, ids, body=message, context=context)
+
+    def _lead_set_partner_send_note(self, cr, uid, ids, context=None):
+        for lead in self.browse(cr, uid, ids, context=context):
+            message = _("%s <b>partner</b> is now set to <em>%s</em>." % (self.case_get_note_msg_prefix(cr, uid, lead, context=context), lead.partner_id.name))
+            lead.message_append_note(body=message)
+        return True
+    
+    def convert_opportunity_send_note(self, cr, uid, lead, context=None):
+        message = _("Lead has been <b>converted to an opportunity</b>.")
+        lead.message_append_note(body=message)
+        return True
 
 crm_lead()