X-Git-Url: http://git.inspyration.org/?a=blobdiff_plain;f=addons%2Fcrm%2Fcrm_lead.py;h=edc61b464f75646c4161c2aa15529c3699142962;hb=8711954c1c6e8d3f0dc60853148b4463667f8082;hp=2b7d9a7672dbd66a89f9747d3145376997742f1e;hpb=6de2650299da568e81576692b524e415f06a8a53;p=odoo%2Fodoo.git diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 2b7d9a7..edc61b4 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -22,8 +22,10 @@ from openerp.addons.base_status.base_stage import base_stage import crm from datetime import datetime -from openerp.osv import fields, osv +from operator import itemgetter +from openerp.osv import fields, osv, orm import time +from openerp import SUPERUSER_ID from openerp import tools from openerp.tools.translate import _ from openerp.tools import html2plaintext @@ -75,26 +77,26 @@ class crm_lead(base_stage, format_address, osv.osv): _track = { 'state': { - 'crm.mt_lead_create': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'new', + 'crm.mt_lead_create': lambda self, cr, uid, obj, ctx=None: obj['state'] in ['new', 'draft'], 'crm.mt_lead_won': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done', 'crm.mt_lead_lost': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'cancel', }, 'stage_id': { - 'crm.mt_lead_stage': lambda self, cr, uid, obj, ctx=None: obj['state'] not in ['new', 'cancel', 'done'], + 'crm.mt_lead_stage': lambda self, cr, uid, obj, ctx=None: obj['state'] not in ['new', 'draft', 'cancel', 'done'], }, } 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) - return super(crm_lead, self).create(cr, uid, vals, context=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 _get_default_section_id(self, cr, uid, context=None): """ Gives default section by checking if present in the context """ @@ -259,7 +261,9 @@ class crm_lead(base_stage, format_address, osv.osv): '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), - 'opt_out': fields.boolean('Opt-Out', oldname='optout', help="If opt-out is checked, this contact has refused to receive emails or unsubscribed to a campaign."), + '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"), 'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True), 'date_closed': fields.datetime('Closed', readonly=True), @@ -353,6 +357,15 @@ class crm_lead(base_stage, format_address, osv.osv): } 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 + to the ones user_id is member of. """ + if user_id: + section_ids = self.pool.get('crm.case.section').search(cr, uid, ['|', ('user_id', '=', user_id), ('member_ids', '=', user_id)], context=context) + if section_ids: + return {'value': {'section_id': section_ids[0]}} + return {'value': {}} + 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 @@ -626,12 +639,13 @@ class crm_lead(base_stage, format_address, osv.osv): opportunities = self.browse(cr, uid, ids, context=context) sequenced_opps = [] for opportunity in opportunities: + sequence = -1 if opportunity.stage_id and opportunity.stage_id.state != 'cancel': - sequenced_opps.append((opportunity.stage_id.sequence, opportunity)) - else: - sequenced_opps.append((-1, opportunity)) - sequenced_opps.sort(key=lambda tup: tup[0], reverse=True) - opportunities = [opportunity for sequence, opportunity in sequenced_opps] + sequence = opportunity.stage_id.sequence + sequenced_opps.append(((int(sequence != -1 and opportunity.type == 'opportunity'), sequence, -opportunity.id), opportunity)) + + sequenced_opps.sort(reverse=True) + opportunities = map(itemgetter(1), sequenced_opps) ids = [opportunity.id for opportunity in opportunities] highest = opportunities[0] opportunities_rest = opportunities[1:] @@ -648,17 +662,17 @@ class crm_lead(base_stage, format_address, osv.osv): # 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('type') == 'opportunity' and merged_data.get('section_id'): - section_stages = self.pool.get('crm.case.section').read(cr, uid, merged_data['section_id'], ['stage_ids'], context=context) - if merged_data.get('stage_id') not in section_stages['stage_ids']: - stages_sequences = self.pool.get('crm.case.stage').search(cr, uid, [('id','in',section_stages['stage_ids'])], order='sequence', limit=1, context=context) - merged_data['stage_id'] = stages_sequences and stages_sequences[0] or False + 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 # Write merged data into first opportunity self.write(cr, uid, [highest.id], merged_data, context=context) - # Delete tail opportunities - self.unlink(cr, uid, [x.id for x in tail_opportunities], context=context) + # 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 + self.unlink(cr, SUPERUSER_ID, [x.id for x in tail_opportunities], context=context) return highest.id @@ -822,9 +836,11 @@ class crm_lead(base_stage, format_address, osv.osv): 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 @@ -917,6 +933,23 @@ class crm_lead(base_stage, format_address, osv.osv): vals['probability'] = stage.probability return super(crm_lead, self).write(cr, uid, ids, vals, context=context) + 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) + 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) + 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 @@ -959,15 +992,18 @@ class crm_lead(base_stage, format_address, osv.osv): def message_get_reply_to(self, cr, uid, ids, context=None): """ Override to get the reply_to of the parent project. """ return [lead.section_id.message_get_reply_to()[0] if lead.section_id else False - for lead in self.browse(cr, uid, ids, context=context)] + for lead in self.browse(cr, SUPERUSER_ID, ids, context=context)] def message_get_suggested_recipients(self, cr, uid, ids, context=None): recipients = super(crm_lead, self).message_get_suggested_recipients(cr, uid, ids, context=context) - for lead in self.browse(cr, uid, ids, context=context): - if lead.partner_id: - self._message_add_suggested_recipient(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')) + 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 return recipients def message_new(self, cr, uid, msg, custom_values=None, context=None): @@ -975,8 +1011,8 @@ class crm_lead(base_stage, format_address, osv.osv): through message_process. This override updates the document according to the email. """ - if custom_values is None: custom_values = {} - + 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"), @@ -1024,9 +1060,20 @@ class crm_lead(base_stage, format_address, osv.osv): 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' - else: prefix = 'Scheduled' - message = _("%s a call for the %s.") % (prefix, phonecall.date) + if action == 'log': + prefix = 'Logged' + else: + prefix = 'Scheduled' + suffix = ' %s' % phonecall.description + message = _("%s a call for %s.%s") % (prefix, phonecall.date, suffix) + 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): + if not duration: + duration = _('unknown') + else: + duration = str(duration) + message = _("Meeting scheduled at '%s'
Subject: %s
Duration: %s hour(s)") % (meeting_date, meeting_subject, duration) return self.message_post(cr, uid, ids, body=message, context=context) def onchange_state(self, cr, uid, ids, state_id, context=None):