[MERGE] improve_crm_leads_flux by abo

bzr revid: al@openerp.com-20121208144139-o9zr10b7qbij48d5
This commit is contained in:
Antony Lesuisse
2012-12-08 15:41:39 +01:00
27 changed files with 704 additions and 810 deletions
+1 -4
View File
@@ -67,15 +67,11 @@ Dashboard for CRM will include:
'security/crm_security.xml',
'security/ir.model.access.csv',
'wizard/crm_lead_to_partner_view.xml',
'wizard/crm_lead_to_opportunity_view.xml',
'wizard/crm_phonecall_to_phonecall_view.xml',
'wizard/crm_phonecall_to_partner_view.xml',
'wizard/crm_phonecall_to_opportunity_view.xml',
'wizard/crm_opportunity_to_phonecall_view.xml',
'wizard/crm_partner_to_opportunity_view.xml',
'wizard/crm_merge_opportunities_view.xml',
@@ -109,6 +105,7 @@ Dashboard for CRM will include:
'test': [
'test/process/communication_with_customer.yml',
'test/process/lead2opportunity2win.yml',
'test/process/lead2opportunity_assign_salesmen.yml',
'test/process/merge_opportunity.yml',
'test/process/cancel_lead.yml',
'test/process/action_rule.yml',
+13 -41
View File
@@ -1,7 +1,13 @@
<?xml version="1.0"?>
<openerp>
<data noupdate="1">
<!-- email server actions -->
<!-- demo automated actions add messge on new lead after 5 days -->
<record id="filter_draft_lead" model="ir.filters">
<field name="name">Draft Leads</field>
<field name="model_id">crm.lead</field>
<field name="domain">[('state','=','draft')]</field>
<field name="user_id" eval="False"/>
</record>
<record id="action_email_reminder_lead" model="ir.actions.server">
<field name="name">Reminder to User</field>
<field name="model_id" ref="model_crm_lead"/>
@@ -10,46 +16,12 @@
<field name="state">email</field>
<field name="email">object.user_id.email</field>
<field name="subject">Reminder on Lead: [[object.id ]] [[object.partner_id and 'of ' +object.partner_id.name or '']]</field>
<field name="message">Hello [[object.user_id.name]],
can you check following lead, it's not open since 5 days ?
Lead: [[object.id ]]
Description:
[[object.description]]
Thanks,
<field name="message">Warning unprocessed incoming lead is more than 5 day old.
Name: [[object.name ]]
ID: [[object.id ]]
Description: [[object.description]]
</field>
</record>
<record id="action_email_reminder_customer_lead" model="ir.actions.server">
<field name="name">Reminder to Customer</field>
<field name="model_id" ref="model_crm_lead"/>
<field name="condition">True</field>
<field name="type">ir.actions.server</field>
<field name="state">email</field>
<field name="email">object.email_from</field>
<field name="subject">Reminder on Lead: [[object.id ]]</field>
<field name="message">Hello [[object.partner_id and object.partner_id.name or '']],
your following lead is not open since 5 days.
Lead: [[object.id ]]
Description:
[[object.description]]
Thanks,
</field>
</record>
<record id="filter_draft_lead" model="ir.filters">
<field name="name">Draft Leads</field>
<field name="model_id">crm.lead</field>
<field name="domain">[('state','=','draft')]</field>
<field name="user_id" eval="False"/>
</record>
<!-- automated actions -->
<record id="rule_set_reminder_lead" model="base.action.rule">
<field name="name">Set Auto Reminder on leads which are not open since 5 days.</field>
<field name="model_id" ref="model_crm_lead"/>
@@ -58,16 +30,16 @@ Thanks,
<field name="trg_date_type">create</field>
<field name="trg_date_range">5</field>
<field name="trg_date_range_type">day</field>
<field name="server_action_ids" eval="[(6,0,[ref('action_email_reminder_customer_lead'), ref('action_email_reminder_lead')])]"/>
<field name="server_action_ids" eval="[(6,0,[ref('action_email_reminder_lead')])]"/>
</record>
<!-- demo automated actions add a followers on urgent USA leads -->
<record id="filter_usa_lead" model="ir.filters">
<field name="name">Leads from USA</field>
<field name="model_id">crm.lead</field>
<field name="domain">[('country_id','=','United States')]</field>
<field name="user_id" eval="False"/>
</record>
<record id="rule_set_followers_lead" model="base.action.rule">
<field name="name">Set Auto Followers on leads which are urgent and come from USA.</field>
<field name="model_id" ref="model_crm_lead"/>
+204 -84
View File
@@ -30,6 +30,36 @@ from tools import html2plaintext
from base.res.res_partner import format_address
CRM_LEAD_FIELDS_TO_MERGE = ['name',
'partner_id',
'channel_id',
'company_id',
'country_id',
'section_id',
'stage_id',
'state_id',
'type_id',
'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']
CRM_LEAD_PENDING_STATES = (
crm.AVAILABLE_STATES[2][0], # Cancelled
crm.AVAILABLE_STATES[3][0], # Done
@@ -120,11 +150,7 @@ class crm_lead(base_stage, format_address, osv.osv):
def _compute_day(self, cr, uid, ids, fields, args, context=None):
"""
@param cr: the current row, from the database cursor,
@param uid: the current user’s ID for security checks,
@param ids: List of Openday’s IDs
@return: difference between current date and log date
@param context: A standard dictionary for contextual values
:return dict: difference between current date and log date
"""
cal_obj = self.pool.get('resource.calendar')
res_obj = self.pool.get('resource.resource')
@@ -413,15 +439,46 @@ class crm_lead(base_stage, format_address, osv.osv):
"""
return self.set_priority(cr, uid, ids, '3')
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
"""
for opp in opps:
if (opp.type == 'opportunity'):
return 'opportunity'
return 'lead'
def _merge_data(self, cr, uid, ids, oldest, fields, context=None):
# prepare opportunity data into dictionary for merging
"""
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
"""
opportunities = self.browse(cr, uid, ids, context=context)
def _get_first_not_null(attr):
if hasattr(oldest, attr):
return getattr(oldest, attr)
for opportunity in opportunities:
if hasattr(opportunity, attr):
return getattr(opportunity, attr)
for opp in opportunities:
if hasattr(opp, attr):
return getattr(opp, attr)
return False
def _get_first_not_null_id(attr):
@@ -429,8 +486,9 @@ class crm_lead(base_stage, format_address, osv.osv):
return res and res.id or False
def _concat_all(attr):
return ', '.join(filter(lambda x: x, [getattr(opportunity, attr) or '' for opportunity in opportunities if hasattr(opportunity, attr)]))
return ', '.join(filter(lambda x: x, [getattr(opp, attr) or '' for opp in opportunities if hasattr(opp, attr)]))
# Process the fields' values
data = {}
for field_name in fields:
field_info = self._all_columns.get(field_name)
@@ -445,24 +503,35 @@ class crm_lead(base_stage, format_address, osv.osv):
data[field_name] = _concat_all(field_name) #not lost
else:
data[field_name] = _get_first_not_null(field_name) #not lost
# Define the resulting type ('lead' or 'opportunity')
data['type'] = self._merge_get_result_type(cr, uid, opportunities, context)
return data
def _merge_find_oldest(self, cr, uid, ids, context=None):
"""
Return the oldest lead found among ids.
:param list ids: list of ids of the leads to inspect
:return object: browse record of the oldest of the leads
"""
if context is None:
context = {}
#TOCHECK: where pass 'convert' in context ?
if context.get('convert'):
ids = list(set(ids) - set(context.get('lead_ids', False)) )
#search opportunities order by create date
opportunity_ids = self.search(cr, uid, [('id', 'in', ids)], order='create_date' , context=context)
oldest_id = opportunity_ids[0]
return self.browse(cr, uid, oldest_id, context=context)
if context.get('convert'):
ids = list(set(ids) - set(context.get('lead_ids', [])))
# Search opportunities order by create date
opportunity_ids = self.search(cr, uid, [('id', 'in', ids)], order='create_date', context=context)
oldest_opp_id = opportunity_ids[0]
return self.browse(cr, uid, oldest_opp_id, context=context)
def _mail_body(self, cr, uid, lead, fields, title=False, context=None):
body = []
if title:
body.append("%s\n" % (title))
for field_name in fields:
field_info = self._all_columns.get(field_name)
if field_info is None:
@@ -483,20 +552,25 @@ class crm_lead(base_stage, format_address, osv.osv):
value = lead[field_name]
body.append("%s: %s" % (field.string, value or ''))
return "\n".join(body + ['---'])
def _merge_notification(self, cr, uid, opportunity_id, opportunities, context=None):
def _merge_notify(self, cr, uid, opportunity_id, opportunities, context=None):
"""
Create a message gathering merged leads/opps information.
"""
#TOFIX: mail template should be used instead of fix body, subject text
details = []
merge_message = _('Merged opportunities')
result_type = self._merge_get_result_type(cr, uid, opportunities, context)
if result_type == 'lead':
merge_message = _('Merged leads')
else:
merge_message = _('Merged opportunities')
subject = [merge_message]
fields = ['name', 'partner_id', 'stage_id', 'section_id', 'user_id', 'categ_ids', 'channel_id', 'company_id', 'contact_name',
'email_from', 'phone', 'fax', 'mobile', 'state_id', 'description', 'probability', 'planned_revenue',
'country_id', 'city', 'street', 'street2', 'zip']
for opportunity in opportunities:
subject.append(opportunity.name)
title = "%s : %s" % (merge_message, opportunity.name)
details.append(self._mail_body(cr, uid, opportunity, fields, title=title, context=context))
details.append(self._mail_body(cr, uid, opportunity, CRM_LEAD_FIELDS_TO_MERGE, title=title, context=context))
# Chatter message's subject
subject = subject[0] + ": " + ", ".join(subject[1:])
@@ -540,42 +614,41 @@ class crm_lead(base_stage, format_address, osv.osv):
def merge_opportunity(self, cr, uid, ids, context=None):
"""
To merge opportunities
:param ids: list of opportunities ids to merge
Different cases of merge:
- merge leads together = 1 new lead
- merge at least 1 opp with anything else (lead or opp) = 1 new opp
:param list ids: leads/opportunities ids to merge
:return int id: id of the resulting lead/opp
"""
if context is None: context = {}
#TOCHECK: where pass lead_ids in context?
lead_ids = context and context.get('lead_ids', []) or []
if len(ids) <= 1:
raise osv.except_osv(_('Warning!'),_('Please select more than one opportunity from the list view.'))
raise osv.except_osv(_('Warning!'),_('Please select more than one element (lead or opportunity) from the list view.'))
lead_ids = context.get('lead_ids', [])
ctx_opportunities = self.browse(cr, uid, lead_ids, context=context)
opportunities = self.browse(cr, uid, ids, context=context)
opportunities_list = list(set(opportunities) - set(ctx_opportunities))
oldest = self._merge_find_oldest(cr, uid, ids, context=context)
if ctx_opportunities :
if ctx_opportunities:
first_opportunity = ctx_opportunities[0]
tail_opportunities = opportunities_list + ctx_opportunities[1:]
else:
first_opportunity = opportunities_list[0]
tail_opportunities = opportunities_list[1:]
fields = ['partner_id', 'title', 'name', 'categ_ids', 'channel_id', 'city', 'company_id', 'contact_name', 'country_id', 'type_id', 'user_id', 'section_id', 'state_id', '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']
data = self._merge_data(cr, uid, ids, oldest, fields, context=context)
merged_data = self._merge_data(cr, uid, ids, oldest, CRM_LEAD_FIELDS_TO_MERGE, context=context)
# Merge messages and attachements into the first opportunity
self._merge_opportunity_history(cr, uid, first_opportunity.id, tail_opportunities, context=context)
self._merge_opportunity_attachments(cr, uid, first_opportunity.id, tail_opportunities, context=context)
# Merge notifications about loss of information
self._merge_notification(cr, uid, first_opportunity, opportunities, context=context)
self._merge_notify(cr, uid, first_opportunity, opportunities, context=context)
# Write merged data into first opportunity
self.write(cr, uid, [first_opportunity.id], data, context=context)
self.write(cr, uid, [first_opportunity.id], merged_data, context=context)
# Delete tail opportunities
self.unlink(cr, uid, [x.id for x in tail_opportunities], context=context)
@@ -599,17 +672,17 @@ class crm_lead(base_stage, format_address, osv.osv):
stage_id = stage_ids and stage_ids[0] or False
return {
'planned_revenue': lead.planned_revenue,
'probability': lead.probability,
'name': lead.name,
'partner_id': customer and customer.id or False,
'user_id': (lead.user_id and lead.user_id.id),
'type': 'opportunity',
'stage_id': stage_id or False,
'date_action': time.strftime('%Y-%m-%d %H:%M:%S'),
'date_open': time.strftime('%Y-%m-%d %H:%M:%S'),
'email_from': customer and customer.email or lead.email_from,
'phone': customer and customer.phone or lead.phone,
'planned_revenue': lead.planned_revenue,
'probability': lead.probability,
'name': lead.name,
'partner_id': customer and customer.id or False,
'user_id': (lead.user_id and lead.user_id.id),
'type': 'opportunity',
'stage_id': stage_id or False,
'date_action': fields.datetime.now(),
'date_open': fields.datetime.now(),
'email_from': customer and customer.email or lead.email_from,
'phone': customer and customer.phone or lead.phone,
}
def convert_opportunity(self, cr, uid, ids, partner_id, user_ids=False, section_id=False, context=None):
@@ -668,40 +741,67 @@ class crm_lead(base_stage, format_address, osv.osv):
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.id or False})
contact_id = res_partner.address_get(cr, uid, [partner_id])['default']
res = lead.write({'partner_id' : partner_id, }, context=context)
res = lead.write({'partner_id': partner_id}, context=context)
self._lead_set_partner_send_note(cr, uid, [lead.id], context)
return res
def convert_partner(self, cr, uid, ids, action='create', partner_id=False, context=None):
def handle_partner_assignation(self, cr, uid, ids, action='create', partner_id=False, context=None):
"""
Convert partner based on action.
Handle partner assignation during a lead conversion.
if action is 'create', create new partner with contact and assign lead to new partner_id.
otherwise assign lead to specified partner_id
otherwise assign lead to the specified partner_id
: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}
"""
if context is None:
context = {}
#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)
partner_ids[lead.id] = partner_id
return partner_ids
def allocate_salesman(self, cr, uid, ids, user_ids, team_id=False, context=None):
def allocate_salesman(self, cr, uid, ids, user_ids=None, team_id=False, context=None):
"""
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
"""
index = 0
for lead_id in ids:
value = {}
if team_id:
value['section_id'] = team_id
if user_ids:
value['user_id'] = user_ids[index]
# Cycle through user_ids
index = (index + 1) % len(user_ids)
if value:
self.write(cr, uid, [lead_id], value, context=context)
@@ -709,7 +809,7 @@ class crm_lead(base_stage, format_address, osv.osv):
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):
"""
action :('schedule','Schedule a call'), ('log','Log a call')
:param string action: ('schedule','Schedule a call'), ('log','Log a call')
"""
phonecall = self.pool.get('crm.phonecall')
model_data = self.pool.get('ir.model.data')
@@ -724,17 +824,17 @@ class crm_lead(base_stage, format_address, osv.osv):
if not user_id:
user_id = lead.user_id and lead.user_id.id or False
vals = {
'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,
'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,
}
new_id = phonecall.create(cr, uid, vals, context=context)
phonecall.case_open(cr, uid, [new_id], context=context)
@@ -744,30 +844,50 @@ class crm_lead(base_stage, format_address, osv.osv):
self.schedule_phonecall_send_note(cr, uid, [lead.id], new_id, action, context=context)
return phonecall_dict
def redirect_opportunity_view(self, cr, uid, opportunity_id, context=None):
models_data = self.pool.get('ir.model.data')
# Get Opportunity views
form_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_form_view_oppor')
tree_view = models_data.get_object_reference(cr, uid, 'crm', 'crm_case_tree_view_oppor')
# Get opportunity views
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')
return {
'name': _('Opportunity'),
'view_type': 'form',
'view_mode': 'tree, form',
'res_model': 'crm.lead',
'domain': [('type', '=', 'opportunity')],
'res_id': int(opportunity_id),
'view_id': False,
'views': [(form_view and form_view[1] or False, 'form'),
(tree_view and tree_view[1] or False, 'tree'),
(False, 'calendar'), (False, 'graph')],
'type': 'ir.actions.act_window',
'name': _('Opportunity'),
'view_type': 'form',
'view_mode': 'tree, form',
'res_model': 'crm.lead',
'domain': [('type', '=', 'opportunity')],
'res_id': int(opportunity_id),
'view_id': False,
'views': [(form_view or False, 'form'),
(tree_view or False, 'tree'),
(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
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')
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,
'views': [(form_view or False, 'form'),
(tree_view or False, 'tree'),
(False, 'calendar'), (False, 'graph')],
'type': 'ir.actions.act_window',
}
def action_makeMeeting(self, cr, uid, ids, context=None):
""" This opens Meeting's calendar view to schedule meeting on current Opportunity
@return : Dictionary value for created Meeting view
"""
Open meeting's calendar view to schedule meeting on current opportunity.
: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)
+26 -1
View File
@@ -247,7 +247,7 @@ Andrew</field>
<field name="contact_name">Bojing Hú</field>
<field name="partner_name">Incom Corporation</field>
<field name="email_from">bhu.a100@gmail.com</field>
<field name="partner_id" ref=""/>
<field name="partner_id" ref="base.res_partner_1"/>
<field name="country_id" ref="base.cn"/>
<field name="city">Shenzhen</field>
<field name="type_id" ref="type_lead3"/>
@@ -548,6 +548,31 @@ Andrew</field>
<field eval="1" name="active"/>
</record>
<record id="crm_case_26" model="crm.lead">
<field name="type">opportunity</field>
<field name="name">Would appreciate more information about your products</field>
<field eval="11000" name="planned_revenue"/>
<field eval="45.0" name="probability"/>
<field name="partner_name">Agrolait</field>
<field name="email_from">info@agrolait.com</field>
<field name="partner_id" ref="base.res_partner_2"/>
<field name="street">69 rue de Chimay</field>
<field name="country_id" ref="base.be"/>
<field name="city">Wavre</field>
<field name="zip">1300</field>
<field name="type_id" ref="type_lead3"/>
<field name="categ_ids" eval="[(6, 0, [categ_oppor2])]"/>
<field name="channel_id" ref="crm_case_channel_website"/>
<field name="priority">3</field>
<field eval="time.strftime('%Y-%m-23')" name="date_deadline"/>
<field eval="time.strftime('%Y-%m-10')" name="date_action"/>
<field name="title_action">Send Catalogue by Email</field>
<field name="section_id" ref="crm_case_section_4"/>
<field name="user_id" ref="base.user_demo"/>
<field name="stage_id" ref="crm.stage_lead3"/>
<field eval="1" name="active"/>
</record>
<!-- Unsubscribe Admin from case15, subscribe Demo -->
<record id="crm_case_15" model="crm.lead">
<field name="message_follower_ids" eval="[(3, ref('base.partner_root')), (4, ref('base.partner_demo'))]"/>
+40 -40
View File
@@ -12,7 +12,7 @@
<field name="context">{'default_type':'lead', 'stage_type':'lead'}</field>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create an unqualified lead.
Click to create an unqualified lead.
</p><p>
Use leads if you need a qualification step before creating an
opportunity or a customer. It can be a business card you received,
@@ -39,48 +39,48 @@
<field name="act_window_id" ref="crm_case_category_act_leads_all"/>
</record>
<record model="ir.actions.act_window" id="crm_case_category_act_oppor11">
<field name="name">Opportunities</field>
<field name="res_model">crm.lead</field>
<field name="view_mode">kanban,tree,graph,form,calendar</field>
<field name="domain">[('type','=','opportunity')]</field>
<field name="context">{'stage_type': 'opportunity', 'default_type': 'opportunity'}</field>
<field name="view_id" eval="False"/>
<field name="search_view_id" ref="crm.view_crm_case_opportunities_filter"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a new opportunity.
</p><p>
OpenERP helps you keep track of your sales pipeline to follow
up potential sales and better forecast your future revenues.
</p><p>
You will be able to plan meetings and phone calls from
opportunities, convert them into quotations, attach related
documents, track all discussions, and much more.
</p>
</field>
</record>
<record model="ir.actions.act_window" id="crm_case_category_act_oppor11">
<field name="name">Opportunities</field>
<field name="res_model">crm.lead</field>
<field name="view_mode">kanban,tree,graph,form,calendar</field>
<field name="domain">[('type','=','opportunity')]</field>
<field name="context">{'stage_type': 'opportunity', 'default_type': 'opportunity', 'default_user_id': uid}</field>
<field name="view_id" eval="False"/>
<field name="search_view_id" ref="crm.view_crm_case_opportunities_filter"/>
<field name="help" type="html">
<p class="oe_view_nocontent_create">
Click to create a new opportunity.
</p><p>
OpenERP helps you keep track of your sales pipeline to follow
up potential sales and better forecast your future revenues.
</p><p>
You will be able to plan meetings and phone calls from
opportunities, convert them into quotations, attach related
documents, track all discussions, and much more.
</p>
</field>
</record>
<record model="ir.actions.act_window.view" id="action_crm_tag_kanban_view_oppor11">
<field name="sequence" eval="0"/>
<field name="view_mode">kanban</field>
<field name="view_id" ref="crm_case_kanban_view_leads"/>
<field name="act_window_id" ref="crm_case_category_act_oppor11"/>
</record>
<record model="ir.actions.act_window.view" id="action_crm_tag_kanban_view_oppor11">
<field name="sequence" eval="0"/>
<field name="view_mode">kanban</field>
<field name="view_id" ref="crm_case_kanban_view_leads"/>
<field name="act_window_id" ref="crm_case_category_act_oppor11"/>
</record>
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_view_oppor11">
<field name="sequence" eval="1"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="crm_case_tree_view_oppor"/>
<field name="act_window_id" ref="crm_case_category_act_oppor11"/>
</record>
<record model="ir.actions.act_window.view" id="action_crm_tag_tree_view_oppor11">
<field name="sequence" eval="1"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="crm_case_tree_view_oppor"/>
<field name="act_window_id" ref="crm_case_category_act_oppor11"/>
</record>
<record model="ir.actions.act_window.view" id="action_crm_tag_form_view_oppor11">
<field name="sequence" eval="2"/>
<field name="view_mode">form</field>
<field name="view_id" ref="crm_case_form_view_oppor"/>
<field name="act_window_id" ref="crm_case_category_act_oppor11"/>
</record>
<record model="ir.actions.act_window.view" id="action_crm_tag_form_view_oppor11">
<field name="sequence" eval="2"/>
<field name="view_mode">form</field>
<field name="view_id" ref="crm_case_form_view_oppor"/>
<field name="act_window_id" ref="crm_case_category_act_oppor11"/>
</record>
<menuitem name="Sales" id="base.menu_sales" parent="base.menu_base_partner" sequence="1" />
<menuitem name="Leads" id="menu_crm_leads" parent="base.menu_sales" action="crm_case_category_act_leads_all" sequence="2" />
+5 -3
View File
@@ -116,8 +116,11 @@
<group>
<group>
<field name="partner_name" string="Company Name"/>
<!-- Preload all the partner's information -->
<field name="partner_id" string="Customer"
on_change="on_change_partner(partner_id)" options='{"create_name_field": "name"}'/>
on_change="on_change_partner(partner_id)"
options='{"create_name_field": "name"}'
context="{'default_name': contact_name, 'default_street': street, 'default_city': city, 'default_state_id': state_id, 'default_zip': zip, 'default_country_id': country_id, 'default_function': function, 'default_phone': phone, 'default_mobile': mobile, 'default_fax': fax, 'default_email': email_from, 'default_user_id': user_id, 'default_section_id': section_id}"/>
<label for="street" string="Address"/>
<div>
<field name="street" placeholder="Street..."/>
@@ -133,7 +136,7 @@
<group>
<label for="contact_name"/>
<div>
<field name="contact_name" class="oe_inline"/>,
<field name="contact_name" class="oe_inline"/><span attrs="{'invisible': [('title', '=', '')]}">, </span>
<field name="title" placeholder="Title" domain="[('domain', '=', 'contact')]" class="oe_inline" options='{"no_open": True}'/>
</div>
<field name="email_from" widget="email"/>
@@ -206,7 +209,6 @@
</field>
</record>
<!-- CRM Lead Tree View -->
<record model="ir.ui.view" id="crm_case_tree_view_leads">
<field name="name">Leads</field>
+28 -10
View File
@@ -182,17 +182,23 @@ class crm_phonecall(base_state, osv.osv):
'phone': phonecall.partner_phone,
})
def convert_partner(self, cr, uid, ids, action='create', partner_id=False, context=None):
def handle_partner_assignation(self, cr, uid, ids, action='create', partner_id=False, context=None):
"""
This function convert partner based on action.
Handle partner assignation during a lead conversion.
if action is 'create', create new partner with contact and assign lead to new partner_id.
otherwise assign lead to specified partner_id
:param list ids: phonecalls 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}
"""
if context is None:
context = {}
#TODO this is a duplication of the handle_partner_assignation method of crm_lead
partner_ids = {}
# If a partner_id is given, force this partner for all elements
force_partner_id = partner_id
for call 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._call_create_partner(cr, uid, call, context=context)
self._call_create_partner_address(cr, uid, call, partner_id, context=context)
@@ -219,7 +225,6 @@ class crm_phonecall(base_state, osv.osv):
}
return value
def convert_opportunity(self, cr, uid, ids, opportunity_summary=False, partner_id=False, planned_revenue=0.0, probability=0.0, context=None):
partner = self.pool.get('res.partner')
opportunity = self.pool.get('crm.lead')
@@ -256,8 +261,9 @@ class crm_phonecall(base_state, osv.osv):
return opportunity_dict
def action_make_meeting(self, cr, uid, ids, context=None):
""" This opens Meeting's calendar view to schedule meeting on current Phonecall
@return : Dictionary value for created Meeting view
"""
Open meeting's calendar view to schedule a meeting on current phonecall.
:return dict: dictionary value for created meeting view
"""
phonecall = 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)
@@ -270,14 +276,27 @@ class crm_phonecall(base_state, osv.osv):
'default_name': phonecall.name,
}
return res
def action_button_convert2opportunity(self, cr, uid, ids, context=None):
"""
Convert a phonecall into an opp and then redirect to the opp view.
:param list ids: list of calls ids to convert (typically contains a single id)
:return dict: containing view information
"""
if len(ids) != 1:
raise osv.except_osv(_('Warning!'),_('It\'s only possible to convert one phonecall at a time.'))
opportunity_dict = self.convert_opportunity(cr, uid, ids, context=context)
return self.pool.get('crm.lead').redirect_opportunity_view(cr, uid, opportunity_dict[ids[0]], context)
# ----------------------------------------
# OpenChatter
# ----------------------------------------
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
return 'Phonecall'
def case_reset_send_note(self, cr, uid, ids, context=None):
message = _('Phonecall has been <b>reset and set as open</b>.')
return self.message_post(cr, uid, ids, body=message, context=context)
@@ -300,5 +319,4 @@ class crm_phonecall(base_state, osv.osv):
def _call_set_partner_send_note(self, cr, uid, ids, context=None):
return self.message_post(cr, uid, ids, body=_("Partner has been <b>created</b>."), context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+24 -27
View File
@@ -3,7 +3,7 @@
<data>
<!-- Phonecall Categories Form View -->
<!-- Phonecall Categories Form View -->
<record id="crm_phonecall_categ_action" model="ir.actions.act_window">
<field name="name">Phonecall Categories</field>
<field name="res_model">crm.case.categ</field>
@@ -24,8 +24,7 @@
<menuitem action="crm_phonecall_categ_action" name="Categories"
id="menu_crm_case_phonecall-act" parent="menu_crm_config_phonecall" groups="base.group_no_one"/>
<!-- PhoneCalls Tree View -->
<!-- Phonecalls Tree View -->
<record model="ir.ui.view" id="crm_case_phone_tree_view">
<field name="name">CRM - Phone Calls Tree</field>
<field name="model">crm.phonecall</field>
@@ -35,13 +34,14 @@
<field name="name"/>
<field name="partner_id"/>
<button string="Meeting"
states="open,pending" icon="gtk-redo"
name="action_make_meeting" type="object"/>
<button string="Convert to Opportunity"
name="%(phonecall2opportunity_act)d"
states="open,pending"
icon="gtk-index"
type="action" attrs="{'invisible':[('opportunity_id','!=',False)]}"/>
name="action_make_meeting"
type="object"/>
<button string="Convert to Opportunity"
type="object"
name="action_button_convert2opportunity"
states="open,pending"
attrs="{'invisible':[('opportunity_id','!=',False)]}"/>
<field name="partner_phone"/>
<field name="user_id"/>
<field name="categ_id" invisible="1"/>
@@ -60,27 +60,26 @@
</field>
</record>
<!-- Phonecalls Form View -->
<!-- Phonecalls Form View -->
<record model="ir.ui.view" id="crm_case_phone_form_view">
<field name="name">CRM - Phone Call Form</field>
<field name="model">crm.phonecall</field>
<field name="arch" type="xml">
<form string="Phone Call" version="7.0">
<header>
<button name="case_close" string="Call Done" type="object" class="oe_highlight"
states="open,pending"/>
<button name="case_reset" string="Reset to Todo" type="object"
states="cancel"/>
<button name="case_cancel" string="Cancel" type="object"
states="draft,open,pending"/>
<field name="state" widget="statusbar" nolabel="1" statusbar_visible="open,done"/>
</header>
<header>
<button name="case_close" string="Call Done" type="object" class="oe_highlight"
states="open,pending"/>
<button name="case_reset" string="Reset to Todo" type="object"
states="cancel"/>
<button name="case_cancel" string="Cancel" type="object"
states="draft,open,pending"/>
<field name="state" widget="statusbar" nolabel="1" statusbar_visible="open,done"/>
</header>
<sheet string="Phone Call">
<div class="oe_right">
<div class="oe_right">
<button string="Convert to Opportunity"
name="%(phonecall2opportunity_act)d"
type="action"
type="object"
name="action_button_convert2opportunity"
attrs="{'invisible':[ '|', ('opportunity_id','!=',False), ('state', 'not in', ('open', 'done'))]}"/>
<button string="Schedule Other Call"
name="%(phonecall_to_phonecall_act)d"
@@ -97,7 +96,7 @@
</div>
<h2><field name="partner_phone"/></h2>
</div>
<group col="4">
<field name="date"/>
<field name="user_id"/>
@@ -122,7 +121,6 @@
</record>
<!--Logged Phonecalls Tree View -->
<record model="ir.ui.view" id="crm_case_inbound_phone_tree_view">
<field name="name">CRM - Logged Phone Calls Tree</field>
<field name="model">crm.phonecall</field>
@@ -149,7 +147,7 @@
icon="gtk-redo"
name="action_make_meeting" type="object"/>
<button string="Convert to Opportunity"
name="%(phonecall2opportunity_act)d"
name="action_button_convert2opportunity"
states="open,pending"
icon="gtk-index"
type="action" attrs="{'invisible':[('opportunity_id','!=',False)]}"/>
@@ -158,7 +156,6 @@
</record>
<!-- Phonecalls Calendar View -->
<record model="ir.ui.view" id="crm_case_phone_calendar_view">
<field name="name">CRM - Phone Calls Calendar</field>
<field name="model">crm.phonecall</field>
+1 -1
View File
@@ -293,7 +293,7 @@
<field name="view_id" ref="crm_case_resource_type_tree"/>
</record>
<menuitem action="crm_case_resource_type_act"
<menuitem action="crm_case_resource_type_act"
id="menu_crm_case_resource_type_act" sequence="4"
groups="base.group_no_one"
parent="base.menu_crm_config_lead"/>
@@ -33,20 +33,4 @@
-
!python {model: crm.lead}: |
lead_ids = self.search(cr, uid, [('email_from','=', 'Mr. John Right <info@customer.com>')])
self.convert_partner(cr, uid, lead_ids, context=context)
-
I convert one phonecall request to a customer and put him into regular customer list.
-
!python {model: crm.phonecall2partner}: |
context.update({'active_model': 'crm.phonecall', 'active_ids': [ref("crm.crm_phonecall_4")], 'active_id': ref("crm.crm_phonecall_4")})
new_id = self.create(cr, uid, {}, context=context)
self.make_partner(cr, uid, [new_id], context=context)
-
I check converted phonecall to partner.
-
!python {model: res.partner}: |
partner_id = self.search(cr, uid, [('phonecall_ids', '=', ref('crm.crm_phonecall_4'))])
assert partner_id, "Customer is not found in regular customer list."
data = self.browse(cr, uid, partner_id, context=context)[0]
assert data.user_id.id == ref("base.user_root"), "User not assigned properly"
assert data.name == "Wanted information about pricing of laptops", "Bad partner name"
self.handle_partner_assignation(cr, uid, lead_ids, context=context)
@@ -1,25 +1,15 @@
-
In order to test convert customer lead into opportunity,
In order to test the conversion of a lead into a opportunity,
-
I open customer lead.
I open a lead.
-
!python {model: crm.lead}: |
self.case_open(cr, uid, [ref("crm_case_4")])
-
I check lead state is "Open".
I check if the lead state is "Open".
-
!assert {model: crm.lead, id: crm.crm_case_4, string: Lead in open state}:
!assert {model: crm.lead, id: crm.crm_case_4, string: Lead state is Open}:
- state == "open"
-
I fill in a lead2partner wizard.
-
!record {model: crm.lead2partner, id: crm_lead2partner_id1, context: '{"active_model": "crm.lead", "active_ids": [ref("crm_case_4")]}'}:
-
I create a partner from the lead2partner wizard.
-
!python {model: crm.lead2partner}: |
context.update({'active_model': 'crm.lead', 'active_ids': [ref('crm_case_4')], 'active_id': ref('crm_case_4')})
self.make_partner(cr, uid ,[ref("crm_lead2partner_id1")], context=context)
-
I convert lead into opportunity for exiting customer.
-
@@ -76,7 +66,7 @@
-
!python {model: crm.lead2opportunity.partner.mass}: |
context.update({'active_model': 'crm.lead', 'active_ids': [ref("crm_case_11"), ref("crm_case_2")], 'active_id': ref("crm_case_11")})
id = self.create(cr, uid, {'user_ids': [ref('base.user_root')], 'section_id': ref('crm.section_sales_department')}, context=context)
id = self.create(cr, uid, {'user_ids': [(6, 0, [ref('base.user_root')])], 'section_id': ref('crm.section_sales_department')}, context=context)
self.mass_convert(cr, uid, [id], context=context)
-
Now I check first lead converted on opportunity.
@@ -0,0 +1,70 @@
-
During a lead to opp conversion, salesmen should be assigned to leads following the round-robin method. Start by creating 6 leads (1 to 6) and 4 salesmen (A to D).
-
!record {model: crm.lead, id: test_crm_lead_01}:
type: 'lead'
name: 'Test lead 1'
stage_id: stage_lead1
-
!record {model: crm.lead, id: test_crm_lead_02}:
type: 'lead'
name: 'Test lead 2'
stage_id: stage_lead1
-
!record {model: crm.lead, id: test_crm_lead_03}:
type: 'lead'
name: 'Test lead 3'
stage_id: stage_lead1
-
!record {model: crm.lead, id: test_crm_lead_04}:
type: 'lead'
name: 'Test lead 4'
stage_id: stage_lead1
-
!record {model: crm.lead, id: test_crm_lead_05}:
type: 'lead'
name: 'Test lead 5'
stage_id: stage_lead1
-
!record {model: crm.lead, id: test_crm_lead_06}:
type: 'lead'
name: 'Test lead 6'
stage_id: stage_lead1
-
!record {model: res.users, id: test_res_user_01}:
name: 'Test user A'
login: 'tua'
new_password: 'tua'
-
!record {model: res.users, id: test_res_user_02}:
name: 'Test user B'
login: 'tub'
new_password: 'tub'
-
!record {model: res.users, id: test_res_user_03}:
name: 'Test user C'
login: 'tuc'
new_password: 'tuc'
-
!record {model: res.users, id: test_res_user_04}:
name: 'Test user D'
login: 'tud'
new_password: 'tud'
-
I create a mass convert wizard and convert all the leads.
-
!python {model: crm.lead2opportunity.partner.mass}: |
context.update({'active_model': 'crm.lead', 'active_ids': [ref("test_crm_lead_01"), ref("test_crm_lead_02"), ref("test_crm_lead_03"), ref("test_crm_lead_04"), ref("test_crm_lead_05"), ref("test_crm_lead_06")], 'active_id': ref("test_crm_lead_01")})
id = self.create(cr, uid, {'user_ids': [(6, 0, [ref('test_res_user_01'), ref('test_res_user_02'), ref('test_res_user_03'), ref('test_res_user_04')])], 'section_id': ref('crm.section_sales_department')}, context=context)
self.mass_convert(cr, uid, [id], context=context)
-
The leads should now be opps with a salesman and a salesteam. Also, salesmen should have been assigned following a round-robin method.
-
!python {model: crm.lead}: |
opps = self.browse(cr, uid, [ref("test_crm_lead_01"), ref("test_crm_lead_02"), ref("test_crm_lead_03"), ref("test_crm_lead_04"), ref("test_crm_lead_05"), ref("test_crm_lead_06")], context)
salesmen_ids = [ref('test_res_user_01'), ref('test_res_user_02'), ref('test_res_user_03'), ref('test_res_user_04')]
i = 0
for opp in opps:
assert opp.type == 'opportunity', 'Type mismatch: this should be an opp, not a lead'
assert opp.user_id.id == salesmen_ids[i], 'Salesman mismatch: expected salesman %r, got %r' % (salesmen_ids[i], opp.user_id.id)
i = i+1 if (i < len(salesmen_ids) - 1) else 0
+114 -22
View File
@@ -1,34 +1,126 @@
-
I make an opportunity from customer.
During a mixed merge (involving leads and opps), data should be handled a certain way following their type (m2o, m2m, text, ...) Start by creating two leads and an opp.
-
!python {model: crm.partner2opportunity}: |
context.update({'active_model': 'res.partner', 'active_ids': [ref("base.res_partner_9")]})
res_id = self.create(cr, uid, {'name': "Enquête pour l'achat de services"}, context=context)
self.make_opportunity(cr, uid, [res_id], context=context)
!record {model: crm.lead, id: test_crm_lead_01}:
type: 'lead'
name: 'Test lead 1'
partner_id: base.res_partner_1
stage_id: stage_lead1
description: This is the description of the test lead 1.
-
I make another opportunity from a phone call with the same customer.
!record {model: crm.lead, id: test_crm_lead_02}:
type: 'lead'
name: 'Test lead 2'
partner_id: base.res_partner_3
stage_id: stage_lead1
description: This is the description of the test lead 2.
-
!python {model: crm.phonecall2opportunity}: |
context.update({'active_model': 'crm.phonecall', 'active_ids': [ref("crm.crm_phonecall_6")]})
res_id = self.create(cr, uid, {'name': "À propos du prix de votre autre service", 'partner_id': ref("base.res_partner_9")}, context=context)
self.make_opportunity(cr, uid, [res_id], context=context)
-
Now I merge these opportunities.
!record {model: crm.lead, id: test_crm_opp_01}:
type: 'opportunity'
name: 'Test opportunity 1'
partner_id: base.res_partner_5
stage_id: stage_lead1
description: This is the description of the test opp 1.
-
!python {model: crm.lead}: |
opportunity_ids = self.search(cr, uid, [('partner_id','=', ref("base.res_partner_9"))])
context.update({'active_model': 'crm.lead', 'active_ids': opportunity_ids, 'active_id': opportunity_ids[0]})
lead_ids = [ref('test_crm_lead_01'), ref('test_crm_lead_02'), ref('test_crm_opp_01')]
context.update({'active_model': 'crm.lead', 'active_ids': lead_ids, 'active_id': lead_ids[0]})
-
!record {model: crm.merge.opportunity, id: opportunity_merge_id }:
I create a merge wizard and merge the leads and opp together in the first item of the list.
-
!record {model: crm.merge.opportunity, id: merge_opp_wizard_01}:
-
!python {model: crm.merge.opportunity}: |
self.action_merge(cr, uid, [ref("opportunity_merge_id")], context=context)
self.action_merge(cr, uid, [ref("merge_opp_wizard_01")], context=context)
-
I check for merged opportunities for customer.
I check for the resulting merged opp (based on name and partner).
-
!python {model: crm.lead}: |
merge_id = self.search(cr, uid, [('partner_id','=', ref("base.res_partner_9"))])
assert merge_id, 'Fail to create Merge opportunity'
merge_data = self.browse(cr, uid, merge_id)[0]
assert merge_data.type == 'opportunity', 'Merged opportunity type not change!'
assert merge_data.partner_id.id == ref("base.res_partner_9"), 'Partner missmatch!'
merge_id = self.search(cr, uid, [('name', '=', 'Test lead 1'), ('partner_id','=', ref("base.res_partner_1"))])
assert merge_id, 'Fail to create merge opportunity wizard'
merge_result = self.browse(cr, uid, merge_id)[0]
assert merge_result.partner_id.id == ref("base.res_partner_1"), 'Partner mismatch: when merging leads/opps with different m2o values, the first not null value prevails (the other are dropped)'
assert merge_result.description == 'This is the description of the test lead 1., This is the description of the test lead 2., This is the description of the test opp 1.', 'Description mismatch: when merging leads/opps with different text values, these values should get concatenated and separeted with ", "'
assert merge_result.type == 'opportunity', 'Type mismatch: when at least one opp in involved in the merge, the result should be a new opp (instead of %s)' % merge_result.type
-
The other (tailing) leads/opps shouldn't exist anymore.
-
!python {model: crm.lead}: |
tailing_lead = self.search(cr, uid, [('id', '=', ref('test_crm_lead_02'))])
assert not tailing_lead, 'This tailing lead (id %s) should not exist anymore' % ref('test_crm_lead_02')
tailing_opp = self.search(cr, uid, [('id', '=', ref('test_crm_opp_01'))])
assert not tailing_opp, 'This tailing opp (id %s) should not exist anymore' % ref('test_crm_opp_01')
-
I want to test leads merge. Start by creating two leads (with the same partner).
-
!record {model: crm.lead, id: test_crm_lead_03}:
type: 'lead'
name: 'Test lead 3'
partner_id: base.res_partner_1
stage_id: stage_lead1
-
!record {model: crm.lead, id: test_crm_lead_04}:
type: 'lead'
name: 'Test lead 4'
partner_id: base.res_partner_1
stage_id: stage_lead1
-
!python {model: crm.lead}: |
lead_ids = [ref('test_crm_lead_03'), ref('test_crm_lead_04')]
context.update({'active_model': 'crm.lead', 'active_ids': lead_ids, 'active_id': lead_ids[0]})
-
I create a merge wizard and merge the leads together.
-
!record {model: crm.merge.opportunity, id: merge_opp_wizard_02}:
-
!python {model: crm.merge.opportunity}: |
self.action_merge(cr, uid, [ref("merge_opp_wizard_02")], context=context)
-
I check for the resulting merged lead (based on name and partner).
-
!python {model: crm.lead}: |
merge_id = self.search(cr, uid, [('name', '=', 'Test lead 3'), ('partner_id','=', ref("base.res_partner_1"))])
assert merge_id, 'Fail to create merge opportunity wizard'
merge_result = self.browse(cr, uid, merge_id)[0]
assert merge_result.partner_id.id == ref("base.res_partner_1"), 'Partner mismatch'
assert merge_result.type == 'lead', 'Type mismatch: when leads get merged together, the result should be a new lead (instead of %s)' % merge_result.type
tailing_lead = self.search(cr, uid, [('id', '=', ref('test_crm_lead_04'))])
assert not tailing_lead, 'This tailing lead (id %s) should not exist anymore' % ref('test_crm_lead_04')
-
I want to test opps merge. Start by creating two opportunities (with the same partner).
-
!record {model: crm.lead, id: test_crm_opp_02}:
type: 'opportunity'
name: 'Test opportunity 2'
partner_id: base.res_partner_5
stage_id: stage_lead1
-
!record {model: crm.lead, id: test_crm_opp_03}:
type: 'opportunity'
name: 'Test opportunity 3'
partner_id: base.res_partner_5
stage_id: stage_lead1
-
!python {model: crm.lead}: |
opp_ids = [ref('test_crm_opp_02'), ref('test_crm_opp_03')]
context.update({'active_model': 'crm.lead', 'active_ids': opp_ids, 'active_id': opp_ids[0]})
-
I create a merge wizard and merge the opps together.
-
!record {model: crm.merge.opportunity, id: merge_opp_wizard_03}:
-
!python {model: crm.merge.opportunity}: |
self.action_merge(cr, uid, [ref("merge_opp_wizard_03")], context=context)
-
I check for the resulting merged opp (based on name and partner).
-
!python {model: crm.lead}: |
merge_id = self.search(cr, uid, [('name', '=', 'Test opportunity 2'), ('partner_id','=', ref("base.res_partner_5"))])
assert merge_id, 'Fail to create merge opportunity wizard'
merge_result = self.browse(cr, uid, merge_id)[0]
assert merge_result.partner_id.id == ref("base.res_partner_5"), 'Partner mismatch'
assert merge_result.type == 'opportunity', 'Type mismatch: when opps get merged together, the result should be a new opp (instead of %s)' % merge_result.type
tailing_opp = self.search(cr, uid, [('id', '=', ref('test_crm_opp_03'))])
assert not tailing_opp, 'This tailing opp (id %s) should not exist anymore' % ref('test_crm_opp_03')
+4 -4
View File
@@ -1,5 +1,5 @@
-
I create lead record to call of partner onchange, stage onchange and Mailing opt-in onchange method.
I create a lead record to call a partner onchange, stage onchange and mailing opt-in onchange method.
-
!record {model: crm.lead, id: crm_case_25}:
name: 'Need more info about your pc2'
@@ -8,7 +8,7 @@
stage_id: crm.stage_lead1
state: draft
-
I create lead record to call Mailing opt-out onchange method.
I create a lead record to call a mailing opt-out onchange method.
-
!record {model: crm.lead, id: crm_case_18}:
name: 'Need 20 Days of Consultancy'
@@ -16,13 +16,13 @@
state: draft
opt_out: True
-
I create phonecall record to call partner onchange method.
I create a phonecall record to call a partner onchange method.
-
!record {model: crm.phonecall, id: crm_phonecall_5}:
name: 'Bad time'
partner_id: base.res_partner_5
-
I setting next stage "New" for the lead.
I set the next stage to "New" for the lead.
-
!python {model: crm.lead}: |
self.stage_next(cr, uid, [ref("crm_case_4")], context={'stage_type': 'lead'})
+2 -8
View File
@@ -19,16 +19,10 @@
#
##############################################################################
import crm_lead_to_partner
import crm_lead_to_opportunity
import crm_partner_binding
import crm_phonecall_to_phonecall
import crm_opportunity_to_phonecall
import crm_phonecall_to_partner
import crm_partner_to_opportunity
import crm_phonecall_to_opportunity
import crm_lead_to_opportunity
import crm_merge_opportunities
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+7 -10
View File
@@ -27,16 +27,13 @@ import re
class crm_lead2opportunity_partner(osv.osv_memory):
_name = 'crm.lead2opportunity.partner'
_description = 'Lead To Opportunity Partner'
_inherit = 'crm.lead2partner'
_inherit = 'crm.partner.binding'
_columns = {
'action': fields.selection([('exist', 'Link to an existing customer'), \
('create', 'Create a new customer'), \
('nothing', 'Do not link to a customer')], \
'Related Customer', required=True),
'name': fields.selection([('convert', 'Convert to opportunity'), \
('merge', 'Merge with existing opportunities')], \
'Conversion Action', required=True),
'name': fields.selection([
('convert', 'Convert to opportunity'),
('merge', 'Merge with existing opportunities')
], 'Conversion Action', required=True),
'opportunity_ids': fields.many2many('crm.lead', string='Opportunities', domain=[('type', '=', 'opportunity')]),
}
@@ -157,8 +154,8 @@ class crm_lead2opportunity_mass_convert(osv.osv_memory):
_inherit = 'crm.lead2opportunity.partner'
_columns = {
'user_ids': fields.many2many('res.users', string='Salesmen'),
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
'user_ids': fields.many2many('res.users', string='Salesmen'),
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
}
def default_get(self, cr, uid, fields, context=None):
@@ -7,7 +7,7 @@
<field name="model">crm.lead2opportunity.partner</field>
<field name="arch" type="xml">
<form string="Convert to Opportunity" version="7.0">
<group>
<group name="name">
<field name="name" class="oe_inline"/>
<field name="opportunity_ids" attrs="{'invisible': [('name', '!=', 'merge')]}">
<tree>
@@ -18,7 +18,7 @@
</tree>
</field>
</group>
<group attrs="{'invisible': [('name', '!=', 'convert')]}">
<group name="action" attrs="{'invisible': [('name', '!=', 'convert')]}">
<field name="action" class="oe_inline"/>
<field name="partner_id"
attrs="{'required': [('action', '=', 'exist')], 'invisible':[('action','!=','exist')]}"
@@ -51,21 +51,17 @@
<group string="Select Opportunities" attrs="{'invisible': [('name', '!=', 'merge')]}">
<field name="opportunity_ids" colspan="4" nolabel="1" attrs="{'invisible': [('name', '=', 'convert')]}">
<tree>
<field name="name" />
<field name="partner_id" />
<field name="user_id" />
<field name="section_id" />
<field name="name"/>
<field name="partner_id"/>
<field name="user_id"/>
<field name="section_id"/>
</tree>
</field>
</group>
<group string="Assign opportunities to" attrs="{'invisible': [('name', '=', '')]}">
<field name="section_id" />
<field name="user_ids" colspan="4">
<tree>
<field name="name" />
</tree>
</field>
<field name="section_id"/>
<field name="user_ids" widget="many2many_tags"/>
</group>
<footer>
-122
View File
@@ -1,122 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# 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/>.
#
##############################################################################
from osv import osv, fields
from tools.translate import _
class crm_lead2partner(osv.osv_memory):
""" Converts lead to partner """
_name = 'crm.lead2partner'
_description = 'Lead to Partner'
_columns = {
'action': fields.selection([('exist', 'Link to an existing customer'), \
('create', 'Create a new customer')], \
'Action', required=True),
'partner_id': fields.many2one('res.partner', 'Customer'),
}
def view_init(self, cr, uid, fields, context=None):
"""
Check for precondition before wizard executes.
"""
if context is None:
context = {}
model = context.get('active_model')
model = self.pool.get(model)
rec_ids = context and context.get('active_ids', [])
for this in model.browse(cr, uid, rec_ids, context=context):
if this.partner_id:
raise osv.except_osv(_('Warning!'),
_('A partner is already defined.'))
def _select_partner(self, cr, uid, context=None):
if context is None:
context = {}
if not context.get('active_model') == 'crm.lead' or not context.get('active_id'):
return False
partner = self.pool.get('res.partner')
lead = self.pool.get('crm.lead')
this = lead.browse(cr, uid, context.get('active_id'), context=context)
if this.partner_id:
return this.partner_id.id
partner_id = False
if this.email_from:
partner_ids = partner.search(cr, uid, [('email', '=', this.email_from)], context=context)
if partner_ids:
partner_id = partner_ids[0]
if not this.partner_id and this.partner_name:
partner_ids = partner.search(cr, uid, [('name', '=', this.partner_name)], context=context)
if partner_ids:
partner_id = partner_ids[0]
return partner_id
def default_get(self, cr, uid, fields, context=None):
res = super(crm_lead2partner, self).default_get(cr, uid, fields, context=context)
partner_id = self._select_partner(cr, uid, context=context)
if 'partner_id' in fields:
res.update({'partner_id': partner_id})
if 'action' in fields:
res.update({'action': partner_id and 'exist' or 'create'})
return res
def open_create_partner(self, cr, uid, ids, context=None):
"""
Open form of create partner.
"""
view_obj = self.pool.get('ir.ui.view')
view_id = view_obj.search(cr, uid, [('model', '=', self._name), \
('name', '=', self._name+'.view')])
return {
'view_mode': 'form',
'view_type': 'form',
'view_id': view_id or False,
'res_model': self._name,
'context': context,
'type': 'ir.actions.act_window',
'target': 'new',
}
def _create_partner(self, cr, uid, ids, context=None):
"""
Create partner based on action.
"""
if context is None:
context = {}
lead = self.pool.get('crm.lead')
lead_ids = context.get('active_ids', [])
data = self.browse(cr, uid, ids, context=context)[0]
partner_id = data.partner_id and data.partner_id.id or False
return lead.convert_partner(cr, uid, lead_ids, data.action, partner_id, context=context)
def make_partner(self, cr, uid, ids, context=None):
"""
Make a partner based on action.
Only called from form view, so only meant to convert one lead at a time.
"""
lead_id = context and context.get('active_id') or False
partner_ids_map = self._create_partner(cr, uid, ids, context=context)
return self.pool.get('res.partner').redirect_partner_form(cr, uid, partner_ids_map.get(lead_id, False), context=context)
crm_lead2partner()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -1,37 +0,0 @@
<openerp>
<data>
<!-- Lead to Partner form view -->
<record id="view_crm_lead2partner" model="ir.ui.view">
<field name="name">crm.lead2partner.view</field>
<field name="model">crm.lead2partner</field>
<field name="arch" type="xml">
<form string="Create a Partner" version="7.0">
<group col="4">
<separator string="Create a Partner"/>
<field name="action"/>
<field name="partner_id" attrs="{'required': [('action', '=', 'exist')], 'invisible':[('action','!=','exist')]}"/>
</group>
<footer>
<button name="make_partner" string="Continue" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<!-- Lead to Partner wizard -->
<record id="action_crm_lead2partner" model="ir.actions.act_window">
<field name="name">Create a Partner</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">crm.lead2partner</field>
<field name="view_type">form</field>
<field name="view_id" ref="view_crm_lead2partner"/>
<field name="target">new</field>
</record>
</data>
</openerp>
+36 -20
View File
@@ -21,33 +21,51 @@ from osv import osv, fields
from tools.translate import _
class crm_merge_opportunity(osv.osv_memory):
"""Merge two Opportunities"""
"""
Merge opportunities together.
If we're talking about opportunities, it's just because it makes more sense
to merge opps than leads, because the leads are more ephemeral objects.
But since opportunities are leads, it's also possible to merge leads
together (resulting in a new lead), or leads and opps together (resulting
in a new opp).
"""
_name = 'crm.merge.opportunity'
_description = 'Merge two Opportunities'
def action_merge(self, cr, uid, ids, context=None):
if context is None:
context = {}
lead = self.pool.get('crm.lead')
record = self.browse(cr, uid, ids[0], context=context)
opportunities = record.opportunity_ids
#TOFIX: why need to check lead_ids here
lead_ids = [opportunities[0].id]
self.write(cr, uid, ids, {'opportunity_ids' : [(6,0, lead_ids)]}, context=context)
context['lead_ids'] = lead_ids
merge_id = lead.merge_opportunity(cr, uid, [x.id for x in opportunities], context=context)
return lead.redirect_opportunity_view(cr, uid, merge_id, context=context)
_description = 'Merge opportunities'
_columns = {
'opportunity_ids': fields.many2many('crm.lead', rel='merge_opportunity_rel', id1='merge_id', id2='opportunity_id', string='Leads/Opportunities'),
}
def action_merge(self, cr, uid, ids, context=None):
if context is None:
context = {}
lead_obj = self.pool.get('crm.lead')
wizard = self.browse(cr, uid, ids[0], context=context)
opportunity2merge_ids = wizard.opportunity_ids
#TODO: why is this passed through the context ?
context['lead_ids'] = [opportunity2merge_ids[0].id]
merge_id = lead_obj.merge_opportunity(cr, uid, [x.id for x in opportunity2merge_ids], context=context)
# The newly created lead might be a lead or an opp: redirect toward the right view
merge_result = lead_obj.browse(cr, uid, merge_id, context=context)
if merge_result.type == 'opportunity':
return lead_obj.redirect_opportunity_view(cr, uid, merge_id, context=context)
else:
return lead_obj.redirect_lead_view(cr, uid, merge_id, context=context)
def default_get(self, cr, uid, fields, context=None):
"""
This function gets default values
Use active_ids from the context to fetch the leads/opps to merge.
In order to get merged, these leads/opps can't be in 'Done' or
'Cancel' state.
"""
record_ids = context and context.get('active_ids', False) or False
if context is None:
context = {}
record_ids = context.get('active_ids', False)
res = super(crm_merge_opportunity, self).default_get(cr, uid, fields, context=context)
if record_ids:
@@ -61,6 +79,4 @@ class crm_merge_opportunity(osv.osv_memory):
return res
crm_merge_opportunity()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
+115
View File
@@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# 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/>.
#
##############################################################################
from osv import osv, fields
from tools.translate import _
class crm_partner_binding(osv.osv_memory):
"""
Handle the partner binding or generation in any CRM wizard that requires
such feature, like the lead2opportunity wizard, or the
phonecall2opportunity wizard. Try to find a matching partner from the
CRM model's information (name, email, phone number, etc) or create a new
one on the fly.
Use it like a mixin with the wizard of your choice.
"""
_name = 'crm.partner.binding'
_description = 'Handle partner binding or generation in CRM wizards.'
_columns = {
'action': fields.selection([
('exist', 'Link to an existing customer'),
('create', 'Create a new customer'),
('nothing', 'Do not link to a customer')
], 'Related Customer', required=True),
'partner_id': fields.many2one('res.partner', 'Customer'),
}
def _find_matching_partner(self, cr, uid, context=None):
"""
Try to find a matching partner regarding the active model data, like
the customer's name, email, phone number, etc.
:return int partner_id if any, False otherwise
"""
if context is None:
context = {}
partner_id = False
partner_obj = self.pool.get('res.partner')
# The active model has to be a lead or a phonecall
if (context.get('active_model') == 'crm.lead') and context.get('active_id'):
active_model = self.pool.get('crm.lead').browse(cr, uid, context.get('active_id'), context=context)
elif (context.get('active_model') == 'crm.phonecall') and context.get('active_id'):
active_model = self.pool.get('crm.phonecall').browse(cr, uid, context.get('active_id'), context=context)
# Find the best matching partner for the active model
if (active_model):
partner_obj = self.pool.get('res.partner')
# A partner is set already
if active_model.partner_id:
partner_id = active_model.partner_id.id
# Search through the existing partners based on the lead's email
elif active_model.email_from:
partner_ids = partner_obj.search(cr, uid, [('email', '=', active_model.email_from)], context=context)
if partner_ids:
partner_id = partner_ids[0]
# Search through the existing partners based on the lead's partner or contact name
elif active_model.partner_name:
partner_ids = partner_obj.search(cr, uid, [('name', 'ilike', '%'+active_model.partner_name+'%')], context=context)
if partner_ids:
partner_id = partner_ids[0]
elif active_model.contact_name:
partner_ids = partner_obj.search(cr, uid, [
('name', 'ilike', '%'+active_model.contact_name+'%')], context=context)
if partner_ids:
partner_id = partner_ids[0]
return partner_id
def default_get(self, cr, uid, fields, context=None):
res = super(crm_partner_binding, self).default_get(cr, uid, fields, context=context)
partner_id = self._find_matching_partner(cr, uid, context=context)
if 'action' in fields:
res['action'] = partner_id and 'exist' or 'create'
if 'partner_id' in fields:
res['partner_id'] = partner_id
return res
def _create_partner(self, cr, uid, ids, context=None):
"""
Create partner based on action.
:return dict: dictionary organized as followed: {lead_id: partner_assigned_id}
"""
#TODO this method in only called by crm_lead2opportunity_partner
#wizard and would probably diserve to be refactored or at least
#moved to a better place
if context is None:
context = {}
lead = self.pool.get('crm.lead')
lead_ids = context.get('active_ids', [])
data = self.browse(cr, uid, ids, context=context)[0]
partner_id = data.partner_id and data.partner_id.id or False
return lead.handle_partner_assignation(cr, uid, lead_ids, data.action, partner_id, context=context)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -1,77 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# 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/>.
#
##############################################################################
from osv import osv, fields
from tools.translate import _
class crm_partner2opportunity(osv.osv_memory):
"""Converts Partner To Opportunity"""
_name = 'crm.partner2opportunity'
_description = 'Partner To Opportunity'
_columns = {
'name' : fields.char('Opportunity Name', size=64, required=True),
'planned_revenue': fields.float('Expected Revenue', digits=(16,2)),
'probability': fields.float('Success Probability', digits=(16,2)),
'partner_id': fields.many2one('res.partner', 'Customer'),
}
def action_cancel(self, cr, uid, ids, context=None):
"""
Closes Partner 2 Opportunity
"""
return {'type':'ir.actions.act_window_close'}
def default_get(self, cr, uid, fields, context=None):
"""
This function gets default values
"""
partner_obj = self.pool.get('res.partner')
data = context and context.get('active_ids', []) or []
res = super(crm_partner2opportunity, self).default_get(cr, uid, fields, context=context)
for partner in partner_obj.browse(cr, uid, data, []):
if 'name' in fields:
res.update({'name': partner.name})
if 'partner_id' in fields:
res.update({'partner_id': data and data[0] or False})
return res
def make_opportunity(self, cr, uid, ids, context=None):
partner_ids = context and context.get('active_ids', []) or []
partner_id = partner_ids[0] if partner_ids else None
partner = self.pool.get('res.partner')
lead = self.pool.get('crm.lead')
data = self.browse(cr, uid, ids, context=context)[0]
opportunity_ids = partner.make_opportunity(cr, uid, partner_ids,
data.name,
data.planned_revenue,
data.probability,
partner_id,
context=context,
)
opportunity_id = opportunity_ids[partner_ids[0]]
return lead.redirect_opportunity_view(cr, uid, opportunity_id, context=context)
crm_partner2opportunity()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -1,27 +0,0 @@
<openerp>
<data>
<!-- partner To Opportunity form -->
<record id="view_crm_partner2opportunity" model="ir.ui.view">
<field name="name">crm.crm.partner2opportunity</field>
<field name="model">crm.partner2opportunity</field>
<field name="arch" type="xml">
<form string="Create Opportunity" version="7.0">
<group col="4">
<field name="name"/>
<field name="partner_id"/>
<field name="planned_revenue"/>
<field name="probability"/>
</group>
<footer>
<button name="make_opportunity" string="Create Opportunity" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
</data>
</openerp>
@@ -1,65 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# 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/>.
#
##############################################################################
from osv import osv, fields
from tools.translate import _
class crm_phonecall2opportunity(osv.osv_memory):
""" Converts Phonecall to Opportunity"""
_name = 'crm.phonecall2opportunity'
_inherit = 'crm.partner2opportunity'
_description = 'Phonecall To Opportunity'
def make_opportunity(self, cr, uid, ids, context=None):
"""
This converts Phonecall to Opportunity and opens Phonecall view
"""
if not len(ids):
return False
call_ids = context and context.get('active_ids', False) or False
this = self.browse(cr, uid, ids[0], context=context)
if not call_ids:
return {}
opportunity = self.pool.get('crm.lead')
phonecall = self.pool.get('crm.phonecall')
opportunity_ids = phonecall.convert_opportunity(cr, uid, call_ids, this.name, this.partner_id and this.partner_id.id or False, \
this.planned_revenue, this.probability, context=context)
return opportunity.redirect_opportunity_view(cr, uid, opportunity_ids[call_ids[0]], context=context)
def default_get(self, cr, uid, fields, context=None):
"""
This function gets default values
"""
record_id = context and context.get('active_id', False) or False
res = {}
if record_id:
phonecall = self.pool.get('crm.phonecall').browse(cr, uid, record_id, context=context)
if 'name' in fields:
res.update({'name': phonecall.name})
if 'partner_id' in fields:
res.update({'partner_id': phonecall.partner_id and phonecall.partner_id.id or False})
return res
crm_phonecall2opportunity()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -1,42 +0,0 @@
<?xml version="1.0"?>
<openerp>
<data>
<!-- Phonecall to Opportunity view -->
<record model="ir.ui.view" id="phonecall2opportunity_view">
<field name="name">crm.phonecall2opportunity.form</field>
<field name="model">crm.phonecall2opportunity</field>
<field name="arch" type="xml">
<form string="Convert To Opportunity " version="7.0">
<group>
<field name="name"/>
<field name="partner_id" />
<label for="planned_revenue"/>
<div>
<field name="planned_revenue" class="oe_inline"/> at
<field name="probability" class="oe_inline"/> %%
</div>
</group>
<footer>
<button name="make_opportunity" type="object" string="_Convert" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<!-- Phonecall to Opportunity action -->
<record model="ir.actions.act_window" id="phonecall2opportunity_act">
<field name="name">Convert to opportunity</field>
<field name="res_model">crm.phonecall2opportunity</field>
<field name="view_type">form</field>
<field name="view_mode">form</field>
<field name="view_id" ref="phonecall2opportunity_view"/>
<field name="target">new</field>
</record>
</data>
</openerp>
@@ -1,67 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
#
# 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/>.
#
##############################################################################
from osv import osv, fields
from tools.translate import _
class crm_phonecall2partner(osv.osv_memory):
""" Converts phonecall to partner """
_name = 'crm.phonecall2partner'
_inherit = 'crm.lead2partner'
_description = 'Phonecall to Partner'
def _select_partner(self, cr, uid, context=None):
"""
This function Searches for Partner from selected phonecall.
"""
if context is None:
context = {}
phonecall_obj = self.pool.get('crm.phonecall')
partner_obj = self.pool.get('res.partner')
rec_ids = context and context.get('active_ids', [])
value = {}
partner_id = False
for phonecall in phonecall_obj.browse(cr, uid, rec_ids, context=context):
partner_ids = partner_obj.search(cr, uid, [('name', '=', phonecall.name or phonecall.name)])
if not partner_ids and (phonecall.partner_phone or phonecall.partner_mobile):
partner_ids = partner_obj.search(cr, uid, ['|', ('phone', '=', phonecall.partner_phone), ('mobile','=',phonecall.partner_mobile)])
partner_id = partner_ids and partner_ids[0] or False
return partner_id
def _create_partner(self, cr, uid, ids, context=None):
"""
This function Creates partner based on action.
"""
if context is None:
context = {}
phonecall = self.pool.get('crm.phonecall')
data = self.browse(cr, uid, ids, context=context)[0]
call_ids = context and context.get('active_ids') or []
partner_id = data.partner_id and data.partner_id.id or False
return phonecall.convert_partner(cr, uid, call_ids, data.action, partner_id, context=context)
crm_phonecall2partner()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
@@ -1,54 +0,0 @@
<openerp>
<data>
<!-- Phonecall to partner confirmation form -->
<record id="view_crm_phonecall2partner_create" model="ir.ui.view">
<field name="name">crm.phonecall2partner.view.create</field>
<field name="model">crm.phonecall2partner</field>
<field name="arch" type="xml">
<form string="Create a Partner" version="7.0">
<label string="Are you sure you want to create a partner based on this Phonecall ?"/>
<newline/>
<label string="You may have to verify that this partner does not exist already."/>
<footer>
<button name="open_create_partner" string="Create Partner" type="object" groups="base.group_partner_manager" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<!-- Phonecall to partner form view -->
<record id="view_crm_phonecall2partner" model="ir.ui.view">
<field name="name">crm.phonecall2partner.view</field>
<field name="model">crm.phonecall2partner</field>
<field name="arch" type="xml">
<form string="Create a Partner" version="7.0">
<group>
<field name="action"/>
<field name="partner_id" attrs="{'required':[('action','=','exist')],'invisible':[('action','!=','exist')]}" />
</group>
<footer>
<button name="make_partner" string="Continue" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<!-- Phonecall to Partner wizard -->
<record id="action_crm_phonecall2partner" model="ir.actions.act_window">
<field name="name">Create a Partner</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">crm.phonecall2partner</field>
<field name="view_type">form</field>
<field name="view_id" ref="view_crm_phonecall2partner_create"/>
<field name="target">new</field>
</record>
</data>
</openerp>