#
##############################################################################
-from openerp.addons.base_status.base_stage import base_stage
import crm
from datetime import datetime
from operator import itemgetter
-from openerp.osv import fields, osv, orm
-import time
+
+import openerp
from openerp import SUPERUSER_ID
from openerp import tools
-from openerp.tools.translate import _
-from openerp.tools import html2plaintext
-
from openerp.addons.base.res.res_partner import format_address
+from openerp.osv import fields, osv, orm
+from openerp.tools import html2plaintext
+from openerp.tools.translate import _
CRM_LEAD_FIELDS_TO_MERGE = ['name',
'partner_id',
'partner_name']
-class crm_lead(base_stage, format_address, osv.osv):
+class crm_lead(format_address, osv.osv):
""" CRM Lead Case """
_name = "crm.lead"
_description = "Lead/Opportunity"
_track = {
'stage_id': {
- '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.probability > 0 and obj.probability < 100,
- 'crm.mt_lead_won': lambda self, cr, uid, obj, ctx=None: obj.probability == 100,
- 'crm.mt_lead_lost': lambda self, cr, uid, obj, ctx=None: obj.probability == 0 and obj.stage_id and obj.stage_id.sequence != 1,
- }
+ # 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,
+ '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,
+ },
}
def get_empty_list_help(self, cr, uid, help, context=None):
context['empty_list_help_document_name'] = _("leads")
return super(crm_lead, self).get_empty_list_help(cr, uid, help, context=context)
- def create(self, cr, uid, vals, context=None):
- if context is None:
- context = {}
- if not vals.get('stage_id'):
- ctx = context.copy()
- if vals.get('section_id'):
- ctx['default_section_id'] = vals['section_id']
- if vals.get('type'):
- ctx['default_type'] = vals['type']
- vals['stage_id'] = self._get_default_stage_id(cr, uid, context=ctx)
- # 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)
-
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) 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, [('fold', '=', False)], context=context)
+
def _resolve_section_id_from_context(self, cr, uid, context=None):
""" Returns ID of section based on the value of 'section_id'
context key, or None if it cannot be resolved to a single
if type(context.get('default_section_id')) in (int, long):
return context.get('default_section_id')
if isinstance(context.get('default_section_id'), basestring):
- section_name = context['default_section_id']
- section_ids = self.pool.get('crm.case.section').name_search(cr, uid, name=section_name, context=context)
+ section_ids = self.pool.get('crm.case.section').name_search(cr, uid, name=context['default_section_id'], context=context)
if len(section_ids) == 1:
return int(section_ids[0][0])
return None
return result, fold
def fields_view_get(self, cr, user, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
+ if view_type == 'form' and context and context.get('opportunity_id'):
+ # TODO: replace by get_formview_action call
+ lead_type = self.browse(cr, user, context['opportunity_id'], context=context).type
+ view_lead_xml_id = 'crm_case_form_view_oppor' if lead_type == 'opportunity' else 'crm_case_form_view_leads'
+ _, view_id = self.pool['ir.model.data'].get_object_reference(cr, user, 'crm', view_lead_xml_id)
res = super(crm_lead, self).fields_view_get(cr, user, view_id, view_type, context, toolbar=toolbar, submenu=submenu)
if view_type == 'form':
res['arch'] = self.fields_view_get_address(cr, user, res['arch'], context=context)
'section_id': fields.many2one('crm.case.section', 'Sales Team',
select=True, track_visibility='onchange', help='When sending mails, the default email address is taken from the sales team.'),
'create_date': fields.datetime('Creation Date', readonly=True),
- 'email_cc': fields.text('Global CC', size=252, 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"),
+ '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"),
'description': fields.text('Notes'),
'write_date': fields.datetime('Update Date', readonly=True),
'categ_ids': fields.many2many('crm.case.categ', 'crm_lead_category_rel', 'lead_id', 'category_id', 'Categories', \
domain="['|',('section_id','=',section_id),('section_id','=',False)]", help="From which campaign (seminar, marketing campaign, mass mailing, ...) did this contact come from?"),
'channel_id': fields.many2one('crm.case.channel', 'Channel', help="Communication channel (mail, direct, phone, ...)"),
'contact_name': fields.char('Contact Name', size=64),
- 'partner_name': fields.char("Customer Name", size=64, help='The name of the future partner company that will be created while converting the lead into opportunity', select=1),
+ 'partner_name': fields.char("Customer Name", size=64,help='The name of the future partner company that will be created while converting the lead into opportunity', select=1),
'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. "
"Filter 'Available for Mass Mailing' allows users to filter the leads when performing mass mailing."),
- 'type': fields.selection([('lead', 'Lead'), ('opportunity', 'Opportunity'), ], 'Type', help="Type is used to separate Leads and Opportunities"),
+ 'type': fields.selection([ ('lead','Lead'), ('opportunity','Opportunity'), ],'Type', select=True, help="Type is used to separate Leads and Opportunities"),
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True),
'date_closed': fields.datetime('Closed', readonly=True),
- 'stage_id': fields.many2one('crm.case.stage', 'Stage', track_visibility='onchange',),
+ 'stage_id': fields.many2one('crm.case.stage', 'Stage', track_visibility='onchange', select=True,
+ domain="['&', ('section_ids', '=', section_id), '|', ('type', '=', type), ('type', '=', 'both')]"),
'user_id': fields.many2one('res.users', 'Salesperson', select=True, track_visibility='onchange'),
'referred': fields.char('Referred By', size=64),
- 'date_open': fields.datetime('Opened', readonly=True),
+ 'date_open': fields.datetime('Assigned', readonly=True),
'day_open': fields.function(_compute_day, string='Days to Open', \
multi='day_open', type="float", store=True),
'day_close': fields.function(_compute_day, string='Days to Close', \
multi='day_close', type="float", store=True),
+ 'date_last_stage_update': fields.datetime('Last Stage Update', select=True),
+ # Messaging and marketing
+ 'message_bounce': fields.integer('Bounce'),
# Only used for type opportunity
'probability': fields.float('Success Rate (%)', group_operator="avg"),
'planned_revenue': fields.float('Expected Revenue', track_visibility='always'),
- 'ref': fields.reference('Reference', selection=crm._links_get, size=128),
- 'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128),
+ '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),
'phone': fields.char("Phone", size=64),
'date_deadline': fields.date('Expected Closing', help="Estimate of the date on which the opportunity will be won."),
'date_action': fields.date('Next Action Date', select=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),
+ 'user_id': lambda s, cr, uid, c: uid,
'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),
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
'color': 0,
+ 'date_last_stage_update': fields.datetime.now,
}
_sql_constraints = [
stage = self.pool.get('crm.case.stage').browse(cr, uid, stage_id, context=context)
if not stage.on_change:
return {'value': {}}
- return {'value': {'probability': stage.probability}}
+ 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}
- def on_change_partner(self, cr, uid, ids, partner_id, context=None):
- result = {}
+ def on_change_partner_id(self, cr, uid, ids, partner_id, context=None):
values = {}
if partner_id:
partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context)
values = {
- 'partner_name' : partner.name,
- '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,
+ 'partner_name': partner.name,
+ '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,
+ 'zip': partner.zip,
}
- return {'value' : values}
+ return {'value': values}
def on_change_user(self, cr, uid, ids, user_id, context=None):
""" When changing the user, also set a section_id or restrict section id
section_id = section_ids[0]
return {'value': {'section_id': section_id}}
- 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 won and lost 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 probability not in (0,100)',
- (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=None, order='sequence', context=None):
""" Override of the base.stage method
Parameter of the stage search taken from the lead:
"""
if isinstance(cases, (int, long)):
cases = self.browse(cr, uid, cases, context=context)
+ 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'])
# collect all section_ids
section_ids = set()
types = ['both']
- if not cases:
+ if not cases and context.get('default_type'):
ctx_type = context.get('default_type')
types += [ctx_type]
if section_id:
search_domain.append(('section_ids', '=', section_id))
search_domain.append(('case_default', '=', True))
# AND with cases types
- search_domain.append(('type', 'in', types))
+ if not avoid_add_type_term:
+ 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)
+ stage_ids = self.pool.get('crm.case.stage').search(cr, uid, search_domain, order=order, limit=1, context=context)
if stage_ids:
return stage_ids[0]
return False
def case_mark_lost(self, cr, uid, ids, context=None):
- """ Mark the case as lost: stage with probability=0, on_change=True """
- 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), ('on_change', '=', True), ('sequence', '>', 1)], context=context)
+ """ Mark the case as lost: state=cancel and probability=0
+ :deprecated: this method will be removed in OpenERP v8.
+ """
+ stages_leads = {}
+ for lead in self.browse(cr, uid, ids, context=context):
+ stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 0.0), ('fold', '=', True), ('sequence', '>', 1)], context=context)
if stage_id:
- self.case_set(cr, uid, [lead.id], new_stage_id=stage_id, context=context)
+ if stages_leads.get(stage_id):
+ stages_leads[stage_id].append(lead.id)
+ else:
+ stages_leads[stage_id] = [lead.id]
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.'))
+ 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.'))
+ for stage_id, lead_ids in stages_leads.items():
+ self.write(cr, uid, lead_ids, {'stage_id': stage_id}, context=context)
return True
def case_mark_won(self, cr, uid, ids, context=None):
- """ Mark the case as won: stage with probability=100, , on_change=True """
- 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), ('on_change', '=', True)], context=context)
+ """ Mark the case as won: state=done and probability=100
+ :deprecated: this method will be removed in OpenERP v8.
+ """
+ stages_leads = {}
+ for lead in self.browse(cr, uid, ids, context=context):
+ stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 100.0), ('fold', '=', True)], context=context)
if stage_id:
- self.case_set(cr, uid, [lead.id], new_stage_id=stage_id, context=context)
+ if stages_leads.get(stage_id):
+ stages_leads[stage_id].append(lead.id)
+ else:
+ stages_leads[stage_id] = [lead.id]
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 % and select "Change Probability Automatically".\n'
- 'Create a specific stage or edit an existing one by editing columns of your opportunity pipe.'))
+ 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 % and select "Change Probability Automatically".\n'
+ 'Create a specific stage or edit an existing one by editing columns of your opportunity pipe.'))
+ for stage_id, lead_ids in stages_leads.items():
+ self.write(cr, uid, lead_ids, {'stage_id': stage_id}, context=context)
return True
- def set_priority(self, cr, uid, ids, priority):
+ 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.\nTherefore you cannot escalate furthermore."))
+ self.write(cr, uid, [case.id], data, context=context)
+ return True
+
+ def set_priority(self, cr, uid, ids, priority, context=None):
""" Set lead priority
"""
- return self.write(cr, uid, ids, {'priority': priority})
+ return self.write(cr, uid, ids, {'priority': priority}, context=context)
def set_high_priority(self, cr, uid, ids, context=None):
""" Set lead priority to high
"""
- return self.set_priority(cr, uid, ids, '1')
+ return self.set_priority(cr, uid, ids, '1', context=context)
def set_normal_priority(self, cr, uid, ids, context=None):
""" Set lead priority to normal
"""
- return self.set_priority(cr, uid, ids, '3')
+ return self.set_priority(cr, uid, ids, '3', context=context)
def _merge_get_result_type(self, cr, uid, opps, context=None):
"""
attachment.write(values)
return True
- def merge_opportunity(self, cr, uid, ids, context=None):
+ def merge_opportunity(self, cr, uid, ids, user_id=False, section_id=False, context=None):
"""
Different cases of merge:
- merge leads together = 1 new lead
opportunities = self.browse(cr, uid, ids, context=context)
sequenced_opps = []
+ # 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
for opportunity in opportunities:
sequence = -1
- if opportunity.stage_id and (opportunity.probability == 0 and opportunity.stage_id and opportunity.stage_id.sequence != 1):
+ if opportunity.stage_id and not opportunity.stage_id.fold:
sequence = opportunity.stage_id.sequence
sequenced_opps.append(((int(sequence != -1 and opportunity.type == 'opportunity'), sequence, -opportunity.id), opportunity))
fields = list(CRM_LEAD_FIELDS_TO_MERGE)
merged_data = self._merge_data(cr, uid, ids, highest, fields, context=context)
+ if user_id:
+ merged_data['user_id'] = user_id
+ if section_id:
+ merged_data['section_id'] = section_id
+
# Merge messages and attachements into the first opportunity
self._merge_opportunity_history(cr, uid, highest.id, tail_opportunities, context=context)
self._merge_opportunity_attachments(cr, uid, highest.id, tail_opportunities, context=context)
# Merge notifications about loss of information
opportunities = [highest]
opportunities.extend(opportunities_rest)
- self._merge_notify(cr, uid, highest, opportunities, context=context)
+ self._merge_notify(cr, uid, highest.id, opportunities, context=context)
# Check if the stage is in the stages of the sales team. If not, assign the stage with the lowest sequence
if merged_data.get('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)
'email_from': customer and customer.email or lead.email_from,
'phone': customer and customer.phone or lead.phone,
}
- if not lead.stage_id or lead.stage_id.type == 'lead':
- val['stage_id'] = self.stage_find(cr, uid, [lead], section_id, ['&', ('sequence', '=', '1'), ('type', 'in', ('opportunity', 'both'))], context=context)
+ if not lead.stage_id or lead.stage_id.type=='lead':
+ val['stage_id'] = self.stage_find(cr, uid, [lead], section_id, [('type', 'in', ('opportunity', 'both'))], context=context)
return val
def convert_opportunity(self, cr, uid, ids, partner_id, user_ids=False, section_id=False, context=None):
partner = self.pool.get('res.partner')
customer = partner.browse(cr, uid, partner_id, context=context)
for lead in self.browse(cr, uid, ids, context=context):
- # avoid done / cancelled leads
- if lead.probability == 100 or (lead.probability == 0 and lead.stage_id and lead.stage_id.sequence == 1):
+ # TDE: was if lead.state in ('done', 'cancel'):
+ if lead.probability == 100 or (lead.probability == 0 and lead.stage_id.fold):
continue
vals = self._convert_opportunity_data(cr, uid, lead, customer, section_id, context=context)
self.write(cr, uid, [lead.id], vals, context=context)
- self.message_post(cr, uid, ids, body=_("Lead <b>converted into an Opportunity</b>"), subtype="crm.mt_lead_convert_to_opportunity", context=context)
if user_ids or section_id:
self.allocate_salesman(cr, uid, ids, user_ids, section_id, context=context)
)
return partner_id
- def _lead_set_partner(self, cr, uid, lead, partner_id, context=None):
- """
- Assign a partner to a lead.
-
- :param object lead: browse record of the lead to process
- :param int partner_id: identifier of the partner to assign
- :return bool: True if the partner has properly been assigned
- """
- res = False
- res_partner = self.pool.get('res.partner')
- if partner_id:
- res_partner.write(cr, uid, partner_id, {'section_id': lead.section_id and lead.section_id.id or False})
- contact_id = res_partner.address_get(cr, uid, [partner_id])['default']
- res = lead.write({'partner_id': partner_id}, context=context)
- message = _("<b>Partner</b> set to <em>%s</em>." % (lead.partner_id.name))
- self.message_post(cr, uid, [lead.id], body=message, context=context)
- return res
-
def handle_partner_assignation(self, cr, uid, ids, action='create', partner_id=False, context=None):
"""
Handle partner assignation during a lead conversion.
"""
#TODO this is a duplication of the handle_partner_assignation method of crm_phonecall
partner_ids = {}
- # If a partner_id is given, force this partner for all elements
- force_partner_id = partner_id
for lead in self.browse(cr, uid, ids, context=context):
# If the action is set to 'create' and no partner_id is set, create a new one
- if action == 'create':
- partner_id = force_partner_id or self._create_lead_partner(cr, uid, lead, context)
- self._lead_set_partner(cr, uid, lead, partner_id, context=context)
+ 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:
+ lead.write({'partner_id': partner_id}, context=context)
partner_ids[lead.id] = partner_id
return partner_ids
model_data = self.pool.get('ir.model.data')
phonecall_dict = {}
if not categ_id:
- res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2')
- if res_id:
+ try:
+ res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2')
categ_id = model_data.browse(cr, uid, res_id, context=context).res_id
+ except ValueError:
+ pass
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
'priority': lead.priority,
}
new_id = phonecall.create(cr, uid, vals, context=context)
- phonecall.case_open(cr, uid, [new_id], context=context)
+ phonecall.write(cr, uid, [new_id], {'state': 'open'}, context=context)
if action == 'log':
- phonecall.case_close(cr, uid, [new_id], context=context)
+ phonecall.write(cr, uid, [new_id], {'state': 'done'}, context=context)
phonecall_dict[lead.id] = new_id
self.schedule_phonecall_send_note(cr, uid, [lead.id], new_id, action, context=context)
return phonecall_dict
:return dict: dictionary value for created Meeting view
"""
opportunity = self.browse(cr, uid, ids[0], context)
- res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'base_calendar', 'action_crm_meeting', context)
+ res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'calendar', 'action_calendar_event', context)
res['context'] = {
'default_opportunity_id': opportunity.id,
'default_partner_id': opportunity.partner_id and opportunity.partner_id.id or False,
}
return res
+ def create(self, cr, uid, vals, context=None):
+ if context is None:
+ context = {}
+ 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')
+
+ # 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)
+
def write(self, cr, uid, ids, vals, context=None):
- stage_pool=self.pool.get('crm.case.stage')
+ # stage change: update date_last_stage_update
+ if 'stage_id' in vals:
+ vals['date_last_stage_update'] = fields.datetime.now()
+ # stage change with new stage: update probability
if vals.get('stage_id') and not vals.get('probability'):
- # change probability of lead(s) if required by stage
- stage = stage_pool.browse(cr, uid, vals['stage_id'], context=context)
- if stage.on_change:
- vals['probability'] = stage.probability
- if vals.get('probability') == 100:
- vals['stage_id'] = stage_pool.search(cr, uid, [('probability','=',100.0)],order='sequence')[0]
+ onchange_stage_values = self.onchange_stage_id(cr, uid, ids, vals.get('stage_id'), context=context)['value']
+ vals.update(onchange_stage_values)
return super(crm_lead, self).write(cr, uid, ids, vals, context=context)
- def new_mail_send(self, cr, uid, ids, context=None):
- '''
- This function opens a window to compose an email, with the edi sale template message loaded by default
- '''
- assert len(ids) == 1, 'This option should only be used for a single id at a time.'
- ir_model_data = self.pool.get('ir.model.data')
- try:
- template_id = ir_model_data.get_object_reference(cr, uid, 'crm', 'email_template_opportunity_mail')[1]
- except ValueError:
- template_id = False
- try:
- compose_form_id = ir_model_data.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form')[1]
- except ValueError:
- compose_form_id = False
- if context is None:
+ def copy(self, cr, uid, id, default=None, context=None):
+ if not default:
+ default = {}
+ if not context:
context = {}
- ctx = context.copy()
- ctx.update({
- 'default_model': 'crm.lead',
- 'default_res_id': ids[0],
- 'default_use_template': bool(template_id),
- 'default_template_id': template_id,
- 'default_composition_mode': 'comment',
- })
- return {
- 'name': _('Compose Email'),
- 'type': 'ir.actions.act_window',
- 'view_type': 'form',
- 'view_mode': 'form',
- 'res_model': 'mail.compose.message',
- 'views': [(compose_form_id, 'form')],
- 'view_id': compose_form_id,
- 'target': 'new',
- 'context': ctx,
- }
+ lead = self.browse(cr, uid, id, context=context)
+ local_context = dict(context)
+ local_context.setdefault('default_type', lead.type)
+ local_context.setdefault('default_section_id', lead.section_id)
+ if lead.type == 'opportunity':
+ default['date_open'] = fields.datetime.now()
+ else:
+ default['date_open'] = False
+ default['date_closed'] = False
+ default['stage_id'] = self._get_default_stage_id(cr, uid, local_context)
+ return super(crm_lead, self).copy(cr, uid, id, default, context=context)
# ----------------------------------------
# Mail Gateway
"""
if custom_values is None:
custom_values = {}
- desc = html2plaintext(msg.get('body')) if msg.get('body') else ''
defaults = {
'name': msg.get('subject') or _("No Subject"),
- 'description': desc,
'email_from': msg.get('from'),
'email_cc': msg.get('cc'),
'partner_id': msg.get('author_id', False),
'user_id': False,
}
if msg.get('author_id'):
- defaults.update(self.onchange_partner_id(cr, uid, None, msg.get('author_id'), context=context)['value'])
+ defaults.update(self.on_change_partner_id(cr, uid, None, msg.get('author_id'), context=context)['value'])
if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES):
defaults['priority'] = msg.get('priority')
defaults.update(custom_values)
"""
if isinstance(ids, (str, int, long)):
ids = [ids]
- if update_vals is None:
- update_vals = {}
+ if update_vals is None: update_vals = {}
+
if msg.get('priority') in dict(crm.AVAILABLE_PRIORITIES):
update_vals['priority'] = msg.get('priority')
maps = {
- 'cost': 'planned_cost',
+ 'cost':'planned_cost',
'revenue': 'planned_revenue',
- 'probability': 'probability',
+ 'probability':'probability',
}
for line in msg.get('body', '').split('\n'):
line = line.strip()
def schedule_phonecall_send_note(self, cr, uid, ids, phonecall_id, action, context=None):
phonecall = self.pool.get('crm.phonecall').browse(cr, uid, [phonecall_id], context=context)[0]
if action == 'log':
- prefix = 'Logged'
+ message = _('Logged a call for %(date)s. %(description)s')
else:
- prefix = 'Scheduled'
- suffix = ' %s' % phonecall.description
- message = _("%s a call for %s.%s") % (prefix, phonecall.date, suffix)
+ message = _('Scheduled a call for %(date)s. %(description)s')
+ 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)
+ html_time = "<time datetime='%s+00:00'>%s</time>" % (phonecall.date, phonecall_usertime)
+ message = message % dict(date=html_time, description=phonecall.description)
return self.message_post(cr, uid, ids, body=message, context=context)
def log_meeting(self, cr, uid, ids, meeting_subject, meeting_date, duration, context=None):