[IMP] crm: usability improvements

Among various small fixes, some changes :
- correct computation of default sales team, located in sales_team with a
small code cleaning. Rely on this method everywhere it is necessary.
- add a configuration option to enable the use of leads in sales team;
otherwise it is hidden
- better display of alias in empty list help;
- add a menu for next activities, that are the opportunities with an action
whose deadline is near assigned to the current user;
- automatic update of aliases configuration on sales team, based on whether
they use leads and/or opportunities;
- add a wizard to log the lost reasong when deactivating a lead or an
opportunity
This commit is contained in:
Hitesh Trivedi
2015-08-11 10:56:09 +02:00
committed by Thibault Delavallée
parent d86406e8af
commit 30a7ef804a
24 changed files with 306 additions and 146 deletions
+1
View File
@@ -54,6 +54,7 @@ Dashboard for CRM will include:
'security/crm_security.xml', 'security/crm_security.xml',
'security/ir.model.access.csv', 'security/ir.model.access.csv',
'wizard/crm_lead_lost_view.xml',
'wizard/crm_lead_to_opportunity_view.xml', 'wizard/crm_lead_to_opportunity_view.xml',
'wizard/crm_merge_opportunities_view.xml', 'wizard/crm_merge_opportunities_view.xml',
+12 -17
View File
@@ -1,22 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<openerp> <openerp>
<data> <data>
<!-- the sequence of the configuration sub menu is 30 -->
<record model="ir.actions.act_window" id="base_partner_merge_automatic_act"> <act_window id="action_partner_deduplicate"
<field name="name">Deduplicate Contacts</field> res_model="base.partner.merge.automatic.wizard"
<field name="res_model">base.partner.merge.automatic.wizard</field> src_model="res.partner"
<field name="view_type">form</field> target="new"
<field name="view_mode">form</field> multi="True"
<field name="target">new</field> key2="client_action_multi"
<field name="context">{'active_test': False}</field> view_mode="form"
</record> name="Deduplicate Contact"
context="{'default_state': 'option'}"
<menuitem id='partner_merge_automatic_menu' />
action='base_partner_merge_automatic_act'
groups='base.group_system'
parent='base.menu_config_address_book'
sequence='4'/>
<record model='ir.ui.view' id='base_partner_merge_automatic_wizard_form'> <record model='ir.ui.view' id='base_partner_merge_automatic_wizard_form'>
<field name='name'>base.partner.merge.automatic.wizard.form</field> <field name='name'>base.partner.merge.automatic.wizard.form</field>
@@ -26,7 +21,7 @@
<sheet> <sheet>
<group attrs="{'invisible': [('state', '!=', 'finished')]}" col="1"> <group attrs="{'invisible': [('state', '!=', 'finished')]}" col="1">
<h2>There is no more contacts to merge for this request...</h2> <h2>There is no more contacts to merge for this request...</h2>
<button name="%(base_partner_merge_automatic_act)d" string="Deduplicate the other Contacts" class="oe_highlight" <button name="%(action_partner_deduplicate)d" string="Deduplicate the other Contacts" class="oe_highlight"
type="action"/> type="action"/>
</group> </group>
<p class="oe_grey" attrs="{'invisible': [('state', '!=', ('option'))]}"> <p class="oe_grey" attrs="{'invisible': [('state', '!=', ('option'))]}">
@@ -118,7 +113,7 @@
</record> </record>
<act_window id="action_partner_merge" res_model="base.partner.merge.automatic.wizard" src_model="res.partner" <act_window id="action_partner_merge" res_model="base.partner.merge.automatic.wizard" src_model="res.partner"
target="new" multi="True" key2="client_action_multi" view_mode="form" name="Automatic Merge"/> target="new" multi="True" key2="client_action_multi" view_mode="form" name="Merge Selected Contacts"/>
</data> </data>
+18 -23
View File
@@ -58,14 +58,6 @@ class crm_lead(format_address, osv.osv):
_inherit = ['mail.thread', 'ir.needaction_mixin', 'utm.mixin'] _inherit = ['mail.thread', 'ir.needaction_mixin', 'utm.mixin']
_mail_mass_mailing = _('Leads / Opportunities') _mail_mass_mailing = _('Leads / Opportunities')
def get_empty_list_help(self, cr, uid, help, context=None):
context = dict(context or {})
if context.get('default_type') == 'lead':
context['empty_list_help_model'] = 'crm.team'
context['empty_list_help_id'] = context.get('default_team_id')
context['empty_list_help_document_name'] = _("leads")
return super(crm_lead, self).get_empty_list_help(cr, uid, help, context=context)
def _get_default_stage_id(self, cr, uid, context=None): def _get_default_stage_id(self, cr, uid, context=None):
""" Gives default stage_id """ """ Gives default stage_id """
team_id = self.pool['crm.team']._get_default_team_id(cr, uid, context=context) team_id = self.pool['crm.team']._get_default_team_id(cr, uid, context=context)
@@ -91,7 +83,7 @@ class crm_lead(format_address, osv.osv):
# - OR ('fold', '=', False): add default columns that are not folded # - OR ('fold', '=', False): add default columns that are not folded
# - OR ('team_ids', '=', team_id), ('fold', '=', False) if team_id: add team columns that are not folded # - OR ('team_ids', '=', team_id), ('fold', '=', False) if team_id: add team columns that are not folded
search_domain = [] search_domain = []
team_id = self.pool['crm.team']._resolve_team_id_from_context(cr, uid, context=context) team_id = context and context.get('default_team_id') or False
if team_id: if team_id:
search_domain += ['|', ('team_ids', '=', team_id)] search_domain += ['|', ('team_ids', '=', team_id)]
search_domain += [('id', 'in', ids)] search_domain += [('id', 'in', ids)]
@@ -183,7 +175,7 @@ class crm_lead(format_address, osv.osv):
[('lead', 'Lead'), ('opportunity', 'Opportunity')], [('lead', 'Lead'), ('opportunity', 'Opportunity')],
string='Type', select=True, required=True, string='Type', select=True, required=True,
help="Type is used to separate Leads and Opportunities"), help="Type is used to separate Leads and Opportunities"),
'priority': fields.selection(crm_stage.AVAILABLE_PRIORITIES, 'Priority', select=True), 'priority': fields.selection(crm_stage.AVAILABLE_PRIORITIES, 'Rating', select=True),
'date_closed': fields.datetime('Closed', readonly=True, copy=False), 'date_closed': fields.datetime('Closed', readonly=True, copy=False),
'stage_id': fields.many2one('crm.stage', 'Stage', track_visibility='onchange', select=True, 'stage_id': fields.many2one('crm.stage', 'Stage', track_visibility='onchange', select=True,
domain="['&', ('team_ids', '=', team_id), '|', ('type', '=', type), ('type', '=', 'both')]"), domain="['&', ('team_ids', '=', team_id), '|', ('type', '=', type), ('type', '=', 'both')]"),
@@ -204,8 +196,6 @@ class crm_lead(format_address, osv.osv):
# Only used for type opportunity # Only used for type opportunity
'probability': fields.float('Probability', group_operator="avg"), 'probability': fields.float('Probability', group_operator="avg"),
'planned_revenue': fields.float('Expected Revenue', track_visibility='always'), 'planned_revenue': fields.float('Expected Revenue', track_visibility='always'),
'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), '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_deadline': fields.date('Expected Closing', help="Estimate of the date on which the opportunity will be won."),
# CRM Actions # CRM Actions
@@ -213,8 +203,8 @@ class crm_lead(format_address, osv.osv):
'next_activity_1': fields.related("next_activity_id", "activity_1_id", "name", type="char", string="Next Activity 1"), 'next_activity_1': fields.related("next_activity_id", "activity_1_id", "name", type="char", string="Next Activity 1"),
'next_activity_2': fields.related("next_activity_id", "activity_2_id", "name", type="char", string="Next Activity 2"), 'next_activity_2': fields.related("next_activity_id", "activity_2_id", "name", type="char", string="Next Activity 2"),
'next_activity_3': fields.related("next_activity_id", "activity_3_id", "name", type="char", string="Next Activity 3"), 'next_activity_3': fields.related("next_activity_id", "activity_3_id", "name", type="char", string="Next Activity 3"),
'date_action': fields.date('Next Action Date', select=True), 'date_action': fields.date('Next Activity Date', select=True),
'title_action': fields.char('Next Action Summary'), 'title_action': fields.char('Next Activity Summary'),
'color': fields.integer('Color Index'), 'color': fields.integer('Color Index'),
'partner_address_name': fields.related('partner_id', 'name', type='char', string='Partner Contact Name', readonly=True), 'partner_address_name': fields.related('partner_id', 'name', type='char', string='Partner Contact Name', readonly=True),
@@ -242,7 +232,7 @@ class crm_lead(format_address, osv.osv):
_defaults = { _defaults = {
'active': 1, 'active': 1,
'type': 'lead', 'type': lambda s, cr, uid, c: 'lead' if s.pool['res.users'].has_group(cr, uid, 'crm.group_use_lead') else 'opportunity',
'user_id': lambda s, cr, uid, c: uid, 'user_id': lambda s, cr, uid, c: uid,
'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c), 'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
'team_id': lambda s, cr, uid, c: s.pool['crm.team']._get_default_team_id(cr, uid, context=c), 'team_id': lambda s, cr, uid, c: s.pool['crm.team']._get_default_team_id(cr, uid, context=c),
@@ -291,11 +281,7 @@ class crm_lead(format_address, osv.osv):
def on_change_user(self, cr, uid, ids, user_id, context=None): def on_change_user(self, cr, uid, ids, user_id, context=None):
""" When changing the user, also set a team_id or restrict team id """ When changing the user, also set a team_id or restrict team id
to the ones user_id is member of. """ to the ones user_id is member of. """
team_id = self.pool['crm.team']._get_default_team_id(cr, uid, user_id=user_id, context=context) team_id = self.pool['crm.team']._get_default_team_id(cr, uid, context=context, user_id=user_id)
if user_id and not team_id:
team_ids = self.pool.get('crm.team').search(cr, uid, ['|', ('user_id', '=', user_id), ('member_ids', '=', user_id)], context=context)
if team_ids:
team_id = team_ids[0]
return {'value': {'team_id': team_id}} return {'value': {'team_id': team_id}}
def stage_find(self, cr, uid, cases, team_id, domain=None, order='sequence', context=None): def stage_find(self, cr, uid, cases, team_id, domain=None, order='sequence', context=None):
@@ -918,9 +904,18 @@ class crm_lead(format_address, osv.osv):
context = dict(context or {}) context = dict(context or {})
context['empty_list_help_model'] = 'crm.team' context['empty_list_help_model'] = 'crm.team'
context['empty_list_help_id'] = context.get('default_team_id', None) context['empty_list_help_id'] = context.get('default_team_id', None)
context['empty_list_help_document_name'] = _("opportunity") context['empty_list_help_document_name'] = _("opportunities")
if context.get('default_type') == 'lead': if help:
context['empty_list_help_document_name'] = _("lead") alias_record = self.pool['ir.model.data'].xmlid_to_object(cr, uid, "crm.mail_alias_lead_info")
if alias_record and alias_record.alias_domain and alias_record.alias_name:
dynamic_help = '<p>%s</p>' % _("""All email incoming to %(link)s will automatically create new opportunity.
Update your business card, phone book, social media,... Send an email right now and see it here.""") % {
'link': "<a href='mailto:%s'>%s</a>" % (alias_record.alias_name, alias_record.alias_domain)
}
return '<p class="oe_view_nocontent_create">%s</p>%s%s' % (
_('Click to add a new opportunity'),
help,
dynamic_help)
return super(crm_lead, self).get_empty_list_help(cr, uid, help, context=context) return super(crm_lead, self).get_empty_list_help(cr, uid, help, context=context)
# ---------------------------------------- # ----------------------------------------
+30 -1
View File
@@ -77,7 +77,36 @@
<menuitem id="base.menu_sales" name="Sales" parent="base.menu_base_partner" sequence="5"/> <menuitem id="base.menu_sales" name="Sales" parent="base.menu_base_partner" sequence="5"/>
<menuitem name="Leads" id="menu_crm_leads" parent="base.menu_sales" sequence="1" <menuitem name="Leads" id="menu_crm_leads" parent="base.menu_sales" sequence="1"
action="crm_lead_all_leads"/> action="crm_lead_all_leads" groups="crm.group_use_lead"/>
<record id="crm_lead_action_activities" model="ir.actions.act_window">
<field name="name">Next Activities</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">crm.lead</field>
<field name="view_mode">tree,form</field>
<field name="search_view_id" ref="crm.view_crm_case_opportunities_filter"/>
<field name="view_ids"
eval="[(5, 0, 0),
(0, 0, {'view_mode': 'tree', 'view_id': ref('crm_lead_view_tree_activity')}),
(0, 0, {'view_mode': 'form', 'view_id': ref('crm_case_form_view_oppor')}),
(0, 0, {'view_mode': 'kanban'})]"/>
<field name="domain">[('type','=','opportunity'), ('date_action','!=',False)]</field>
<field name="context">{
'default_type': 'opportunity',
'default_user_id': uid,
'search_default_assigned_to_me': 1
}
</field>
<field name="help" type="html">
<p>
Here is the list of your next activities. Those are linked to your opportunities.
To set a next activity, go on an opportunity and add one. It will then appear in this list.
</p>
</field>
</record>
<menuitem id="crm_lead_menu_activities" name="Next Activities" sequence="5"
parent="base.menu_sales" action="crm_lead_action_activities" />
</data> </data>
</openerp> </openerp>
+82 -53
View File
@@ -126,20 +126,24 @@
<sheet> <sheet>
<div class="oe_button_box" name="button_box"/> <div class="oe_button_box" name="button_box"/>
<div class="oe_title"> <div class="oe_title">
<label for="name" class="oe_edit_only"/> <label for="name" class="oe_edit_only" string="Lead"/>
<h1><field name="name" placeholder="Describe the lead..."/></h1> <h1><field name="name" placeholder="Describe the lead..."/></h1>
<span class="label label-danger" <label for="tag_ids" class="oe_edit_only"/>
attrs="{'invisible': ['|', ('probability', '&gt;', 0), ('active', '=', True)]}">Lost</span> <field name="tag_ids" widget="many2many_tags"/>
<span class="label label-success" <div class="mt4">
attrs="{'invisible': [('probability', '&lt;', 100)]}">Won</span> <span class="label label-danger"
attrs="{'invisible': ['|', ('probability', '&gt;', 0), ('active', '=', True)]}">Lost</span>
<span class="label label-success"
attrs="{'invisible': [('probability', '&lt;', 100)]}">Won</span>
</div>
</div> </div>
<group> <group>
<group> <group>
<field name="partner_name" string="Company Name"/>
<!-- Preload all the partner's information --> <!-- Preload all the partner's information -->
<field name="partner_id" string="Customer" <field name="partner_id" string="Customer"
options='{"create_name_field": "name"}' 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_team_id': team_id}"/> 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_team_id': team_id}" groups="base.group_no_one"/>
<field name="partner_name" string="Company Name"/>
<label for="street" string="Address"/> <label for="street" string="Address"/>
<div class="o_address_format"> <div class="o_address_format">
<field name="street" placeholder="Street..." class="o_address_street"/> <field name="street" placeholder="Street..." class="o_address_street"/>
@@ -153,7 +157,7 @@
<group> <group>
<label for="contact_name"/> <label for="contact_name"/>
<div class="o_row"> <div class="o_row">
<field name="contact_name"/><span attrs="{'invisible': [('title', '=', '')]}">, </span> <field name="contact_name"/>
<field name="title" placeholder="Title" domain="[('domain', '=', 'contact')]" options='{"no_open": True}'/> <field name="title" placeholder="Title" domain="[('domain', '=', 'contact')]" options='{"no_open": True}'/>
</div> </div>
<field name="email_from" widget="email"/> <field name="email_from" widget="email"/>
@@ -164,15 +168,11 @@
</group> </group>
<group> <group>
<field name="user_id" on_change="on_change_user(user_id, context)" <field name="user_id" on_change="on_change_user(user_id, context)"
context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_salesman_all_leads'] }"/> context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_salesman_all_leads'] }" widget="selection"/>
<label for="team_id"/> <field name="team_id" widget="selection" domain="[('use_leads','=',True)]"/>
<div class="o_row">
<field name="team_id"/>
</div>
<field name="type" invisible="1"/> <field name="type" invisible="1"/>
</group> </group>
<group> <group>
<field name="tag_ids" widget="many2many_tags"/>
<field name="priority" widget="priority"/> <field name="priority" widget="priority"/>
</group> </group>
</group> </group>
@@ -182,9 +182,9 @@
</page> </page>
<page name="extra" string="Extra Info"> <page name="extra" string="Extra Info">
<group> <group>
<group string="Mailings"> <group>
<field name="opt_out"/> <field name="opt_out"/>
<field name="message_bounce" readonly="1"/> <field name="message_bounce" readonly="1" groups="base.group_no_one"/>
</group> </group>
<group string="Categorization" groups="base.group_multi_company,base.group_no_one" name="categorization"> <group string="Categorization" groups="base.group_multi_company,base.group_no_one" name="categorization">
<field name="company_id" <field name="company_id"
@@ -194,7 +194,7 @@
<field name="medium_id"/> <field name="medium_id"/>
<field name="source_id" /> <field name="source_id" />
</group> </group>
<group name="misc" string="Misc"> <group name="misc">
<field name="probability" groups="base.group_no_one"/> <field name="probability" groups="base.group_no_one"/>
<field name="active"/> <field name="active"/>
<field name="referred"/> <field name="referred"/>
@@ -247,7 +247,7 @@
<tree string="Leads" decoration-bf="message_unread==True" decoration-muted="probability == 100"> <tree string="Leads" decoration-bf="message_unread==True" decoration-muted="probability == 100">
<field name="date_deadline" invisible="1"/> <field name="date_deadline" invisible="1"/>
<field name="create_date"/> <field name="create_date"/>
<field name="name"/> <field name="name" string="Lead"/>
<field name="contact_name"/> <field name="contact_name"/>
<field name="country_id"/> <field name="country_id"/>
<field name="email_from"/> <field name="email_from"/>
@@ -324,6 +324,14 @@
<div class="text-muted"> <div class="text-muted">
<span t-if="record.planned_revenue.raw_value"><t t-esc="record.planned_revenue.value"/><field name="company_currency"/></span> <span t-if="record.partner_id.value"> - <t t-esc="record.partner_id.value"/></span> <span t-if="record.planned_revenue.raw_value"><t t-esc="record.planned_revenue.value"/><field name="company_currency"/></span> <span t-if="record.partner_id.value"> - <t t-esc="record.partner_id.value"/></span>
</div> </div>
<div class="text-muted">
<t t-if="record.date_deadline.raw_value and record.date_deadline.raw_value lt (new Date())" t-set="red">oe_kanban_text_red</t>
<span t-attf-class="#{red || ''}">
<field name="date_action"/>
<t t-if="record.date_action.raw_value"> : </t>
<field name="next_activity_id"/>
</span>
</div>
<div class="o_kanban_footer"> <div class="o_kanban_footer">
<field name="priority" widget="priority" groups="base.group_user"/> <field name="priority" widget="priority" groups="base.group_user"/>
<t t-if="record.message_unread_counter.raw_value"> <t t-if="record.message_unread_counter.raw_value">
@@ -410,8 +418,9 @@
<button name="action_set_won" string="Mark Won" <button name="action_set_won" string="Mark Won"
type="object" class="oe_highlight" type="object" class="oe_highlight"
attrs="{'invisible': ['|', ('active','=',False), ('probability', '=', 100)]}"/> attrs="{'invisible': ['|', ('active','=',False), ('probability', '=', 100)]}"/>
<button name="action_set_lost" string="Mark Lost" <button name="%(crm.crm_lead_lost_action)d" string="Mark Lost"
type="object" class="oe_highlight" type="action" class="oe_highlight"
context="{'default_lead_id': active_id}"
attrs="{'invisible': [('active', '=', False)]}"/> attrs="{'invisible': [('active', '=', False)]}"/>
<button name="action_set_active" string="Unarchive" <button name="action_set_active" string="Unarchive"
type="object" class="oe_highlight" type="object" class="oe_highlight"
@@ -426,27 +435,41 @@
<button class="oe_stat_button" type="object" <button class="oe_stat_button" type="object"
context="{'partner_id': partner_id}" context="{'partner_id': partner_id}"
name="action_schedule_meeting" icon="fa-calendar"> name="action_schedule_meeting" icon="fa-calendar">
<field string="Meetings" name="meeting_count" widget="statinfo"/> <div class="o_stat_info">
<field name="meeting_count" class="o_stat_value"/>
<span class="o_stat_text" attrs="{'invisible': [('meeting_count', '&lt;', 2)]}"> Meetings</span>
<span class="o_stat_text" attrs="{'invisible': [('meeting_count', '&gt;', 1)]}"> Meeting</span>
</div>
</button> </button>
</div> </div>
<div class="oe_title"> <div>
<label for="name" class="oe_edit_only"/> <label for="name" class="oe_edit_only"/>
<h1><field name="name" placeholder="e.g. Product Pricing"/></h1> <h1><field name="name" placeholder="e.g. Product Pricing"/></h1>
<span class="label label-danger" <label for="tag_ids" class="oe_edit_only"/>
attrs="{'invisible': ['|', ('probability', '&gt;', 0), ('active', '=', True)]}">Lost</span> <field name="tag_ids" widget="many2many_tags"/>
<span class="label label-success" <div class="mt4">
attrs="{'invisible': [('probability', '&lt;', 100)]}">Won</span> <span class="label label-danger"
<div class="o_row"> attrs="{'invisible': ['|', ('probability', '&gt;', 0), ('active', '=', True)]}">Lost</span>
<label for="planned_revenue" class="oe_edit_only"/> <span class="label label-success"
<label for="probability" class="oe_edit_only"/> attrs="{'invisible': [('probability', '&lt;', 100)]}">Won</span>
</div>
<div class="o_row">
<div class="oe_left">
<label for="planned_revenue" class="oe_edit_only"/>
<h2 class="o_row">
<field name="company_currency" invisible="1"/>
<field name="planned_revenue" widget='monetary' options="{'currency_field': 'company_currency'}"/>
<span class="oe_grey"> at </span>
</h2>
</div>
<div>
<label for="probability" class="oe_edit_only"/>
<h2 class="o_row">
<field name="probability" widget="integer"/>
<span>%%</span>
</h2>
</div>
</div> </div>
<h2 class="o_row">
<field name="company_currency" invisible="1"/>
<field name="planned_revenue" widget='monetary' options="{'currency_field': 'company_currency'}"/>
<span class="oe_grey"> at </span>
<field name="probability" widget="integer"/>
<span>%%</span>
</h2>
</div> </div>
<group> <group>
<group> <group>
@@ -489,19 +512,15 @@
</div> </div>
</div> </div>
<field name="date_deadline"/> <field name="date_deadline"/>
<field name="priority" widget="priority"/>
</group> </group>
<group> <group>
<field name="user_id" on_change="on_change_user(user_id, context)" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_salesman_all_leads']}"/> <field name="user_id" on_change="on_change_user(user_id, context)" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_salesman_all_leads']}" widget="selection"/>
<label for="team_id"/> <field name="team_id" widget="selection"/>
<div class="o_row">
<field name="team_id" widget="selection"/>
</div>
</group> </group>
<group> <group>
<field name="tag_ids" widget="many2many_tags"/> <field name="priority" widget="priority"/>
<field name="lost_reason"/> <field name="lost_reason" attrs="{'invisible': [('active', '=', True)]}"/>
<field name="date_conversion" invisible="1"/> <field name="date_conversion" invisible="1"/>
</group> </group>
</group> </group>
@@ -510,7 +529,7 @@
<page string="Internal Notes"> <page string="Internal Notes">
<field name="description"/> <field name="description"/>
</page> </page>
<page name="lead" string="Lead"> <page name="lead" string="Lead" groups="crm.group_use_lead">
<group> <group>
<group> <group>
<field name="partner_name"/> <field name="partner_name"/>
@@ -534,26 +553,20 @@
<field name="function"/> <field name="function"/>
<field name="mobile"/> <field name="mobile"/>
<field name="fax"/> <field name="fax"/>
<field name="opt_out"/>
</group> </group>
<group string="Marketing"> <group string="Marketing">
<field name="campaign_id" /> <field name="campaign_id" />
<field name="medium_id" /> <field name="medium_id" />
<field name="source_id" /> <field name="source_id" />
</group> </group>
<group string="Mailings" name="mailings"> <group string="Misc" name="Misc">
<field name="opt_out"/>
</group>
<group string="Misc">
<field name="active"/> <field name="active"/>
<field name="day_open" groups="base.group_no_one"/> <field name="day_open" groups="base.group_no_one"/>
<field name="day_close" groups="base.group_no_one"/> <field name="day_close" groups="base.group_no_one"/>
<field name="referred"/> <field name="referred"/>
<field name="type" invisible="1"/> <field name="type" invisible="1"/>
</group> </group>
<group name="references" string="References">
<field name="ref"/>
<field name="ref2"/>
</group>
</group> </group>
</page> </page>
</notebook> </notebook>
@@ -608,6 +621,8 @@
<field name="user_id"/> <field name="user_id"/>
<field name="partner_id" operator="child_of" string="Customer"/> <field name="partner_id" operator="child_of" string="Customer"/>
<field name="stage_id" domain="[]"/> <field name="stage_id" domain="[]"/>
<field name="next_activity_id"/>
<field name="title_action"/>
<field name="probability"/> <field name="probability"/>
<field name="lost_reason"/> <field name="lost_reason"/>
<separator/> <separator/>
@@ -648,6 +663,20 @@
</field> </field>
</record> </record>
<record id="crm_lead_view_tree_activity" model="ir.ui.view">
<field name="name">crm.lead.next.activity.tree</field>
<field name="model">crm.lead</field>
<field name="arch" type="xml">
<tree string="Next Activity" create="false" colors="red:date_action and (date_action &lt; current_date)" default_order="date_action">
<field name="name"/>
<field name="date_action"/>
<field name="next_activity_id"/>
<field name="title_action"/>
<field name="date_deadline"/>
</tree>
</field>
</record>
<!-- <!--
MASS MAILING MASS MAILING
--> -->
+22
View File
@@ -34,5 +34,27 @@
<field name="placement">auto top</field> <field name="placement">auto top</field>
</record> </record>
<record model="web.tip" id="crm_tip_5">
<field name="title"></field>
<field name="description"><![CDATA[Use the graph view to have a clear reporting of your opportunity pipeline.]]></field>
<field name="model">crm.lead</field>
<field name="mode">kanban</field>
<field name="trigger_selector">.o_kanban_record:eq(20):visible</field>
<field name="highlight_selector">button[data-view-type=graph]</field>
<field name="end_event">mousedown</field>
<field name="placement">left</field>
</record>
<record model="web.tip" id="crm_tip_6">
<field name="title"></field>
<field name="description"><![CDATA[Gain some time and change the stage of your opportunity right here, no need to go back to your pipeline.]]></field>
<field name="model">crm.lead</field>
<field name="mode">form</field>
<field name="trigger_selector">.oe_form_field_status.oe_form_status_clickable:visible</field>
<field name="highlight_selector">.oe_form_field_status.oe_form_status_clickable</field>
<field name="end_event">mousedown</field>
<field name="placement">bottom</field>
</record>
</data> </data>
</openerp> </openerp>
+5 -1
View File
@@ -16,7 +16,7 @@ class crm_activity_report(models.Model):
author_id = fields.Many2one('res.partner', 'Author', readonly=True) author_id = fields.Many2one('res.partner', 'Author', readonly=True)
user_id = fields.Many2one('res.users', 'Responsible', readonly=True) user_id = fields.Many2one('res.users', 'Responsible', readonly=True)
team_id = fields.Many2one('crm.team', 'Sales Team', readonly=True) team_id = fields.Many2one('crm.team', 'Sales Team', readonly=True)
subtype_id = fields.Many2one('mail.message.subtype', 'Action', readonly=True) subtype_id = fields.Many2one('mail.message.subtype', 'Activity', readonly=True)
country_id = fields.Many2one('res.country', 'Country', readonly=True) country_id = fields.Many2one('res.country', 'Country', readonly=True)
company_id = fields.Many2one('res.company', 'Company', readonly=True) company_id = fields.Many2one('res.company', 'Company', readonly=True)
stage_id = fields.Many2one('crm.stage', 'Stage', readonly=True) stage_id = fields.Many2one('crm.stage', 'Stage', readonly=True)
@@ -48,6 +48,10 @@ class crm_activity_report(models.Model):
"crm_lead" l "crm_lead" l
on on
(m.res_id = l.id) (m.res_id = l.id)
inner join
"crm_activity" a
on
(m.subtype_id = a.subtype_id)
WHERE WHERE
(m.model = 'crm.lead') (m.model = 'crm.lead')
)""") )""")
@@ -179,7 +179,7 @@ teams of the sales pipeline.</field>
(including those converted into opportunities).</field> (including those converted into opportunities).</field>
</record> </record>
<menuitem id="crm_opportunity_report_menu_lead" name="Leads Analysis" <menuitem id="crm_opportunity_report_menu_lead" name="Leads Analysis"
parent="base.menu_sale_report" action="crm_opportunity_report_action_lead" sequence="6"/> parent="base.menu_sale_report" groups="crm.group_use_lead" action="crm_opportunity_report_action_lead" sequence="6"/>
</data> </data>
</openerp> </openerp>
+3
View File
@@ -15,6 +15,9 @@ class crm_configuration(osv.TransientModel):
help="Odoo will generate an email alias based on the sales team name"), help="Odoo will generate an email alias based on the sales team name"),
'alias_prefix': fields.char('Default Alias Name for Leads'), 'alias_prefix': fields.char('Default Alias Name for Leads'),
'alias_domain' : fields.char('Alias Domain'), 'alias_domain' : fields.char('Alias Domain'),
'group_use_lead': fields.boolean(
"Use leads if you need a qualification step before creating an opportunity or a customer",
implied_group='crm.group_use_lead')
} }
_defaults = { _defaults = {
+7
View File
@@ -32,6 +32,13 @@
</div> </div>
</group> </group>
<group>
<label for="id" string="Manage Leads"/>
<div>
<field name="group_use_lead" class="oe_inline"/>
<label for="group_use_lead"/>
</div>
</group>
</div> </div>
</data> </data>
</field> </field>
+31 -6
View File
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from openerp.osv import fields, osv from openerp.osv import fields, osv
from openerp.tools.safe_eval import safe_eval as eval
from openerp.tools.translate import _ from openerp.tools.translate import _
@@ -46,10 +47,25 @@ class crm_team(osv.Model):
'use_opportunities': True, 'use_opportunities': True,
} }
def onchange_use_leads(self, cr, uid, ids, use_leads, context=None): def onchange_use_leads_opportunities(self, cr, uid, ids, use_leads, use_opportunities, context=None):
if not use_leads: if use_leads or use_opportunities:
return {'value': {'alias_name': False}} return {'value': {}}
return {'value': {}} return {'value': {'alias_name': False}}
def _get_alias_defaults_values(self, cr, uid, ids, context=None):
res = dict.fromkeys(ids, False)
is_group_use_lead = self.pool['res.users'].has_group(cr, uid, 'crm.group_use_lead')
for team in self.browse(cr, uid, ids, context=context):
alias_defaults = eval(team.alias_defaults)
alias_defaults.update({
'type': 'lead' if is_group_use_lead and team.use_leads else 'opportunity',
'team_id': team.id,
})
res[team.id] = {
'alias_defaults': alias_defaults,
'alias_parent_thread_id': team.id,
}
return res
def create(self, cr, uid, vals, context=None): def create(self, cr, uid, vals, context=None):
if context is None: if context is None:
@@ -59,10 +75,19 @@ class crm_team(osv.Model):
if generate_alias_name and not vals.get('alias_name'): if generate_alias_name and not vals.get('alias_name'):
vals['alias_name'] = vals.get('name') vals['alias_name'] = vals.get('name')
team_id = super(crm_team, self).create(cr, uid, vals, context=create_context) team_id = super(crm_team, self).create(cr, uid, vals, context=create_context)
team = self.browse(cr, uid, team_id, context=context) self.write(cr, uid, [team_id],
self.pool.get('mail.alias').write(cr, uid, [team.alias_id.id], {'alias_parent_thread_id': team_id, 'alias_defaults': {'team_id': team_id, 'type': 'lead'}}, context=context) self._get_alias_defaults_values(cr, uid, [team_id], context=context)[team_id],
context=context)
return team_id return team_id
def write(self, cr, uid, ids, vals, context=None):
res = super(crm_team, self).write(cr, uid, ids, vals, context=context)
if vals.get('use_leads') or vals.get('alias_defaults'):
alias_res = self._get_alias_defaults_values(cr, uid, ids, context=context)
for team_id, values in alias_res.iteritems():
super(crm_team, self).write(cr, uid, [team_id], values, context=context)
return res
def unlink(self, cr, uid, ids, context=None): def unlink(self, cr, uid, ids, context=None):
# Cascade-delete mail aliases as well, as they should not exist without the sales team. # Cascade-delete mail aliases as well, as they should not exist without the sales team.
mail_alias = self.pool.get('mail.alias') mail_alias = self.pool.get('mail.alias')
+2 -2
View File
@@ -42,7 +42,7 @@
</xpath> </xpath>
<xpath expr="//div[contains(@class, 'o_kanban_manage_view')]" position="inside"> <xpath expr="//div[contains(@class, 'o_kanban_manage_view')]" position="inside">
<div t-if="record.use_leads.raw_value"> <div t-if="record.use_leads.raw_value" groups="crm.group_use_lead">
<a name="%(crm_case_form_view_salesteams_lead)d" type="action"> <a name="%(crm_case_form_view_salesteams_lead)d" type="action">
Leads Leads
</a> </a>
@@ -63,7 +63,7 @@
</xpath> </xpath>
<xpath expr="//div[contains(@class, 'o_kanban_manage_reports')]" position="inside"> <xpath expr="//div[contains(@class, 'o_kanban_manage_reports')]" position="inside">
<div> <div t-if="record.use_leads.raw_value" groups="crm.group_use_lead">
<a name="%(action_report_crm_lead_salesteam)d" type="action"> <a name="%(action_report_crm_lead_salesteam)d" type="action">
Leads Leads
</a> </a>
+8 -6
View File
@@ -87,9 +87,11 @@
<field name="inherit_id" ref="sales_team.crm_team_view_form"/> <field name="inherit_id" ref="sales_team.crm_team_view_form"/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="//div[@name='options_active']" position="inside"> <xpath expr="//div[@name='options_active']" position="inside">
<field name="use_leads" on_change="onchange_use_leads(use_leads)"/> <div groups="crm.group_use_lead">
<label for="use_leads" string="Leads"/> <field name="use_leads" on_change="onchange_use_leads_opportunities(use_leads, use_opportunities)"/>
<field name="use_opportunities" class="oe_inline"/> <label for="use_leads" string="Leads"/>
</div>
<field name="use_opportunities" class="oe_inline" on_change="onchange_use_leads_opportunities(use_leads, use_opportunities)"/>
<label for="use_opportunities"/> <label for="use_opportunities"/>
</xpath> </xpath>
<xpath expr="//page[@name='members']" position="after"> <xpath expr="//page[@name='members']" position="after">
@@ -101,10 +103,10 @@
<xpath expr="//field[@name='code']" position="after"> <xpath expr="//field[@name='code']" position="after">
<label for="alias_name" string="Email Alias" <label for="alias_name" string="Email Alias"
groups="base.group_no_one" groups="base.group_no_one"
attrs="{'invisible': [('use_leads', '=', False)]}"/> attrs="{'invisible': [('use_leads', '=', False), ('use_opportunities', '=', False)]}"/>
<div name="alias_def" <div name="alias_def"
groups="base.group_no_one" groups="base.group_no_one"
attrs="{'invisible': [('use_leads', '=', False)]}"> attrs="{'invisible': [('use_leads', '=', False), ('use_opportunities', '=', False)]}">
<field name="alias_id" class="oe_read_only oe_inline" <field name="alias_id" class="oe_read_only oe_inline"
string="Email Alias" required="0"/> string="Email Alias" required="0"/>
<div class="oe_edit_only oe_inline" name="edit_alias" style="display: inline;" > <div class="oe_edit_only oe_inline" name="edit_alias" style="display: inline;" >
@@ -113,7 +115,7 @@
</div> </div>
<field name="alias_contact" class="oe_inline" <field name="alias_contact" class="oe_inline"
string="Accept Emails From" string="Accept Emails From"
attrs="{'invisible': [('use_leads', '=', False)]}"/> attrs="{'invisible': [('use_leads', '=', False), ('use_opportunities', '=', False)]}"/>
</xpath> </xpath>
</field> </field>
</record> </record>
+5
View File
@@ -24,6 +24,11 @@
<field name="users" eval="[(4, ref('base.user_root'))]"/> <field name="users" eval="[(4, ref('base.user_root'))]"/>
</record> </record>
<record id="group_use_lead" model="res.groups">
<field name="name">Show Lead Menu</field>
<field name="category_id" ref="base.module_category_hidden"/>
</record>
<record model="res.users" id="base.user_root"> <record model="res.users" id="base.user_root">
<field eval="[(4,ref('base.group_partner_manager'))]" name="groups_id"/> <field eval="[(4,ref('base.group_partner_manager'))]" name="groups_id"/>
</record> </record>
+1
View File
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details. # Part of Odoo. See LICENSE file for full copyright and licensing details.
import crm_lead_lost
import crm_partner_binding import crm_partner_binding
import crm_lead_to_opportunity import crm_lead_to_opportunity
import crm_merge_opportunities import crm_merge_opportunities
+18
View File
@@ -0,0 +1,18 @@
# -*- coding: utf-8 -*-
from openerp import api, fields, models
class CrmLeadLost(models.TransientModel):
_name = 'crm.lead.lost'
_description = 'Get Lost Reason'
lead_id = fields.Many2one('crm.lead', 'Lead', required=True)
lost_reason_id = fields.Many2one('crm.lost.reason', 'Lost Reason')
@api.multi
def action_lost_reason_apply(self):
res = False
for wizard in self:
self.lead_id.lost_reason_id = self.lost_reason_id.id
res = self.lead_id.action_set_lost()
return res
+29
View File
@@ -0,0 +1,29 @@
<?xml version="1.0"?>
<odoo>
<record id="crm_lead_lost_view_form" model="ir.ui.view">
<field name="name">crm.lead.lost.form</field>
<field name="model">crm.lead.lost</field>
<field name="arch" type="xml">
<form string="Lost Reason">
<group class="oe_title">
<field name="lead_id" invisible="1"/>
<field name="lost_reason_id"/>
</group>
<footer>
<button name="action_lost_reason_apply" string="Submit" type="object" class="oe_highlight"/>
or
<button string="Cancel" class="oe_link" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="crm_lead_lost_action" model="ir.actions.act_window">
<field name="name">Lost Reason</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">crm.lead.lost</field>
<field name="view_mode">form</field>
<field name="view_id" ref="crm_lead_lost_view_form"/>
<field name="target">new</field>
</record>
</odoo>
@@ -11,8 +11,8 @@
<field name="name" widget="radio"/> <field name="name" widget="radio"/>
</group> </group>
<group string="Assign these opportunities to"> <group string="Assign these opportunities to">
<field name="user_id" on_change="on_change_user(user_id, team_id, context)"/> <field name="user_id" on_change="on_change_user(user_id, team_id, context)" widget="selection"/>
<field name="team_id"/> <field name="team_id" widget="selection"/>
</group> </group>
<group string="Opportunities" attrs="{'invisible': [('name', '!=', 'merge')]}"> <group string="Opportunities" attrs="{'invisible': [('name', '!=', 'merge')]}">
<field name="opportunity_ids" nolabel="1"> <field name="opportunity_ids" nolabel="1">
+4 -1
View File
@@ -33,7 +33,10 @@
<button class="oe_stat_button" type="action" <button class="oe_stat_button" type="action"
name="%(sale_action_quotations)d" icon="fa-usd" name="%(sale_action_quotations)d" icon="fa-usd"
context="{'search_default_partner_id': partner_id, 'default_partner_id': partner_id}"> context="{'search_default_partner_id': partner_id, 'default_partner_id': partner_id}">
<field name="sale_number" widget="statinfo" string="Quotes"/> <div class="o_stat_info">
<field name="sale_number" class="o_stat_value"/>
<span class="o_stat_text"> Quote(s) </span>
</div>
</button> </button>
<button class="oe_stat_button" type="action" attrs="{'invisible': [('sale_amount_total', '=', 0)]}" <button class="oe_stat_button" type="action" attrs="{'invisible': [('sale_amount_total', '=', 0)]}"
name="%(sale_action_quotations)d" icon="fa-usd" name="%(sale_action_quotations)d" icon="fa-usd"
+11 -22
View File
@@ -1,8 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details. # Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import date, datetime
from dateutil import relativedelta
from openerp import tools
from openerp.osv import fields, osv from openerp.osv import fields, osv
@@ -16,27 +14,18 @@ class crm_team(osv.Model):
def get_full_name(self, cr, uid, ids, field_name, arg, context=None): def get_full_name(self, cr, uid, ids, field_name, arg, context=None):
return dict(self.name_get(cr, uid, ids, context=context)) return dict(self.name_get(cr, uid, ids, context=context))
def _get_default_team_id(self, cr, uid, user_id=None, context=None): def _get_default_team_id(self, cr, uid, context=None, user_id=None):
team_id = self._resolve_team_id_from_context(cr, uid, context=context) or False
if not team_id:
team_ids = self.search(cr, uid, [('member_ids', '=', user_id or uid)], limit=1, context=context)
team_id = team_ids[0] if team_ids else False
return team_id
def _resolve_team_id_from_context(self, cr, uid, context=None):
""" Returns ID of team based on the value of 'default_team_id'
context key, or None if it cannot be resolved to a single
Sales Team.
"""
if context is None: if context is None:
context = {} context = {}
if type(context.get('default_team_id')) in (int, long): if user_id is None:
return context.get('default_team_id') user_id = uid
if isinstance(context.get('default_team_id'), basestring): team_id = context.get('default_team_id')
team_ids = self.name_search(cr, uid, name=context['default_team_id'], context=context) if not team_id:
if len(team_ids) == 1: team_ids = self.search(cr, uid, ['|', ('user_id', '=', user_id), ('member_ids', 'in', user_id)], limit=1, context=context)
return int(team_ids[0][0]) team_id = team_ids[0] if team_ids else False
return None if not team_id:
team_id = self.pool['ir.model.data'].xmlid_to_res_id(cr, uid, 'sales_team.team_sales_department')
return team_id
_columns = { _columns = {
'name': fields.char('Sales Team', size=64, required=True, translate=True), 'name': fields.char('Sales Team', size=64, required=True, translate=True),
@@ -88,12 +88,3 @@ class Lead(models.Model):
if 'user_id' in vals: if 'user_id' in vals:
vals['assign_date'] = vals.get('user_id') and fields.datetime.now() or False vals['assign_date'] = vals.get('user_id') and fields.datetime.now() or False
return super(Lead, self).write(vals) return super(Lead, self).write(vals)
@api.multi
@api.onchange('user_id')
def on_change_user(self, user_id):
ret = super(Lead, self).on_change_user(user_id)
if user_id:
team_ids = self.env['crm.team'].search([('team_user_ids.user_id', '=', user_id)])
ret['value']['team_id'] = team_ids and team_ids[0] or False
return ret
@@ -75,6 +75,15 @@ class team_user(models.Model):
class crm_team(osv.osv): class crm_team(osv.osv):
_inherit = "crm.team" _inherit = "crm.team"
@api.model
def _get_default_team_id(self, user_id=None):
team_id = super(crm_team, self)._get_default_team_id(user_id=user_id)
if user_id is None:
user_id = self.env.user.id
if not team_id:
team_id = self.search([('team_user_ids.user_id', '=', user_id)], limit=1).id
return team_id
@api.one @api.one
def _count_leads(self): def _count_leads(self):
if self.id: if self.id:
+3
View File
@@ -60,6 +60,7 @@ class TestScoring(common.TransactionCase):
'country_id': self.belgium, 'country_id': self.belgium,
'email_from': 'lead0@test.com', 'email_from': 'lead0@test.com',
'user_id': None, 'user_id': None,
'team_id': False,
'stage_id': self.stage, 'stage_id': self.stage,
}) })
@@ -68,12 +69,14 @@ class TestScoring(common.TransactionCase):
'country_id': self.france, 'country_id': self.france,
'email_from': 'lead1@test.com', 'email_from': 'lead1@test.com',
'user_id': None, 'user_id': None,
'team_id': False,
'stage_id': self.stage, 'stage_id': self.stage,
}) })
self.lead2 = self.crm_lead.create(cr, uid, { self.lead2 = self.crm_lead.create(cr, uid, {
'name': 'lead2', 'name': 'lead2',
'email_from': 'lead2@test.com', 'email_from': 'lead2@test.com',
'user_id': None, 'user_id': None,
'team_id': False,
'stage_id': self.stage, 'stage_id': self.stage,
}) })
+2 -2
View File
@@ -81,10 +81,10 @@
<field name="model">crm.lead</field> <field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.crm_case_form_view_oppor" /> <field name="inherit_id" ref="crm.crm_case_form_view_oppor" />
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="user_id" position="after"> <field name="priority" position="after">
<field name="score" /> <field name="score" />
</field> </field>
<xpath expr="//group[@name='references']" position="after"> <xpath expr="//group[@name='Misc']" position="after">
<group string="Scoring"> <group string="Scoring">
<field name="score_ids" <field name="score_ids"
widget="many2many_tags" widget="many2many_tags"