2009-12-30 10:20:44 +00:00
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
2012-05-25 15:20:46 +00:00
# Copyright (C) 2004-today OpenERP SA (<http://www.openerp.com>)
2009-12-30 10:20:44 +00:00
#
# 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 <http://www.gnu.org/licenses/>.
#
##############################################################################
2010-01-13 13:34:09 +00:00
import crm
2012-05-25 15:20:46 +00:00
from datetime import datetime
2013-03-29 11:16:09 +00:00
from operator import itemgetter
2013-07-16 15:10:03 +00:00
2013-10-06 11:58:08 +00:00
import openerp
2013-04-17 08:59:27 +00:00
from openerp import SUPERUSER_ID
2012-12-06 14:56:32 +00:00
from openerp import tools
2013-02-19 08:42:55 +00:00
from openerp . addons . base . res . res_partner import format_address
2013-07-16 15:10:03 +00:00
from openerp . osv import fields , osv , orm
from openerp . tools . translate import _
2014-09-22 14:14:04 +00:00
from openerp . tools import email_re , email_split
2010-07-08 14:03:52 +00:00
2012-12-04 13:30:00 +00:00
CRM_LEAD_FIELDS_TO_MERGE = [ ' name ' ,
' partner_id ' ,
2014-07-08 15:33:00 +00:00
' campaign_id ' ,
2012-12-04 13:30:00 +00:00
' company_id ' ,
' country_id ' ,
' section_id ' ,
' state_id ' ,
2013-02-06 11:06:56 +00:00
' stage_id ' ,
2014-07-08 15:33:00 +00:00
' medium_id ' ,
' source_id ' ,
2012-12-04 13:30:00 +00:00
' user_id ' ,
' title ' ,
' city ' ,
' contact_name ' ,
' 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 ' ]
2013-07-16 15:10:03 +00:00
2010-07-08 14:03:52 +00:00
2013-07-08 10:30:11 +00:00
class crm_lead ( format_address , osv . osv ) :
2010-03-23 14:12:01 +00:00
""" CRM Lead Case """
2009-12-30 10:20:44 +00:00
_name = " crm.lead "
2010-12-20 11:38:20 +00:00
_description = " Lead/Opportunity "
2014-08-22 12:22:26 +00:00
_order = " priority desc,date_action,id desc "
2014-07-08 15:33:00 +00:00
_inherit = [ ' mail.thread ' , ' ir.needaction_mixin ' , ' crm.tracking.mixin ' ]
2012-12-18 12:56:34 +00:00
_track = {
' stage_id ' : {
2013-10-23 10:04:29 +00:00
# this is only an heuristics; depending on your particular stage configuration it may not match all 'new' stages
' crm.mt_lead_create ' : lambda self , cr , uid , obj , ctx = None : obj . probability == 0 and obj . stage_id and obj . stage_id . sequence < = 1 ,
' crm.mt_lead_stage ' : lambda self , cr , uid , obj , ctx = None : ( obj . stage_id and obj . stage_id . sequence > 1 ) and obj . probability < 100 ,
2013-11-04 11:57:35 +00:00
' crm.mt_lead_won ' : lambda self , cr , uid , obj , ctx = None : obj . probability == 100 and obj . stage_id and obj . stage_id . fold ,
' crm.mt_lead_lost ' : lambda self , cr , uid , obj , ctx = None : obj . probability == 0 and obj . stage_id and obj . stage_id . fold and obj . stage_id . sequence > 1 ,
2012-12-18 12:56:34 +00:00
} ,
}
2014-04-16 08:28:51 +00:00
_mail_mass_mailing = _ ( ' Leads / Opportunities ' )
2011-08-25 04:10:37 +00:00
2013-03-21 13:31:39 +00:00
def get_empty_list_help ( self , cr , uid , help , context = None ) :
2014-07-06 14:44:26 +00:00
context = dict ( context or { } )
2013-03-25 10:25:55 +00:00
if context . get ( ' default_type ' ) == ' lead ' :
2013-03-21 13:31:39 +00:00
context [ ' empty_list_help_model ' ] = ' crm.case.section '
2013-03-25 10:25:55 +00:00
context [ ' empty_list_help_id ' ] = context . get ( ' default_section_id ' )
2013-03-25 10:40:12 +00:00
context [ ' empty_list_help_document_name ' ] = _ ( " leads " )
2013-03-21 13:31:39 +00:00
return super ( crm_lead , self ) . get_empty_list_help ( cr , uid , help , context = context )
2013-02-05 16:10:02 +00:00
2014-08-29 13:02:31 +00:00
def _get_default_section_id ( self , cr , uid , user_id = False , context = None ) :
2012-05-24 14:26:16 +00:00
""" Gives default section by checking if present in the context """
2014-05-12 11:41:49 +00:00
section_id = self . _resolve_section_id_from_context ( cr , uid , context = context ) or False
if not section_id :
2014-08-29 13:02:31 +00:00
section_id = self . pool . get ( ' res.users ' ) . browse ( cr , uid , user_id or uid , context ) . default_section_id . id or False
2014-05-12 11:41:49 +00:00
return section_id
2012-05-24 14:26:16 +00:00
2012-05-25 11:51:02 +00:00
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 )
2013-10-23 10:04:29 +00:00
return self . stage_find ( cr , uid , [ ] , section_id , [ ( ' fold ' , ' = ' , False ) ] , context = context )
2012-05-25 11:51:02 +00:00
2012-05-22 14:11:27 +00:00
def _resolve_section_id_from_context ( self , cr , uid , context = None ) :
""" Returns ID of section based on the value of ' section_id '
2012-05-24 14:26:16 +00:00
context key , or None if it cannot be resolved to a single
Sales Team .
2012-05-22 14:11:27 +00:00
"""
if context is None :
context = { }
2012-05-24 14:26:16 +00:00
if type ( context . get ( ' default_section_id ' ) ) in ( int , long ) :
return context . get ( ' default_section_id ' )
if isinstance ( context . get ( ' default_section_id ' ) , basestring ) :
2013-07-08 10:30:11 +00:00
section_ids = self . pool . get ( ' crm.case.section ' ) . name_search ( cr , uid , name = context [ ' default_section_id ' ] , context = context )
2012-05-22 14:29:54 +00:00
if len ( section_ids ) == 1 :
2012-05-24 14:26:16 +00:00
return int ( section_ids [ 0 ] [ 0 ] )
2012-05-25 15:20:46 +00:00
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 ' )
2011-08-25 04:10:37 +00:00
2011-11-23 13:31:32 +00:00
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
2011-11-13 12:07:15 +00:00
stage_obj = self . pool . get ( ' crm.case.stage ' )
2011-11-21 16:49:40 +00:00
order = stage_obj . _order
2012-05-22 15:08:11 +00:00
# lame hack to allow reverting search, should just work in the trivial case
2011-11-21 16:49:40 +00:00
if read_group_order == ' stage_id desc ' :
order = " %s desc " % order
2012-05-22 15:08:11 +00:00
# retrieve section_id from the context and write the domain
2012-05-31 13:16:26 +00:00
# - ('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
2012-05-22 14:11:27 +00:00
search_domain = [ ]
2012-05-22 15:08:11 +00:00
section_id = self . _resolve_section_id_from_context ( cr , uid , context = context )
2012-05-22 14:11:27 +00:00
if section_id :
2012-09-06 16:18:12 +00:00
search_domain + = [ ' | ' , ( ' section_ids ' , ' = ' , section_id ) ]
2012-12-16 15:58:43 +00:00
search_domain + = [ ( ' id ' , ' in ' , ids ) ]
else :
search_domain + = [ ' | ' , ( ' id ' , ' in ' , ids ) , ( ' case_default ' , ' = ' , True ) ]
2012-05-31 13:16:26 +00:00
# retrieve type from the context (if set: choose 'type' or 'both')
type = self . _resolve_type_from_context ( cr , uid , context = context )
2012-05-25 15:20:46 +00:00
if type :
search_domain + = [ ' | ' , ( ' type ' , ' = ' , type ) , ( ' type ' , ' = ' , ' both ' ) ]
2012-05-22 14:11:27 +00:00
# perform search
stage_ids = stage_obj . _search ( cr , uid , search_domain , order = order , access_rights_uid = access_rights_uid , context = context )
2011-11-23 13:31:32 +00:00
result = stage_obj . name_get ( cr , access_rights_uid , stage_ids , context = context )
2011-11-21 16:49:40 +00:00
# restore order of the search
2013-07-16 15:10:03 +00:00
result . sort ( lambda x , y : cmp ( stage_ids . index ( x [ 0 ] ) , stage_ids . index ( y [ 0 ] ) ) )
2012-09-06 15:23:03 +00:00
fold = { }
for stage in stage_obj . browse ( cr , access_rights_uid , stage_ids , context = context ) :
fold [ stage . id ] = stage . fold or False
return result , fold
2011-11-12 00:44:05 +00:00
2012-09-13 18:19:52 +00:00
def fields_view_get ( self , cr , user , view_id = None , view_type = ' form ' , context = None , toolbar = False , submenu = False ) :
2014-09-24 15:19:22 +00:00
if context and context . get ( ' opportunity_id ' ) :
action = self . _get_formview_action ( cr , user , context [ ' opportunity_id ' ] , context = context )
if action . get ( ' views ' ) and any ( view_id for view_id in action [ ' views ' ] if view_id [ 1 ] == view_type ) :
view_id = next ( view_id [ 0 ] for view_id in action [ ' views ' ] if view_id [ 1 ] == view_type )
2013-07-16 15:10:03 +00:00
res = super ( crm_lead , self ) . fields_view_get ( cr , user , view_id , view_type , context , toolbar = toolbar , submenu = submenu )
2012-09-13 18:19:52 +00:00
if view_type == ' form ' :
res [ ' arch ' ] = self . fields_view_get_address ( cr , user , res [ ' arch ' ] , context = context )
return res
2011-11-12 00:44:05 +00:00
2011-11-13 12:07:15 +00:00
_group_by_full = {
' stage_id ' : _read_group_stage_ids
}
2011-11-12 00:44:05 +00:00
2010-11-19 13:48:01 +00:00
def _compute_day ( self , cr , uid , ids , fields , args , context = None ) :
2010-03-22 10:40:26 +00:00
"""
2012-12-05 18:35:45 +00:00
: return dict : difference between current date and log date
2010-04-22 06:40:17 +00:00
"""
2010-04-05 06:12:50 +00:00
res = { }
2010-11-19 13:48:01 +00:00
for lead in self . browse ( cr , uid , ids , context = context ) :
2010-04-05 06:12:50 +00:00
for field in fields :
res [ lead . id ] = { }
duration = 0
2010-04-07 11:08:20 +00:00
ans = False
2010-04-05 06:12:50 +00:00
if field == ' day_open ' :
if lead . date_open :
date_create = datetime . strptime ( lead . create_date , " % Y- % m- %d % H: % M: % S " )
date_open = datetime . strptime ( lead . date_open , " % Y- % m- %d % H: % M: % S " )
ans = date_open - date_create
elif field == ' day_close ' :
if lead . date_closed :
date_create = datetime . strptime ( lead . create_date , " % Y- % m- %d % H: % M: % S " )
date_close = datetime . strptime ( lead . date_closed , " % Y- % m- %d % H: % M: % S " )
ans = date_close - date_create
2010-04-07 11:08:20 +00:00
if ans :
2014-05-13 14:09:26 +00:00
duration = abs ( int ( ans . days ) )
res [ lead . id ] [ field ] = duration
2010-04-05 06:12:50 +00:00
return res
2014-03-24 05:09:21 +00:00
def _meeting_count ( self , cr , uid , ids , field_name , arg , context = None ) :
2014-05-07 10:09:54 +00:00
Event = self . pool [ ' calendar.event ' ]
return {
opp_id : Event . search_count ( cr , uid , [ ( ' opportunity_id ' , ' = ' , opp_id ) ] , context = context )
for opp_id in ids
}
2010-01-11 12:57:42 +00:00
_columns = {
2012-12-20 11:47:30 +00:00
' partner_id ' : fields . many2one ( ' res.partner ' , ' Partner ' , ondelete = ' set null ' , track_visibility = ' onchange ' ,
2012-10-23 12:02:57 +00:00
select = True , help = " Linked partner (optional). Usually created when converting the lead. " ) ,
2010-10-08 14:40:49 +00:00
2011-10-12 11:11:42 +00:00
' id ' : fields . integer ( ' ID ' , readonly = True ) ,
2014-05-21 09:52:05 +00:00
' name ' : fields . char ( ' Subject ' , required = True , select = 1 ) ,
2010-06-25 17:41:41 +00:00
' active ' : fields . boolean ( ' Active ' , required = False ) ,
2010-05-19 13:42:43 +00:00
' date_action_last ' : fields . datetime ( ' Last Action ' , readonly = 1 ) ,
' date_action_next ' : fields . datetime ( ' Next Action ' , readonly = 1 ) ,
2012-06-25 13:42:53 +00:00
' email_from ' : fields . char ( ' Email ' , size = 128 , help = " Email address of the contact " , select = 1 ) ,
2012-12-18 12:56:34 +00:00
' section_id ' : fields . many2one ( ' crm.case.section ' , ' Sales Team ' ,
2012-12-20 11:47:30 +00:00
select = True , track_visibility = ' onchange ' , help = ' When sending mails, the default email address is taken from the sales team. ' ) ,
2013-07-16 15:10:03 +00:00
' create_date ' : fields . datetime ( ' Creation Date ' , readonly = True ) ,
' email_cc ' : fields . text ( ' Global CC ' , help = " These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma " ) ,
2010-05-20 17:34:35 +00:00
' description ' : fields . text ( ' Notes ' ) ,
2013-07-16 15:10:03 +00:00
' write_date ' : fields . datetime ( ' Update Date ' , readonly = True ) ,
2014-01-23 10:00:58 +00:00
' categ_ids ' : fields . many2many ( ' crm.case.categ ' , ' crm_lead_category_rel ' , ' lead_id ' , ' category_id ' , ' Tags ' , \
2014-02-26 13:32:22 +00:00
domain = " [ ' | ' , ( ' section_id ' , ' = ' , section_id), ( ' section_id ' , ' = ' , False), ( ' object_id.model ' , ' = ' , ' crm.lead ' )] " , help = " Classify and analyze your lead/opportunity categories like: Training, Service " ) ,
2011-02-24 15:03:09 +00:00
' contact_name ' : fields . char ( ' Contact Name ' , size = 64 ) ,
2012-03-05 11:03:46 +00:00
' 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 ) ,
2013-03-13 11:20:13 +00:00
' opt_out ' : fields . boolean ( ' Opt-Out ' , oldname = ' optout ' ,
help = " If opt-out is checked, this contact has refused to receive emails for mass mailing and marketing campaign. "
2013-03-13 12:12:21 +00:00
" Filter ' Available for Mass Mailing ' allows users to filter the leads when performing mass mailing. " ) ,
2014-04-02 12:20:23 +00:00
' type ' : fields . selection ( [ ( ' lead ' , ' Lead ' ) , ( ' opportunity ' , ' Opportunity ' ) , ] , ' Type ' , select = True , help = " Type is used to separate Leads and Opportunities " ) ,
2011-12-09 06:03:08 +00:00
' priority ' : fields . selection ( crm . AVAILABLE_PRIORITIES , ' Priority ' , select = True ) ,
2014-07-06 14:44:26 +00:00
' date_closed ' : fields . datetime ( ' Closed ' , readonly = True , copy = False ) ,
2014-04-02 12:20:23 +00:00
' stage_id ' : fields . many2one ( ' crm.case.stage ' , ' Stage ' , track_visibility = ' onchange ' , select = True ,
2013-03-21 12:56:26 +00:00
domain = " [ ' & ' , ( ' section_ids ' , ' = ' , section_id), ' | ' , ( ' type ' , ' = ' , type), ( ' type ' , ' = ' , ' both ' )] " ) ,
2012-12-20 11:47:30 +00:00
' user_id ' : fields . many2one ( ' res.users ' , ' Salesperson ' , select = True , track_visibility = ' onchange ' ) ,
2014-05-21 09:52:05 +00:00
' referred ' : fields . char ( ' Referred By ' ) ,
2013-07-17 14:13:36 +00:00
' date_open ' : fields . datetime ( ' Assigned ' , readonly = True ) ,
2014-08-06 11:00:06 +00:00
' day_open ' : fields . function ( _compute_day , string = ' Days to Assign ' ,
multi = ' day_open ' , type = " float " ,
store = { ' crm.lead ' : ( lambda self , cr , uid , ids , c = { } : ids , [ ' date_open ' ] , 10 ) } ) ,
' day_close ' : fields . function ( _compute_day , string = ' Days to Close ' ,
multi = ' day_open ' , type = " float " ,
store = { ' crm.lead ' : ( lambda self , cr , uid , ids , c = { } : ids , [ ' date_closed ' ] , 10 ) } ) ,
2013-07-17 14:13:36 +00:00
' date_last_stage_update ' : fields . datetime ( ' Last Stage Update ' , select = True ) ,
2010-02-28 10:57:24 +00:00
2013-08-06 15:11:43 +00:00
# Messaging and marketing
' message_bounce ' : fields . integer ( ' Bounce ' ) ,
2011-08-25 04:10:37 +00:00
# Only used for type opportunity
2013-07-16 15:10:03 +00:00
' probability ' : fields . float ( ' Success Rate ( % ) ' , group_operator = " avg " ) ,
2012-12-20 11:47:30 +00:00
' planned_revenue ' : fields . float ( ' Expected Revenue ' , track_visibility = ' always ' ) ,
2013-10-06 11:58:08 +00:00
' ref ' : fields . reference ( ' Reference ' , selection = openerp . addons . base . res . res_request . referencable_models ) ,
' ref2 ' : fields . reference ( ' Reference 2 ' , selection = openerp . addons . base . res . res_request . referencable_models ) ,
2011-08-25 04:10:37 +00:00
' phone ' : fields . char ( " Phone " , size = 64 ) ,
2012-11-02 16:26:11 +00:00
' date_deadline ' : fields . date ( ' Expected Closing ' , help = " Estimate of the date on which the opportunity will be won. " ) ,
2011-12-09 06:03:08 +00:00
' date_action ' : fields . date ( ' Next Action Date ' , select = True ) ,
2014-05-21 09:52:05 +00:00
' title_action ' : fields . char ( ' Next Action ' ) ,
2011-09-13 16:29:07 +00:00
' color ' : fields . integer ( ' Color Index ' ) ,
2012-02-29 12:41:42 +00:00
' 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 ) ,
2012-11-24 17:53:19 +00:00
' company_currency ' : fields . related ( ' company_id ' , ' currency_id ' , type = ' many2one ' , string = ' Currency ' , readonly = True , relation = " res.currency " ) ,
2012-08-10 14:43:39 +00:00
' user_email ' : fields . related ( ' user_id ' , ' email ' , type = ' char ' , string = ' User Email ' , readonly = True ) ,
2011-09-13 16:29:07 +00:00
' user_login ' : fields . related ( ' user_id ' , ' login ' , type = ' char ' , string = ' User Login ' , readonly = True ) ,
2012-06-28 14:05:16 +00:00
# Fields for address, due to separation from crm and res.partner
2014-05-21 09:52:05 +00:00
' street ' : fields . char ( ' Street ' ) ,
' street2 ' : fields . char ( ' Street2 ' ) ,
2012-06-28 14:05:16 +00:00
' zip ' : fields . char ( ' Zip ' , change_default = True , size = 24 ) ,
2014-05-21 09:52:05 +00:00
' city ' : fields . char ( ' City ' ) ,
2012-10-30 09:08:06 +00:00
' state_id ' : fields . many2one ( " res.country.state " , ' State ' ) ,
2012-06-28 14:05:16 +00:00
' country_id ' : fields . many2one ( ' res.country ' , ' Country ' ) ,
2014-05-21 09:52:05 +00:00
' phone ' : fields . char ( ' Phone ' ) ,
' fax ' : fields . char ( ' Fax ' ) ,
' mobile ' : fields . char ( ' Mobile ' ) ,
' function ' : fields . char ( ' Function ' ) ,
2012-06-28 15:12:32 +00:00
' title ' : fields . many2one ( ' res.partner.title ' , ' Title ' ) ,
2012-06-28 14:05:16 +00:00
' company_id ' : fields . many2one ( ' res.company ' , ' Company ' , select = 1 ) ,
2012-07-31 10:40:43 +00:00
' payment_mode ' : fields . many2one ( ' crm.payment.mode ' , ' Payment Mode ' , \
2012-07-31 09:49:32 +00:00
domain = " [( ' section_id ' , ' = ' ,section_id)] " ) ,
' planned_cost ' : fields . float ( ' Planned Costs ' ) ,
2014-03-24 05:09:21 +00:00
' meeting_count ' : fields . function ( _meeting_count , string = ' # Meetings ' , type = ' integer ' ) ,
2011-08-25 04:10:37 +00:00
}
2010-02-28 13:21:56 +00:00
_defaults = {
2012-04-19 13:33:09 +00:00
' active ' : 1 ,
2012-05-25 15:20:46 +00:00
' type ' : ' lead ' ,
2013-07-08 10:30:11 +00:00
' user_id ' : lambda s , cr , uid , c : uid ,
2012-05-24 14:26:16 +00:00
' stage_id ' : lambda s , cr , uid , c : s . _get_default_stage_id ( cr , uid , c ) ,
2014-08-29 13:02:31 +00:00
' section_id ' : lambda s , cr , uid , c : s . _get_default_section_id ( cr , uid , context = c ) ,
2010-04-05 06:12:50 +00:00
' company_id ' : lambda s , cr , uid , c : s . pool . get ( ' res.company ' ) . _company_default_get ( cr , uid , ' crm.lead ' , context = c ) ,
' priority ' : lambda * a : crm . AVAILABLE_PRIORITIES [ 2 ] [ 0 ] ,
2011-09-13 16:29:07 +00:00
' color ' : 0 ,
2013-09-19 14:23:38 +00:00
' date_last_stage_update ' : fields . datetime . now ,
2010-02-28 13:21:56 +00:00
}
2011-02-24 15:03:09 +00:00
2012-11-19 10:08:43 +00:00
_sql_constraints = [
2012-11-30 10:05:16 +00:00
( ' check_probability ' , ' check(probability >= 0 and probability <= 100) ' , ' The probability of closing the deal should be between 0 % a nd 100 % ! ' )
2012-11-19 10:08:43 +00:00
]
2012-03-05 18:40:03 +00:00
def onchange_stage_id ( self , cr , uid , ids , stage_id , context = None ) :
2011-08-25 04:10:37 +00:00
if not stage_id :
2013-07-08 10:30:11 +00:00
return { ' value ' : { } }
2013-07-16 15:10:03 +00:00
stage = self . pool . get ( ' crm.case.stage ' ) . browse ( cr , uid , stage_id , context = context )
2011-08-25 04:10:37 +00:00
if not stage . on_change :
2013-07-08 10:30:11 +00:00
return { ' value ' : { } }
2014-03-12 12:57:57 +00:00
vals = { ' probability ' : stage . probability }
if stage . probability > = 100 or ( stage . probability == 0 and stage . sequence > 1 ) :
vals [ ' date_closed ' ] = fields . datetime . now ( )
return { ' value ' : vals }
2011-08-25 04:10:37 +00:00
2013-07-08 10:30:11 +00:00
def on_change_partner_id ( self , cr , uid , ids , partner_id , context = None ) :
2012-08-23 14:35:21 +00:00
values = { }
2012-08-03 07:40:51 +00:00
if partner_id :
partner = self . pool . get ( ' res.partner ' ) . browse ( cr , uid , partner_id , context = context )
2012-08-08 09:08:46 +00:00
values = {
2014-05-30 10:32:35 +00:00
' partner_name ' : partner . parent_id . name if partner . parent_id else partner . name ,
' contact_name ' : partner . name if partner . parent_id else False ,
2013-07-08 10:30:11 +00:00
' street ' : partner . street ,
' street2 ' : partner . street2 ,
' city ' : partner . city ,
' state_id ' : partner . state_id and partner . state_id . id or False ,
' country_id ' : partner . country_id and partner . country_id . id or False ,
' email_from ' : partner . email ,
' phone ' : partner . phone ,
' mobile ' : partner . mobile ,
' fax ' : partner . fax ,
2014-02-17 12:40:44 +00:00
' zip ' : partner . zip ,
2012-08-08 09:08:46 +00:00
}
2013-07-08 10:30:11 +00:00
return { ' value ' : values }
2012-08-03 07:40:51 +00:00
2013-04-04 15:54:37 +00:00
def on_change_user ( self , cr , uid , ids , user_id , context = None ) :
2013-04-11 12:33:56 +00:00
""" When changing the user, also set a section_id or restrict section id
to the ones user_id is member of . """
2014-08-29 13:02:31 +00:00
section_id = self . _get_default_section_id ( cr , uid , user_id = user_id , context = context ) or False
2013-05-21 12:22:42 +00:00
if user_id and not section_id :
2013-04-11 12:33:56 +00:00
section_ids = self . pool . get ( ' crm.case.section ' ) . search ( cr , uid , [ ' | ' , ( ' user_id ' , ' = ' , user_id ) , ( ' member_ids ' , ' = ' , user_id ) ] , context = context )
if section_ids :
section_id = section_ids [ 0 ]
2014-04-01 13:04:23 +00:00
return { ' value ' : { ' section_id ' : section_id } }
2014-05-12 11:41:49 +00:00
2012-10-29 18:11:56 +00:00
def stage_find ( self , cr , uid , cases , section_id , domain = None , order = ' sequence ' , context = None ) :
2012-05-24 14:26:16 +00:00
""" 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
2012-05-25 15:20:46 +00:00
be a default stage ; if not set , stages must be default
stages
2010-05-05 11:55:13 +00:00
"""
2012-05-24 14:26:16 +00:00
if isinstance ( cases , ( int , long ) ) :
cases = self . browse ( cr , uid , cases , context = context )
2013-10-16 10:58:55 +00:00
if context is None :
context = { }
# check whether we should try to add a condition on type
avoid_add_type_term = any ( [ term for term in domain if len ( term ) == 3 if term [ 0 ] == ' type ' ] )
2012-05-31 14:19:30 +00:00
# collect all section_ids
2013-07-16 15:10:03 +00:00
section_ids = set ( )
2012-05-31 14:19:30 +00:00
types = [ ' both ' ]
2013-10-16 10:58:55 +00:00
if not cases and context . get ( ' default_type ' ) :
2013-07-16 15:10:03 +00:00
ctx_type = context . get ( ' default_type ' )
types + = [ ctx_type ]
2012-05-24 14:26:16 +00:00
if section_id :
2013-07-16 15:10:03 +00:00
section_ids . add ( section_id )
2012-05-24 14:26:16 +00:00
for lead in cases :
2012-05-31 14:19:30 +00:00
if lead . section_id :
2013-07-16 15:10:03 +00:00
section_ids . add ( lead . section_id . id )
2012-05-31 14:19:30 +00:00
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 ) )
2013-07-16 15:10:03 +00:00
search_domain . append ( ( ' case_default ' , ' = ' , True ) )
2012-05-31 14:19:30 +00:00
# AND with cases types
2013-10-16 10:58:55 +00:00
if not avoid_add_type_term :
search_domain . append ( ( ' type ' , ' in ' , types ) )
2012-05-31 14:19:30 +00:00
# AND with the domain in parameter
search_domain + = list ( domain )
# perform search, return the first found
2013-10-23 10:04:29 +00:00
stage_ids = self . pool . get ( ' crm.case.stage ' ) . search ( cr , uid , search_domain , order = order , limit = 1 , context = context )
2012-05-24 14:26:16 +00:00
if stage_ids :
return stage_ids [ 0 ]
2011-08-25 04:10:37 +00:00
return False
2011-02-24 15:03:09 +00:00
2012-02-23 10:46:41 +00:00
def case_mark_lost ( self , cr , uid , ids , context = None ) :
2013-10-18 14:44:48 +00:00
""" Mark the case as lost: state=cancel and probability=0
"""
2013-11-14 13:13:13 +00:00
stages_leads = { }
for lead in self . browse ( cr , uid , ids , context = context ) :
2013-10-23 10:04:29 +00:00
stage_id = self . stage_find ( cr , uid , [ lead ] , lead . section_id . id or False , [ ( ' probability ' , ' = ' , 0.0 ) , ( ' fold ' , ' = ' , True ) , ( ' sequence ' , ' > ' , 1 ) ] , context = context )
2011-08-25 04:10:37 +00:00
if stage_id :
2013-11-14 13:13:13 +00:00
if stages_leads . get ( stage_id ) :
stages_leads [ stage_id ] . append ( lead . id )
else :
stages_leads [ stage_id ] = [ lead . id ]
2013-07-16 15:10:03 +00:00
else :
raise osv . except_osv ( _ ( ' Warning! ' ) ,
_ ( ' To relieve your sales pipe and group all Lost opportunities, configure one of your sales stage as follow: \n '
' probability = 0 % , select " Change Probability Automatically " . \n '
' Create a specific stage or edit an existing one by editing columns of your opportunity pipe. ' ) )
2013-11-14 13:13:13 +00:00
for stage_id , lead_ids in stages_leads . items ( ) :
2014-03-12 12:57:57 +00:00
self . write ( cr , uid , lead_ids , { ' stage_id ' : stage_id } , context = context )
2013-11-14 13:13:13 +00:00
return True
2011-08-25 04:10:37 +00:00
2012-02-23 10:46:41 +00:00
def case_mark_won ( self , cr , uid , ids , context = None ) :
2013-10-18 14:44:48 +00:00
""" Mark the case as won: state=done and probability=100
"""
2013-11-14 13:13:13 +00:00
stages_leads = { }
for lead in self . browse ( cr , uid , ids , context = context ) :
2013-11-04 11:57:35 +00:00
stage_id = self . stage_find ( cr , uid , [ lead ] , lead . section_id . id or False , [ ( ' probability ' , ' = ' , 100.0 ) , ( ' fold ' , ' = ' , True ) ] , context = context )
2011-08-25 04:10:37 +00:00
if stage_id :
2013-11-14 13:13:13 +00:00
if stages_leads . get ( stage_id ) :
stages_leads [ stage_id ] . append ( lead . id )
else :
stages_leads [ stage_id ] = [ lead . id ]
2013-07-16 15:10:03 +00:00
else :
raise osv . except_osv ( _ ( ' Warning! ' ) ,
_ ( ' To relieve your sales pipe and group all Won opportunities, configure one of your sales stage as follow: \n '
' probability = 100 % a nd select " Change Probability Automatically " . \n '
' Create a specific stage or edit an existing one by editing columns of your opportunity pipe. ' ) )
2013-11-14 13:13:13 +00:00
for stage_id , lead_ids in stages_leads . items ( ) :
2014-03-12 12:57:57 +00:00
self . write ( cr , uid , lead_ids , { ' stage_id ' : stage_id } , context = context )
2013-11-14 13:13:13 +00:00
return True
2013-07-08 10:30:11 +00:00
def case_escalate ( self , cr , uid , ids , context = None ) :
""" Escalates case to parent level """
for case in self . browse ( cr , uid , ids , context = context ) :
data = { ' active ' : True }
if case . section_id . parent_id :
data [ ' section_id ' ] = case . section_id . parent_id . id
if case . section_id . parent_id . change_responsible :
if case . section_id . parent_id . user_id :
data [ ' user_id ' ] = case . section_id . parent_id . user_id . id
else :
raise osv . except_osv ( _ ( ' Error! ' ) , _ ( " You are already at the top level of your sales-team category. \n Therefore you cannot escalate furthermore. " ) )
self . write ( cr , uid , [ case . id ] , data , context = context )
2012-05-21 14:02:16 +00:00
return True
2011-08-25 04:10:37 +00:00
2012-12-04 15:32:22 +00:00
def _merge_get_result_type ( self , cr , uid , opps , context = None ) :
"""
Define the type of the result of the merge . If at least one of the
element to merge is an opp , the resulting new element will be an opp .
Otherwise it will be a lead .
We ' ll directly use a list of browse records instead of a list of ids
for performances ' sake: it will spare a second browse of the
leads / opps .
: param list opps : list of browse records containing the leads / opps to process
: return string type : the type of the final element
"""
2012-12-05 16:19:35 +00:00
for opp in opps :
2012-12-04 15:32:22 +00:00
if ( opp . type == ' opportunity ' ) :
return ' opportunity '
return ' lead '
2011-10-26 09:21:33 +00:00
def _merge_data ( self , cr , uid , ids , oldest , fields , context = None ) :
2012-11-26 17:49:18 +00:00
"""
Prepare lead / opp data into a dictionary for merging . Different types
of fields are processed in different ways :
- text : all the values are concatenated
- m2m and o2m : those fields aren ' t processed
- m2o : the first not null value prevails ( the other are dropped )
- any other type of field : same as m2o
: param list ids : list of ids of the leads to process
: param list fields : list of leads ' fields to process
: return dict data : contains the merged values
"""
2011-10-21 07:01:11 +00:00
opportunities = self . browse ( cr , uid , ids , context = context )
2012-11-26 17:49:18 +00:00
2011-10-21 07:01:11 +00:00
def _get_first_not_null ( attr ) :
2012-11-26 17:49:18 +00:00
for opp in opportunities :
2013-02-05 11:30:24 +00:00
if hasattr ( opp , attr ) and bool ( getattr ( opp , attr ) ) :
2012-11-26 17:49:18 +00:00
return getattr ( opp , attr )
2011-10-21 07:01:11 +00:00
return False
def _get_first_not_null_id ( attr ) :
res = _get_first_not_null ( attr )
return res and res . id or False
2011-12-31 07:57:20 +00:00
2011-10-21 07:01:11 +00:00
def _concat_all ( attr ) :
2013-02-06 13:22:19 +00:00
return ' \n \n ' . join ( filter ( lambda x : x , [ getattr ( opp , attr ) or ' ' for opp in opportunities if hasattr ( opp , attr ) ] ) )
2012-11-26 17:49:18 +00:00
# Process the fields' values
2011-10-26 09:21:33 +00:00
data = { }
for field_name in fields :
[IMP] use model._fields instead of model._all_columns to cover all fields
The old-api model._all_columns contains information about model._columns and
inherited columns. This dictionary is missing new-api computed non-stored
fields, and the new field objects provide a more readable api...
This commit contains the following changes:
- adapt several methods of BaseModel to use fields instead of columns and
_all_columns
- copy all semantic-free attributes of related fields from their source
- add attribute 'group_operator' on integer and float fields
- base, base_action_rule, crm, edi, hr, mail, mass_mailing, pad,
payment_acquirer, share, website, website_crm, website_mail: simply use
_fields instead of _all_columns
- base, decimal_precision, website: adapt qweb rendering methods to use fields
instead of columns
2014-11-03 15:00:50 +00:00
field = self . _fields . get ( field_name )
if field is None :
2011-10-26 09:21:33 +00:00
continue
[IMP] use model._fields instead of model._all_columns to cover all fields
The old-api model._all_columns contains information about model._columns and
inherited columns. This dictionary is missing new-api computed non-stored
fields, and the new field objects provide a more readable api...
This commit contains the following changes:
- adapt several methods of BaseModel to use fields instead of columns and
_all_columns
- copy all semantic-free attributes of related fields from their source
- add attribute 'group_operator' on integer and float fields
- base, base_action_rule, crm, edi, hr, mail, mass_mailing, pad,
payment_acquirer, share, website, website_crm, website_mail: simply use
_fields instead of _all_columns
- base, decimal_precision, website: adapt qweb rendering methods to use fields
instead of columns
2014-11-03 15:00:50 +00:00
if field . type in ( ' many2many ' , ' one2many ' ) :
2011-12-31 07:57:20 +00:00
continue
[IMP] use model._fields instead of model._all_columns to cover all fields
The old-api model._all_columns contains information about model._columns and
inherited columns. This dictionary is missing new-api computed non-stored
fields, and the new field objects provide a more readable api...
This commit contains the following changes:
- adapt several methods of BaseModel to use fields instead of columns and
_all_columns
- copy all semantic-free attributes of related fields from their source
- add attribute 'group_operator' on integer and float fields
- base, base_action_rule, crm, edi, hr, mail, mass_mailing, pad,
payment_acquirer, share, website, website_crm, website_mail: simply use
_fields instead of _all_columns
- base, decimal_precision, website: adapt qweb rendering methods to use fields
instead of columns
2014-11-03 15:00:50 +00:00
elif field . type == ' many2one ' :
2011-10-26 09:21:33 +00:00
data [ field_name ] = _get_first_not_null_id ( field_name ) # !!
[IMP] use model._fields instead of model._all_columns to cover all fields
The old-api model._all_columns contains information about model._columns and
inherited columns. This dictionary is missing new-api computed non-stored
fields, and the new field objects provide a more readable api...
This commit contains the following changes:
- adapt several methods of BaseModel to use fields instead of columns and
_all_columns
- copy all semantic-free attributes of related fields from their source
- add attribute 'group_operator' on integer and float fields
- base, base_action_rule, crm, edi, hr, mail, mass_mailing, pad,
payment_acquirer, share, website, website_crm, website_mail: simply use
_fields instead of _all_columns
- base, decimal_precision, website: adapt qweb rendering methods to use fields
instead of columns
2014-11-03 15:00:50 +00:00
elif field . type == ' text ' :
2011-10-26 09:21:33 +00:00
data [ field_name ] = _concat_all ( field_name ) #not lost
else :
data [ field_name ] = _get_first_not_null ( field_name ) #not lost
2012-11-26 17:49:18 +00:00
# Define the resulting type ('lead' or 'opportunity')
2012-12-04 15:32:22 +00:00
data [ ' type ' ] = self . _merge_get_result_type ( cr , uid , opportunities , context )
2011-10-21 07:01:11 +00:00
return data
2012-08-17 12:06:06 +00:00
def _mail_body ( self , cr , uid , lead , fields , title = False , context = None ) :
2011-10-26 09:21:33 +00:00
body = [ ]
if title :
body . append ( " %s \n " % ( title ) )
2012-12-04 17:42:31 +00:00
2011-10-26 09:21:33 +00:00
for field_name in fields :
[IMP] use model._fields instead of model._all_columns to cover all fields
The old-api model._all_columns contains information about model._columns and
inherited columns. This dictionary is missing new-api computed non-stored
fields, and the new field objects provide a more readable api...
This commit contains the following changes:
- adapt several methods of BaseModel to use fields instead of columns and
_all_columns
- copy all semantic-free attributes of related fields from their source
- add attribute 'group_operator' on integer and float fields
- base, base_action_rule, crm, edi, hr, mail, mass_mailing, pad,
payment_acquirer, share, website, website_crm, website_mail: simply use
_fields instead of _all_columns
- base, decimal_precision, website: adapt qweb rendering methods to use fields
instead of columns
2014-11-03 15:00:50 +00:00
field = self . _fields . get ( field_name )
if field is None :
2011-10-26 09:21:33 +00:00
continue
2012-11-28 05:53:37 +00:00
value = ' '
2011-10-26 09:21:33 +00:00
[IMP] use model._fields instead of model._all_columns to cover all fields
The old-api model._all_columns contains information about model._columns and
inherited columns. This dictionary is missing new-api computed non-stored
fields, and the new field objects provide a more readable api...
This commit contains the following changes:
- adapt several methods of BaseModel to use fields instead of columns and
_all_columns
- copy all semantic-free attributes of related fields from their source
- add attribute 'group_operator' on integer and float fields
- base, base_action_rule, crm, edi, hr, mail, mass_mailing, pad,
payment_acquirer, share, website, website_crm, website_mail: simply use
_fields instead of _all_columns
- base, decimal_precision, website: adapt qweb rendering methods to use fields
instead of columns
2014-11-03 15:00:50 +00:00
if field . type == ' selection ' :
if callable ( field . selection ) :
2011-10-26 09:21:33 +00:00
key = field . selection ( self , cr , uid , context = context )
else :
key = field . selection
value = dict ( key ) . get ( lead [ field_name ] , lead [ field_name ] )
[IMP] use model._fields instead of model._all_columns to cover all fields
The old-api model._all_columns contains information about model._columns and
inherited columns. This dictionary is missing new-api computed non-stored
fields, and the new field objects provide a more readable api...
This commit contains the following changes:
- adapt several methods of BaseModel to use fields instead of columns and
_all_columns
- copy all semantic-free attributes of related fields from their source
- add attribute 'group_operator' on integer and float fields
- base, base_action_rule, crm, edi, hr, mail, mass_mailing, pad,
payment_acquirer, share, website, website_crm, website_mail: simply use
_fields instead of _all_columns
- base, decimal_precision, website: adapt qweb rendering methods to use fields
instead of columns
2014-11-03 15:00:50 +00:00
elif field . type == ' many2one ' :
2011-10-26 09:21:33 +00:00
if lead [ field_name ] :
value = lead [ field_name ] . name_get ( ) [ 0 ] [ 1 ]
[IMP] use model._fields instead of model._all_columns to cover all fields
The old-api model._all_columns contains information about model._columns and
inherited columns. This dictionary is missing new-api computed non-stored
fields, and the new field objects provide a more readable api...
This commit contains the following changes:
- adapt several methods of BaseModel to use fields instead of columns and
_all_columns
- copy all semantic-free attributes of related fields from their source
- add attribute 'group_operator' on integer and float fields
- base, base_action_rule, crm, edi, hr, mail, mass_mailing, pad,
payment_acquirer, share, website, website_crm, website_mail: simply use
_fields instead of _all_columns
- base, decimal_precision, website: adapt qweb rendering methods to use fields
instead of columns
2014-11-03 15:00:50 +00:00
elif field . type == ' many2many ' :
2012-11-26 09:07:33 +00:00
if lead [ field_name ] :
2012-11-27 08:54:21 +00:00
for val in lead [ field_name ] :
field_value = val . name_get ( ) [ 0 ] [ 1 ]
2012-11-28 05:36:58 +00:00
value + = field_value + " , "
2011-10-26 09:21:33 +00:00
else :
value = lead [ field_name ]
2011-11-30 12:06:40 +00:00
body . append ( " %s : %s " % ( field . string , value or ' ' ) )
2012-11-29 13:17:20 +00:00
return " <br/> " . join ( body + [ ' <br/> ' ] )
2011-10-26 09:21:33 +00:00
2012-12-04 17:42:31 +00:00
def _merge_notify ( self , cr , uid , opportunity_id , opportunities , context = None ) :
"""
Create a message gathering merged leads / opps information .
"""
2011-10-21 07:01:11 +00:00
#TOFIX: mail template should be used instead of fix body, subject text
details = [ ]
2012-12-04 15:32:22 +00:00
result_type = self . _merge_get_result_type ( cr , uid , opportunities , context )
2012-12-08 08:30:38 +00:00
if result_type == ' lead ' :
merge_message = _ ( ' Merged leads ' )
else :
merge_message = _ ( ' Merged opportunities ' )
2011-10-21 07:01:11 +00:00
subject = [ merge_message ]
for opportunity in opportunities :
subject . append ( opportunity . name )
2013-02-06 11:15:36 +00:00
title = " %s : %s " % ( opportunity . type == ' opportunity ' and _ ( ' Merged opportunity ' ) or _ ( ' Merged lead ' ) , opportunity . name )
2013-02-08 17:20:25 +00:00
fields = list ( CRM_LEAD_FIELDS_TO_MERGE )
details . append ( self . _mail_body ( cr , uid , opportunity , fields , title = title , context = context ) )
2011-12-31 07:57:20 +00:00
2012-09-26 13:54:05 +00:00
# Chatter message's subject
subject = subject [ 0 ] + " : " + " , " . join ( subject [ 1 : ] )
2011-10-21 07:01:11 +00:00
details = " \n \n " . join ( details )
2012-08-22 11:34:39 +00:00
return self . message_post ( cr , uid , [ opportunity_id ] , body = details , subject = subject , context = context )
2011-12-31 07:57:20 +00:00
2011-10-21 07:01:11 +00:00
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 , {
2011-12-31 07:57:20 +00:00
' res_id ' : opportunity_id ,
' subject ' : _ ( " From %s : %s " ) % ( opportunity . name , history . subject )
2011-10-21 07:01:11 +00:00
} , context = context )
return True
def _merge_opportunity_attachments ( self , cr , uid , opportunity_id , opportunities , context = None ) :
2013-02-20 12:40:52 +00:00
attach_obj = self . pool . get ( ' ir.attachment ' )
2011-10-21 07:01:11 +00:00
# return attachments of opportunity
def _get_attachments ( opportunity_id ) :
2013-02-20 12:40:52 +00:00
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 )
2011-10-21 07:01:11 +00:00
first_attachments = _get_attachments ( opportunity_id )
2013-02-20 14:09:36 +00:00
#counter of all attachments to move. Used to make sure the name is different for all attachments
count = 1
2011-10-21 07:01:11 +00:00
for opportunity in opportunities :
attachments = _get_attachments ( opportunity . id )
2013-02-20 12:40:52 +00:00
for attachment in attachments :
2013-02-20 14:09:36 +00:00
values = { ' res_id ' : opportunity_id , }
for attachment_in_first in first_attachments :
if attachment . name == attachment_in_first . name :
2014-08-29 13:02:31 +00:00
values [ ' name ' ] = " %s ( %s ) " % ( attachment . name , count , ) ,
2013-02-20 14:09:36 +00:00
count + = 1
attachment . write ( values )
2011-12-31 07:57:20 +00:00
return True
2011-10-21 07:01:11 +00:00
2014-09-25 11:40:45 +00:00
def _merge_opportunity_phonecalls ( self , cr , uid , opportunity_id , opportunities , context = None ) :
phonecall_obj = self . pool [ ' crm.phonecall ' ]
for opportunity in opportunities :
for phonecall_id in phonecall_obj . search ( cr , uid , [ ( ' opportunity_id ' , ' = ' , opportunity . id ) ] , context = context ) :
phonecall_obj . write ( cr , uid , phonecall_id , { ' opportunity_id ' : opportunity_id } , context = context )
return True
2014-09-22 14:14:04 +00:00
def get_duplicated_leads ( self , cr , uid , ids , partner_id , include_lost = False , context = None ) :
"""
Search for opportunities that have the same partner and that arent done or cancelled
"""
lead = self . browse ( cr , uid , ids [ 0 ] , context = context )
email = lead . partner_id and lead . partner_id . email or lead . email_from
return self . pool [ ' crm.lead ' ] . _get_duplicated_leads_by_emails ( cr , uid , partner_id , email , include_lost = include_lost , context = context )
def _get_duplicated_leads_by_emails ( self , cr , uid , partner_id , email , include_lost = False , context = None ) :
"""
Search for opportunities that have the same partner and that arent done or cancelled
"""
final_stage_domain = [ ( ' stage_id.probability ' , ' < ' , 100 ) , ' | ' , ( ' stage_id.probability ' , ' > ' , 0 ) , ( ' stage_id.sequence ' , ' <= ' , 1 ) ]
partner_match_domain = [ ]
for email in set ( email_split ( email ) + [ email ] ) :
partner_match_domain . append ( ( ' email_from ' , ' =ilike ' , email ) )
if partner_id :
partner_match_domain . append ( ( ' partner_id ' , ' = ' , partner_id ) )
partner_match_domain = [ ' | ' ] * ( len ( partner_match_domain ) - 1 ) + partner_match_domain
if not partner_match_domain :
return [ ]
domain = partner_match_domain
if not include_lost :
domain + = final_stage_domain
return self . search ( cr , uid , domain , context = context )
def merge_dependences ( self , cr , uid , highest , opportunities , context = None ) :
self . _merge_notify ( cr , uid , highest , opportunities , context = context )
self . _merge_opportunity_history ( cr , uid , highest , opportunities , context = context )
self . _merge_opportunity_attachments ( cr , uid , highest , opportunities , context = context )
2014-09-26 10:53:59 +00:00
self . _merge_opportunity_phonecalls ( cr , uid , highest , opportunities , context = context )
2014-09-22 14:14:04 +00:00
2013-04-17 16:17:03 +00:00
def merge_opportunity ( self , cr , uid , ids , user_id = False , section_id = False , context = None ) :
2011-10-21 07:01:11 +00:00
"""
2012-11-20 18:13:33 +00:00
Different cases of merge :
- merge leads together = 1 new lead
- merge at least 1 opp with anything else ( lead or opp ) = 1 new opp
2012-11-26 17:49:18 +00:00
: param list ids : leads / opportunities ids to merge
2012-11-20 18:13:33 +00:00
: return int id : id of the resulting lead / opp
2011-10-21 07:01:11 +00:00
"""
2013-02-05 16:35:02 +00:00
if context is None :
context = { }
2011-12-31 07:57:20 +00:00
2011-10-21 07:01:11 +00:00
if len ( ids ) < = 1 :
2013-02-05 16:35:02 +00:00
raise osv . except_osv ( _ ( ' Warning! ' ) , _ ( ' Please select more than one element (lead or opportunity) from the list view. ' ) )
opportunities = self . browse ( cr , uid , ids , context = context )
sequenced_opps = [ ]
2013-10-16 14:48:49 +00:00
# Sorting the leads/opps according to the confidence level of its stage, which relates to the probability of winning it
# The confidence level increases with the stage sequence, except when the stage probability is 0.0 (Lost cases)
# An Opportunity always has higher confidence level than a lead, unless its stage probability is 0.0
2013-02-05 16:35:02 +00:00
for opportunity in opportunities :
2013-03-29 11:16:09 +00:00
sequence = - 1
2013-10-23 10:04:29 +00:00
if opportunity . stage_id and not opportunity . stage_id . fold :
2013-03-29 11:16:09 +00:00
sequence = opportunity . stage_id . sequence
2013-04-02 13:14:34 +00:00
sequenced_opps . append ( ( ( int ( sequence != - 1 and opportunity . type == ' opportunity ' ) , sequence , - opportunity . id ) , opportunity ) )
2013-03-29 11:16:09 +00:00
sequenced_opps . sort ( reverse = True )
opportunities = map ( itemgetter ( 1 ) , sequenced_opps )
2013-02-05 16:35:02 +00:00
ids = [ opportunity . id for opportunity in opportunities ]
highest = opportunities [ 0 ]
opportunities_rest = opportunities [ 1 : ]
2013-02-04 17:51:06 +00:00
tail_opportunities = opportunities_rest
2011-10-21 07:01:11 +00:00
2013-02-08 17:20:25 +00:00
fields = list ( CRM_LEAD_FIELDS_TO_MERGE )
merged_data = self . _merge_data ( cr , uid , ids , highest , fields , context = context )
2011-10-21 07:01:11 +00:00
2013-04-17 16:17:03 +00:00
if user_id :
merged_data [ ' user_id ' ] = user_id
if section_id :
merged_data [ ' section_id ' ] = section_id
2012-09-26 12:44:43 +00:00
# Merge notifications about loss of information
2013-02-05 16:35:02 +00:00
opportunities = [ highest ]
2013-02-05 11:30:24 +00:00
opportunities . extend ( opportunities_rest )
2014-09-22 14:14:04 +00:00
self . merge_dependences ( cr , uid , highest . id , tail_opportunities , context = context )
2013-02-06 11:06:56 +00:00
# Check if the stage is in the stages of the sales team. If not, assign the stage with the lowest sequence
2013-04-02 12:58:09 +00:00
if merged_data . get ( ' section_id ' ) :
section_stage_ids = self . pool . get ( ' crm.case.stage ' ) . search ( cr , uid , [ ( ' section_ids ' , ' in ' , merged_data [ ' section_id ' ] ) , ( ' type ' , ' = ' , merged_data . get ( ' type ' ) ) ] , order = ' sequence ' , context = context )
if merged_data . get ( ' stage_id ' ) not in section_stage_ids :
merged_data [ ' stage_id ' ] = section_stage_ids and section_stage_ids [ 0 ] or False
2012-09-26 12:44:43 +00:00
# Write merged data into first opportunity
2013-02-05 16:35:02 +00:00
self . write ( cr , uid , [ highest . id ] , merged_data , context = context )
2013-05-31 12:34:14 +00:00
# Delete tail opportunities
# We use the SUPERUSER to avoid access rights issues because as the user had the rights to see the records it should be safe to do so
2013-05-31 11:47:16 +00:00
self . unlink ( cr , SUPERUSER_ID , [ x . id for x in tail_opportunities ] , context = context )
2011-10-21 07:01:11 +00:00
2013-02-05 16:35:02 +00:00
return highest . id
2011-10-21 07:01:11 +00:00
2011-10-24 13:30:10 +00:00
def _convert_opportunity_data ( self , cr , uid , lead , customer , section_id = False , context = None ) :
2011-10-21 07:01:11 +00:00
crm_stage = self . pool . get ( ' crm.case.stage ' )
2011-11-23 07:20:20 +00:00
contact_id = False
if customer :
contact_id = self . pool . get ( ' res.partner ' ) . address_get ( cr , uid , [ customer . id ] ) [ ' default ' ]
2011-10-24 13:30:10 +00:00
if not section_id :
section_id = lead . section_id and lead . section_id . id or False
2013-01-07 17:30:37 +00:00
val = {
2012-11-27 16:49:40 +00:00
' planned_revenue ' : lead . planned_revenue ,
' probability ' : lead . probability ,
' name ' : lead . name ,
' partner_id ' : customer and customer . id or False ,
' type ' : ' opportunity ' ,
2012-12-04 15:33:51 +00:00
' date_action ' : fields . datetime . now ( ) ,
' date_open ' : fields . datetime . now ( ) ,
2012-11-27 16:49:40 +00:00
' email_from ' : customer and customer . email or lead . email_from ,
' phone ' : customer and customer . phone or lead . phone ,
2011-10-21 07:01:11 +00:00
}
2013-01-07 17:30:37 +00:00
if not lead . stage_id or lead . stage_id . type == ' lead ' :
2013-10-23 10:04:29 +00:00
val [ ' stage_id ' ] = self . stage_find ( cr , uid , [ lead ] , section_id , [ ( ' type ' , ' in ' , ( ' opportunity ' , ' both ' ) ) ] , context = context )
2013-01-07 17:30:37 +00:00
return val
2012-01-25 16:04:17 +00:00
2011-10-24 13:30:10 +00:00
def convert_opportunity ( self , cr , uid , ids , partner_id , user_ids = False , section_id = False , context = None ) :
2011-11-23 07:20:20 +00:00
customer = False
if partner_id :
2012-11-06 17:51:42 +00:00
partner = self . pool . get ( ' res.partner ' )
2011-11-23 07:20:20 +00:00
customer = partner . browse ( cr , uid , partner_id , context = context )
2011-10-21 07:01:11 +00:00
for lead in self . browse ( cr , uid , ids , context = context ) :
2013-07-16 15:38:18 +00:00
# TDE: was if lead.state in ('done', 'cancel'):
2013-10-23 10:04:29 +00:00
if lead . probability == 100 or ( lead . probability == 0 and lead . stage_id . fold ) :
2011-10-24 13:30:10 +00:00
continue
vals = self . _convert_opportunity_data ( cr , uid , lead , customer , section_id , context = context )
2011-10-21 07:01:11 +00:00
self . write ( cr , uid , [ lead . id ] , vals , context = context )
2012-08-23 20:06:29 +00:00
if user_ids or section_id :
self . allocate_salesman ( cr , uid , ids , user_ids , section_id , context = context )
2011-12-31 07:57:20 +00:00
2011-10-24 13:30:10 +00:00
return True
2011-10-21 07:01:11 +00:00
2012-03-05 11:03:46 +00:00
def _lead_create_contact ( self , cr , uid , lead , name , is_company , parent_id = False , context = None ) :
2011-10-21 07:01:11 +00:00
partner = self . pool . get ( ' res.partner ' )
2013-01-31 15:28:20 +00:00
vals = { ' name ' : name ,
2012-03-05 11:03:46 +00:00
' 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 ,
2013-02-27 12:15:06 +00:00
' email ' : tools . email_split ( lead . email_from ) and tools . email_split ( lead . email_from ) [ 0 ] or False ,
2012-03-05 11:03:46 +00:00
' 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 ,
2012-03-06 11:12:07 +00:00
' type ' : ' contact '
2012-03-05 11:03:46 +00:00
}
2013-01-31 15:28:20 +00:00
partner = partner . create ( cr , uid , vals , context = context )
2012-03-05 11:03:46 +00:00
return partner
def _create_lead_partner ( self , cr , uid , lead , context = None ) :
2013-01-31 15:28:20 +00:00
partner_id = False
2012-03-05 11:03:46 +00:00
if lead . partner_name and lead . contact_name :
2012-03-09 04:48:43 +00:00
partner_id = self . _lead_create_contact ( cr , uid , lead , lead . partner_name , True , context = context )
2012-08-23 19:31:30 +00:00
partner_id = self . _lead_create_contact ( cr , uid , lead , lead . contact_name , False , partner_id , context = context )
2012-03-05 11:03:46 +00:00
elif lead . partner_name and not lead . contact_name :
2012-03-09 04:48:43 +00:00
partner_id = self . _lead_create_contact ( cr , uid , lead , lead . partner_name , True , context = context )
2012-03-05 11:03:46 +00:00
elif not lead . partner_name and lead . contact_name :
2012-03-09 04:48:43 +00:00
partner_id = self . _lead_create_contact ( cr , uid , lead , lead . contact_name , False , context = context )
2013-02-01 12:04:24 +00:00
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 ]
2013-01-31 15:28:20 +00:00
partner_id = self . _lead_create_contact ( cr , uid , lead , contact_name , False , context = context )
2012-03-05 11:03:46 +00:00
else :
2013-02-01 12:04:24 +00:00
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> " ) ' )
)
2011-10-21 07:01:11 +00:00
return partner_id
2012-12-05 19:21:46 +00:00
def handle_partner_assignation ( self , cr , uid , ids , action = ' create ' , partner_id = False , context = None ) :
2011-10-21 07:01:11 +00:00
"""
2012-12-05 17:18:15 +00:00
Handle partner assignation during a lead conversion .
2011-10-26 16:27:27 +00:00
if action is ' create ' , create new partner with contact and assign lead to new partner_id .
2012-12-05 19:21:46 +00:00
otherwise assign lead to the specified partner_id
2012-12-05 17:18:15 +00:00
: param list ids : leads / opportunities ids to process
: param string action : what has to be done regarding partners ( create it , assign an existing one , or nothing )
: param int partner_id : partner to assign if any
: return dict : dictionary organized as followed : { lead_id : partner_assigned_id }
2011-10-21 07:01:11 +00:00
"""
2012-12-05 19:24:54 +00:00
#TODO this is a duplication of the handle_partner_assignation method of crm_phonecall
2011-10-24 13:30:10 +00:00
partner_ids = { }
2011-10-21 07:01:11 +00:00
for lead in self . browse ( cr , uid , ids , context = context ) :
2012-12-05 17:18:15 +00:00
# If the action is set to 'create' and no partner_id is set, create a new one
2014-01-23 18:02:11 +00:00
if lead . partner_id :
partner_ids [ lead . id ] = lead . partner_id . id
continue
if not partner_id and action == ' create ' :
partner_id = self . _create_lead_partner ( cr , uid , lead , context )
self . pool [ ' res.partner ' ] . write ( cr , uid , partner_id , { ' section_id ' : lead . section_id and lead . section_id . id or False } )
if partner_id :
2014-11-13 11:30:26 +00:00
lead . write ( { ' partner_id ' : partner_id } )
2011-10-24 13:30:10 +00:00
partner_ids [ lead . id ] = partner_id
2011-10-21 07:01:11 +00:00
return partner_ids
2012-11-27 16:54:01 +00:00
def allocate_salesman ( self , cr , uid , ids , user_ids = None , team_id = False , context = None ) :
2012-11-27 14:26:28 +00:00
"""
Assign salesmen and salesteam to a batch of leads . If there are more
leads than salesmen , these salesmen will be assigned in round - robin .
E . g . : 4 salesmen ( S1 , S2 , S3 , S4 ) for 6 leads ( L1 , L2 , . . . L6 ) . They
will be assigned as followed : L1 - S1 , L2 - S2 , L3 - S3 , L4 - S4 ,
L5 - S1 , L6 - S2 .
: param list ids : leads / opportunities ids to process
: param list user_ids : salesmen to assign
: param int team_id : salesteam to assign
: return bool
"""
2011-10-21 07:01:11 +00:00
index = 0
2012-11-27 16:54:01 +00:00
2011-10-21 07:01:11 +00:00
for lead_id in ids :
value = { }
if team_id :
value [ ' section_id ' ] = team_id
2012-11-27 16:54:01 +00:00
if user_ids :
2011-10-21 07:01:11 +00:00
value [ ' user_id ' ] = user_ids [ index ]
2012-11-27 16:54:01 +00:00
# Cycle through user_ids
2012-04-25 17:48:05 +00:00
index = ( index + 1 ) % len ( user_ids )
2011-10-21 07:01:11 +00:00
if value :
self . write ( cr , uid , [ lead_id ] , value , context = context )
return True
2011-11-25 06:27:03 +00:00
def schedule_phonecall ( self , cr , uid , ids , schedule_time , call_summary , desc , phone , contact_name , user_id = False , section_id = False , categ_id = False , action = ' schedule ' , context = None ) :
2011-10-24 13:30:10 +00:00
"""
2012-11-27 14:26:28 +00:00
: param string action : ( ' schedule ' , ' Schedule a call ' ) , ( ' log ' , ' Log a call ' )
2011-10-24 13:30:10 +00:00
"""
phonecall = self . pool . get ( ' crm.phonecall ' )
model_data = self . pool . get ( ' ir.model.data ' )
phonecall_dict = { }
if not categ_id :
2013-11-14 09:57:21 +00:00
try :
res_id = model_data . _get_id ( cr , uid , ' crm ' , ' categ_phone2 ' )
2011-10-24 13:30:10 +00:00
categ_id = model_data . browse ( cr , uid , res_id , context = context ) . res_id
2013-11-14 09:57:21 +00:00
except ValueError :
pass
2011-10-24 13:30:10 +00:00
for lead in self . browse ( cr , uid , ids , context = context ) :
if not section_id :
section_id = lead . section_id and lead . section_id . id or False
if not user_id :
user_id = lead . user_id and lead . user_id . id or False
vals = {
2012-12-05 18:35:45 +00:00
' name ' : call_summary ,
' opportunity_id ' : lead . id ,
' user_id ' : user_id or False ,
' categ_id ' : categ_id or False ,
' description ' : desc or ' ' ,
' date ' : schedule_time ,
' section_id ' : section_id or False ,
' partner_id ' : lead . partner_id and lead . partner_id . id 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 ,
2011-10-24 13:30:10 +00:00
}
new_id = phonecall . create ( cr , uid , vals , context = context )
2013-08-13 09:20:42 +00:00
phonecall . write ( cr , uid , [ new_id ] , { ' state ' : ' open ' } , context = context )
2011-10-24 13:30:10 +00:00
if action == ' log ' :
2013-08-13 09:20:42 +00:00
phonecall . write ( cr , uid , [ new_id ] , { ' state ' : ' done ' } , context = context )
2011-10-24 13:30:10 +00:00
phonecall_dict [ lead . id ] = new_id
2012-03-20 13:22:11 +00:00
self . schedule_phonecall_send_note ( cr , uid , [ lead . id ] , new_id , action , context = context )
2011-10-24 13:30:10 +00:00
return phonecall_dict
2011-10-21 07:01:11 +00:00
def redirect_opportunity_view ( self , cr , uid , opportunity_id , context = None ) :
models_data = self . pool . get ( ' ir.model.data ' )
2012-11-19 18:12:24 +00:00
# Get opportunity views
2012-12-05 15:50:43 +00:00
dummy , form_view = models_data . get_object_reference ( cr , uid , ' crm ' , ' crm_case_form_view_oppor ' )
dummy , tree_view = models_data . get_object_reference ( cr , uid , ' crm ' , ' crm_case_tree_view_oppor ' )
2011-10-21 07:01:11 +00:00
return {
2012-11-19 18:12:24 +00:00
' name ' : _ ( ' Opportunity ' ) ,
' view_type ' : ' form ' ,
' view_mode ' : ' tree, form ' ,
' res_model ' : ' crm.lead ' ,
' domain ' : [ ( ' type ' , ' = ' , ' opportunity ' ) ] ,
' res_id ' : int ( opportunity_id ) ,
' view_id ' : False ,
2012-12-05 15:50:43 +00:00
' views ' : [ ( form_view or False , ' form ' ) ,
2014-02-05 16:03:47 +00:00
( tree_view or False , ' tree ' ) , ( False , ' kanban ' ) ,
2012-11-19 18:12:24 +00:00
( False , ' calendar ' ) , ( False , ' graph ' ) ] ,
' type ' : ' ir.actions.act_window ' ,
}
def redirect_lead_view ( self , cr , uid , lead_id , context = None ) :
models_data = self . pool . get ( ' ir.model.data ' )
# Get lead views
2012-12-05 15:50:43 +00:00
dummy , form_view = models_data . get_object_reference ( cr , uid , ' crm ' , ' crm_case_form_view_leads ' )
dummy , tree_view = models_data . get_object_reference ( cr , uid , ' crm ' , ' crm_case_tree_view_leads ' )
2012-11-19 18:12:24 +00:00
return {
' name ' : _ ( ' Lead ' ) ,
' view_type ' : ' form ' ,
' view_mode ' : ' tree, form ' ,
' res_model ' : ' crm.lead ' ,
' domain ' : [ ( ' type ' , ' = ' , ' lead ' ) ] ,
' res_id ' : int ( lead_id ) ,
' view_id ' : False ,
2012-12-05 15:50:43 +00:00
' views ' : [ ( form_view or False , ' form ' ) ,
( tree_view or False , ' tree ' ) ,
2012-11-19 18:12:24 +00:00
( False , ' calendar ' ) , ( False , ' graph ' ) ] ,
' type ' : ' ir.actions.act_window ' ,
2011-10-21 07:01:11 +00:00
}
2014-04-17 14:56:45 +00:00
def action_schedule_meeting ( self , cr , uid , ids , context = None ) :
2012-12-05 18:35:45 +00:00
"""
Open meeting ' s calendar view to schedule meeting on current opportunity.
: return dict : dictionary value for created Meeting view
2011-08-25 04:10:37 +00:00
"""
2014-04-17 14:56:45 +00:00
lead = self . browse ( cr , uid , ids [ 0 ] , context )
2014-01-15 09:38:05 +00:00
res = self . pool . get ( ' ir.actions.act_window ' ) . for_xml_id ( cr , uid , ' calendar ' , ' action_calendar_event ' , context )
2014-04-17 14:56:45 +00:00
partner_ids = [ self . pool [ ' res.users ' ] . browse ( cr , uid , uid , context = context ) . partner_id . id ]
if lead . partner_id :
partner_ids . append ( lead . partner_id . id )
2012-07-06 12:29:59 +00:00
res [ ' context ' ] = {
2014-04-17 14:56:45 +00:00
' default_opportunity_id ' : lead . type == ' opportunity ' and lead . id or False ,
' default_partner_id ' : lead . partner_id and lead . partner_id . id or False ,
' default_partner_ids ' : partner_ids ,
' default_section_id ' : lead . section_id and lead . section_id . id or False ,
' default_name ' : lead . name ,
2012-07-06 12:29:59 +00:00
}
return res
2011-08-27 21:19:48 +00:00
2013-07-08 10:30:11 +00:00
def create ( self , cr , uid , vals , context = None ) :
2014-07-06 14:44:26 +00:00
context = dict ( context or { } )
2013-07-08 10:30:11 +00:00
if vals . get ( ' type ' ) and not context . get ( ' default_type ' ) :
context [ ' default_type ' ] = vals . get ( ' type ' )
if vals . get ( ' section_id ' ) and not context . get ( ' default_section_id ' ) :
context [ ' default_section_id ' ] = vals . get ( ' section_id ' )
2014-08-06 11:00:06 +00:00
if vals . get ( ' user_id ' ) :
vals [ ' date_open ' ] = fields . datetime . now ( )
2013-07-08 10:30:11 +00:00
# context: no_log, because subtype already handle this
create_context = dict ( context , mail_create_nolog = True )
return super ( crm_lead , self ) . create ( cr , uid , vals , context = create_context )
2011-08-25 04:10:37 +00:00
def write ( self , cr , uid , ids , vals , context = None ) :
2013-07-17 14:13:36 +00:00
# stage change: update date_last_stage_update
if ' stage_id ' in vals :
vals [ ' date_last_stage_update ' ] = fields . datetime . now ( )
2014-08-06 11:00:06 +00:00
if vals . get ( ' user_id ' ) :
vals [ ' date_open ' ] = fields . datetime . now ( )
# stage change with new stage: update probability and date_closed
2012-05-30 09:03:40 +00:00
if vals . get ( ' stage_id ' ) and not vals . get ( ' probability ' ) :
2013-07-08 10:30:11 +00:00
onchange_stage_values = self . onchange_stage_id ( cr , uid , ids , vals . get ( ' stage_id ' ) , context = context ) [ ' value ' ]
vals . update ( onchange_stage_values )
2012-12-19 16:40:48 +00:00
return super ( crm_lead , self ) . write ( cr , uid , ids , vals , context = context )
2012-06-04 10:20:49 +00:00
2013-11-14 20:45:50 +00:00
def copy ( self , cr , uid , id , default = None , context = None ) :
if not default :
default = { }
if not context :
context = { }
lead = self . browse ( cr , uid , id , context = context )
local_context = dict ( context )
local_context . setdefault ( ' default_type ' , lead . type )
2014-10-15 10:29:33 +00:00
local_context . setdefault ( ' default_section_id ' , lead . section_id . id )
2013-11-14 20:45:50 +00:00
if lead . type == ' opportunity ' :
default [ ' date_open ' ] = fields . datetime . now ( )
else :
default [ ' date_open ' ] = False
2014-07-06 14:44:26 +00:00
return super ( crm_lead , self ) . copy ( cr , uid , id , default , context = local_context )
2013-11-14 20:45:50 +00:00
2013-12-20 11:00:45 +00:00
def get_empty_list_help ( self , cr , uid , help , context = None ) :
2014-07-06 14:44:26 +00:00
context = dict ( context or { } )
2013-12-20 11:00:45 +00:00
context [ ' empty_list_help_model ' ] = ' crm.case.section '
context [ ' empty_list_help_id ' ] = context . get ( ' default_section_id ' , None )
2014-01-03 07:03:03 +00:00
context [ ' empty_list_help_document_name ' ] = _ ( " opportunity " )
if context . get ( ' default_type ' ) == ' lead ' :
context [ ' empty_list_help_document_name ' ] = _ ( " lead " )
2013-12-20 11:00:45 +00:00
return super ( crm_lead , self ) . get_empty_list_help ( cr , uid , help , context = context )
2012-06-04 10:20:49 +00:00
# ----------------------------------------
# Mail Gateway
# ----------------------------------------
2011-10-21 07:01:11 +00:00
2013-01-03 17:03:16 +00:00
def message_get_reply_to ( self , cr , uid , ids , context = None ) :
""" Override to get the reply_to of the parent project. """
2014-06-20 11:38:22 +00:00
leads = self . browse ( cr , SUPERUSER_ID , ids , context = context )
section_ids = set ( [ lead . section_id . id for lead in leads if lead . section_id ] )
aliases = self . pool [ ' crm.case.section ' ] . message_get_reply_to ( cr , uid , list ( section_ids ) , context = context )
return dict ( ( lead . id , aliases . get ( lead . section_id and lead . section_id . id or 0 , False ) ) for lead in leads )
2013-01-03 17:03:16 +00:00
2014-04-16 14:37:55 +00:00
def get_formview_id ( self , cr , uid , id , context = None ) :
2013-04-26 13:28:47 +00:00
obj = self . browse ( cr , uid , id , context = context )
if obj . type == ' opportunity ' :
model , view_id = self . pool . get ( ' ir.model.data ' ) . get_object_reference ( cr , uid , ' crm ' , ' crm_case_form_view_oppor ' )
2014-04-16 14:37:55 +00:00
else :
2014-05-12 07:59:19 +00:00
view_id = super ( crm_lead , self ) . get_formview_id ( cr , uid , id , context = context )
2014-04-16 14:37:55 +00:00
return view_id
2013-01-03 17:03:16 +00:00
2013-02-20 12:49:23 +00:00
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 )
2013-04-17 08:59:27 +00:00
try :
for lead in self . browse ( cr , uid , ids , context = context ) :
if lead . partner_id :
self . _message_add_suggested_recipient ( cr , uid , recipients , lead , partner = lead . partner_id , reason = _ ( ' Customer ' ) )
elif lead . email_from :
self . _message_add_suggested_recipient ( cr , uid , recipients , lead , email = lead . email_from , reason = _ ( ' Customer Email ' ) )
except ( osv . except_osv , orm . except_orm ) : # no read access rights -> just ignore suggested recipients because this imply modifying followers
pass
2013-02-20 12:49:23 +00:00
return recipients
2011-07-22 18:23:37 +00:00
def message_new ( self , cr , uid , msg , custom_values = None , context = None ) :
2012-06-04 14:12:54 +00:00
""" Overrides mail_thread message_new that is called by the mailgateway
through message_process .
This override updates the document according to the email .
2012-06-04 10:20:49 +00:00
"""
2013-03-07 15:32:16 +00:00
if custom_values is None :
custom_values = { }
2013-01-10 17:27:23 +00:00
defaults = {
2012-06-04 14:12:54 +00:00
' name ' : msg . get ( ' subject ' ) or _ ( " No Subject " ) ,
' email_from ' : msg . get ( ' from ' ) ,
2010-06-24 19:53:32 +00:00
' email_cc ' : msg . get ( ' cc ' ) ,
2013-01-15 13:43:59 +00:00
' partner_id ' : msg . get ( ' author_id ' , False ) ,
2010-06-24 19:53:32 +00:00
' user_id ' : False ,
2013-01-10 17:27:23 +00:00
}
2013-01-29 14:38:15 +00:00
if msg . get ( ' author_id ' ) :
2013-07-08 10:30:11 +00:00
defaults . update ( self . on_change_partner_id ( cr , uid , None , msg . get ( ' author_id ' ) , context = context ) [ ' value ' ] )
2012-06-04 10:20:49 +00:00
if msg . get ( ' priority ' ) in dict ( crm . AVAILABLE_PRIORITIES ) :
2013-01-10 17:27:23 +00:00
defaults [ ' priority ' ] = msg . get ( ' priority ' )
defaults . update ( custom_values )
return super ( crm_lead , self ) . message_new ( cr , uid , msg , custom_values = defaults , context = context )
2010-06-24 19:53:32 +00:00
2012-06-04 14:12:54 +00:00
def message_update ( self , cr , uid , ids , msg , update_vals = None , context = None ) :
""" Overrides mail_thread message_update that is called by the mailgateway
through message_process .
This method updates the document according to the email .
2012-06-04 10:20:49 +00:00
"""
2010-06-24 19:53:32 +00:00
if isinstance ( ids , ( str , int , long ) ) :
ids = [ ids ]
2012-06-04 14:12:54 +00:00
if update_vals is None : update_vals = { }
2011-07-22 18:23:37 +00:00
2010-08-26 15:31:21 +00:00
if msg . get ( ' priority ' ) in dict ( crm . AVAILABLE_PRIORITIES ) :
2012-08-23 20:06:29 +00:00
update_vals [ ' priority ' ] = msg . get ( ' priority ' )
2010-06-24 19:53:32 +00:00
maps = {
' cost ' : ' planned_cost ' ,
' revenue ' : ' planned_revenue ' ,
2012-06-04 14:12:54 +00:00
' probability ' : ' probability ' ,
2010-06-24 19:53:32 +00:00
}
2012-08-17 12:06:06 +00:00
for line in msg . get ( ' body ' , ' ' ) . split ( ' \n ' ) :
2010-06-24 19:53:32 +00:00
line = line . strip ( )
2012-11-19 13:21:41 +00:00
res = tools . command_re . match ( line )
2010-06-25 12:05:00 +00:00
if res and maps . get ( res . group ( 1 ) . lower ( ) ) :
2010-06-24 19:53:32 +00:00
key = maps . get ( res . group ( 1 ) . lower ( ) )
2012-08-23 20:06:29 +00:00
update_vals [ key ] = res . group ( 2 ) . lower ( )
2010-06-24 19:53:32 +00:00
2012-06-04 14:12:54 +00:00
return super ( crm_lead , self ) . message_update ( cr , uid , ids , msg , update_vals = update_vals , context = context )
2011-08-25 04:10:37 +00:00
2012-04-02 17:14:14 +00:00
# ----------------------------------------
# OpenChatter methods and notifications
# ----------------------------------------
2011-08-27 21:19:48 +00:00
2012-04-02 17:14:14 +00:00
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 ]
2013-03-06 14:01:07 +00:00
if action == ' log ' :
2014-01-07 16:58:40 +00:00
message = _ ( ' Logged a call for %(date)s . %(description)s ' )
2013-03-06 14:01:07 +00:00
else :
2014-01-07 16:58:40 +00:00
message = _ ( ' Scheduled a call for %(date)s . %(description)s ' )
2014-01-06 05:30:39 +00:00
phonecall_date = datetime . strptime ( phonecall . date , tools . DEFAULT_SERVER_DATETIME_FORMAT )
phonecall_usertime = fields . datetime . context_timestamp ( cr , uid , phonecall_date , context = context ) . strftime ( tools . DEFAULT_SERVER_DATETIME_FORMAT )
2014-01-07 15:49:42 +00:00
html_time = " <time datetime= ' %s +00:00 ' > %s </time> " % ( phonecall . date , phonecall_usertime )
2014-01-07 16:58:40 +00:00
message = message % dict ( date = html_time , description = phonecall . description )
2012-08-17 10:03:02 +00:00
return self . message_post ( cr , uid , ids , body = message , context = context )
2012-04-02 17:14:14 +00:00
2013-04-15 14:15:07 +00:00
def log_meeting ( self , cr , uid , ids , meeting_subject , meeting_date , duration , context = None ) :
if not duration :
2013-04-16 07:43:35 +00:00
duration = _ ( ' unknown ' )
2013-04-15 14:15:07 +00:00
else :
2013-04-17 09:09:25 +00:00
duration = str ( duration )
message = _ ( " Meeting scheduled at ' %s ' <br> Subject: %s <br> Duration: %s hour(s) " ) % ( meeting_date , meeting_subject , duration )
2013-04-15 14:15:07 +00:00
return self . message_post ( cr , uid , ids , body = message , context = context )
2012-10-25 09:42:31 +00:00
def onchange_state ( self , cr , uid , ids , state_id , context = None ) :
2012-10-25 10:06:29 +00:00
if state_id :
2012-10-25 10:26:36 +00:00
country_id = self . pool . get ( ' res.country.state ' ) . browse ( cr , uid , state_id , context ) . country_id . id
2012-11-02 09:13:25 +00:00
return { ' value ' : { ' country_id ' : country_id } }
return { }
2010-03-22 10:40:26 +00:00
2014-07-31 13:33:41 +00:00
def message_partner_info_from_emails ( self , cr , uid , id , emails , link_mail = False , context = None ) :
res = super ( crm_lead , self ) . message_partner_info_from_emails ( cr , uid , id , emails , link_mail = link_mail , context = context )
lead = self . browse ( cr , uid , id , context = context )
for partner_info in res :
if not partner_info . get ( ' partner_id ' ) and ( lead . partner_name or lead . contact_name ) :
emails = email_re . findall ( partner_info [ ' full_name ' ] or ' ' )
email = emails and emails [ 0 ] or ' '
if email and lead . email_from and email . lower ( ) == lead . email_from . lower ( ) :
partner_info [ ' full_name ' ] = ' %s < %s > ' % ( lead . partner_name or lead . contact_name , email )
break
return res
2010-03-23 14:12:01 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: