##############################################################################
#
# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+# Copyright (C) 2004-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
#
##############################################################################
-from osv import fields, osv
-from datetime import datetime
+import binascii
+from base_status.base_stage import base_stage
import crm
+from datetime import datetime
+from mail.mail_message import to_email
+from osv import fields, osv
import time
-from tools.translate import _
-from crm import crm_case
-import binascii
import tools
-from mail.mail_message import to_email
+from tools.translate import _
CRM_LEAD_PENDING_STATES = (
crm.AVAILABLE_STATES[2][0], # Cancelled
crm.AVAILABLE_STATES[4][0], # Pending
)
-class crm_lead(crm_case, osv.osv):
+class crm_lead(base_stage, osv.osv):
""" CRM Lead Case """
_name = "crm.lead"
_description = "Lead/Opportunity"
def _get_default_section_id(self, cr, uid, context=None):
""" Gives default section by checking if present in the context """
- return self._resolve_section_id_from_context(cr, uid, context=context)
+ return (self._resolve_section_id_from_context(cr, uid, context=context) or False)
+
+ def _get_default_stage_id(self, cr, uid, context=None):
+ """ Gives default stage_id """
+ section_id = self._get_default_section_id(cr, uid, context=context)
+ return self.stage_find(cr, uid, [], section_id, [('state', '=', 'draft'), ('type', '=', 'both')], context=context)
def _resolve_section_id_from_context(self, cr, uid, context=None):
""" Returns ID of section based on the value of 'section_id'
section_ids = self.pool.get('crm.case.section').name_search(cr, uid, name=section_name, context=context)
if len(section_ids) == 1:
return int(section_ids[0][0])
- return False
+ return None
+
+ def _resolve_type_from_context(self, cr, uid, context=None):
+ """ Returns the type (lead or opportunity) from the type context
+ key. Returns None if it cannot be resolved.
+ """
+ if context is None:
+ context = {}
+ return context.get('default_type')
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
if read_group_order == 'stage_id desc':
order = "%s desc" % order
# retrieve section_id from the context and write the domain
+ # - ('id', 'in', 'ids'): add columns that should be present
+ # - OR ('case_default', '=', True), ('fold', '=', False): add default columns that are not folded
+ # - OR ('section_ids', '=', section_id), ('fold', '=', False) if section_id: add section columns that are not folded
search_domain = []
section_id = self._resolve_section_id_from_context(cr, uid, context=context)
if section_id:
- search_domain += ['|', '&', ('section_ids', '=', section_id), ('fold', '=', True)]
- search_domain += ['|', ('id', 'in', ids), '&', ('case_default', '=', 1), ('fold', '=', False)]
+ search_domain += ['|', '&', ('section_ids', '=', section_id), ('fold', '=', False)]
+ search_domain += ['|', ('id', 'in', ids), '&', ('case_default', '=', True), ('fold', '=', False)]
+ # retrieve type from the context (if set: choose 'type' or 'both')
+ type = self._resolve_type_from_context(cr, uid, context=context)
+ if type:
+ search_domain += ['|', ('type', '=', type), ('type', '=', 'both')]
# perform search
stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
select=True, help="Optional linked partner, usually after conversion of the lead"),
'id': fields.integer('ID', readonly=True),
- 'name': fields.char('Name', size=64, select=1),
+ 'name': fields.char('Subject', size=64, required=True, select=1),
'active': fields.boolean('Active', required=False),
'date_action_last': fields.datetime('Last Action', readonly=1),
'date_action_next': fields.datetime('Next Action', readonly=1),
'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), ('case_default', '=', True),\
- '|', ('type', '=', type), ('type', '=', 'both')]"),
+ domain="['&', '|', ('section_ids', '=', section_id), ('case_default', '=', True), '|', ('type', '=', type), ('type', '=', 'both')]"),
'user_id': fields.many2one('res.users', 'Salesperson'),
'referred': fields.char('Referred By', size=64),
'date_open': fields.datetime('Opened', readonly=True),
When the case is over, the state is set to \'Done\'.\
If the case needs to be reviewed then the state is \
set to \'Pending\'.'),
- '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
'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),
-
}
_defaults = {
'active': 1,
+ 'type': 'lead',
'user_id': lambda s, cr, uid, c: s._get_default_user(cr, uid, c),
'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c),
- 'type': 'lead',
'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c),
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c),
return {'value':{}}
return {'value':{'probability': stage.probability}}
- def _get_default_stage_id(self, cr, uid, context=None):
- """ Gives default stage_id """
- section_id = self._get_default_section_id(cr, uid, context=context)
- return self.stage_find(cr, uid, [], section_id, [('state', '=', 'draft'), ('type', '=', 'both')], context=context)
+ def _check(self, cr, uid, ids=False, context=None):
+ """ Override of the base.stage method.
+ Function called by the scheduler to process cases for date actions
+ Only works on not done and cancelled cases
+ """
+ cr.execute('select * from crm_case \
+ where (date_action_last<%s or date_action_last is null) \
+ and (date_action_next<=%s or date_action_next is null) \
+ and state not in (\'cancel\',\'done\')',
+ (time.strftime("%Y-%m-%d %H:%M:%S"),
+ time.strftime('%Y-%m-%d %H:%M:%S')))
+
+ ids2 = map(lambda x: x[0], cr.fetchall() or [])
+ cases = self.browse(cr, uid, ids2, context=context)
+ return self._action(cr, uid, cases, False, context=context)
def stage_find(self, cr, uid, cases, section_id, domain=[], order='sequence', context=None):
""" Override of the base.stage method
Parameter of the stage search taken from the lead:
- type: stage type must be the same or 'both'
- section_id: if set, stages must belong to this section or
- be a default case
+ be a default stage; if not set, stages must be default
+ stages
"""
if isinstance(cases, (int, long)):
cases = self.browse(cr, uid, cases, context=context)
- domain = list(domain)
+ # collect all section_ids
+ section_ids = []
+ types = ['both']
if section_id:
- domain += ['|', ('section_ids', '=', section_id), ('case_default', '=', True)]
+ section_ids.append(section_id)
for lead in cases:
- lead_section_id = lead.section_id.id if lead.section_id else None
- domain += ['|', ('type', '=', lead.type), ('type', '=', 'both')]
- if lead_section_id:
- domain += ['|', ('section_ids', '=', lead_section_id), ('case_default', '=', True)]
- stage_ids = self.pool.get('crm.case.stage').search(cr, uid, domain, order=order, context=context)
+ if lead.section_id:
+ section_ids.append(lead.section_id.id)
+ if lead.type not in types:
+ types.append(lead.type)
+ # OR all section_ids and OR with case_default
+ search_domain = []
+ if section_ids:
+ search_domain += [('|')] * len(section_ids)
+ for section_id in section_ids:
+ search_domain.append(('section_ids', '=', section_id))
+ search_domain.append(('case_default', '=', True))
+ # AND with cases types
+ search_domain.append(('type', 'in', types))
+ # AND with the domain in parameter
+ search_domain += list(domain)
+ # perform search, return the first found
+ stage_ids = self.pool.get('crm.case.stage').search(cr, uid, search_domain, order=order, context=context)
if stage_ids:
return stage_ids[0]
return False
def case_cancel(self, cr, uid, ids, context=None):
- """Overrides cancel for crm_case for setting probability
- """
+ """ Overrides case_cancel from base_stage to set probability """
res = super(crm_lead, self).case_cancel(cr, uid, ids, context=context)
self.write(cr, uid, ids, {'probability' : 0.0}, context=context)
return res
def case_reset(self, cr, uid, ids, context=None):
- """Overrides reset as draft in order to set the stage field as empty
- """
+ """ Overrides case_reset from base_stage to set probability """
res = super(crm_lead, self).case_reset(cr, uid, ids, context=context)
self.write(cr, uid, ids, {'probability': 0.0}, context=context)
return res
def case_mark_lost(self, cr, uid, ids, context=None):
- """Mark the case as lost: state = done and probability = 0%
- """
+ """ Mark the case as lost: state=cancel and probability=0 """
for lead in self.browse(cr, uid, ids):
stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 0.0)], context=context)
if stage_id:
return True
def case_mark_won(self, cr, uid, ids, context=None):
- """Mark the case as lost: state = done and probability = 0%
- """
+ """ Mark the case as lost: state=done and probability=100 """
for lead in self.browse(cr, uid, ids):
stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 100.0)], context=context)
if stage_id:
return True
def set_priority(self, cr, uid, ids, priority):
- """Set lead priority
+ """ Set lead priority
"""
return self.write(cr, uid, ids, {'priority' : priority})
def set_high_priority(self, cr, uid, ids, context=None):
- """Set lead priority to high
+ """ Set lead priority to high
"""
return self.set_priority(cr, uid, ids, '1')
def set_normal_priority(self, cr, uid, ids, context=None):
- """Set lead priority to normal
+ """ 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)
"You should better cancel it, instead of deleting it.") % lead.name)
return super(crm_lead, self).unlink(cr, uid, ids, context)
-
def write(self, cr, uid, ids, vals, context=None):
- if not context:
- context = {}
-
- if 'date_closed' in vals:
- return super(crm_lead,self).write(cr, uid, ids, vals, context=context)
-
- if vals.get('stage_id'):
- stage = self.pool.get('crm.case.stage').browse(cr, uid, vals['stage_id'], context=context)
+ if vals.get('stage_id') and not vals.get('probability'):
# change probability of lead(s) if required by stage
- if not vals.get('probability') and stage.on_change:
+ stage = self.pool.get('crm.case.stage').browse(cr, uid, vals['stage_id'], context=context)
+ if stage.on_change:
vals['probability'] = stage.probability
- for case in self.browse(cr, uid, ids, context=context):
- 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)
# ----------------------------------------
sub_ids.append(obj.user_id.id)
return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
+ 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 <b>%s</b>.") % (stage_name), 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]
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)
+ 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):