[FIX] CRM: suggeste recipint for partner OR email, not both.
[odoo/odoo.git] / addons / crm / crm_lead.py
index 7575389..6d00ac1 100644 (file)
@@ -36,8 +36,8 @@ CRM_LEAD_FIELDS_TO_MERGE = ['name',
     'company_id',
     'country_id',
     'section_id',
-    'stage_id',
     'state_id',
+    'stage_id',
     'type_id',
     'user_id',
     'title',
@@ -488,10 +488,8 @@ class crm_lead(base_stage, format_address, osv.osv):
         opportunities = self.browse(cr, uid, ids, context=context)
 
         def _get_first_not_null(attr):
-            if hasattr(oldest, attr):
-                return getattr(oldest, attr)
             for opp in opportunities:
-                if hasattr(opp, attr):
+                if hasattr(opp, attr) and bool(getattr(opp, attr)):
                     return getattr(opp, attr)
             return False
 
@@ -500,7 +498,7 @@ class crm_lead(base_stage, format_address, osv.osv):
             return res and res.id or False
 
         def _concat_all(attr):
-            return ', '.join(filter(lambda x: x, [getattr(opp, attr) or '' for opp in opportunities if hasattr(opp, attr)]))
+            return '\n\n'.join(filter(lambda x: x, [getattr(opp, attr) or '' for opp in opportunities if hasattr(opp, attr)]))
 
         # Process the fields' values
         data = {}
@@ -520,27 +518,8 @@ class crm_lead(base_stage, format_address, osv.osv):
 
         # Define the resulting type ('lead' or 'opportunity')
         data['type'] = self._merge_get_result_type(cr, uid, opportunities, context)
-
         return data
 
-    def _merge_find_oldest(self, cr, uid, ids, context=None):
-        """
-        Return the oldest lead found among ids.
-
-        :param list ids: list of ids of the leads to inspect
-        :return object: browse record of the oldest of the leads
-        """
-        if context is None:
-            context = {}
-
-        if context.get('convert'):
-            ids = list(set(ids) - set(context.get('lead_ids', [])))
-
-        # Search opportunities order by create date
-        opportunity_ids = self.search(cr, uid, [('id', 'in', ids)], order='create_date', context=context)
-        oldest_opp_id = opportunity_ids[0]
-        return self.browse(cr, uid, oldest_opp_id, context=context)
-
     def _mail_body(self, cr, uid, lead, fields, title=False, context=None):
         body = []
         if title:
@@ -587,8 +566,9 @@ class crm_lead(base_stage, format_address, osv.osv):
         subject = [merge_message]
         for opportunity in opportunities:
             subject.append(opportunity.name)
-            title = "%s : %s" % (merge_message, opportunity.name)
-            details.append(self._mail_body(cr, uid, opportunity, CRM_LEAD_FIELDS_TO_MERGE, title=title, context=context))
+            title = "%s : %s" % (opportunity.type == 'opportunity' and _('Merged opportunity') or _('Merged lead'), opportunity.name)
+            fields = list(CRM_LEAD_FIELDS_TO_MERGE)
+            details.append(self._mail_body(cr, uid, opportunity, fields, title=title, context=context))
 
         # Chatter message's subject
         subject = subject[0] + ": " + ", ".join(subject[1:])
@@ -607,27 +587,25 @@ class crm_lead(base_stage, format_address, osv.osv):
         return True
 
     def _merge_opportunity_attachments(self, cr, uid, opportunity_id, opportunities, context=None):
-        attachment = self.pool.get('ir.attachment')
+        attach_obj = self.pool.get('ir.attachment')
 
         # return attachments of opportunity
         def _get_attachments(opportunity_id):
-            attachment_ids = attachment.search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', opportunity_id)], context=context)
-            return attachment.browse(cr, uid, attachment_ids, context=context)
+            attachment_ids = attach_obj.search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', opportunity_id)], context=context)
+            return attach_obj.browse(cr, uid, attachment_ids, context=context)
 
-        count = 1
         first_attachments = _get_attachments(opportunity_id)
+        #counter of all attachments to move. Used to make sure the name is different for all attachments
+        count = 1
         for opportunity in opportunities:
             attachments = _get_attachments(opportunity.id)
-            for first in first_attachments:
-                for attachment in attachments:
-                    if attachment.name == first.name:
-                        values = dict(
-                            name = "%s (%s)" % (attachment.name, count,),
-                            res_id = opportunity_id,
-                        )
-                        attachment.write(values)
-                        count+=1
-
+            for attachment in attachments:
+                values = {'res_id': opportunity_id,}
+                for attachment_in_first in first_attachments:
+                    if attachment.name == attachment_in_first.name:
+                        name = "%s (%s)" % (attachment.name, count,),
+                count+=1
+                attachment.write(values)
         return True
 
     def merge_opportunity(self, cr, uid, ids, context=None):
@@ -639,40 +617,50 @@ class crm_lead(base_stage, format_address, osv.osv):
         :param list ids: leads/opportunities ids to merge
         :return int id: id of the resulting lead/opp
         """
-        if context is None: context = {}
+        if context is None:
+            context = {}
 
         if len(ids) <= 1:
-            raise osv.except_osv(_('Warning!'),_('Please select more than one element (lead or opportunity) from the list view.'))
+            raise osv.except_osv(_('Warning!'), _('Please select more than one element (lead or opportunity) from the list view.'))
 
-        lead_ids = context.get('lead_ids', [])
-
-        ctx_opportunities = self.browse(cr, uid, lead_ids, context=context)
         opportunities = self.browse(cr, uid, ids, context=context)
-        opportunities_list = list(set(opportunities) - set(ctx_opportunities))
-        oldest = self._merge_find_oldest(cr, uid, ids, context=context)
-        if ctx_opportunities:
-            first_opportunity = ctx_opportunities[0]
-            tail_opportunities = opportunities_list + ctx_opportunities[1:]
-        else:
-            first_opportunity = opportunities_list[0]
-            tail_opportunities = opportunities_list[1:]
+        sequenced_opps = []
+        for opportunity in opportunities:
+            if opportunity.stage_id and opportunity.stage_id.state != 'cancel':
+                sequenced_opps.append((opportunity.stage_id.sequence, opportunity))
+            else:
+                sequenced_opps.append((-1, opportunity))
+        sequenced_opps.sort(key=lambda tup: tup[0], reverse=True)
+        opportunities = [opportunity for sequence, opportunity in sequenced_opps]
+        ids = [opportunity.id for opportunity in opportunities]
+        highest = opportunities[0]
+        opportunities_rest = opportunities[1:]
+
+        tail_opportunities = opportunities_rest
 
-        merged_data = self._merge_data(cr, uid, ids, oldest, CRM_LEAD_FIELDS_TO_MERGE, context=context)
+        fields = list(CRM_LEAD_FIELDS_TO_MERGE)
+        merged_data = self._merge_data(cr, uid, ids, highest, fields, context=context)
 
         # Merge messages 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_history(cr, uid, highest.id, tail_opportunities, context=context)
+        self._merge_opportunity_attachments(cr, uid, highest.id, tail_opportunities, context=context)
 
         # Merge notifications about loss of information
-        self._merge_notify(cr, uid, first_opportunity, opportunities, context=context)
+        opportunities = [highest]
+        opportunities.extend(opportunities_rest)
+        self._merge_notify(cr, uid, highest, opportunities, context=context)
+        # Check if the stage is in the stages of the sales team. If not, assign the stage with the lowest sequence
+        if merged_data.get('type') == 'opportunity' and merged_data.get('section_id'):
+            section_stages = self.pool.get('crm.case.section').read(cr, uid, merged_data['section_id'], ['stage_ids'], context=context)
+            if merged_data.get('stage_id') not in section_stages['stage_ids']:
+                stages_sequences = self.pool.get('crm.case.stage').search(cr, uid, [('id','in',section_stages['stage_ids'])], order='sequence', limit=1, context=context)
+                merged_data['stage_id'] = stages_sequences and stages_sequences[0] or False
         # Write merged data into first opportunity
-        self.write(cr, uid, [first_opportunity.id], merged_data, context=context)
+        self.write(cr, uid, [highest.id], merged_data, context=context)
         # Delete tail opportunities
         self.unlink(cr, uid, [x.id for x in tail_opportunities], context=context)
 
-        # Open first opportunity
-        self.case_open(cr, uid, [first_opportunity.id])
-        return first_opportunity.id
+        return highest.id
 
     def _convert_opportunity_data(self, cr, uid, lead, customer, section_id=False, context=None):
         crm_stage = self.pool.get('crm.case.stage')
@@ -748,11 +736,14 @@ class crm_lead(base_stage, format_address, osv.osv):
             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)
-        elif lead.email_from:
-            contact_name, contact_email = self.pool.get('res.partner')._parse_partner_name(lead.email_from, context=context)
+        elif lead.email_from and self.pool.get('res.partner')._parse_partner_name(lead.email_from, context=context)[0]:
+            contact_name = self.pool.get('res.partner')._parse_partner_name(lead.email_from, context=context)[0]
             partner_id = self._lead_create_contact(cr, uid, lead, contact_name, False, context=context)
         else:
-            partner_id = self._lead_create_contact(cr, uid, lead, lead.name, False, context=context)
+            raise osv.except_osv(
+                _('Warning!'),
+                _('No customer name defined. Please fill one of the following fields: Company Name, Contact Name or Email ("Name <email@address>")')
+            )
         return partner_id
 
     def _lead_set_partner(self, cr, uid, lead, partner_id, context=None):
@@ -766,7 +757,7 @@ class crm_lead(base_stage, format_address, osv.osv):
         res = False
         res_partner = self.pool.get('res.partner')
         if partner_id:
-            res_partner.write(cr, uid, partner_id, {'section_id': lead.section_id.id or False})
+            res_partner.write(cr, uid, partner_id, {'section_id': lead.section_id and lead.section_id.id or False})
             contact_id = res_partner.address_get(cr, uid, [partner_id])['default']
             res = lead.write({'partner_id': partner_id}, context=context)
             message = _("<b>Partner</b> set to <em>%s</em>." % (lead.partner_id.name))
@@ -875,8 +866,8 @@ class crm_lead(base_stage, format_address, osv.osv):
             'res_id': int(opportunity_id),
             'view_id': False,
             'views': [(form_view or False, 'form'),
-                      (tree_view or False, 'tree'),
-                      (False, 'calendar'), (False, 'graph')],
+                    (tree_view or False, 'tree'),
+                    (False, 'calendar'), (False, 'graph')],
             'type': 'ir.actions.act_window',
         }
 
@@ -970,6 +961,15 @@ class crm_lead(base_stage, format_address, osv.osv):
         return [lead.section_id.message_get_reply_to()[0] if lead.section_id else False
                     for lead in self.browse(cr, uid, ids, context=context)]
 
+    def message_get_suggested_recipients(self, cr, uid, ids, context=None):
+        recipients = super(crm_lead, self).message_get_suggested_recipients(cr, uid, ids, context=context)
+        for lead in self.browse(cr, uid, ids, context=context):
+            if lead.partner_id:
+                self._message_add_suggested_recipient(recipients, lead, partner=lead.partner_id, reason=_('Customer'))
+            elif lead.email_from:
+                self._message_add_suggested_recipient(recipients, lead, email=lead.email_from, reason=_('Customer Email'))
+        return recipients
+
     def message_new(self, cr, uid, msg, custom_values=None, context=None):
         """ Overrides mail_thread message_new that is called by the mailgateway
             through message_process.