diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py index b790f9bc23300625f4da98c7a1795a640ba7dd2f..4818a507db5ea9c7674a2370fdff752f1d30f252 100644 --- a/addons/crm/__openerp__.py +++ b/addons/crm/__openerp__.py @@ -54,6 +54,7 @@ Dashboard for CRM will include: 'security/crm_security.xml', 'security/ir.model.access.csv', + 'wizard/crm_lead_lost_view.xml', 'wizard/crm_lead_to_opportunity_view.xml', 'wizard/crm_merge_opportunities_view.xml', diff --git a/addons/crm/base_partner_merge_view.xml b/addons/crm/base_partner_merge_view.xml index db580a6835de8279b5ad53845441515e4ca604dd..54632487de782a375ef2caf08b5d2749a6edbab0 100644 --- a/addons/crm/base_partner_merge_view.xml +++ b/addons/crm/base_partner_merge_view.xml @@ -1,22 +1,17 @@ <?xml version="1.0" encoding="UTF-8"?> <openerp> <data> - <!-- the sequence of the configuration sub menu is 30 --> - <record model="ir.actions.act_window" id="base_partner_merge_automatic_act"> - <field name="name">Deduplicate Contacts</field> - <field name="res_model">base.partner.merge.automatic.wizard</field> - <field name="view_type">form</field> - <field name="view_mode">form</field> - <field name="target">new</field> - <field name="context">{'active_test': False}</field> - </record> - - <menuitem id='partner_merge_automatic_menu' - action='base_partner_merge_automatic_act' - groups='base.group_system' - parent='base.menu_config_address_book' - sequence='4'/> + <act_window id="action_partner_deduplicate" + res_model="base.partner.merge.automatic.wizard" + src_model="res.partner" + target="new" + multi="True" + key2="client_action_multi" + view_mode="form" + name="Deduplicate Contact" + context="{'default_state': 'option'}" + /> <record model='ir.ui.view' id='base_partner_merge_automatic_wizard_form'> <field name='name'>base.partner.merge.automatic.wizard.form</field> @@ -26,7 +21,7 @@ <sheet> <group attrs="{'invisible': [('state', '!=', 'finished')]}" col="1"> <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"/> </group> <p class="oe_grey" attrs="{'invisible': [('state', '!=', ('option'))]}"> @@ -118,7 +113,7 @@ </record> <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> diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 5c42e2831e4f5c03b7103d295db912adf790b64b..2cc6f85a5287f5ac5e8ee3993a2ff4327d76d678 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -58,14 +58,6 @@ class crm_lead(format_address, osv.osv): _inherit = ['mail.thread', 'ir.needaction_mixin', 'utm.mixin'] _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): """ Gives default stage_id """ 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 ('team_ids', '=', team_id), ('fold', '=', False) if team_id: add team columns that are not folded 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: search_domain += ['|', ('team_ids', '=', team_id)] search_domain += [('id', 'in', ids)] @@ -183,7 +175,7 @@ class crm_lead(format_address, osv.osv): [('lead', 'Lead'), ('opportunity', 'Opportunity')], string='Type', select=True, required=True, 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), 'stage_id': fields.many2one('crm.stage', 'Stage', track_visibility='onchange', select=True, 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 'probability': fields.float('Probability', group_operator="avg"), '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), 'date_deadline': fields.date('Expected Closing', help="Estimate of the date on which the opportunity will be won."), # 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_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"), - 'date_action': fields.date('Next Action Date', select=True), - 'title_action': fields.char('Next Action Summary'), + 'date_action': fields.date('Next Activity Date', select=True), + 'title_action': fields.char('Next Activity Summary'), 'color': fields.integer('Color Index'), '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 = { '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, '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), @@ -291,11 +281,7 @@ class crm_lead(format_address, osv.osv): 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 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) - 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] + team_id = self.pool['crm.team']._get_default_team_id(cr, uid, context=context, user_id=user_id) return {'value': {'team_id': team_id}} 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['empty_list_help_model'] = 'crm.team' context['empty_list_help_id'] = context.get('default_team_id', None) - context['empty_list_help_document_name'] = _("opportunity") - if context.get('default_type') == 'lead': - context['empty_list_help_document_name'] = _("lead") + context['empty_list_help_document_name'] = _("opportunities") + if help: + 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) # ---------------------------------------- diff --git a/addons/crm/crm_lead_menu.xml b/addons/crm/crm_lead_menu.xml index f6ec9e09b88c343da0632fc366bbb357417b57d9..b7c124b038242f877827167ff1a382184febccbb 100644 --- a/addons/crm/crm_lead_menu.xml +++ b/addons/crm/crm_lead_menu.xml @@ -77,7 +77,36 @@ <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" - 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> </openerp> diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 8f5a77c0a5b91f2284044048eba179a6717cd1cf..8880876962f61a9add07d315171a40518c8f9fc3 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -126,20 +126,24 @@ <sheet> <div class="oe_button_box" name="button_box"/> <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> - <span class="label label-danger" - attrs="{'invisible': ['|', ('probability', '>', 0), ('active', '=', True)]}">Lost</span> - <span class="label label-success" - attrs="{'invisible': [('probability', '<', 100)]}">Won</span> + <label for="tag_ids" class="oe_edit_only"/> + <field name="tag_ids" widget="many2many_tags"/> + <div class="mt4"> + <span class="label label-danger" + attrs="{'invisible': ['|', ('probability', '>', 0), ('active', '=', True)]}">Lost</span> + <span class="label label-success" + attrs="{'invisible': [('probability', '<', 100)]}">Won</span> + </div> </div> <group> <group> - <field name="partner_name" string="Company Name"/> <!-- Preload all the partner's information --> <field name="partner_id" string="Customer" 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"/> <div class="o_address_format"> <field name="street" placeholder="Street..." class="o_address_street"/> @@ -153,7 +157,7 @@ <group> <label for="contact_name"/> <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}'/> </div> <field name="email_from" widget="email"/> @@ -164,15 +168,11 @@ </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'] }"/> - <label for="team_id"/> - <div class="o_row"> - <field name="team_id"/> - </div> + context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_salesman_all_leads'] }" widget="selection"/> + <field name="team_id" widget="selection" domain="[('use_leads','=',True)]"/> <field name="type" invisible="1"/> </group> <group> - <field name="tag_ids" widget="many2many_tags"/> <field name="priority" widget="priority"/> </group> </group> @@ -182,9 +182,9 @@ </page> <page name="extra" string="Extra Info"> <group> - <group string="Mailings"> + <group> <field name="opt_out"/> - <field name="message_bounce" readonly="1"/> + <field name="message_bounce" readonly="1" groups="base.group_no_one"/> </group> <group string="Categorization" groups="base.group_multi_company,base.group_no_one" name="categorization"> <field name="company_id" @@ -194,7 +194,7 @@ <field name="medium_id"/> <field name="source_id" /> </group> - <group name="misc" string="Misc"> + <group name="misc"> <field name="probability" groups="base.group_no_one"/> <field name="active"/> <field name="referred"/> @@ -247,7 +247,7 @@ <tree string="Leads" decoration-bf="message_unread==True" decoration-muted="probability == 100"> <field name="date_deadline" invisible="1"/> <field name="create_date"/> - <field name="name"/> + <field name="name" string="Lead"/> <field name="contact_name"/> <field name="country_id"/> <field name="email_from"/> @@ -324,6 +324,14 @@ <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> </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"> <field name="priority" widget="priority" groups="base.group_user"/> <t t-if="record.message_unread_counter.raw_value"> @@ -410,8 +418,9 @@ <button name="action_set_won" string="Mark Won" type="object" class="oe_highlight" attrs="{'invisible': ['|', ('active','=',False), ('probability', '=', 100)]}"/> - <button name="action_set_lost" string="Mark Lost" - type="object" class="oe_highlight" + <button name="%(crm.crm_lead_lost_action)d" string="Mark Lost" + type="action" class="oe_highlight" + context="{'default_lead_id': active_id}" attrs="{'invisible': [('active', '=', False)]}"/> <button name="action_set_active" string="Unarchive" type="object" class="oe_highlight" @@ -426,27 +435,41 @@ <button class="oe_stat_button" type="object" context="{'partner_id': partner_id}" 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', '<', 2)]}"> Meetings</span> + <span class="o_stat_text" attrs="{'invisible': [('meeting_count', '>', 1)]}"> Meeting</span> + </div> </button> </div> - <div class="oe_title"> + <div> <label for="name" class="oe_edit_only"/> <h1><field name="name" placeholder="e.g. Product Pricing"/></h1> - <span class="label label-danger" - attrs="{'invisible': ['|', ('probability', '>', 0), ('active', '=', True)]}">Lost</span> - <span class="label label-success" - attrs="{'invisible': [('probability', '<', 100)]}">Won</span> + <label for="tag_ids" class="oe_edit_only"/> + <field name="tag_ids" widget="many2many_tags"/> + <div class="mt4"> + <span class="label label-danger" + attrs="{'invisible': ['|', ('probability', '>', 0), ('active', '=', True)]}">Lost</span> + <span class="label label-success" + attrs="{'invisible': [('probability', '<', 100)]}">Won</span> + </div> <div class="o_row"> - <label for="planned_revenue" class="oe_edit_only"/> - <label for="probability" class="oe_edit_only"/> + <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> - <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> <group> <group> @@ -489,19 +512,15 @@ </div> </div> <field name="date_deadline"/> - <field name="priority" widget="priority"/> </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']}"/> - <label for="team_id"/> - <div class="o_row"> - <field name="team_id" widget="selection"/> - </div> + <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"/> + <field name="team_id" widget="selection"/> </group> <group> - <field name="tag_ids" widget="many2many_tags"/> - <field name="lost_reason"/> + <field name="priority" widget="priority"/> + <field name="lost_reason" attrs="{'invisible': [('active', '=', True)]}"/> <field name="date_conversion" invisible="1"/> </group> </group> @@ -510,7 +529,7 @@ <page string="Internal Notes"> <field name="description"/> </page> - <page name="lead" string="Lead"> + <page name="lead" string="Lead" groups="crm.group_use_lead"> <group> <group> <field name="partner_name"/> @@ -534,26 +553,20 @@ <field name="function"/> <field name="mobile"/> <field name="fax"/> + <field name="opt_out"/> </group> <group string="Marketing"> <field name="campaign_id" /> <field name="medium_id" /> <field name="source_id" /> </group> - <group string="Mailings" name="mailings"> - <field name="opt_out"/> - </group> - <group string="Misc"> + <group string="Misc" name="Misc"> <field name="active"/> <field name="day_open" groups="base.group_no_one"/> <field name="day_close" groups="base.group_no_one"/> <field name="referred"/> <field name="type" invisible="1"/> </group> - <group name="references" string="References"> - <field name="ref"/> - <field name="ref2"/> - </group> </group> </page> </notebook> @@ -608,6 +621,8 @@ <field name="user_id"/> <field name="partner_id" operator="child_of" string="Customer"/> <field name="stage_id" domain="[]"/> + <field name="next_activity_id"/> + <field name="title_action"/> <field name="probability"/> <field name="lost_reason"/> <separator/> @@ -648,6 +663,20 @@ </field> </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 < 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 --> diff --git a/addons/crm/crm_tip_data.xml b/addons/crm/crm_tip_data.xml index d07a03f447c7cf729d1840547f451197debee226..c2df524d710de72ae16d0a8a94a84092f5d1d5e4 100644 --- a/addons/crm/crm_tip_data.xml +++ b/addons/crm/crm_tip_data.xml @@ -34,5 +34,27 @@ <field name="placement">auto top</field> </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> </openerp> diff --git a/addons/crm/report/crm_activity_report.py b/addons/crm/report/crm_activity_report.py index 3f29a01f4da2dbb523e6119ad200bf7655255988..5bf4445fc7207c55e8ef76d863513a868a579b48 100644 --- a/addons/crm/report/crm_activity_report.py +++ b/addons/crm/report/crm_activity_report.py @@ -16,7 +16,7 @@ class crm_activity_report(models.Model): author_id = fields.Many2one('res.partner', 'Author', readonly=True) user_id = fields.Many2one('res.users', 'Responsible', 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) company_id = fields.Many2one('res.company', 'Company', readonly=True) stage_id = fields.Many2one('crm.stage', 'Stage', readonly=True) @@ -48,6 +48,10 @@ class crm_activity_report(models.Model): "crm_lead" l on (m.res_id = l.id) + inner join + "crm_activity" a + on + (m.subtype_id = a.subtype_id) WHERE (m.model = 'crm.lead') )""") diff --git a/addons/crm/report/crm_opportunity_report_view.xml b/addons/crm/report/crm_opportunity_report_view.xml index 2c97a41a95bc2d2be18f3e7c30c7faa77ee6ede1..ac2a51fde459d56d4cf1e3d1e4b4e30acd835612 100644 --- a/addons/crm/report/crm_opportunity_report_view.xml +++ b/addons/crm/report/crm_opportunity_report_view.xml @@ -179,7 +179,7 @@ teams of the sales pipeline.</field> (including those converted into opportunities).</field> </record> <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> </openerp> diff --git a/addons/crm/res_config.py b/addons/crm/res_config.py index 160f6982bc8432a7323f20bfa2fc5797468a49aa..bed7d5437a69cb62b525593a8b9c7f107fe6f088 100644 --- a/addons/crm/res_config.py +++ b/addons/crm/res_config.py @@ -15,6 +15,9 @@ class crm_configuration(osv.TransientModel): help="Odoo will generate an email alias based on the sales team name"), 'alias_prefix': fields.char('Default Alias Name for Leads'), '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 = { diff --git a/addons/crm/res_config_view.xml b/addons/crm/res_config_view.xml index 723318dcd68c6569326adcc3cc1be787c6cc6dfd..ab879f47d229c9a17231761f318fb666f3b1b9d1 100644 --- a/addons/crm/res_config_view.xml +++ b/addons/crm/res_config_view.xml @@ -32,6 +32,13 @@ </div> </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> </data> </field> diff --git a/addons/crm/sales_team.py b/addons/crm/sales_team.py index e33edaa2ec60448f3a87dc84105aa90c88d4c8e1..ef273eb57bff6fc3569f7c0233ced02cda4f6f0e 100644 --- a/addons/crm/sales_team.py +++ b/addons/crm/sales_team.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from openerp.osv import fields, osv +from openerp.tools.safe_eval import safe_eval as eval from openerp.tools.translate import _ @@ -46,10 +47,25 @@ class crm_team(osv.Model): 'use_opportunities': True, } - def onchange_use_leads(self, cr, uid, ids, use_leads, context=None): - if not use_leads: - return {'value': {'alias_name': False}} - return {'value': {}} + def onchange_use_leads_opportunities(self, cr, uid, ids, use_leads, use_opportunities, context=None): + if use_leads or use_opportunities: + 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): if context is None: @@ -59,10 +75,19 @@ class crm_team(osv.Model): if generate_alias_name and not vals.get('alias_name'): vals['alias_name'] = vals.get('name') team_id = super(crm_team, self).create(cr, uid, vals, context=create_context) - team = self.browse(cr, uid, team_id, context=context) - 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.write(cr, uid, [team_id], + self._get_alias_defaults_values(cr, uid, [team_id], context=context)[team_id], + context=context) 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): # Cascade-delete mail aliases as well, as they should not exist without the sales team. mail_alias = self.pool.get('mail.alias') diff --git a/addons/crm/sales_team_dashboard.xml b/addons/crm/sales_team_dashboard.xml index c75be5ae6586c3cefebd71e6cf867b90b142ed81..bef7416a3f88ea32215d30422b747afca8ccb98e 100644 --- a/addons/crm/sales_team_dashboard.xml +++ b/addons/crm/sales_team_dashboard.xml @@ -42,7 +42,7 @@ </xpath> <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"> Leads </a> @@ -63,7 +63,7 @@ </xpath> <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"> Leads </a> diff --git a/addons/crm/sales_team_view.xml b/addons/crm/sales_team_view.xml index 2cbf338e3287000402ef70b9b7ad171b883c2d03..5f09103dd2814a9cbfcfb46b49e880b6f21480a6 100644 --- a/addons/crm/sales_team_view.xml +++ b/addons/crm/sales_team_view.xml @@ -87,9 +87,11 @@ <field name="inherit_id" ref="sales_team.crm_team_view_form"/> <field name="arch" type="xml"> <xpath expr="//div[@name='options_active']" position="inside"> - <field name="use_leads" on_change="onchange_use_leads(use_leads)"/> - <label for="use_leads" string="Leads"/> - <field name="use_opportunities" class="oe_inline"/> + <div groups="crm.group_use_lead"> + <field name="use_leads" on_change="onchange_use_leads_opportunities(use_leads, use_opportunities)"/> + <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"/> </xpath> <xpath expr="//page[@name='members']" position="after"> @@ -101,10 +103,10 @@ <xpath expr="//field[@name='code']" position="after"> <label for="alias_name" string="Email Alias" groups="base.group_no_one" - attrs="{'invisible': [('use_leads', '=', False)]}"/> + attrs="{'invisible': [('use_leads', '=', False), ('use_opportunities', '=', False)]}"/> <div name="alias_def" 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" string="Email Alias" required="0"/> <div class="oe_edit_only oe_inline" name="edit_alias" style="display: inline;" > @@ -113,7 +115,7 @@ </div> <field name="alias_contact" class="oe_inline" string="Accept Emails From" - attrs="{'invisible': [('use_leads', '=', False)]}"/> + attrs="{'invisible': [('use_leads', '=', False), ('use_opportunities', '=', False)]}"/> </xpath> </field> </record> diff --git a/addons/crm/security/crm_security.xml b/addons/crm/security/crm_security.xml index ef398b6b35a3aa78d14f2ce3115df3fd5aaa6310..765568140e26c7f12570a8da349bad9f5d575796 100644 --- a/addons/crm/security/crm_security.xml +++ b/addons/crm/security/crm_security.xml @@ -24,6 +24,11 @@ <field name="users" eval="[(4, ref('base.user_root'))]"/> </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"> <field eval="[(4,ref('base.group_partner_manager'))]" name="groups_id"/> </record> diff --git a/addons/crm/wizard/__init__.py b/addons/crm/wizard/__init__.py index 99dd897bb8b0808892fe322cbc88796ccb0edee9..e4c563df3a1e687e0fdad393918623caf3988689 100644 --- a/addons/crm/wizard/__init__.py +++ b/addons/crm/wizard/__init__.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +import crm_lead_lost import crm_partner_binding import crm_lead_to_opportunity import crm_merge_opportunities diff --git a/addons/crm/wizard/crm_lead_lost.py b/addons/crm/wizard/crm_lead_lost.py new file mode 100644 index 0000000000000000000000000000000000000000..98b85771722e22d93a3f155dc0d2bc48220e12cf --- /dev/null +++ b/addons/crm/wizard/crm_lead_lost.py @@ -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 diff --git a/addons/crm/wizard/crm_lead_lost_view.xml b/addons/crm/wizard/crm_lead_lost_view.xml new file mode 100644 index 0000000000000000000000000000000000000000..9acd3bd80bbc3c9bbba474bec1031ebcb9509fc2 --- /dev/null +++ b/addons/crm/wizard/crm_lead_lost_view.xml @@ -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> diff --git a/addons/crm/wizard/crm_lead_to_opportunity_view.xml b/addons/crm/wizard/crm_lead_to_opportunity_view.xml index a67542a2ce27a01ef01048c53ef34ad2df903bf9..903c63dd8a45f5cdf20e39d0058e8a5b54b68f35 100644 --- a/addons/crm/wizard/crm_lead_to_opportunity_view.xml +++ b/addons/crm/wizard/crm_lead_to_opportunity_view.xml @@ -11,8 +11,8 @@ <field name="name" widget="radio"/> </group> <group string="Assign these opportunities to"> - <field name="user_id" on_change="on_change_user(user_id, team_id, context)"/> - <field name="team_id"/> + <field name="user_id" on_change="on_change_user(user_id, team_id, context)" widget="selection"/> + <field name="team_id" widget="selection"/> </group> <group string="Opportunities" attrs="{'invisible': [('name', '!=', 'merge')]}"> <field name="opportunity_ids" nolabel="1"> diff --git a/addons/sale_crm/sale_crm_view.xml b/addons/sale_crm/sale_crm_view.xml index cb8d14a708c9969a2a1cace8a6f7f0dc20e8241f..e6814970fdee8bef8fdeb3eb1bef77c2c41187f3 100644 --- a/addons/sale_crm/sale_crm_view.xml +++ b/addons/sale_crm/sale_crm_view.xml @@ -33,7 +33,10 @@ <button class="oe_stat_button" type="action" name="%(sale_action_quotations)d" icon="fa-usd" 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 class="oe_stat_button" type="action" attrs="{'invisible': [('sale_amount_total', '=', 0)]}" name="%(sale_action_quotations)d" icon="fa-usd" diff --git a/addons/sales_team/sales_team.py b/addons/sales_team/sales_team.py index 4defeb4868d0e6ed6de1bba670da7014e323ae53..8c871b88bbc1a3f52a22117855b2aa5770c06ade 100644 --- a/addons/sales_team/sales_team.py +++ b/addons/sales_team/sales_team.py @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- # 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 @@ -16,28 +14,19 @@ class crm_team(osv.Model): def get_full_name(self, cr, uid, ids, field_name, arg, context=None): return dict(self.name_get(cr, uid, ids, context=context)) - def _get_default_team_id(self, cr, uid, user_id=None, context=None): - team_id = self._resolve_team_id_from_context(cr, uid, context=context) or False + def _get_default_team_id(self, cr, uid, context=None, user_id=None): + if context is None: + context = {} + if user_id is None: + user_id = uid + team_id = context.get('default_team_id') if not team_id: - team_ids = self.search(cr, uid, [('member_ids', '=', user_id or uid)], limit=1, context=context) + team_ids = self.search(cr, uid, ['|', ('user_id', '=', user_id), ('member_ids', 'in', user_id)], limit=1, context=context) team_id = team_ids[0] if team_ids else False + 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 - 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: - context = {} - if type(context.get('default_team_id')) in (int, long): - return context.get('default_team_id') - if isinstance(context.get('default_team_id'), basestring): - team_ids = self.name_search(cr, uid, name=context['default_team_id'], context=context) - if len(team_ids) == 1: - return int(team_ids[0][0]) - return None - _columns = { 'name': fields.char('Sales Team', size=64, required=True, translate=True), 'complete_name': fields.function(get_full_name, type='char', size=256, readonly=True, store=True, string="Full Name"), diff --git a/addons/website_crm_score/models/crm_lead.py b/addons/website_crm_score/models/crm_lead.py index f17ddd689a5dd3d08d196557dcf82d83197a900f..47e84cb824880c8ae3ca0dfd02933ef41caf70bc 100644 --- a/addons/website_crm_score/models/crm_lead.py +++ b/addons/website_crm_score/models/crm_lead.py @@ -88,12 +88,3 @@ class Lead(models.Model): if 'user_id' in vals: vals['assign_date'] = vals.get('user_id') and fields.datetime.now() or False 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 diff --git a/addons/website_crm_score/models/sales_team.py b/addons/website_crm_score/models/sales_team.py index 528f5864237b5a7a598eac2dadbec30fb732c96f..432ccc14ac310f203efc0a8f4cd11089d69c7cb2 100644 --- a/addons/website_crm_score/models/sales_team.py +++ b/addons/website_crm_score/models/sales_team.py @@ -75,6 +75,15 @@ class team_user(models.Model): class crm_team(osv.osv): _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 def _count_leads(self): if self.id: diff --git a/addons/website_crm_score/tests/common.py b/addons/website_crm_score/tests/common.py index 5bdfecc95e64fd5dc6e427cf7877f9d58548c413..b76cf666dc47c6ccf2b2a627f6fbeb97f206159b 100644 --- a/addons/website_crm_score/tests/common.py +++ b/addons/website_crm_score/tests/common.py @@ -60,6 +60,7 @@ class TestScoring(common.TransactionCase): 'country_id': self.belgium, 'email_from': 'lead0@test.com', 'user_id': None, + 'team_id': False, 'stage_id': self.stage, }) @@ -68,12 +69,14 @@ class TestScoring(common.TransactionCase): 'country_id': self.france, 'email_from': 'lead1@test.com', 'user_id': None, + 'team_id': False, 'stage_id': self.stage, }) self.lead2 = self.crm_lead.create(cr, uid, { 'name': 'lead2', 'email_from': 'lead2@test.com', 'user_id': None, + 'team_id': False, 'stage_id': self.stage, }) diff --git a/addons/website_crm_score/views/sales.xml b/addons/website_crm_score/views/sales.xml index b4518eec4efb76c62e5db9caca032d7bfe340a36..d2fffcb8048115bf5096076237bd0fdf560f92b8 100644 --- a/addons/website_crm_score/views/sales.xml +++ b/addons/website_crm_score/views/sales.xml @@ -81,10 +81,10 @@ <field name="model">crm.lead</field> <field name="inherit_id" ref="crm.crm_case_form_view_oppor" /> <field name="arch" type="xml"> - <field name="user_id" position="after"> + <field name="priority" position="after"> <field name="score" /> </field> - <xpath expr="//group[@name='references']" position="after"> + <xpath expr="//group[@name='Misc']" position="after"> <group string="Scoring"> <field name="score_ids" widget="many2many_tags"