[REF] website_crm_score: migrate code to be compatible with master.

Courtesy Review : xmo, mat
This commit is contained in:
Jeremy Kersten
2015-01-19 09:53:31 +01:00
parent 8b407a41a4
commit ba294e2318
23 changed files with 338 additions and 624 deletions
+1
View File
@@ -1,2 +1,3 @@
# -*- coding: utf-8 -*-
import models
import controllers
+2 -2
View File
@@ -1,9 +1,9 @@
{
'name': "Lead Scoring",
'category': "Test",
'category': "CRM",
'version': "1.0",
'depends': ['base', 'sales_team', 'marketing', 'website_crm'],
'author': "OpenErp S.A.",
'author': "Odoo S.A.",
'description': """\
Lead scoring""",
'data': [
@@ -1 +1,2 @@
# -*- coding: utf-8 -*-
import main
+21 -20
View File
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from openerp import addons, http, SUPERUSER_ID, fields
from openerp.http import request
@@ -9,24 +10,23 @@ class PageController(addons.website.controllers.main.Website):
response = super(PageController, self).page(page, **opt)
# duplication of ir_http.py
if hasattr(response, 'status_code') and response.status_code == 200:
if getattr(response, 'status_code', 0) == 200:
try:
view = request.website.get_template(page)
except:
pass # view not found
else:
if view.track: # avoid tracking redirected page
cr, uid, context = request.cr, request.uid, request.context
lead_id = request.registry["crm.lead"].decode(request)
lead_id = request.env["crm.lead"].decode(request)
url = request.httprequest.url
vals = {'lead_id': lead_id, 'partner_id': request.session.get('uid', None), 'url': url}
vals = {'lead_id': lead_id, 'user_id': request.session.get('uid'), 'url': url}
if lead_id and request.registry['website.crm.pageview'].create_pageview(cr, uid, vals, context=context):
# create_pageview was successful
pass
else:
if not lead_id or request.registry['website.crm.pageview'].create_pageview(cr, uid, vals, context=context):
# create_pageview failed
response.delete_cookie('lead_id')
request.session.setdefault('pages_viewed', {})[url] = fields.Datetime.now()
request.session.modified = True
except:
pass # view not found
return response
@@ -45,8 +45,6 @@ class ContactController(addons.website_crm.controllers.main.contactus):
# sign the lead_id
sign = lead_model.encode(lead_id)
response.set_cookie('lead_id', sign, domain=lead_model.get_score_domain_cookies())
else:
pass # lead_id == None because no lead was created
return response
def create_real_lead(self, request, values, kwargs):
@@ -54,7 +52,7 @@ class ContactController(addons.website_crm.controllers.main.contactus):
return super(ContactController, self).create_lead(request, values, kwargs)
def create_lead(self, request, values, kwargs):
cr, uid, context = request.cr, request.uid, request.context
cr, context = request.cr, request.context
lead_model = request.registry["crm.lead"]
lead_id = lead_model.decode(request)
@@ -62,10 +60,15 @@ class ContactController(addons.website_crm.controllers.main.contactus):
# domain: leads that are still open:
# NOT [ on_change AND (proba = 0 OR proba = 100) ]
# the condition on the lead_id is prepended
domain = [('id', '=', lead_id),
'!', '&', ('stage_id.on_change', '!=', True),
'|', ('stage_id.probability', '!=', 0.0), ('stage_id.probability', '!=', 100.0)
]
domain = [
('id', '=', lead_id),
'|',
('stage_id.on_change', '=', False),
'&',
('stage_id.probability', '!=', 0),
('stage_id.probability', '!=', 100)
]
domain.extend(['|', ('stage_id.on_change', '=', False), '&', ('stage_id.probability', '!=', 0), ('stage_id.probability', '!=', 100)])
lead_instance = lead_model.search(cr, SUPERUSER_ID, domain, context=context)
if lead_instance:
@@ -75,7 +78,7 @@ class ContactController(addons.website_crm.controllers.main.contactus):
# NOTE: the following should be changed when dynamic forms exist
changed_values = {}
for fieldname, fieldvalue in values.items():
if fieldname in lead._all_columns and fieldvalue: # and not lead[fieldname]: # rem : why this last condition ?
if fieldname in lead._all_columns and fieldvalue:
if lead[fieldname] and lead[fieldname] != fieldvalue:
changed_values[fieldname] = fieldvalue
else:
@@ -89,7 +92,6 @@ class ContactController(addons.website_crm.controllers.main.contactus):
else:
# either no lead_id cookie OR the lead_id doesn't exist in db OR the current one is closed -> a lead is created
lang = context.get('lang', False)
lang_id = request.registry["res.lang"].search(cr, SUPERUSER_ID, [('code', '=', lang)], context=context)
lang_id = lang_id and lang_id[0] or False
@@ -101,7 +103,7 @@ class ContactController(addons.website_crm.controllers.main.contactus):
url_list = []
pages_viewed = request.session['pages_viewed']
for url, date in pages_viewed.iteritems():
vals = {'partner_id': request.session.get('uid', None), 'url': url, 'view_date': date}
vals = {'user_id': request.session.get('uid'), 'url': url, 'view_date': date}
score_pageview_ids.append((0, 0, vals))
url_list.append(url)
del request.session['pages_viewed']
@@ -117,5 +119,4 @@ class ContactController(addons.website_crm.controllers.main.contactus):
if body:
request.registry['crm.lead'].message_post(cr, SUPERUSER_ID, [new_lead_id], body=body, subject="Pages visited", context=context)
# TODO : try to write the cookies here, after retreiving a response ?
return new_lead_id
@@ -2,11 +2,11 @@
<openerp>
<data>
<record model="crm.case.section" id="sales_team.section_sales_department">
<field name="score_section_domain">[['country_id', '=', 'France']]</field>
<record model="crm.team" id="sales_team.team_sales_department">
<field name="score_team_domain">[['country_id', '=', 'France']]</field>
</record>
<record model="crm.case.section" id="website.salesteam_website_sales">
<field name="score_section_domain">[['country_id', '=', 'Belgium']]</field>
<record model="crm.team" id="website.salesteam_website_sales">
<field name="score_team_domain">[['country_id', '=', 'Belgium']]</field>
</record>
<!-- Creating salesmen -->
@@ -85,152 +85,152 @@
<!-- Assigning salesmen to salesteams -->
<!-- Team 0 -->
<record id="website_crm_score_section_user_0" model="section.user">
<record id="website_crm_score_team_user_0" model="team.user">
<field name="user_id" ref="website_crm_score_user_0"/>
<field name="section_id" ref="sales_team.section_sales_department"/>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="maximum_user_leads">2</field>
<field name="section_user_domain">['|', ['country_id', '=', 'Belgium'], ['country_id', '=', 'France']]</field>
<field name="team_user_domain">['|', ['country_id', '=', 'Belgium'], ['country_id', '=', 'France']]</field>
</record>
<record id="website_crm_score_section_user_1" model="section.user">
<record id="website_crm_score_team_user_1" model="team.user">
<field name="user_id" ref="website_crm_score_user_2"/>
<field name="section_id" ref="sales_team.section_sales_department"/>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="maximum_user_leads">1</field>
<field name="section_user_domain">[['country_id', '!=', 'Belgium']]</field>
<field name="team_user_domain">[['country_id', '!=', 'Belgium']]</field>
</record>
<!-- Team 1 -->
<record id="website_crm_score_section_user_3" model="section.user">
<record id="website_crm_score_team_user_3" model="team.user">
<field name="user_id" ref="website_crm_score_user_1"/>
<field name="section_id" ref="website.salesteam_website_sales"/>
<field name="team_id" ref="website.salesteam_website_sales"/>
<field name="maximum_user_leads">1</field>
</record>
<record id="website_crm_score_section_user_4" model="section.user">
<record id="website_crm_score_team_user_4" model="team.user">
<field name="user_id" ref="website_crm_score_user_3"/>
<field name="section_id" ref="website.salesteam_website_sales"/>
<field name="team_id" ref="website.salesteam_website_sales"/>
<field name="maximum_user_leads">2</field>
<field name="section_user_domain">['&amp;', ['lang_id', '=', 'English'], ['country_id', '=', 'Belgium']]</field>
<field name="team_user_domain">['&amp;', ['lang_id', '=', 'English'], ['country_id', '=', 'Belgium']]</field>
</record>
<!-- Team 3 -->
<record id="website_crm_score_section_user_6" model="section.user">
<record id="website_crm_score_team_user_6" model="team.user">
<field name="user_id" ref="website_crm_score_user_3"/>
<field name="section_id" ref="sales_team.crm_case_section_1"/>
<field name="team_id" ref="sales_team.crm_team_1"/>
<field name="maximum_user_leads">1</field>
</record>
<!-- Team 4 -->
<record id="website_crm_score_section_user_7" model="section.user">
<record id="website_crm_score_team_user_7" model="team.user">
<field name="user_id" ref="website_crm_score_user_2"/>
<field name="section_id" ref="sales_team.crm_case_section_2"/>
<field name="team_id" ref="sales_team.crm_team_2"/>
<field name="maximum_user_leads">3</field>
<field name="section_user_domain">[['country_id', '=', 'France']]</field>
<field name="team_user_domain">[['country_id', '=', 'France']]</field>
</record>
<!-- Leads -->
<record id="crm.crm_case_1" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_2" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_3" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_4" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_5" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_6" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_7" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_8" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_9" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_10" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_11" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_12" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_13" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_14" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_15" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_16" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_17" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_18" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_19" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_20" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_21" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_22" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_23" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_24" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_25" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<record id="crm.crm_case_26" model="crm.lead">
<field name="section_id" ref=""/>
<field name="user_id" ref=""/>
<field name="team_id" />
<field name="user_id" />
</record>
<!-- Scores -->
+2 -1
View File
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
import website_crm_score
import crm_lead
import ir
import sales_team
import pageview
import leads_dry_run
import leads_dry_run
+25 -25
View File
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from openerp.http import request
from openerp import api, fields, models, SUPERUSER_ID
import md5
@@ -34,17 +35,17 @@ class Lead(models.Model):
lang_id = fields.Many2one('res.lang', string='Language', help="Language from the website when lead has been created")
def encode(self, lead_id):
encrypted_lead_id = md5.new("%s%s" % (lead_id, self.get_key())).hexdigest()
return "%s-%s" % (str(lead_id), encrypted_lead_id)
md5_lead_id = md5.new("%s%s" % (lead_id, self.get_key())).hexdigest()
return "%s-%s" % (str(lead_id), md5_lead_id)
def decode(self, request):
# opens the cookie, verifies the signature of the lead_id
# returns lead_id if the verification passes and None otherwise
cookie_content = request.httprequest.cookies.get('lead_id')
if cookie_content:
lead_id, encrypted_lead_id = cookie_content.split('-', 1)
lead_id, md5_lead_id = cookie_content.split('-', 1)
expected_encryped_lead_id = md5.new("%s%s" % (lead_id, self.get_key())).hexdigest()
if encrypted_lead_id == expected_encryped_lead_id:
if md5_lead_id == expected_encryped_lead_id:
return int(lead_id)
else:
return None
@@ -53,38 +54,37 @@ class Lead(models.Model):
return self.pool['ir.config_parameter'].get_param(request.cr, SUPERUSER_ID, 'database.uuid')
def get_score_domain_cookies(self):
# TODO should be return request.httprequest.host in master
# return request.httprequest.host
dom = "." + ".".join(request.httprequest.host.rsplit(".", 2)[1:])
if ':' in dom: # Hack if in local mode with port
dom = request.httprequest.host
return dom
return request.httprequest.host
def _merge_pageviews(self, cr, uid, opportunity_id, opportunities, context=None):
@api.model
def _merge_pageviews(self, opportunity_id, opportunities):
crmpv = self.env['website.crm.pageview']
lead_ids = [opp.id for opp in opportunities if opp.id != opportunity_id]
pv_ids = self.pool.get('website.crm.pageview').search(cr, uid, [('lead_id', 'in', lead_ids)], context=context)
self.pool.get('website.crm.pageview').write(cr, SUPERUSER_ID, pv_ids, {'lead_id': opportunity_id}, context=context)
return True
pv_ids = crmpv.sudo().search([('lead_id', 'in', lead_ids)])
pv_ids.write({'lead_id': opportunity_id})
def _merge_scores(self, cr, uid, opportunity_id, opportunities, context=None):
@api.model
def _merge_scores(self, opportunity_id, opportunities):
# We needs to delete score from opportunity_id, to be sure that all rules will be re-evaluated.
self.write(cr, SUPERUSER_ID, [opportunity_id], {'score_ids': [(6, 0, [])]}, context=context)
return True
self.sudo().browse(opportunity_id).write({'score_ids': [(6, 0, [])]})
def merge_dependences(self, cr, uid, highest, opportunities, context=None):
self._merge_pageviews(cr, uid, highest, opportunities, context=context)
self._merge_scores(cr, uid, highest, opportunities, context=context)
@api.model
def merge_dependences(self, highest, opportunities):
self._merge_pageviews(highest, opportunities)
self._merge_scores(highest, opportunities)
# Call default merge function
super(Lead, self).merge_dependences(cr, uid, highest, opportunities, context=context)
super(Lead, self).merge_dependences(highest, opportunities)
# Overwritte ORM to add or remove the assign date
def create(self, cr, uid, vals, context=None):
@api.model
def create(self, vals):
if vals.get('user_id'):
vals['assign_date'] = fields.datetime.now()
return super(Lead, self).create(cr, uid, vals, context=context)
return super(Lead, self).create(vals)
def write(self, cr, uid, ids, vals, context=None):
@api.multi
def write(self, vals):
if 'user_id' in vals:
vals['assign_date'] = vals.get('user_id') and fields.datetime.now() or False
return super(Lead, self).write(cr, uid, ids, vals, context=context)
return super(Lead, self).write(vals)
+7 -9
View File
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from openerp import fields, models
from openerp.http import request
from openerp.osv import osv
@@ -9,16 +10,13 @@ class ir_http(models.AbstractModel):
def _dispatch(self):
response = super(ir_http, self)._dispatch()
if hasattr(response, 'status_code') and response.status_code == 200: # may not be needed because find_handler not used anymore
if request.endpoint and request.endpoint.routing and request.endpoint.routing.get('track', False):
cr, uid, context = request.cr, request.uid, request.context
lead_id = request.registry["crm.lead"].decode(request)
if getattr(response, 'status_code', 0) == 200:
if request.endpoint and request.endpoint.routing and request.endpoint.routing.get('track'):
lead_id = request.env["crm.lead"].decode(request)
url = request.httprequest.url
vals = {'lead_id': lead_id, 'partner_id': request.session.get('uid', None), 'url': url}
if lead_id and request.registry['website.crm.pageview'].create_pageview(cr, uid, vals, context=context):
# create_pageview was successful
pass
else:
vals = {'lead_id': lead_id, 'user_id': request.session.get('uid'), 'url': url}
if not lead_id or request.env['website.crm.pageview'].create_pageview(vals):
# create_pageview was fail
response.delete_cookie('lead_id')
request.session.setdefault('pages_viewed', {})[url] = fields.Datetime.now()
request.session.modified = True
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from openerp import fields, models, api
@@ -5,7 +6,7 @@ class leads_dry_run(models.TransientModel):
_name = "leads.dry.run"
lead_id = fields.Many2one('crm.lead', string='Lead', required=True)
section_id = fields.Many2one('crm.case.section', string='SaleTeam', required=True)
team_id = fields.Many2one('crm.team', string='SaleTeam', required=True, oldname='section_id')
user_id = fields.Many2one('res.users', string='Saleman')
@api.model
@@ -13,17 +14,13 @@ class leads_dry_run(models.TransientModel):
# Allow to assign the result from a previous dry run.
# Once the user agrees with the result shown by a dry run
# It differs from launching the assignement process again,
# because salemen would be selected at random again
dry_run_fields = ['lead_id',
'section_id',
'user_id',
]
all_dry_run = self.search_read(domain=[('user_id','!=',False)], fields=dry_run_fields)
# because salemen would be selected at random again
all_dry_run = self.search([('user_id', '!=', False)])
for dry_run in all_dry_run:
lead_record = self.env['crm.lead'].browse(dry_run['lead_id'][0])
lead_record = dry_run.lead_id
values = {
'section_id': dry_run['section_id'][0],
'user_id': dry_run['user_id'][0],
'team_id': dry_run.team_id.id,
'user_id': dry_run.user_id.id,
'assign_date': fields.Datetime.now()
}
lead_record.write(values)
+17 -16
View File
@@ -1,4 +1,5 @@
from openerp import fields, models, SUPERUSER_ID
# -*- coding: utf-8 -*-
from openerp import fields, models, SUPERUSER_ID, tools, api
from psycopg2 import IntegrityError
@@ -7,21 +8,20 @@ class pageview(models.Model):
view_date = fields.Datetime(string='Viewing Date')
lead_id = fields.Many2one('crm.lead', string='Lead')
partner_id = fields.Many2one('res.partner', string='Partner')
user_id = fields.Many2one('res.users', string='User', oldname='partner_id')
url = fields.Char(string='Url')
def create_pageview(self, cr, uid, vals, context=None, test=False):
@api.model
def create_pageview(self, vals, test=False):
# returns True if the operation in the db was successful, False otherwise
lead_id = vals.get('lead_id', None)
partner_id = vals.get('partner_id', None)
url = vals.get('url', None)
lead_id = vals.get('lead_id')
user_id = vals.get('user_id')
url = vals.get('url')
view_date = fields.Datetime.now()
# registry = modules.registry.RegistryManager.get(request.session.db)
# with registry.cursor() as pv_cr:
with self.pool.cursor() as pv_cr:
if test:
pv_cr = cr
pv_cr = self._cr
pv_cr.execute('''
UPDATE website_crm_pageview SET view_date=%s WHERE lead_id=%s AND url=%s RETURNING id;
''', (view_date, lead_id, url))
@@ -31,17 +31,18 @@ class pageview(models.Model):
else:
# update failed
try:
pv_cr.execute('''
INSERT INTO website_crm_pageview (lead_id, partner_id, url, view_date)
SELECT %s,%s,%s,%s
RETURNING id;
''', (lead_id, partner_id, url, view_date))
with tools.mute_logger('openerp.sql_db'):
pv_cr.execute('''
INSERT INTO website_crm_pageview (lead_id, user_id, url, view_date)
SELECT %s,%s,%s,%s
RETURNING id;
''', (lead_id, user_id, url, view_date))
fetch = pv_cr.fetchone()
if fetch:
# a new pageview has been created, a message is posted
body = '<a href="' + url + '" target="_blank"><b>' + url + '</b></a>'
ctx = dict(context or {}, mail_notify_noemail=True)
self.pool['crm.lead'].message_post(cr, SUPERUSER_ID, [lead_id], body=body, subject="Page visited", context=ctx)
ctx = dict(self._context, mail_notify_noemail=True)
self.pool['crm.lead'].message_post(self._cr, SUPERUSER_ID, [lead_id], body=body, subject="Page visited", context=ctx)
return True
except IntegrityError:
return False
+58 -50
View File
@@ -1,9 +1,9 @@
from openerp.osv import osv
# -*- coding: utf-8 -*-
from openerp import fields, api, models
import datetime
from openerp.osv import osv
from openerp.tools.safe_eval import safe_eval
from random import randint, shuffle
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT
import datetime
import logging
import math
@@ -20,21 +20,23 @@ try:
return bool(address.validate_address(mail))
except ImportError:
_logger.warning('Flanker library not found, Flanker features (check email on lead) disabled. If you plan to use it, please install the Flanker library from http://pypi.python.org/pypi/flanker')
_logger.warning('Flanker library not found, Flanker features (check email on lead) disabled. \
If you plan to use it, please install the Flanker library from http://pypi.python.org/pypi/flanker')
def checkmail(mail):
return True
class section_user(models.Model):
_name = 'section.user'
class team_user(models.Model):
_name = 'team.user'
@api.one
def _count_leads(self):
if self.id:
limit_date = datetime.datetime.now() - datetime.timedelta(days=30)
domain = [('user_id', '=', self.user_id.id),
('section_id', '=', self.section_id.id),
('assign_date', '>', limit_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT))
('team_id', '=', self.team_id.id),
('assign_date', '>', fields.Datetime.to_string(limit_date))
]
self.leads_count = self.env['crm.lead'].search_count(domain)
else:
@@ -48,19 +50,19 @@ class section_user(models.Model):
self.percentage_leads = 0.0
@api.one
@api.constrains('section_user_domain')
@api.constrains('team_user_domain')
def _assert_valid_domain(self):
try:
domain = safe_eval(self.section_user_domain or '[]', evaluation_context)
domain = safe_eval(self.team_user_domain or '[]', evaluation_context)
self.env['crm.lead'].search(domain)
except Exception:
raise Warning('The domain is incorrectly formatted')
section_id = fields.Many2one('crm.case.section', string='SaleTeam', required=True)
team_id = fields.Many2one('crm.team', string='SaleTeam', required=True, oldname='section_id')
user_id = fields.Many2one('res.users', string='Saleman', required=True)
name = fields.Char(related='user_id.partner_id.display_name')
running = fields.Boolean(string='Running', default=True)
section_user_domain = fields.Char('Domain')
team_user_domain = fields.Char('Domain')
maximum_user_leads = fields.Integer('Leads Per Month')
leads_count = fields.Integer('Assigned Leads', compute='_count_leads', help='Assigned Leads this last month')
percentage_leads = fields.Float(compute='_get_percentage', string='Percentage leads')
@@ -70,51 +72,53 @@ class section_user(models.Model):
self.running = not self.running
class crm_case_section(osv.osv):
_inherit = "crm.case.section"
class crm_team(osv.osv):
_inherit = "crm.team"
@api.one
def _count_leads(self):
if self.id:
self.leads_count = self.env['crm.lead'].search_count([('section_id', '=', self.id)])
self.leads_count = self.env['crm.lead'].search_count([('team_id', '=', self.id)])
else:
self.leads_count = 0
@api.one
def _assigned_leads(self):
limit_date = datetime.datetime.now() - datetime.timedelta(days=30)
domain = [('assign_date', '>=', limit_date.strftime(DEFAULT_SERVER_DATETIME_FORMAT)),
('section_id', '=', self.id),
domain = [('assign_date', '>=', fields.Datetime.to_string(limit_date)),
('team_id', '=', self.id),
('user_id', '!=', False)
]
self.assigned_leads = self.env['crm.lead'].search_count(domain)
@api.one
def _unassigned_leads(self):
self.unassigned_leads = self.env['crm.lead'].search_count([('section_id', '=', self.id), ('user_id', '=', False), ('assign_date', '=', False)])
self.unassigned_leads = self.env['crm.lead'].search_count(
[('team_id', '=', self.id), ('user_id', '=', False), ('assign_date', '=', False)]
)
@api.one
def _capacity(self):
self.capacity = sum(s.maximum_user_leads for s in self.section_user_ids)
self.capacity = sum(s.maximum_user_leads for s in self.team_user_ids)
@api.one
@api.constrains('score_section_domain')
@api.constrains('score_team_domain')
def _assert_valid_domain(self):
try:
domain = safe_eval(self.score_section_domain or '[]', evaluation_context)
domain = safe_eval(self.score_team_domain or '[]', evaluation_context)
self.env['crm.lead'].search(domain)
except Exception:
raise Warning('The domain is incorrectly formatted')
ratio = fields.Float(string='Ratio')
score_section_domain = fields.Char('Domain')
score_team_domain = fields.Char('Domain')
leads_count = fields.Integer(compute='_count_leads')
assigned_leads = fields.Integer(compute='_assigned_leads')
unassigned_leads = fields.Integer(compute='_unassigned_leads')
capacity = fields.Integer(compute='_capacity')
section_user_ids = fields.One2many('section.user', 'section_id', string='Salesman')
user_ids = fields.Many2many('res.users', 'section_user', 'section_id', 'user_id', readonly=True) # Use for display in Kanban
min_for_assign = fields.Integer("Minimum score", help="Minimum Score for a lead to be automatically assign (>=)", default=0, required=True)
team_user_ids = fields.One2many('team.user', 'team_id', string='Salesman')
user_ids = fields.Many2many('res.users', 'team_user', 'team_id', 'user_id', readonly=True) # Use for display in Kanban
min_for_assign = fields.Integer("Minimum score", help="Minimum score to be automatically assign (>=)", default=0, required=True)
@api.model
def direct_assign_leads(self, ids=[]):
@@ -134,8 +138,8 @@ class crm_case_section(osv.osv):
while haslead:
haslead = False
for salesteam in all_salesteams:
domain = safe_eval(salesteam['score_section_domain'], evaluation_context)
domain.extend([('section_id', '=', False), ('user_id', '=', False)])
domain = safe_eval(salesteam['score_team_domain'], evaluation_context)
domain.extend([('team_id', '=', False), ('user_id', '=', False)])
domain.extend(['|', ('stage_id.on_change', '=', False), '&', ('stage_id.probability', '!=', 0), ('stage_id.probability', '!=', 100)])
leads = self.env["crm.lead"].search(domain, limit=50)
haslead = haslead or (len(leads) == 50 and not dry)
@@ -145,59 +149,62 @@ class crm_case_section(osv.osv):
if dry:
for lead in leads:
values = {'lead_id': lead.id, 'section_id': salesteam['id']}
values = {'lead_id': lead.id, 'team_id': salesteam['id']}
self.env['leads.dry.run'].create(values)
else:
leads.write({'section_id': salesteam['id']})
leads.write({'team_id': salesteam['id']})
# Erase fake/false email
spams = map(lambda x: x.id, filter(lambda x: x.email_from and not checkmail(x.email_from), leads))
if spams:
self.env["crm.lead"].browse(spams).write({'email_from': False})
# Merge duplicated lead
leads_done = []
leads_done = set()
for lead in leads:
if lead.id not in leads_done:
leads_duplicated = lead.get_duplicated_leads(False)
if len(leads_duplicated) > 1:
self.env["crm.lead"].browse(leads_duplicated).merge_opportunity(False, False)
leads_done += leads_duplicated
leads_done.update(leads_duplicated)
self._cr.commit()
self._cr.commit()
@api.model
def assign_leads_to_salesmen(self, all_section_users, dry=False):
def assign_leads_to_salesmen(self, all_team_users, dry=False):
users = []
for su in all_section_users:
for su in all_team_users:
if (su.maximum_user_leads - su.leads_count) <= 0:
continue
domain = safe_eval(su.section_user_domain or '[]', evaluation_context)
domain.append(('user_id', '=', False))
domain.append(('assign_date', '=', False))
domain.append(('score', '>=', su.section_id.min_for_assign))
domain = safe_eval(su.team_user_domain or '[]', evaluation_context)
domain.extend([
('user_id', '=', False),
('assign_date', '=', False),
('score', '>=', su.team_id.min_for_assign)
])
# assignation rythm: 2 days of leads if a lot of leads should be assigned
limit = int(math.ceil(su.maximum_user_leads / 15.0))
if dry:
dry_leads = self.env["leads.dry.run"].search([('section_id', '=', su.section_id.id)])
domain.append(['id', 'in', [dl.lead_id.id for dl in dry_leads]])
dry_leads = self.env["leads.dry.run"].search([('team_id', '=', su.team_id.id)])
domain.append(['id', 'in', dry_leads.mapped('lead_id.id')])
else:
domain.append(('section_id', '=', su.section_id.id))
domain.append(('team_id', '=', su.team_id.id))
leads = self.env["crm.lead"].search(domain, order='score desc', limit=limit * len(su.section_id.section_user_ids))
leads = self.env["crm.lead"].search(domain, order='score desc', limit=limit * len(su.team_id.team_user_ids))
users.append({
"su": su,
"nbr": min(su.maximum_user_leads - su.leads_count, limit),
"leads": leads
})
assigned = {}
assigned = set()
while users:
i = 0
# statistically select the user that should receive the next lead
# statistically select the user that should receive the next lead
idx = randint(0, reduce(lambda nbr, x: nbr + x['nbr'], users, 0) - 1)
while idx > users[i]['nbr']:
@@ -214,16 +221,16 @@ class crm_case_section(osv.osv):
#lead convert for this user
lead = user['leads'][0]
assigned[lead] = True
assigned.add(lead)
if dry:
values = {'lead_id': lead.id, 'section_id': user['su'].section_id.id, 'user_id': user['su'].user_id.id}
values = {'lead_id': lead.id, 'team_id': user['su'].team_id.id, 'user_id': user['su'].user_id.id}
self.env['leads.dry.run'].create(values)
else:
# Assign date will be setted by write function
data = {'user_id': user['su'].user_id.id}
lead.write(data)
lead.convert_opportunity(None)
lead.convert_opportunity(lead.partner_id and lead.partner_id.id or None)
self._cr.commit()
user['nbr'] -= 1
@@ -237,14 +244,15 @@ class crm_case_section(osv.osv):
TRUNCATE TABLE leads_dry_run;
""")
all_salesteams = self.search_read(fields=['score_section_domain'], domain=[('score_section_domain', '!=', False)])
# casting the list into a dict to ease the access afterwards
all_salesteams = self.search_read(fields=['score_team_domain'], domain=[('score_team_domain', '!=', False)])
all_section_users = self.env['section.user'].search([('running', '=', True)])
all_team_users = self.env['team.user'].search([('running', '=', True)])
self.env['website.crm.score'].assign_scores_to_leads()
self.assign_leads_to_salesteams(all_salesteams, dry=dry)
# Compute score after assign to salesteam, because if a merge has been done, the score for leads is removed.
self.env['website.crm.score'].assign_scores_to_leads()
self.assign_leads_to_salesmen(all_section_users, dry=dry)
self.assign_leads_to_salesmen(all_team_users, dry=dry)
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from openerp import fields, models, api
from openerp.tools.safe_eval import safe_eval
from openerp.osv.expression import expression
@@ -54,7 +55,7 @@ class website_crm_score(models.Model):
domain = safe_eval(score['domain'], evaluation_context)
# Don't replace the domain with a 'not in' like below... that doesn't make the same thing !!!
#domain.extend(['|', ('stage_id.on_change', '=', False), ('stage_id.probability', 'not in', [0,100])])
# domain.extend(['|', ('stage_id.on_change', '=', False), ('stage_id.probability', 'not in', [0,100])])
domain.extend(['|', ('stage_id.on_change', '=', False), '&', ('stage_id.probability', '!=', 0), ('stage_id.probability', '!=', 100)])
e = expression(self._cr, self._uid, domain, self.pool['crm.lead'], self._context)
@@ -68,7 +69,7 @@ class website_crm_score(models.Model):
FROM crm_lead
WHERE %s RETURNING lead_id""" % (score['id'], where_clause), where_params)
# Todo: force recompute of fields that depends on score_ids
# Force recompute of fields that depends on score_ids
lead_ids = [resp[0] for resp in self._cr.fetchall()]
leads = self.env["crm.lead"].browse(lead_ids)
leads.modified(['score_ids'])
@@ -1,7 +1,7 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_website_crm_score,access_website_crm_score,model_website_crm_score,,1,0,0,0
access_website_crm_score_manager,access_website_crm_score_manager,model_website_crm_score,base.group_website_designer,1,1,1,1
access_section_user,access_section_user,model_section_user,,1,0,0,0
access_section_user_manager,access_section_user_manager,model_section_user,marketing.group_marketing_manager,1,1,1,1
access_team_user,access_team_user,model_team_user,,1,0,0,0
access_team_user_manager,access_team_user_manager,model_team_user,marketing.group_marketing_manager,1,1,1,1
access_website_crm_pageview,access_website_crm_pageview,model_website_crm_pageview,,1,0,0,0
access_website_crm_pageview_manager,access_website_crm_pageview_manager,model_website_crm_pageview,marketing.group_marketing_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_website_crm_score access_website_crm_score model_website_crm_score 1 0 0 0
3 access_website_crm_score_manager access_website_crm_score_manager model_website_crm_score base.group_website_designer 1 1 1 1
4 access_section_user access_team_user access_section_user access_team_user model_section_user model_team_user 1 0 0 0
5 access_section_user_manager access_team_user_manager access_section_user_manager access_team_user_manager model_section_user model_team_user marketing.group_marketing_manager 1 1 1 1
6 access_website_crm_pageview access_website_crm_pageview model_website_crm_pageview 1 0 0 0
7 access_website_crm_pageview_manager access_website_crm_pageview_manager model_website_crm_pageview marketing.group_marketing_manager 1 1 1 1
@@ -1,304 +0,0 @@
openerp.website_crm_score = function(instance) {
var _t = instance.web._t,
_lt = instance.web._lt;
var QWeb = instance.web.qweb;
instance.website_crm_score = {};
instance.website_crm_score.filters = instance.web_kanban.AbstractField.extend({
/**
bnf grammar of a filter:
<filter> ::= <expr>
<expr> ::= <tuple> | '!' <expr> | <bin_op> <expr> <expr>
<bin_op> ::= '&' | '|'
<tuple> ::= '(' <field_name> ',' <operator> ',' <field_value> ')'
<operator> ::= '=' | '!=' | '<=' | '<' | '>' | '>=' | '=?' |
'=like' | '=ilike' | 'like' | 'not like' |
'ilike' | 'not ilike' | 'in' | 'not in' | 'child_of'
some operators are negative
*/
NEG_OP: ['!=', 'not like', 'not ilike', 'not in'],
MAX_LEN: 5,
start: function() {
var val = this.field.raw_value;
var self = this;
if (val) {
val = eval(val);
if (val.length <= this.MAX_LEN) {
var i = 0;
while (i < val.length) {
var res = this.interpret(val, i);
i = res[0];
var $span = res[1];
// var $span = '<h2>' + res[1] + '</h2>';
self.$el.append($span);
}
}
else {
var $span = '<span class="oe_tag" style="background-color:#fce9e9;">Domain too big<span>';
self.$el.append($span);
}
}
},
interpret: function(val, i) {
var a = val[i];
if(typeof a !== 'string'){
// a is a tuple (field, op, value)
var tag = a[0]; // field name
var tip = a[2]; // field value
if (this.NEG_OP.indexOf(a[1]) !== -1){
// op in NEG_OP
tip = 'not ' + tip;
}
var span = '<span class="oe_tag" title="' + tip + '">'+ tag +'</span>';
// var span = '<span class="label label-info" title="' + tip + '">'+ tag +'</span>';
return [i+1, span];
}
else if (a === '!'){
var res = this.interpret(val, i+1);
// var span = '<span class="label label-danger">' + res[1] + '</span>';
var span = '<span class="oe_tag">!</span>' + res[1];
// var span = '<span class="label label-danger" style="border-style: solid; border-width: 1px; border-color: #666;">' + res[1] + '</span>';
return [res[0], span];
}
else {
// binary operator
var res = this.binary_operator(val, i);
return res;
}
return [i+1, ''];
},
binary_operator: function(val, i) {
var resA = this.interpret(val, i+1);
var resB = this.interpret(val, resA[0]);
var label = '';
var op = '';
if (val[i] === '|') {
label = 'label-success';
op = ' or ';
}
else if (val[i] === '&') {
label = 'label-primary';
op = ' and ';
}
// var span = '<span class="label ' + label + '"">' + resA[1] + ' ' + resB[1] + '</span>';
var span = resA[1] + resB[1];
return [resB[0], span];
}
});
instance.web_kanban.fields_registry.add('filters', 'instance.website_crm_score.filters');
instance.website_crm_score.smart_filters = instance.web_kanban.AbstractField.extend({
/**
bnf grammar of a filter:
<filter> ::= <expr>
<expr> ::= <tuple> | '!' <expr> | <bin_op> <expr> <expr>
<bin_op> ::= '&' | '|'
<tuple> ::= '(' <field_name> ',' <operator> ',' <field_value> ')'
<operator> ::= '=' | '!=' | '<=' | '<' | '>' | '>=' | '=?' |
'=like' | '=ilike' | 'like' | 'not like' |
'ilike' | 'not ilike' | 'in' | 'not in' | 'child_of'
some operators are negative
the widget works in 3 steps (all 3 are recursive):
- parse the filter and generate a tree
- flatten the tree to a certain point
- format the tree so that it can be displayed
- parsing:
done by interpret()
the general representation are lists with list[0] a marker and list[1:] being arguments
a tuple is represented as ['d', tip, tag]
tag being the field name
tip being the field value
note: a negative statement is cast into a negation as described below
eg: [(field, '!=', value)] = ['not', ['d', tag, tip]]
a negation is represented as ['not', elem]
elem being a list (eg : ['d', tip, tag], ['not', elem], ...)
a binary operation (or/and) is represented as ['or/and', elem1, elem2]
elem1 and elem2 are the operands of the or/and
example:
- ['&', ['|', ['d',..], ['d',..] ], ['d',..] ] = ['&', '|', d1, d2, d3]
- flattening:
done by flatten()
if similar operations are successive, then the lists are flatten
exemple:
- ['&', ['&', d1, d2], d3 ] = ['&', d1, d2, d3]
- ['not', ['not', d1]] = d1
- formatting
done by format_domain()
the list somehow represents a tree due to its format ([marker, args..])
browses a list/tree and returns an html representation
this is fairly simple as it only needs to match the marker and act accordinly
*/
NEG_OP: ['!=', 'not like', 'not ilike', 'not in'],
MAX_LEN: 10,
start: function() {
var val = this.field.raw_value;
var self = this;
if (val) {
val = eval(val);
if (val.length <= this.MAX_LEN) {
var i = 0;
while (i < val.length) {
var res = this.interpret(val, i);
// the returned index i is the index of the first elem not parsed yet in val
i = res[0];
var flat = this.flatten(res[1]);
var form = this.format_domain(flat);
var $span = form;
self.$el.append($span);
}
}
else {
var $span = '<span class="oe_tag" style="background-color:#fce9e9;">Domain too big<span>';
self.$el.append($span);
}
}
},
interpret: function(val, i) {
// returns the index of the next element to parse and the representation of the parsed element
var a = val[i];
if(typeof a !== 'string'){
// a is a tuple (field, op, value)
var tag = a[0]; // field name
var tip = a[2]; // field value
var span = ['d', tip, tag];
if (this.NEG_OP.indexOf(a[1]) !== -1){
// op in NEG_OP
span = ['not', span];
}
return [i+1, span];
}
else if (a === '!'){
var res = this.interpret(val, i+1);
var span = ['not', res[1]];
return [res[0], span];
}
else {
// binary operator ( | or & )
var res = this.binary_operator(val, i);
return res;
}
return [i+1, ''];
},
binary_operator: function(val, i) {
var resA = this.interpret(val, i+1);
var resB = this.interpret(val, resA[0]);
var label = '';
var op = '';
if (val[i] === '|') {
label = 'label-success';
op = 'or';
}
else if (val[i] === '&') {
label = 'label-primary';
op = 'and';
}
var span = [op, resA[1], resB[1]];
return [resB[0], span];
},
flatten: function(t) {
/**
Python code (which is easier to read and understand):
/!\ in the following code, a domain is a string in dom instead of an array ['d', tag, tip] like in js
it explains the differences but the logic is the same
def flatten(t):
op = t[0]
for c in t[1:]:
if not c in dom:
fc = flatten(c)
t.remove(c)
if fc[0] == op:
if op == 'not':
t = fc[1]
else:
t.extend(fc[1:])
else:
t.append(fc)
return t
*/
var self = this;
var op = t[0];
if (op != 'd') {
var ts = t.slice(1);
_.each(ts, function(c) {
if (c[0] != 'd') {
var fc = self.flatten(c);
var idx = t.indexOf(c);
t.splice(idx, 1);
if (fc[0] == op) {
if (op == 'not') {
t = fc[1];
}
else {
t.push.apply(t,fc.slice(1));
}
}
else {
t.push(fc);
}
}
});
}
return t;
},
format_domain: function(a) {
var self = this;
var style = 'padding:1px; border-style: solid; border-width:1px; border-color=black; text-align:center; border-radius: 4px;';
if (a[0] == 'd'){
/** format here what a domain should look like
a[1] : tip
a[2] : tag
*/
var span = '<span class="oe_tag" title="' + a[1] + '">'+ a[2] +'</span>';
return span;
}
else if (a[0] == 'not') {
var res = this.format_domain(a[1]);
/** format here what a negation should look like
res is the expression that is negated
*/
var color = '#d66';
var span = '<div style="background-color:' + color + '; ' + style + '">' + res + '</div>';
return span;
}
else if (a[0] == 'and' || a[0] == 'or') {
var label = '';
var color = '';
/** format here what an or/and shoud look like
as there can be more than 2 operands, it is needed to loop over a[1:]
*/
if (a[0] == 'or') {
color = '#5c5';
// label = 'label-success';
}
else if (a[0] == 'and') {
color = '#77e';
// label = 'label-primary';
}
var span = '<div style="background-color:' + color + '; ' + style + '">';
var as = a.slice(1);
_.each(as, function(s) {
var res = self.format_domain(s);
span += res;
});
span += '</div>';
return span;
}
},
});
instance.web_kanban.fields_registry.add('smart_filters', 'instance.website_crm_score.smart_filters');
}
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
from . import test_assign, test_dry_run, test_pageview
checks = [
+29 -27
View File
@@ -1,26 +1,28 @@
# -*- coding: utf-8 -*-
from openerp.tests import common
from mock import Mock
class TestScoring(common.TransactionCase):
def _init_(self):
pass
# self._build_email_args_list = []
# self._build_email_kwargs_list = []
def setUp(self):
super(TestScoring, self).setUp()
cr, uid = self.cr, self.uid
cr.commit = Mock(return_value=None)
# empty tables before testing to only use test records
cr.execute("""
UPDATE res_partner SET section_id=NULL;
UPDATE res_partner SET team_id=NULL;
""")
cr.execute("""
TRUNCATE TABLE section_user;
TRUNCATE TABLE team_user;
""")
cr.execute("""
DELETE FROM crm_case_section;
DELETE FROM crm_team;
""")
cr.execute("""
DELETE FROM crm_lead;
@@ -37,16 +39,16 @@ class TestScoring(common.TransactionCase):
self.leads_dry_run = self.registry('leads.dry.run')
self.pageview = self.registry('website.crm.pageview')
self.website_crm_score = self.registry('website.crm.score')
self.section = self.registry('crm.case.section')
self.team = self.registry('crm.team')
self.res_users = self.registry('res.users')
self.section_user = self.registry('section.user')
self.team_user = self.registry('team.user')
self.country = self.registry('res.country')
self.crm_case_stage = self.registry('crm.case.stage')
self.crm_stage = self.registry('crm.stage')
self.belgium = self.country.search(cr, uid, [('name', '=', 'Belgium')])[0]
self.france = self.country.search(cr, uid, [('name', '=', 'France')])[0]
self.stage = self.crm_case_stage.create(cr, uid, {
self.stage = self.crm_stage.create(cr, uid, {
'name': 'testing',
'probability': '50',
'on_change': False,
@@ -86,15 +88,15 @@ class TestScoring(common.TransactionCase):
})
# Salesteam
self.section0 = self.section.create(cr, uid, {
'name': 'section0',
self.team0 = self.team.create(cr, uid, {
'name': 'team0',
'code': 'S0',
'score_section_domain': [('country_id', '=', 'Belgium')],
'score_team_domain': [('country_id', '=', 'Belgium')],
})
self.section1 = self.section.create(cr, uid, {
'name': 'section1',
self.team1 = self.team.create(cr, uid, {
'name': 'team1',
'code': 'S1',
'score_section_domain': [('country_id', '=', 'France')],
'score_team_domain': [('country_id', '=', 'France')],
})
# Salesmen
@@ -113,22 +115,22 @@ class TestScoring(common.TransactionCase):
# 'groups_id': [(6, 0, [self.group_employee_id])]
}, {'no_reset_password': True})
# Section_user
self.section_user0 = self.section_user.create(cr, uid, {
# team_user
self.team_user0 = self.team_user.create(cr, uid, {
'user_id': self.salesmen0,
'section_id': self.section0,
'team_id': self.team0,
'maximum_user_leads': 1,
'section_user_domain': [('country_id', '=', 'Belgium')],
'team_user_domain': [('country_id', '=', 'Belgium')],
})
self.section_user1 = self.section_user.create(cr, uid, {
self.team_user1 = self.team_user.create(cr, uid, {
'user_id': self.salesmen1,
'section_id': self.section0,
'team_id': self.team0,
'maximum_user_leads': 0,
'section_user_domain': [('country_id', '=', 'France')],
'team_user_domain': [('country_id', '=', 'France')],
})
self.section_user2 = self.section_user.create(cr, uid, {
self.team_user2 = self.team_user.create(cr, uid, {
'user_id': self.salesmen1,
'section_id': self.section1,
'team_id': self.team1,
'maximum_user_leads': 1,
})
+11 -8
View File
@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
from openerp.addons.website_crm_score.tests.common import TestScoring
from openerp.tools import mute_logger
class test_assign(TestScoring):
def test_assign(self):
@mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
def test_00_assign(self):
cr, uid = self.cr, self.uid
# scoring
self.website_crm_score.assign_scores_to_leads(cr, uid)
@@ -17,15 +20,15 @@ class test_assign(TestScoring):
self.assertEqual(l2['score'], 0, 'scoring failed')
# assignation
self.section.direct_assign_leads(cr, uid, None)
self.team.direct_assign_leads(cr, uid, None)
l0 = self.crm_lead.read(cr, uid, self.lead0, ['section_id', 'user_id'], None)
l1 = self.crm_lead.read(cr, uid, self.lead1, ['section_id', 'user_id'], None)
l2 = self.crm_lead.read(cr, uid, self.lead2, ['section_id', 'user_id'], None)
l0 = self.crm_lead.read(cr, uid, self.lead0, ['team_id', 'user_id'], None)
l1 = self.crm_lead.read(cr, uid, self.lead1, ['team_id', 'user_id'], None)
l2 = self.crm_lead.read(cr, uid, self.lead2, ['team_id', 'user_id'], None)
self.assertEqual(l0['section_id'][0], self.section0, 'assignation failed')
self.assertEqual(l1['section_id'][0], self.section1, 'assignation failed')
self.assertEqual(l2['section_id'], False, 'assignation failed')
self.assertEqual(l0['team_id'][0], self.team0, 'assignation failed')
self.assertEqual(l1['team_id'][0], self.team1, 'assignation failed')
self.assertEqual(l2['team_id'], False, 'assignation failed')
self.assertEqual(l0['user_id'][0], self.salesmen0, 'assignation failed')
self.assertEqual(l1['user_id'][0], self.salesmen1, 'assignation failed')
+16 -13
View File
@@ -1,9 +1,12 @@
# -*- coding: utf-8 -*-
from openerp.addons.website_crm_score.tests.common import TestScoring
from openerp.tools import mute_logger
class test_dry_run(TestScoring):
def test_dry_run(self):
@mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
def test_00_dry_run(self):
cr, uid = self.cr, self.uid
# scoring
@@ -18,9 +21,9 @@ class test_dry_run(TestScoring):
self.assertEqual(l2['score'], 0, 'scoring failed')
# dry run
self.section.dry_assign_leads(cr, uid, None)
self.team.dry_assign_leads(cr, uid, None)
fields = ['section_id', 'user_id', 'lead_id']
fields = ['team_id', 'user_id', 'lead_id']
dr0_sect = self.leads_dry_run.search_read(cr, uid, [('lead_id', '=', self.lead0), ('user_id', '=', False)], fields)[0]
dr0_user = self.leads_dry_run.search_read(cr, uid, [('lead_id', '=', self.lead0), ('user_id', '!=', False)], fields)[0]
@@ -30,12 +33,12 @@ class test_dry_run(TestScoring):
dr2 = self.leads_dry_run.search_read(cr, uid, [('lead_id', '=', self.lead2)], fields)
self.assertEqual(dr0_sect['section_id'][0], self.section0, 'dry run failed')
self.assertEqual(dr0_user['section_id'][0], self.section0, 'dry run failed')
self.assertEqual(dr0_sect['team_id'][0], self.team0, 'dry run failed')
self.assertEqual(dr0_user['team_id'][0], self.team0, 'dry run failed')
self.assertEqual(dr0_user['user_id'][0], self.salesmen0, 'dry run failed')
self.assertEqual(dr1_sect['section_id'][0], self.section1, 'dry run failed')
self.assertEqual(dr1_user['section_id'][0], self.section1, 'dry run failed')
self.assertEqual(dr1_sect['team_id'][0], self.team1, 'dry run failed')
self.assertEqual(dr1_user['team_id'][0], self.team1, 'dry run failed')
self.assertEqual(dr1_user['user_id'][0], self.salesmen1, 'dry run failed')
self.assertEqual(dr2, [], 'dry run failed')
@@ -46,13 +49,13 @@ class test_dry_run(TestScoring):
# assignement after dry run
self.leads_dry_run.assign_leads(cr, uid, None)
l0 = self.crm_lead.read(cr, uid, self.lead0, ['section_id', 'user_id'], None)
l1 = self.crm_lead.read(cr, uid, self.lead1, ['section_id', 'user_id'], None)
l2 = self.crm_lead.read(cr, uid, self.lead2, ['section_id', 'user_id'], None)
l0 = self.crm_lead.read(cr, uid, self.lead0, ['team_id', 'user_id'], None)
l1 = self.crm_lead.read(cr, uid, self.lead1, ['team_id', 'user_id'], None)
l2 = self.crm_lead.read(cr, uid, self.lead2, ['team_id', 'user_id'], None)
self.assertEqual(l0['section_id'][0], self.section0, 'assignation failed')
self.assertEqual(l1['section_id'][0], self.section1, 'assignation failed')
self.assertEqual(l2['section_id'], False, 'assignation failed')
self.assertEqual(l0['team_id'][0], self.team0, 'assignation failed')
self.assertEqual(l1['team_id'][0], self.team1, 'assignation failed')
self.assertEqual(l2['team_id'], False, 'assignation failed')
self.assertEqual(l0['user_id'][0], self.salesmen0, 'assignation failed')
self.assertEqual(l1['user_id'][0], self.salesmen1, 'assignation failed')
@@ -1,21 +1,26 @@
# -*- coding: utf-8 -*-
from openerp.addons.website_crm_score.tests.common import TestScoring
from psycopg2 import IntegrityError
from openerp.tools import mute_logger
class test_assign(TestScoring):
def test_pageview(self):
@mute_logger('openerp.addons.base.ir.ir_model', 'openerp.models')
def test_00_pageview(self):
cr, uid = self.cr, self.uid
right_lead = self.pageview.create_pageview(cr, uid, {'lead_id': self.lead0, 'url':'url2'}, test=True)
right_lead = self.pageview.create_pageview(cr, uid, {'lead_id': self.lead0, 'url': 'url2'}, test=True)
self.assertTrue(right_lead, 'create_pageview should have succeded')
pg0 = self.pageview.search_read(cr, uid, [('lead_id', '=', self.lead0), ('url', '=', 'url2')], ['view_date'])
self.assertNotEqual(pg0, [], 'pageview was not created')
update_lead = self.pageview.create_pageview(cr, uid, {'lead_id': self.lead0, 'url':'url2'}, test=True)
update_lead = self.pageview.create_pageview(cr, uid, {'lead_id': self.lead0, 'url': 'url2'}, test=True)
self.assertTrue(update_lead, 'create_pageview should have updated the lead')
# with self.assertRaises(IntegrityError):
# self.pageview.create_pageview(cr, uid, {'lead_id': -1, 'url':'url2'}, test=True)
self.assertFalse(
self.pageview.create_pageview(cr, uid, {'lead_id': -1, 'url': 'url2'}, test=True),
'create_pageview should not works with unexisting lead id'
)
+34 -34
View File
@@ -13,7 +13,7 @@
<field name="interval_type">days</field>
<field name="numbercall">-1</field>
<field eval="False" name="doall" />
<field eval="'crm.case.section'" name="model" />
<field eval="'crm.team'" name="model" />
<field eval="'direct_assign_leads'" name="function" />
</record>
@@ -115,12 +115,12 @@
<menuitem id="scores_menu" name="Scoring Rules" parent="leads_menu" action="score_action" />
<!--
SECTION USER
TEAM USER
-->
<!-- Section User From View -->
<record model="ir.ui.view" id="view_crm_section_user_form">
<field name="name">Section User Form</field>
<field name="model">section.user</field>
<!-- Team User From View -->
<record model="ir.ui.view" id="view_crm_team_user_form">
<field name="name">Team User Form</field>
<field name="model">team.user</field>
<field name="arch" type="xml">
<form string="Sales Men">
<sheet>
@@ -128,26 +128,26 @@
<field name="user_id"/>
</h1>
<group>
<field name="section_id" readonly="context.get('ro_section', False)"/>
<field name="team_id" readonly="context.get('ro_team', False)"/>
<label for="maximum_user_leads" String="Maximum Leads / 30 days" />
<div>
<field name="maximum_user_leads" class="oe_inline"/>
</div>
<!--<field name="section_user_domain" widget="char_domain" options="{'model': 'crm.lead'}"/>-->
<field name="section_user_domain" />
<!--<field name="team_user_domain" widget="char_domain" options="{'model': 'crm.lead'}"/>-->
<field name="team_user_domain" />
</group>
</sheet>
</form>
</field>
</record>
<!-- Section User Tree View -->
<record model="ir.ui.view" id="view_crm_section_user_tree">
<field name="name">Section User Tree</field>
<field name="model">section.user</field>
<!-- team User Tree View -->
<record model="ir.ui.view" id="view_crm_team_user_tree">
<field name="name">Team User Tree</field>
<field name="model">team.user</field>
<field name="arch" type="xml">
<tree string="Sales Men">
<field name="section_id"/>
<field name="team_id"/>
<field name="user_id"/>
<field name="maximum_user_leads"/>
<field name="leads_count"/>
@@ -155,10 +155,10 @@
</field>
</record>
<!-- Section User Kanban View -->
<record model="ir.ui.view" id="section_user_kanban">
<field name="name">section.user.kanban</field>
<field name="model">section.user</field>
<!-- Team User Kanban View -->
<record model="ir.ui.view" id="team_user_kanban">
<field name="name">team.user.kanban</field>
<field name="model">team.user</field>
<field name="arch" type="xml">
<kanban quick_create="false" create="true">
<field name="user_id"/>
@@ -204,9 +204,9 @@
<b><field name="name"/></b>
</a></li>
<li><a type="open">
<field name="section_id"/>
<field name="team_id"/>
</a></li>
<li><field name="section_user_domain" widget='filters'/></li>
<li><field name="team_user_domain" widget='filters'/></li>
</ul>
</td>
<td>
@@ -227,10 +227,10 @@
</field>
</record>
<!-- Section User Action - form - tree -->
<record model="ir.actions.act_window" id="section_user_action">
<!-- Team User Action - form - tree -->
<record model="ir.actions.act_window" id="team_user_action">
<field name="name">Sales Men</field>
<field name="res_model">section.user</field>
<field name="res_model">team.user</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,tree,form</field>
<field name="help" type="html">
@@ -243,20 +243,20 @@
</field>
</record>
<!-- Section User Menu Item -->
<menuitem id="section_user" name="Leads Assignation" parent="leads_menu" action="section_user_action" />
<!-- Team User Menu Item -->
<menuitem id="team_user" name="Leads Assignation" parent="leads_menu" action="team_user_action" />
<!-- SECTION -->
<!-- Section Action - form - tree -->
<record model="ir.actions.act_window" id="section_action">
<!-- TEAM -->
<!-- Team Action - form - tree -->
<record model="ir.actions.act_window" id="team_action">
<field name="name">Sales Teams</field>
<field name="res_model">crm.case.section</field>
<field name="res_model">crm.team</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<!-- Section User Menu Item -->
<menuitem id="section" name="Teams Assignation" parent="leads_menu" action="section_action" />
<!-- Team User Menu Item -->
<menuitem id="team" name="Teams Assignation" parent="leads_menu" action="team_action" />
<!-- LEAD DRY RUN -->
@@ -271,7 +271,7 @@
<field name="lead_id"/>
</h1>
<group>
<field name="section_id"/>
<field name="team_id"/>
<field name="user_id"/>
<button name="assign_leads" type="object" string="Assign Now" class="oe_link"/>
</group>
@@ -287,7 +287,7 @@
<field name="arch" type="xml">
<tree string="Assignation Dry Run">
<field name="lead_id"/>
<field name="section_id"/>
<field name="team_id"/>
<field name="user_id"/>
</tree>
</field>
@@ -301,7 +301,7 @@
<field name="view_mode">tree,form</field>
</record>
<!-- Section User Menu Item -->
<!-- Team User Menu Item -->
<menuitem id="leads_dry_run" name="Assignation dry run" parent="leads_menu" action="lead_dry_run_action" groups="base.group_no_one"/>
</data>
+2 -2
View File
@@ -30,7 +30,7 @@
</h1>
<group>
<field name="lead_id"/>
<field name="partner_id"/>
<field name="user_id"/>
<field name="view_date"/>
</group>
</sheet>
@@ -56,7 +56,7 @@
<field name="arch" type="xml">
<tree string="Page Views">
<field name="lead_id"/>
<field name="partner_id"/>
<field name="user_id"/>
<field name="url"/>
<field name="view_date"/>
</tree>
+16 -16
View File
@@ -29,7 +29,7 @@
<field name="phone" position="after">
<field name="score" />
</field>
<field name="section_id" position="after">
<field name="team_id" position="after">
<field name="user_id" />
</field>
</field>
@@ -71,7 +71,7 @@
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.crm_case_tree_view_oppor" />
<field name="arch" type="xml">
<field name="section_id" position="after">
<field name="team_id" position="after">
<field name="score" />
</field>
</field>
@@ -101,14 +101,14 @@
<!-- SALESTEAM -->
<!-- SalesTeam Kanban View - Lead information added -->
<record id="crm_case_section_salesteams_view_kanban_inherit" model="ir.ui.view">
<field name="name">crm.case.section.kanban.inherit</field>
<field name="model">crm.case.section</field>
<field name="inherit_id" ref="sales_team.crm_case_section_salesteams_view_kanban"/>
<record id="crm_team_salesteams_view_kanban_inherit" model="ir.ui.view">
<field name="name">crm.team.kanban.inherit</field>
<field name="model">crm.team</field>
<field name="inherit_id" ref="sales_team.crm_team_salesteams_view_kanban"/>
<field name="arch" type="xml">
<data>
<field name="member_ids" position="after">
<field name="section_user_ids"/>
<field name="team_user_ids"/>
</field>
<xpath expr="//div[@class='oe_clear']" position="after">
<div class="oe_items_list">
@@ -175,21 +175,21 @@
<!-- SalesTeam Form View - Lead information and page Assignation added -->
<record model="ir.ui.view" id="sales_team_form_view_assign">
<field name="name">crm.case.section.form.inherit</field>
<field name="model">crm.case.section</field>
<field name="inherit_id" ref="sales_team.crm_case_section_view_form"/>
<field name="name">crm.team.form.inherit</field>
<field name="model">crm.team</field>
<field name="inherit_id" ref="sales_team.crm_team_view_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@class='oe_title']" position="before">
<div class="oe_right oe_button_box" name="buttons">
<button class="oe_inline oe_stat_button" type="action"
name="%(crm_score_views_action_graph)d" icon="fa-tags"
context="{'search_default_section_id': active_id, 'search_default_unassigned': 1 }"
context="{'search_default_team_id': active_id, 'search_default_unassigned': 1 }"
>
<field string="Unassigned leads" name="unassigned_leads" widget="statinfo"/>
</button>
<button class="oe_inline oe_stat_button" type="action"
name="%(crm_score_views_action_table)d" icon="fa-tags"
context="{'search_default_section_id': active_id}"
context="{'search_default_team_id': active_id}"
>
<field string="Opportunities" name="leads_count" widget="statinfo"/>
</button>
@@ -197,7 +197,7 @@
</xpath>
<xpath expr="//group[@name='left']" position="after">
<group name="right">
<field name="score_section_domain" options="{'model': 'crm.lead'}"/>
<field name="score_team_domain" options="{'model': 'crm.lead'}"/>
<label for="assigned_leads"/>
<div>
<field name="assigned_leads" class="oe_inline"/> /
@@ -217,7 +217,7 @@
</xpath>
<xpath expr="//page[@string='Team Members']" position="replace">
<page string="Assignation">
<field name="section_user_ids" widget="many2many_kanban" context="{'default_section_id':active_id, 'ro_section':True}">
<field name="team_user_ids" widget="many2many_kanban" context="{'default_team_id':active_id, 'ro_team':True}">
<kanban quick_create="true" create="true" delete="true">
<field name="user_id"/>
<field name="running"/>
@@ -261,8 +261,8 @@
<li><a type="open">
<b><field name="name"/></b>
</a></li>
<!-- <li><field name="section_user_domain" widget="smart_filters"/></li> -->
<li><field name="section_user_domain" widget='filters'/></li>
<!-- <li><field name="team_user_domain" widget="smart_filters"/></li> -->
<li><field name="team_user_domain" widget='filters'/></li>
</ul>
</td>
<td>
@@ -8,10 +8,5 @@
</xpath>
</template>
<template id="assets_backend" name="web_kanban_gauge assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/website_crm_score/static/src/js/display_filters.js"></script>
</xpath>
</template>
</data>
</openerp>