diff --git a/addons/crm/__init__.py b/addons/crm/__init__.py index a630240552b..41d2950249f 100644 --- a/addons/crm/__init__.py +++ b/addons/crm/__init__.py @@ -4,8 +4,8 @@ import crm import crm_lead import sales_team +import models import calendar_event -import crm_phonecall import report import wizard import res_partner diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py index 15763b33171..1fd0fd332eb 100644 --- a/addons/crm/__openerp__.py +++ b/addons/crm/__openerp__.py @@ -7,12 +7,12 @@ 'version': '1.0', 'category': 'Customer Relationship Management', 'sequence': 2, - 'summary': 'Leads, Opportunities, Phone Calls', + 'summary': 'Leads, Opportunities, Activities', 'description': """ The generic OpenERP Customer Relationship Management ==================================================== -This application enables a group of people to intelligently and efficiently manage leads, opportunities, meetings and phone calls. +This application enables a group of people to intelligently and efficiently manage leads, opportunities, meetings and activities. It manages key tasks such as communication, identification, prioritization, assignment, resolution and notification. @@ -44,33 +44,27 @@ Dashboard for CRM will include: 'web_planner', ], 'data': [ + 'data/crm_action_data.xml', 'crm_data.xml', 'crm_lead_data.xml', - 'crm_phonecall_data.xml', 'crm_tip_data.xml', 'security/crm_security.xml', 'security/ir.model.access.csv', 'wizard/crm_lead_to_opportunity_view.xml', - - 'wizard/crm_phonecall_to_phonecall_view.xml', - 'wizard/crm_merge_opportunities_view.xml', 'crm_view.xml', - - 'crm_phonecall_view.xml', - 'crm_phonecall_menu.xml', - 'crm_lead_view.xml', 'crm_lead_menu.xml', + 'views/crm_action_views.xml', 'calendar_event_menu.xml', 'report/crm_lead_report_view.xml', + 'report/crm_activity_report_view.xml', 'report/crm_opportunity_report_view.xml', - 'report/crm_phonecall_report_view.xml', 'res_partner_view.xml', @@ -85,7 +79,7 @@ Dashboard for CRM will include: 'demo': [ 'crm_demo.xml', 'crm_lead_demo.xml', - 'crm_phonecall_demo.xml', + 'data/crm_action_demo.xml', 'crm_action_rule_demo.xml', ], 'test': [ @@ -95,7 +89,6 @@ Dashboard for CRM will include: 'test/lead2opportunity_assign_salesmen.yml', 'test/crm_lead_merge.yml', 'test/crm_lead_cancel.yml', - 'test/phonecalls.yml', 'test/crm_lead_onchange.yml', 'test/crm_lead_copy.yml', 'test/crm_lead_unlink.yml', diff --git a/addons/crm/calendar_event.py b/addons/crm/calendar_event.py index 3fac82acd30..996f6681a51 100644 --- a/addons/crm/calendar_event.py +++ b/addons/crm/calendar_event.py @@ -10,7 +10,6 @@ class calendar_event(osv.Model): """ Model for Calendar Event """ _inherit = 'calendar.event' _columns = { - 'phonecall_id': fields.many2one('crm.phonecall', 'Phonecall'), 'opportunity_id': fields.many2one('crm.lead', 'Opportunity', domain="[('type', '=', 'opportunity')]"), } diff --git a/addons/crm/crm_data.xml b/addons/crm/crm_data.xml index 13bdc502834..98df2167462 100644 --- a/addons/crm/crm_data.xml +++ b/addons/crm/crm_data.xml @@ -33,7 +33,7 @@ notification CRM application installed! - From the top Sales menu you can track leads and opportunities, get accurate forecast on your sales pipeline, plan meetings and phonecalls, get realtime statistics and efficiently organize the communication with your prospects.

+ From the top Sales menu you can track leads and opportunities, get accurate forecast on your sales pipeline, plan meetings, get realtime statistics and efficiently organize the communication with your prospects.

To manage quotations and sale orders, install the "Sales Management" application.

]]>
diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index f2e5fc00ccd..cf471442247 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import crm -from datetime import datetime +from datetime import datetime, timedelta +import logging from operator import itemgetter +import crm import openerp from openerp import SUPERUSER_ID from openerp import tools, api @@ -14,6 +15,7 @@ from openerp.tools.translate import _ from openerp.tools import email_re, email_split from openerp.exceptions import UserError, AccessError +_logger = logging.getLogger(__name__) CRM_LEAD_FIELDS_TO_MERGE = ['name', 'partner_id', @@ -155,14 +157,6 @@ class crm_lead(format_address, osv.osv): for opp_id in ids } - def _calls_count(self, cr, uid, ids, field_name, args, context=None): - res = dict.fromkeys(ids, 0) - phonecall_data = self.pool['crm.phonecall'].read_group( - cr, uid, [('opportunity_id', 'in', ids)], ['opportunity_id'], ['opportunity_id'], context=context) - for data in phonecall_data: - res[data['opportunity_id'][0]] = data['opportunity_id_count'] - return res - _columns = { 'partner_id': fields.many2one('res.partner', 'Partner', ondelete='set null', track_visibility='onchange', select=True, help="Linked partner (optional). Usually created when converting the lead."), @@ -211,8 +205,14 @@ class crm_lead(format_address, osv.osv): '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 + 'next_activity_id': fields.many2one("crm.activity", "Next Activity", select=True), + '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'), + 'title_action': fields.char('Next Action Summary'), + 'color': fields.integer('Color Index'), 'partner_address_name': fields.related('partner_id', 'name', type='char', string='Partner Contact Name', readonly=True), 'partner_address_email': fields.related('partner_id', 'email', type='char', string='Partner Contact Email', readonly=True), @@ -236,7 +236,6 @@ class crm_lead(format_address, osv.osv): 'planned_cost': fields.float('Planned Costs'), 'meeting_count': fields.function(_meeting_count, string='# Meetings', type='integer'), 'lost_reason': fields.many2one('crm.lost.reason', 'Lost Reason', select=True, track_visibility='onchange'), - 'calls_count': fields.function(_calls_count, string='# Phonecalls', type='integer'), } _defaults = { @@ -381,6 +380,73 @@ class crm_lead(format_address, osv.osv): self.write(cr, uid, lead_ids, {'stage_id': stage_id}, context=context) return True + def log_next_activity_1(self, cr, uid, ids, context=None): + return self.log_next_activity_done(cr, uid, ids, next_activity_name='activity_1_id', context=context) + + def log_next_activity_2(self, cr, uid, ids, context=None): + return self.log_next_activity_done(cr, uid, ids, next_activity_name='activity_2_id', context=context) + + def log_next_activity_3(self, cr, uid, ids, context=None): + return self.log_next_activity_done(cr, uid, ids, next_activity_name='activity_3_id', context=context) + + def log_next_activity_done(self, cr, uid, ids, context=None, next_activity_name=False): + to_clear_ids = [] + for lead in self.browse(cr, uid, ids, context=context): + if not lead.next_activity_id: + continue + body_html = """
${object.next_activity_id.name}
+%if object.title_action: +
${object.title_action}
+%endif""" + body_html = self.pool['mail.template'].render_template(cr, uid, body_html, 'crm.lead', lead.id, context=context) + msg_id = lead.message_post(body_html, subtype_id=lead.next_activity_id.subtype_id.id) + # update subtype after posting NOT SURE + + next_activity = next_activity_name and getattr(lead.next_activity_id, next_activity_name, False) or False + if next_activity: + date_action = False + if next_activity.days: + date_action = (datetime.now() + timedelta(days=next_activity.days)).strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT), + lead.write({ + 'next_activity_id': next_activity.id, + 'date_action': date_action, + 'title_action': next_activity.description, + }) + else: + to_clear_ids.append(lead.id) + + if to_clear_ids: + self.cancel_next_activity(cr, uid, to_clear_ids, context=context) + return True + + def cancel_next_activity(self, cr, uid, ids, context=None): + return self.write(cr, uid, ids, { + 'next_activity_id': False, + 'date_action': False, + 'title_action': False, + }, context=context) + + def onchange_next_activity_id(self, cr, uid, ids, next_activity_id, context=None): + if not next_activity_id: + return {'value': { + 'next_action1': False, + 'next_action2': False, + 'next_action3': False, + 'title_action': False, + 'date_action': False, + }} + activity = self.pool['crm.activity'].browse(cr, uid, next_activity_id, context=context) + date_action = False + if activity.days: + date_action = (datetime.now() + timedelta(days=activity.days)).strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT) + return {'value': { + 'next_activity_1': activity.activity_1_id and activity.activity_1_id.name or False, + 'next_activity_2': activity.activity_2_id and activity.activity_2_id.name or False, + 'next_activity_3': activity.activity_3_id and activity.activity_3_id.name or False, + 'title_action': activity.description, + 'date_action': date_action, + }} + 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 @@ -534,13 +600,6 @@ class crm_lead(format_address, osv.osv): attachment.write(values) return True - def _merge_opportunity_phonecalls(self, cr, uid, opportunity_id, opportunities, context=None): - phonecall_obj = self.pool['crm.phonecall'] - for opportunity in opportunities: - for phonecall_id in phonecall_obj.search(cr, uid, [('opportunity_id', '=', opportunity.id)], context=context): - phonecall_obj.write(cr, uid, phonecall_id, {'opportunity_id': opportunity_id}, context=context) - return True - def get_duplicated_leads(self, cr, uid, ids, partner_id, include_lost=False, context=None): """ Search for opportunities that have the same partner and that arent done or cancelled @@ -571,7 +630,6 @@ class crm_lead(format_address, osv.osv): self._merge_notify(cr, uid, highest, opportunities, context=context) self._merge_opportunity_history(cr, uid, highest, opportunities, context=context) self._merge_opportunity_attachments(cr, uid, highest, opportunities, context=context) - self._merge_opportunity_phonecalls(cr, uid, highest, opportunities, context=context) def merge_opportunity(self, cr, uid, ids, user_id=False, team_id=False, context=None): """ @@ -647,7 +705,6 @@ class crm_lead(format_address, osv.osv): 'name': lead.name, 'partner_id': customer and customer.id or False, 'type': 'opportunity', - '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, @@ -726,7 +783,6 @@ class crm_lead(format_address, osv.osv): :param int partner_id: partner to assign if any :return dict: dictionary organized as followed: {lead_id: partner_assigned_id} """ - #TODO this is a duplication of the handle_partner_assignation method of crm_phonecall partner_ids = {} 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 @@ -767,45 +823,6 @@ class crm_lead(format_address, osv.osv): self.write(cr, uid, [lead_id], value, context=context) return True - def schedule_phonecall(self, cr, uid, ids, schedule_time, call_summary, desc, phone, contact_name, user_id=False, team_id=False, categ_id=False, action='schedule', context=None): - """ - :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') - phonecall_dict = {} - if not categ_id: - try: - res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2') - categ_id = model_data.browse(cr, uid, res_id, context=context).res_id - except ValueError: - pass - for lead in self.browse(cr, uid, ids, context=context): - if not team_id: - team_id = lead.team_id and lead.team_id.id or False - 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, - 'team_id': team_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.write(cr, uid, [new_id], {'state': 'open'}, context=context) - if action == 'log': - phonecall.write(cr, uid, [new_id], {'state': 'done'}, context=context) - phonecall_dict[lead.id] = new_id - self.schedule_phonecall_send_note(cr, uid, [lead.id], new_id, action, context=context) - return phonecall_dict - def redirect_opportunity_view(self, cr, uid, opportunity_id, context=None): models_data = self.pool.get('ir.model.data') @@ -1006,22 +1023,6 @@ class crm_lead(format_address, osv.osv): return super(crm_lead, self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context) - # ---------------------------------------- - # OpenChatter methods and notifications - # ---------------------------------------- - - def schedule_phonecall_send_note(self, cr, uid, ids, phonecall_id, action, context=None): - phonecall = self.pool.get('crm.phonecall').browse(cr, uid, [phonecall_id], context=context)[0] - if action == 'log': - message = _('Logged a call for %(date)s. %(description)s') - else: - message = _('Scheduled a call for %(date)s. %(description)s') - phonecall_date = datetime.strptime(phonecall.date, tools.DEFAULT_SERVER_DATETIME_FORMAT) - phonecall_usertime = fields.datetime.context_timestamp(cr, uid, phonecall_date, context=context).strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT) - html_time = "" % (phonecall.date, phonecall_usertime) - message = message % dict(date=html_time, description=phonecall.description) - return self.message_post(cr, uid, ids, body=message, context=context) - def log_meeting(self, cr, uid, ids, meeting_subject, meeting_date, duration, context=None): if not duration: duration = _('unknown') diff --git a/addons/crm/crm_lead_demo.xml b/addons/crm/crm_lead_demo.xml index 34d1cbab637..3904637a905 100644 --- a/addons/crm/crm_lead_demo.xml +++ b/addons/crm/crm_lead_demo.xml @@ -319,6 +319,7 @@ Andrew
1 + Meeting for pricing information. @@ -345,6 +346,7 @@ Andrew 2 + Send Catalogue by Email @@ -368,6 +370,7 @@ Andrew 1 + Call to ask system requirement @@ -396,6 +399,7 @@ Andrew 2 + Convert to quote @@ -421,6 +425,7 @@ Andrew 1 + Send price list regarding our interventions @@ -450,6 +455,7 @@ Andrew 1 + Call to define real needs about training @@ -478,6 +484,7 @@ Andrew 1 + Ask for the good receprion of the proposition @@ -498,6 +505,7 @@ Andrew 2 + @@ -512,6 +520,7 @@ Andrew 2 + @@ -531,6 +540,7 @@ Andrew 1 + @@ -547,6 +557,7 @@ Andrew 0 + @@ -567,6 +578,7 @@ Andrew 0 + @@ -591,6 +603,7 @@ Andrew B7313 2 + Conf call with technical service @@ -614,6 +627,7 @@ Andrew 2 + Send Catalogue by Email @@ -640,6 +654,7 @@ Andrew 2 + Send Catalogue by Email @@ -703,7 +718,7 @@ Andrew crm.lead Hello

-

After our phonecall and discussion with our technical experts, here is the offer of YourCompany. We believe it will meet every requirement you had in mind. Please feel free to contact me for any detail or technical detail that is not clear enough for you.

+

After our discussion with our technical experts, here is the offer of YourCompany. We believe it will meet every requirement you had in mind. Please feel free to contact me for any detail or technical detail that is not clear enough for you.

Notice that as agreed on phone, we offer you a 10% discount on the hardware!

Best regards,

]]>
email diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 88370b0965f..d5977bacbdf 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -88,15 +88,6 @@ - - -
- -
+
diff --git a/addons/crm/crm_phonecall.py b/addons/crm/crm_phonecall.py deleted file mode 100644 index 9e53655f646..00000000000 --- a/addons/crm/crm_phonecall.py +++ /dev/null @@ -1,292 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -import crm -from datetime import datetime -from openerp.osv import fields, osv -from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT -from openerp.tools.translate import _ -from openerp.exceptions import UserError - -class crm_phonecall(osv.osv): - """ Model for CRM phonecalls """ - _name = "crm.phonecall" - _description = "Phonecall" - _order = "id desc" - _inherit = ['mail.thread'] - - _columns = { - 'date_action_last': fields.datetime('Last Action', readonly=1), - 'date_action_next': fields.datetime('Next Action', readonly=1), - 'create_date': fields.datetime('Creation Date' , readonly=True), - 'team_id': fields.many2one('crm.team', 'Sales Team', oldname='section_id',\ - select=True, help='Sales team to which Case belongs to.'), - 'user_id': fields.many2one('res.users', 'Responsible'), - 'partner_id': fields.many2one('res.partner', 'Contact'), - 'company_id': fields.many2one('res.company', 'Company'), - 'description': fields.text('Description'), - 'state': fields.selection( - [('open', 'Confirmed'), - ('cancel', 'Cancelled'), - ('pending', 'Pending'), - ('done', 'Held') - ], string='Status', readonly=True, track_visibility='onchange', - help='The status is set to Confirmed, when a case is created.\n' - 'When the call is over, the status is set to Held.\n' - 'If the callis not applicable anymore, the status can be set to Cancelled.'), - 'email_from': fields.char('Email', size=128, help="These people will receive email."), - 'date_open': fields.datetime('Opened', readonly=True), - # phonecall fields - 'name': fields.char('Call Summary', required=True), - 'active': fields.boolean('Active', required=False), - 'duration': fields.float('Duration', help='Duration in minutes and seconds.'), - 'categ_id': fields.many2one('crm.phonecall.category', 'Category'), - 'partner_phone': fields.char('Phone'), - 'partner_mobile': fields.char('Mobile'), - 'priority': fields.selection([('0','Low'), ('1','Normal'), ('2','High')], 'Priority'), - 'date_closed': fields.datetime('Closed', readonly=True), - 'date': fields.datetime('Date'), - 'opportunity_id': fields.many2one ('crm.lead', 'Lead/Opportunity'), - } - - def _get_default_state(self, cr, uid, context=None): - if context and context.get('default_state'): - return context.get('default_state') - return 'open' - - _defaults = { - 'date': fields.datetime.now, - 'priority': '1', - 'state': _get_default_state, - 'user_id': lambda self, cr, uid, ctx: uid, - 'team_id': lambda s, cr, uid, c: s.pool['crm.team']._get_default_team_id(cr, uid, context=c), - 'active': 1 - } - - def on_change_partner_id(self, cr, uid, ids, partner_id, context=None): - values = {} - if partner_id: - partner = self.pool.get('res.partner').browse(cr, uid, partner_id, context=context) - values = { - 'partner_phone': partner.phone, - 'partner_mobile': partner.mobile, - } - return {'value': values} - - def write(self, cr, uid, ids, values, context=None): - """ Override to add case management: open/close dates """ - if values.get('state'): - if values.get('state') == 'done': - values['date_closed'] = fields.datetime.now() - self.compute_duration(cr, uid, ids, context=context) - elif values.get('state') == 'open': - values['date_open'] = fields.datetime.now() - values['duration'] = 0.0 - return super(crm_phonecall, self).write(cr, uid, ids, values, context=context) - - def compute_duration(self, cr, uid, ids, context=None): - for phonecall in self.browse(cr, uid, ids, context=context): - if phonecall.duration <= 0: - duration = datetime.now() - datetime.strptime(phonecall.date, DEFAULT_SERVER_DATETIME_FORMAT) - values = {'duration': duration.seconds/float(60)} - self.write(cr, uid, [phonecall.id], values, context=context) - return True - - def schedule_another_phonecall(self, cr, uid, ids, schedule_time, call_summary, \ - user_id=False, team_id=False, categ_id=False, action='schedule', context=None): - """ - action :('schedule','Schedule a call'), ('log','Log a call') - """ - model_data = self.pool.get('ir.model.data') - phonecall_dict = {} - if not categ_id: - try: - res_id = model_data._get_id(cr, uid, 'crm', 'categ_phone2') - categ_id = model_data.browse(cr, uid, res_id, context=context).res_id - except ValueError: - pass - for call in self.browse(cr, uid, ids, context=context): - if not team_id: - team_id = call.team_id and call.team_id.id or False - if not user_id: - user_id = call.user_id and call.user_id.id or False - if not schedule_time: - schedule_time = call.date - vals = { - 'name' : call_summary, - 'user_id' : user_id or False, - 'categ_id' : categ_id or False, - 'description' : call.description or False, - 'date' : schedule_time, - 'team_id' : team_id or False, - 'partner_id': call.partner_id and call.partner_id.id or False, - 'partner_phone' : call.partner_phone, - 'partner_mobile' : call.partner_mobile, - 'priority': call.priority, - 'opportunity_id': call.opportunity_id and call.opportunity_id.id or False, - } - new_id = self.create(cr, uid, vals, context=context) - if action == 'log': - self.write(cr, uid, [new_id], {'state': 'done'}, context=context) - phonecall_dict[call.id] = new_id - return phonecall_dict - - def _call_create_partner(self, cr, uid, phonecall, context=None): - partner = self.pool.get('res.partner') - partner_id = partner.create(cr, uid, { - 'name': phonecall.name, - 'user_id': phonecall.user_id.id, - 'comment': phonecall.description, - 'address': [] - }) - return partner_id - - def on_change_opportunity(self, cr, uid, ids, opportunity_id, context=None): - values = {} - if opportunity_id: - opportunity = self.pool.get('crm.lead').browse(cr, uid, opportunity_id, context=context) - values = { - 'team_id' : opportunity.team_id and opportunity.team_id.id or False, - 'partner_phone' : opportunity.phone, - 'partner_mobile' : opportunity.mobile, - 'partner_id' : opportunity.partner_id and opportunity.partner_id.id or False, - } - return {'value' : values} - - def _call_set_partner(self, cr, uid, ids, partner_id, context=None): - write_res = self.write(cr, uid, ids, {'partner_id' : partner_id}, context=context) - self._call_set_partner_send_note(cr, uid, ids, context) - return write_res - - def _call_create_partner_address(self, cr, uid, phonecall, partner_id, context=None): - address = self.pool.get('res.partner') - return address.create(cr, uid, { - 'parent_id': partner_id, - 'name': phonecall.name, - 'phone': phonecall.partner_phone, - }) - - def handle_partner_assignation(self, cr, uid, ids, action='create', partner_id=False, context=None): - """ - 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} - """ - #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) - self._call_set_partner(cr, uid, [call.id], partner_id, context=context) - partner_ids[call.id] = partner_id - return partner_ids - - - def redirect_phonecall_view(self, cr, uid, phonecall_id, context=None): - model_data = self.pool.get('ir.model.data') - # Select the view - tree_view = model_data.get_object_reference(cr, uid, 'crm', 'crm_case_phone_tree_view') - form_view = model_data.get_object_reference(cr, uid, 'crm', 'crm_case_phone_form_view') - search_view = model_data.get_object_reference(cr, uid, 'crm', 'view_crm_case_phonecalls_filter') - value = { - 'name': _('Phone Call'), - 'view_type': 'form', - 'view_mode': 'tree,form', - 'res_model': 'crm.phonecall', - 'res_id' : int(phonecall_id), - 'views': [(form_view and form_view[1] or False, 'form'), (tree_view and tree_view[1] or False, 'tree'), (False, 'calendar')], - 'type': 'ir.actions.act_window', - 'search_view_id': search_view and search_view[1] or False, - } - 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') - opportunity_dict = {} - default_contact = False - for call in self.browse(cr, uid, ids, context=context): - if not partner_id: - partner_id = call.partner_id and call.partner_id.id or False - if partner_id: - address_id = partner.address_get(cr, uid, [partner_id])['default'] - if address_id: - default_contact = partner.browse(cr, uid, address_id, context=context) - opportunity_id = opportunity.create(cr, uid, { - 'name': opportunity_summary or call.name, - 'planned_revenue': planned_revenue, - 'probability': probability, - 'partner_id': partner_id or False, - 'mobile': default_contact and default_contact.mobile, - 'team_id': call.team_id and call.team_id.id or False, - 'description': call.description or False, - 'priority': call.priority, - 'type': 'opportunity', - 'phone': call.partner_phone or False, - 'email_from': default_contact and default_contact.email, - }) - vals = { - 'partner_id': partner_id, - 'opportunity_id': opportunity_id, - 'state': 'done', - } - self.write(cr, uid, [call.id], vals, context=context) - opportunity_dict[call.id] = opportunity_id - return opportunity_dict - - def action_make_meeting(self, cr, uid, ids, context=None): - """ - Open meeting's calendar view to schedule a meeting on current phonecall. - :return dict: dictionary value for created meeting view - """ - partner_ids = [] - phonecall = self.browse(cr, uid, ids[0], context) - if phonecall.partner_id and phonecall.partner_id.email: - partner_ids.append(phonecall.partner_id.id) - res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'calendar', 'action_calendar_event', context) - res['context'] = { - 'default_phonecall_id': phonecall.id, - 'default_partner_ids': partner_ids, - 'default_user_id': uid, - 'default_email_from': phonecall.email_from, - 'default_name': phonecall.name, - 'default_opportunity_id': phonecall.opportunity_id.id, - } - 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 UserError(_('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 _call_set_partner_send_note(self, cr, uid, ids, context=None): - return self.message_post(cr, uid, ids, body=_("Partner has been created."), context=context) - -class crm_phonecall_category(osv.Model): - _name = "crm.phonecall.category" - _description = "Category of phonecall" - _columns = { - 'name': fields.char('Name', required=True, translate=True), - 'team_id': fields.many2one('crm.team', 'Sales Team'), - } diff --git a/addons/crm/crm_phonecall_data.xml b/addons/crm/crm_phonecall_data.xml deleted file mode 100644 index 0eb5c999b27..00000000000 --- a/addons/crm/crm_phonecall_data.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - Inbound - - - - Outbound - - - - - diff --git a/addons/crm/crm_phonecall_demo.xml b/addons/crm/crm_phonecall_demo.xml deleted file mode 100644 index d36aabbc5f0..00000000000 --- a/addons/crm/crm_phonecall_demo.xml +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - - - - - - Left the message - done - +34 934 340 230 - - - - - - - - - - - Need more information on the proposed deal - done - +44 121 690 4596 - - - - - - - - - - - Ask for convenient time of meeting - open - +1 786 525 0724 - - - - - - - - - - Wanted information about pricing of laptops - done - (077) 582-4035 - (077) 341-3591 - - - - - - - - - - - More information on the proposed deal - pending - +1 312 349 2324 - - - - - - - - - Proposal for discount offer - open - +34 230 953 485 - - - - - - - - diff --git a/addons/crm/crm_phonecall_menu.xml b/addons/crm/crm_phonecall_menu.xml deleted file mode 100644 index 19418a049fa..00000000000 --- a/addons/crm/crm_phonecall_menu.xml +++ /dev/null @@ -1,97 +0,0 @@ - - - - - - - - - - - - Phone Calls - crm.phonecall - form - tree,calendar - - {"search_default_user_id":uid} - - - - - - tree - - - - - - - calendar - - - - - - - form - - - - - - Logged Calls - crm.phonecall - form - tree,calendar - - - -

- Click to log the summary of a phone call. -

- Odoo allows you to log inbound calls on the fly to track the - history of the communication with a customer or to inform another - team member. -

- In order to follow up on the call, you can trigger a request for - another call, a meeting or an opportunity. -

-
-
- - - - tree - - - - - - - calendar - - - - - - - form - - - - - - -
-
diff --git a/addons/crm/crm_phonecall_view.xml b/addons/crm/crm_phonecall_view.xml deleted file mode 100644 index 1383c996078..00000000000 --- a/addons/crm/crm_phonecall_view.xml +++ /dev/null @@ -1,232 +0,0 @@ - - - - - - - - - crm.phonecall.category.form - crm.phonecall.category - -
- - - - -
-
-
- - - crm.phonecall.category.tree - crm.phonecall.category - - - - - - - - - - Phonecall Categories - crm.phonecall.category - form - - -

- Click to add a new category. -

- Create specific phone call categories to better define the type of - calls tracked in the system. -

-
-
- - - - - - CRM - Phone Calls Tree - crm.phonecall - - - - - - - - - -
-
-
-
-

-
-
-

-
- - - - - - -
-
- - -
- -
- - - - - CRM - Logged Phone Calls Tree - crm.phonecall - - - - - - - - - - - - - - diff --git a/addons/crm/sales_team_view.xml b/addons/crm/sales_team_view.xml index 38b18f4ce11..74e3b1bc347 100644 --- a/addons/crm/sales_team_view.xml +++ b/addons/crm/sales_team_view.xml @@ -53,7 +53,7 @@ Odoo helps you keep track of your sales pipeline to follow up potential sales and better forecast your future revenues.

- You will be able to plan meetings and phone calls from + You will be able to plan meetings and log activities from opportunities, convert them into quotations, attach related documents, track all discussions, and much more.

diff --git a/addons/crm/security/crm_security.xml b/addons/crm/security/crm_security.xml index 8d280758fe6..5e8823b0812 100644 --- a/addons/crm/security/crm_security.xml +++ b/addons/crm/security/crm_security.xml @@ -29,11 +29,6 @@
- - Show Scheduled Calls Menu - - - @@ -70,19 +65,6 @@ ['|',('user_id','=',user.id),('show_as','=','busy')] - - Personal Phone Calls - - ['|',('user_id','=',user.id),('user_id','=',False)] - - - - All Phones - - [(1,'=',1)] - - - Personal Leads Analysis diff --git a/addons/crm/security/ir.model.access.csv b/addons/crm/security/ir.model.access.csv index 6330d405714..ea4d00efc9c 100644 --- a/addons/crm/security/ir.model.access.csv +++ b/addons/crm/security/ir.model.access.csv @@ -1,12 +1,8 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink access_crm_lead_manager,crm.lead.manager,model_crm_lead,base.group_sale_manager,1,1,1,1 -access_crm_phonecall_manager,crm.phonecall.manager,model_crm_phonecall,base.group_sale_manager,1,1,1,1 access_crm_lead,crm.lead,model_crm_lead,base.group_sale_salesman,1,1,1,0 -access_crm_phonecall,crm.phonecall,model_crm_phonecall,base.group_sale_salesman,1,1,1,0 access_crm_stage,crm.stage,model_crm_stage,,1,0,0,0 access_crm_stage_manager,crm.stage,model_crm_stage,base.group_sale_manager,1,1,1,1 -access_crm_phonecall_report_user,crm.phonecall.report.user,model_crm_phonecall_report,base.group_sale_salesman,1,0,0,0 -access_crm_phonecall_report_manager,crm.phonecall.report,model_crm_phonecall_report,base.group_sale_manager,1,1,1,1 access_crm_opportunity_report_user,crm.opportunity.report,model_crm_opportunity_report,base.group_sale_salesman,1,0,0,0 access_crm_opportunity_report_manager,crm.opportunity.report,model_crm_opportunity_report,base.group_sale_manager,1,1,1,1 access_res_partner_manager,res.partner.crm.manager,base.model_res_partner,base.group_sale_manager,1,0,0,0 @@ -18,13 +14,13 @@ access_base_action_rule_manager,base.action.rule manager,base_action_rule.model_ access_crm_lead_report_user,crm.lead.report user,model_crm_lead_report,base.group_sale_salesman,1,1,1,1 access_res_partner_bank_type_crm_user,res.partner.bank.type.crm.user,base.model_res_partner_bank_type,base.group_sale_salesman,1,0,0,0 access_crm_lead_partner_manager,crm.lead.partner.manager,model_crm_lead,base.group_partner_manager,1,0,0,0 -access_crm_phonecall_partner_manager,crm.phonecall.partner.manager,model_crm_phonecall,base.group_partner_manager,1,1,1,1 access_crm_lead_tag,crm_lead_tag,model_crm_lead_tag,base.group_user,1,0,0,0 access_crm_lead_tag_salesman,crm_lead_tag salesman,model_crm_lead_tag,base.group_sale_salesman,1,1,1,0 access_crm_lead_tag_manager,crm_lead_tag manager,model_crm_lead_tag,base.group_sale_manager,1,1,1,1 -access_crm_phonecall_category,crm_phonecall_category,model_crm_phonecall_category,base.group_user,1,0,0,0 -access_crm_phonecall_category_salesman,crm_phonecall_category salesman,model_crm_phonecall_category,base.group_sale_salesman,1,1,1,0 -access_crm_phonecall_category_manager,crm_phonecall_category manager,model_crm_phonecall_category,base.group_sale_manager,1,1,1,1 access_crm_lost_reason_manager,crm.lost.reason.manager,model_crm_lost_reason,base.group_sale_manager,1,1,1,1 access_crm_lost_reason_salesman,crm.lost.reason.salesman,model_crm_lost_reason,base.group_sale_salesman,1,1,1,0 access_crm_lost_reason_user,crm.lost.reason.user,model_crm_lost_reason,base.group_user,1,0,0,0 +access_crm_activity_user,crm.activity.user,model_crm_activity,base.group_user,1,1,1,0 +access_crm_activity_manager,crm.activity.manager,model_crm_activity,base.group_sale_manager,1,1,1,1 +access_crm_activity_salesman,crm.activity.salesman,model_crm_activity,base.group_sale_salesman,1,1,1,0 +access_crm_activity_report_user,crm.activity.report.user,model_crm_activity_report,base.group_user,1,0,0,0 diff --git a/addons/crm/static/description/index.html b/addons/crm/static/description/index.html index 602c536916b..dda42865fcd 100644 --- a/addons/crm/static/description/index.html +++ b/addons/crm/static/description/index.html @@ -14,7 +14,7 @@

-Manage your sales funnel with no effort. Attract leads, follow-up on phone calls and meetings. Analyse the quality of your leads to make informed decisions and save time by integrating emails directly into the application. +Manage your sales funnel with no effort. Attract leads, follow-up on activities and meetings. Analyse the quality of your leads to make informed decisions and save time by integrating emails directly into the application.

Start your free trial @@ -69,7 +69,7 @@ Find duplicates, merge leads and assign them to the right salesperson in one ope

-Get your opportunities organized to stay focused on the best deals. Manage all your customer interactions from the opportunity like emails, phone calls, internal notes, meetings and quotations. +Get your opportunities organized to stay focused on the best deals. Manage all your customer interactions from the opportunity like emails, internal notes, meetings and quotations.

Follow opportunities that interest you to get notified upon specific events: deal won or lost, stage changed, new customer demand, etc.

@@ -105,7 +105,7 @@ Route, sort and filter incoming emails automatically. Odoo CRM handles incoming

-Schedule your meetings and phone calls using the integrated calendar. You can see your agenda and your colleagues' in one view. As a manager, it's easy to see what your team is busy with. +Schedule your meetings using the integrated calendar. You can see your agenda and your colleagues' in one view. As a manager, it's easy to see what your team is busy with.

diff --git a/addons/crm/test/crm_lead_onchange.yml b/addons/crm/test/crm_lead_onchange.yml index f6300d7b965..d2580fde999 100644 --- a/addons/crm/test/crm_lead_onchange.yml +++ b/addons/crm/test/crm_lead_onchange.yml @@ -17,9 +17,3 @@ type: opportunity stage_id: crm.stage_lead1 opt_out: True -- - Sales manager 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 \ No newline at end of file diff --git a/addons/crm/test/lead2opportunity2win.yml b/addons/crm/test/lead2opportunity2win.yml index b86fbcfe4aa..a86ebf9d3d4 100644 --- a/addons/crm/test/lead2opportunity2win.yml +++ b/addons/crm/test/lead2opportunity2win.yml @@ -28,15 +28,6 @@ assert lead.type == 'opportunity', 'Lead is not converted to opportunity!' assert lead.partner_id.id == ref("base.res_partner_2"), 'Partner mismatch!' assert lead.stage_id.id == ref("stage_lead1"), 'Stage of opportunity is incorrect!' -- - Now I begin communication and schedule a phone call with the customer. -- - !python {model: crm.phonecall2phonecall}: | - import time - context.update({'active_model': 'crm.lead', 'active_ids': [ref('crm_case_3')],'active_id': ref('crm_case_3')}) - call_id = self.create(cr, uid, {'date': time.strftime('%Y-%m-%d %H:%M:%S'), - 'name': "Bonjour M. Jean, Comment allez-vous? J'ai bien reçu votre demande, pourrions-nous en parler quelques minutes?"}, context=context) - self.action_schedule(cr, uid, [call_id], context=context) - Now I schedule meeting with customer. - diff --git a/addons/crm/test/phonecalls.yml b/addons/crm/test/phonecalls.yml deleted file mode 100644 index dfc3d6e33b3..00000000000 --- a/addons/crm/test/phonecalls.yml +++ /dev/null @@ -1,17 +0,0 @@ -- - Salesman check the phone calls data so test with the access rights of salesman. -- - !context - uid: 'crm_res_users_salesman' -- - I schedule a phone call with a customer. -- - !python {model: crm.phonecall2phonecall}: | - context.update({'active_model': 'crm.phonecall', 'active_ids': [ref("crm.crm_phonecall_6")], 'active_id': ref("crm.crm_phonecall_6")}) - res_id = self.create(cr, uid, {'name': "Proposition de réduction"}, context=context) - self.action_schedule(cr, uid, [res_id], context=context) -- - I schedule a meeting based on this phone call. -- - !python {model: crm.phonecall}: | - self.action_make_meeting(cr, uid, [ref("crm.crm_phonecall_6")]) diff --git a/addons/crm/views/crm_action_views.xml b/addons/crm/views/crm_action_views.xml new file mode 100644 index 00000000000..e9c6a6f50f3 --- /dev/null +++ b/addons/crm/views/crm_action_views.xml @@ -0,0 +1,67 @@ + + + + + crm.activity.view.search + crm.activity + + + + + + + + + + crm.activity.view.form + crm.activity + +
+
+
+ + + crm.activity.view.tree + crm.activity + + + + + + + + + + + + Activities + crm.activity + form + tree,form + +

+ Click to create a new activity. +

+
+
+ + + +
diff --git a/addons/crm/wizard/__init__.py b/addons/crm/wizard/__init__.py index 22fd2509e82..99dd897bb8b 100644 --- a/addons/crm/wizard/__init__.py +++ b/addons/crm/wizard/__init__.py @@ -2,6 +2,5 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. import crm_partner_binding -import crm_phonecall_to_phonecall import crm_lead_to_opportunity import crm_merge_opportunities diff --git a/addons/crm/wizard/crm_partner_binding.py b/addons/crm/wizard/crm_partner_binding.py index 2bdd8e3166d..8acba356956 100644 --- a/addons/crm/wizard/crm_partner_binding.py +++ b/addons/crm/wizard/crm_partner_binding.py @@ -36,11 +36,9 @@ class crm_partner_binding(osv.osv_memory): partner_id = False partner_obj = self.pool.get('res.partner') - # The active model has to be a lead or a phonecall + # The active model has to be a lead 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): diff --git a/addons/crm/wizard/crm_phonecall_to_meeting.py b/addons/crm/wizard/crm_phonecall_to_meeting.py deleted file mode 100644 index 0954fa5920c..00000000000 --- a/addons/crm/wizard/crm_phonecall_to_meeting.py +++ /dev/null @@ -1,42 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from openerp.osv import osv -from openerp.tools.translate import _ - -class crm_phonecall2meeting(osv.osv_memory): - """ Phonecall to Meeting """ - - _name = 'crm.phonecall2meeting' - _description = 'Phonecall To Meeting' - - def action_cancel(self, cr, uid, ids, context=None): - """ - Closes Phonecall to Meeting form - @param self: The object pointer - @param cr: the current row, from the database cursor, - @param uid: the current user’s ID for security checks, - @param ids: List of Phonecall to Meeting IDs - @param context: A standard dictionary for contextual values - - """ - return {'type':'ir.actions.act_window_close'} - - 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 - """ - res = {} - phonecall_id = context and context.get('active_id', False) or False - if phonecall_id: - phonecall = self.pool.get('crm.phonecall').browse(cr, uid, phonecall_id, context) - res = self.pool.get('ir.actions.act_window').for_xml_id(cr, uid, 'calendar', 'action_calendar_event', context) - res['context'] = { - 'default_phonecall_id': phonecall.id, - 'default_partner_id': phonecall.partner_id and phonecall.partner_id.id or False, - 'default_user_id': uid, - 'default_email_from': phonecall.email_from, - 'default_state': 'open', - 'default_name': phonecall.name, - } - return res diff --git a/addons/crm/wizard/crm_phonecall_to_meeting_view.xml b/addons/crm/wizard/crm_phonecall_to_meeting_view.xml deleted file mode 100644 index 5327c617e0f..00000000000 --- a/addons/crm/wizard/crm_phonecall_to_meeting_view.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - crm.phonecall2meeting.form - crm.phonecall2meeting - -
-
-
- - - - - Convert To Meeting - crm.phonecall2meeting - form - form - - new - - -
-
diff --git a/addons/crm/wizard/crm_phonecall_to_phonecall.py b/addons/crm/wizard/crm_phonecall_to_phonecall.py deleted file mode 100644 index 13149ae0f5b..00000000000 --- a/addons/crm/wizard/crm_phonecall_to_phonecall.py +++ /dev/null @@ -1,79 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from openerp.osv import fields, osv -from openerp.tools.translate import _ - -import time - -class crm_phonecall2phonecall(osv.osv_memory): - _name = 'crm.phonecall2phonecall' - _description = 'Phonecall To Phonecall' - - _columns = { - 'name' : fields.char('Call summary', required=True, select=1), - 'user_id' : fields.many2one('res.users',"Assign To"), - 'contact_name':fields.char('Contact'), - 'phone':fields.char('Phone'), - 'categ_id': fields.many2one('crm.phonecall.category', 'Category'), - 'date': fields.datetime('Date'), - 'team_id':fields.many2one('crm.team','Sales Team', oldname='section_id'), - 'action': fields.selection([('schedule','Schedule a call'), ('log','Log a call')], 'Action', required=True), - 'partner_id' : fields.many2one('res.partner', "Partner"), - 'note':fields.text('Note') - } - - - def action_cancel(self, cr, uid, ids, context=None): - """ - Closes Phonecall to Phonecall form - """ - return {'type':'ir.actions.act_window_close'} - - def action_schedule(self, cr, uid, ids, context=None): - value = {} - if context is None: - context = {} - phonecall = self.pool.get('crm.phonecall') - phonecall_ids = context and context.get('active_ids') or [] - for this in self.browse(cr, uid, ids, context=context): - phocall_ids = phonecall.schedule_another_phonecall(cr, uid, phonecall_ids, this.date, this.name, \ - this.user_id and this.user_id.id or False, \ - this.team_id and this.team_id.id or False, \ - this.categ_id and this.categ_id.id or False, \ - action=this.action, context=context) - - return phonecall.redirect_phonecall_view(cr, uid, phocall_ids[phonecall_ids[0]], context=context) - - def default_get(self, cr, uid, fields, context=None): - """ - This function gets default values - - """ - res = super(crm_phonecall2phonecall, self).default_get(cr, uid, fields, context=context) - record_id = context and context.get('active_id', False) or False - res.update({'action': 'schedule', 'date': time.strftime('%Y-%m-%d %H:%M:%S')}) - if record_id: - phonecall = self.pool.get('crm.phonecall').browse(cr, uid, record_id, context=context) - - categ_id = False - data_obj = self.pool.get('ir.model.data') - try: - res_id = data_obj._get_id(cr, uid, 'crm', 'categ_phone2') - categ_id = data_obj.browse(cr, uid, res_id, context=context).res_id - except ValueError: - pass - - if 'name' in fields: - res.update({'name': phonecall.name}) - if 'user_id' in fields: - res.update({'user_id': phonecall.user_id and phonecall.user_id.id or False}) - if 'date' in fields: - res.update({'date': False}) - if 'team_id' in fields: - res.update({'team_id': phonecall.team_id and phonecall.team_id.id or False}) - if 'categ_id' in fields: - res.update({'categ_id': categ_id}) - if 'partner_id' in fields: - res.update({'partner_id': phonecall.partner_id and phonecall.partner_id.id or False}) - return res diff --git a/addons/crm/wizard/crm_phonecall_to_phonecall_view.xml b/addons/crm/wizard/crm_phonecall_to_phonecall_view.xml deleted file mode 100644 index 58c5269f050..00000000000 --- a/addons/crm/wizard/crm_phonecall_to_phonecall_view.xml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - crm.phonecall2phonecall.form - crm.phonecall2phonecall - -
- - - - - - - - -
-
-
-
-
- - - - - Schedule Other Call - crm.phonecall2phonecall - form - form - - new - - -
-
diff --git a/addons/gamification_sale_crm/sale_crm_goals.xml b/addons/gamification_sale_crm/sale_crm_goals.xml index 06e57425c25..56ddb1f6b19 100644 --- a/addons/gamification_sale_crm/sale_crm_goals.xml +++ b/addons/gamification_sale_crm/sale_crm_goals.xml @@ -50,16 +50,6 @@
- - Logged Calls - Log a certain number of calls to reach this goal - count - calls - - - [('user_id','=',user.id),('state','=','done')] - - New Opportunities Based on the opening date