[REM] email.smtp_server: removed email.smtp_server and did changes in email module
[odoo/odoo.git] / addons / crm / crm.py
index 43b18be..687cd83 100644 (file)
@@ -22,6 +22,7 @@
 import time
 import base64
 import tools
+
 from osv import fields
 from osv import osv
 from tools.translate import _
@@ -46,7 +47,66 @@ AVAILABLE_PRIORITIES = [
 class crm_case(object):
     """A simple python class to be used for common functions """
 
+    def _find_lost_stage(self, cr, uid, type, section_id):
+        return self._find_percent_stage(cr, uid, 0.0, type, section_id)
+
+    def _find_won_stage(self, cr, uid, type, section_id):
+        return self._find_percent_stage(cr, uid, 100.0, type, section_id)
+
+    def _find_percent_stage(self, cr, uid, percent, type, section_id):
+        """
+            Return the first stage with a probability == percent
+        """
+        stage_pool = self.pool.get('crm.case.stage')
+        if section_id :
+            ids = stage_pool.search(cr, uid, [("probability", '=', percent), ("type", 'like', type), ("section_ids", 'in', [section_id])])
+        else :
+            ids = stage_pool.search(cr, uid, [("probability", '=', percent), ("type", 'like', type)])
+
+        if ids:
+            return ids[0]
+        return False
+
+
+    def _find_first_stage(self, cr, uid, type, section_id):
+        """
+            return the first stage that has a sequence number equal or higher than sequence
+        """
+        stage_pool = self.pool.get('crm.case.stage')
+        if section_id :
+            ids = stage_pool.search(cr, uid, [("sequence", '>', 0), ("type", 'like', type), ("section_ids", 'in', [section_id])])
+        else :
+            ids = stage_pool.search(cr, uid, [("sequence", '>', 0), ("type", 'like', type)])
+
+        if ids:
+            stages = stage_pool.browse(cr, uid, ids)
+            stage_min = stages[0]
+            for stage in stages:
+                if stage_min.sequence > stage.sequence:
+                    stage_min = stage
+            return stage_min.id
+        else :
+            return False
+
+    def onchange_stage_id(self, cr, uid, ids, stage_id, context={}):
+
+        """ @param self: The object pointer
+            @param cr: the current row, from the database cursor,
+            @param uid: the current user’s ID for security checks,
+            @param ids: List of stage’s IDs
+            @stage_id: change state id on run time """
+
+        if not stage_id:
+            return {'value':{}}
+
+        stage = self.pool.get('crm.case.stage').browse(cr, uid, stage_id, context)
+
+        if not stage.on_change:
+            return {'value':{}}
+        return {'value':{'probability': stage.probability}}
+
     def _get_default_partner_address(self, cr, uid, context=None):
+
         """Gives id of default address for current user
         @param self: The object pointer
         @param cr: the current row, from the database cursor,
@@ -91,19 +151,19 @@ class crm_case(object):
             default = {}
 
         default.update({
-                    'message_ids': [], 
+                    'message_ids': [],
                 })
         if hasattr(self, '_columns'):
             if self._columns.get('date_closed'):
                 default.update({
-                    'date_closed': False, 
+                    'date_closed': False,
                 })
             if self._columns.get('date_open'):
                 default.update({
                     'date_open': False
                 })
         return super(osv.osv, self).copy(cr, uid, id, default, context=context)
-    
+
     def _get_default_email(self, cr, uid, context=None):
         """Gives default email address for current user
         @param self: The object pointer
@@ -139,6 +199,60 @@ class crm_case(object):
         user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
         return user.context_section_id.id or False
 
+    def _find_next_stage(self, cr, uid, stage_list, index, current_seq, stage_pool, context=None):
+        if index + 1 == len(stage_list):
+            return False
+        next_stage_id = stage_list[index + 1]
+        next_stage = stage_pool.browse(cr, uid, next_stage_id, context=context)
+        if not next_stage:
+            return False
+        next_seq = next_stage.sequence
+        if not current_seq :
+            current_seq = 0
+
+        if (abs(next_seq - current_seq)) >= 1:
+            return next_stage
+        else :
+            return self._find_next_stage(cr, uid, stage_list, index + 1, current_seq, stage_pool)
+
+    def stage_change(self, cr, uid, ids, context=None, order='sequence'):
+        if context is None:
+            context = {}
+
+        stage_pool = self.pool.get('crm.case.stage')
+        stage_type = context and context.get('stage_type','')
+        current_seq = False
+        next_stage_id = False
+
+        for case in self.browse(cr, uid, ids, context=context):
+
+            next_stage = False
+            value = {}
+            if case.section_id.id :
+                domain = [('type', '=', stage_type),('section_ids', '=', case.section_id.id)]
+            else :
+                domain = [('type', '=', stage_type)]
+
+
+            stages = stage_pool.search(cr, uid, domain, order=order)
+            current_seq = case.stage_id.sequence
+            index = -1
+            if case.stage_id and case.stage_id.id in stages:
+                index = stages.index(case.stage_id.id)
+
+            next_stage = self._find_next_stage(cr, uid, stages, index, current_seq, stage_pool, context=context)
+
+            if next_stage:
+                next_stage_id = next_stage.id
+                value.update({'stage_id': next_stage.id})
+                if next_stage.on_change:
+                    value.update({'probability': next_stage.probability})
+            self.write(cr, uid, [case.id], value, context=context)
+
+
+        return next_stage_id #FIXME should return a list of all id
+
+
     def stage_next(self, cr, uid, ids, context=None):
         """This function computes next stage for case from its current stage
              using available stage for that case type
@@ -147,29 +261,8 @@ class crm_case(object):
         @param uid: the current user’s ID for security checks,
         @param ids: List of case IDs
         @param context: A standard dictionary for contextual values"""
-        stage_pool = self.pool.get('crm.case.stage')
-        model = self._name
-        for case in self.browse(cr, uid, ids, context=context):
-            next_stage = False
-            data = {}
-            domain = [('object_id.model', '=', model),('section_ids', '=', case.section_id.id)]
-            if case.section_id and case.section_id.stage_ids:
-                domain.append(('id', 'in', map(lambda x: x.id, case.section_id.stage_ids)))
-            stages = stage_pool.search(cr, uid, domain, order='sequence')
-            index = -1
-            if case.stage_id and case.stage_id.id in stages:
-                index = stages.index(case.stage_id.id)
-            if index + 1 == len(stages):
-                return False
-            else:
-                next_stage = stages[index + 1]
-            if next_stage:
-                data = {'stage_id': next_stage}
-                stage = stage_pool.browse(cr, uid, next_stage, context=context)
-                if stage.on_change:
-                    data.update({'probability': stage.probability})
-            self.write(cr, uid, [case.id], data, context=context)
-        return next_stage
+
+        return self.stage_change(cr, uid, ids, context=context, order='sequence')
 
     def stage_previous(self, cr, uid, ids, context=None):
         """This function computes previous stage for case from its current stage
@@ -179,29 +272,7 @@ class crm_case(object):
         @param uid: the current user’s ID for security checks,
         @param ids: List of case IDs
         @param context: A standard dictionary for contextual values"""
-        stage_pool = self.pool.get('crm.case.stage')
-        model = self._name
-        for case in self.browse(cr, uid, ids, context=context):
-            prev_stage = False
-            data = {}
-            domain = [('object_id.model', '=', model),('section_ids', '=', case.section_id.id)]
-            if case.section_id and case.section_id.stage_ids:
-                domain.append(('id', 'in', map(lambda x: x.id, case.section_id.stage_ids)))
-            stages = stage_pool.search(cr, uid, domain, order='sequence')
-            index = 0
-            if case.stage_id and case.stage_id.id in stages:
-                index = stages.index(case.stage_id.id)
-            if index == 0:
-                return False
-            else:
-                prev_stage = stages[index - 1]
-            if prev_stage:
-                data = {'stage_id': prev_stage}
-                stage = stage_pool.browse(cr, uid, prev_stage, context=context)
-                if stage.on_change:
-                    data.update({'probability': stage.probability})
-            self.write(cr, uid, [case.id], data, context=context)
-        return prev_stage
+        return self.stage_change(cr, uid, ids, context=context, order='sequence desc')
 
     def onchange_partner_id(self, cr, uid, ids, part, email=False):
         """This function returns value of partner address based on partner
@@ -212,14 +283,11 @@ class crm_case(object):
         @param part: Partner's id
         @email: Partner's email ID
         """
-        if not part:
-            return {'value': {'partner_address_id': False,
-                            'email_from': False, 
-                            'phone': False
-                            }}
-        addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact'])
-        data = {'partner_address_id': addr['contact']}
-        data.update(self.onchange_partner_address_id(cr, uid, ids, addr['contact'])['value'])
+        data={}
+        if  part:
+            addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact'])
+            data = {'partner_address_id': addr['contact']}
+            data.update(self.onchange_partner_address_id(cr, uid, ids, addr['contact'])['value'])
         return {'value': data}
 
     def onchange_partner_address_id(self, cr, uid, ids, add, email=False):
@@ -234,10 +302,13 @@ class crm_case(object):
         if not add:
             return {'value': {'email_from': False}}
         address = self.pool.get('res.partner.address').browse(cr, uid, add)
-        return {'value': {'email_from': address.email, 'phone': address.phone}}
+        if address.email:
+            return {'value': {'email_from': address.email, 'phone': address.phone}}
+        else:
+            return {'value': {'phone': address.phone}}
 
     def _history(self, cr, uid, cases, keyword, history=False, subject=None, email=False, details=None, email_from=False, message_id=False, attach=[], context=None):
-        mailgate_pool = self.pool.get('mailgate.thread')
+        mailgate_pool = self.pool.get('email.thread')
         return mailgate_pool.history(cr, uid, cases, keyword, history=history,\
                                        subject=subject, email=email, \
                                        details=details, email_from=email_from,\
@@ -252,6 +323,7 @@ class crm_case(object):
         @param ids: List of case Ids
         @param *args: Tuple Value for additional Params
         """
+
         cases = self.browse(cr, uid, ids)
         self._history(cr, uid, cases, _('Open'))
         for case in cases:
@@ -259,6 +331,8 @@ class crm_case(object):
             if not case.user_id:
                 data['user_id'] = uid
             self.write(cr, uid, case.id, data)
+
+
         self._action(cr, uid, cases, 'open')
         return True
 
@@ -366,6 +440,7 @@ class crm_case(object):
         @param context: A standard dictionary for contextual values
 
         """
+
         return self.remind_user(cr, uid, ids, context, attach,
                 destination=False)
 
@@ -374,45 +449,51 @@ class crm_case(object):
         @param self: The object pointer
         @param cr: the current row, from the database cursor,
         @param uid: the current user’s ID for security checks,
-        @param ids: List of Remind user's IDs
+        @param ids: List of case's IDs to remind
         @param context: A standard dictionary for contextual values
-
         """
+        email_message_obj = self.pool.get('email.message')
         for case in self.browse(cr, uid, ids, context=context):
-            if not case.section_id.reply_to:
-                raise osv.except_osv(_('Error!'), ("Reply To is not specified in the sales team"))
-            if not case.email_from:
+            if not destination and not case.email_from:
                 raise osv.except_osv(_('Error!'), ("Partner Email is not specified in Case"))
-            if case.section_id.reply_to and case.email_from:
-                src = case.email_from
-                dest = case.section_id.reply_to
-                body = case.description or ""
-                if case.message_ids:
-                    body = case.message_ids[0].description or ""
-                if not destination:
-                    src, dest = dest, src
-                    if body and case.user_id.signature:
-                        if body:
-                            body += '\n\n%s' % (case.user_id.signature)
-                        else:
-                            body = '\n\n%s' % (case.user_id.signature)
-
-                body = self.format_body(body)
-
-                attach_to_send = None
-
-                if attach:
-                    attach_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', case.id)])
-                    attach_to_send = self.pool.get('ir.attachment').read(cr, uid, attach_ids, ['datas_fname','datas'])
-                    attach_to_send = map(lambda x: (x['datas_fname'], base64.decodestring(x['datas'])), attach_to_send)
+            if not case.user_id.user_email:
+               raise osv.except_osv(_('Error!'), ("User Email is not specified in Case"))
+
+            if destination and case.section_id.user_id:
+                case_email = case.section_id.user_id.user_email
+            else:
+                case_email = case.user_id.user_email
+
+            src = case_email
+            dest = case.user_id
+            body = case.description or ""
+            if case.message_ids:
+                body = case.message_ids[0].description or ""
+            if not destination:
+                src, dest = dest, case.email_from
+                if body and case.user_id.signature:
+                    if body:
+                        body += '\n\n%s' % (case.user_id.signature)
+                    else:
+                        body = '\n\n%s' % (case.user_id.signature)
+
+            body = self.format_body(body)
+
+            attach_to_send = None
+
+            if attach:
+                attach_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', case.id)])
+                attach_to_send = self.pool.get('ir.attachment').read(cr, uid, attach_ids, ['datas_fname', 'datas'])
+                attach_to_send = map(lambda x: (x['datas_fname'], base64.decodestring(x['datas'])), attach_to_send)
 
                 # Send an email
                 subject = "Reminder: [%s] %s" % (str(case.id), case.name, )
-                tools.email_send(
+                email_message_obj.schedule_with_attach(cr, uid,
                     src,
                     [dest],
-                    subject, 
+                    subject,
                     body,
+                    model='crm.case',
                     reply_to=case.section_id.reply_to,
                     openobject_id=str(case.id),
                     attach=attach_to_send
@@ -479,30 +560,37 @@ class crm_case_stage(osv.osv):
     _rec_name = 'name'
     _order = "sequence"
 
+
+
+    def _get_type_value(self, cr, user, context):
+        return [('lead','Lead'),('opportunity','Opportunity')]
+
+
     _columns = {
         'name': fields.char('Stage Name', size=64, required=True, translate=True),
         'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of case stages."),
-        'object_id': fields.many2one('ir.model', 'Object Name'),
         'probability': fields.float('Probability (%)', required=True, help="This percentage depicts the default/average probability of the Case for this stage to be a success"),
         'on_change': fields.boolean('Change Probability Automatically', \
                          help="Change Probability on next and previous stages."),
-        'requirements': fields.text('Requirements')
+        'requirements': fields.text('Requirements'),
+        'type': fields.selection(_get_type_value, 'Type', required=True),
     }
-    def _find_object_id(self, cr, uid, context=None):
-        """Finds id for case object
+
+
+    def _find_stage_type(self, cr, uid, context=None):
+        """Finds type of stage according to object.
         @param self: The object pointer
         @param cr: the current row, from the database cursor,
         @param uid: the current user’s ID for security checks,
         @param context: A standard dictionary for contextual values
         """
-        object_id = context and context.get('object_id', False) or False
-        ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', object_id)])
-        return ids and ids[0]
+        type = context and context.get('type', '') or ''
+        return type
 
     _defaults = {
         'sequence': lambda *args: 1,
         'probability': lambda *args: 0.0,
-        'object_id' : _find_object_id
+        'type': _find_stage_type,
     }
 
 crm_case_stage()
@@ -531,9 +619,9 @@ class crm_case_section(osv.osv):
         'reply_to': fields.char('Reply-To', size=64, help="The email address put in the 'Reply-To' of all emails sent by OpenERP about cases in this sales team"),
         'parent_id': fields.many2one('crm.case.section', 'Parent Team'),
         'child_ids': fields.one2many('crm.case.section', 'parent_id', 'Child Teams'),
-        'resource_calendar_id': fields.many2one('resource.calendar', "Resource's Calendar"),
+        'resource_calendar_id': fields.many2one('resource.calendar', "Working Time"),
         'note': fields.text('Description'),
-        'working_hours': fields.float('Working Hours', digits=(16,2 )), 
+        'working_hours': fields.float('Working Hours', digits=(16,2 )),
         'stage_ids': fields.many2many('crm.case.stage', 'section_stage_rel', 'section_id', 'stage_id', 'Stages'),
     }
 
@@ -629,6 +717,7 @@ class crm_case_stage(osv.osv):
     _columns = {
         'section_ids':fields.many2many('crm.case.section', 'section_stage_rel', 'stage_id', 'section_id', 'Sections'),
     }
+
 crm_case_stage()
 
 
@@ -662,6 +751,14 @@ class users(osv.osv):
     _columns = {
         'context_section_id': fields.many2one('crm.case.section', 'Sales Team'),
     }
+
+    def create(self, cr, uid, vals, context=None):
+        res = super(users, self).create(cr, uid, vals, context=context)
+        section_obj=self.pool.get('crm.case.section')
+
+        if vals.get('context_section_id', False):
+            section_obj.write(cr, uid, [vals['context_section_id']], {'member_ids':[(4, res)]}, context)
+        return res
 users()