1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Affero General Public License for more details.
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 ##############################################################################
23 from osv import fields
26 from tools.translate import _
30 regex = '<coordinates>([+-]?[0-9\.]+),([+-]?[0-9\.]+),([+-]?[0-9\.]+)</coordinates>'
31 url = 'http://maps.google.com/maps/geo?q=' + urllib.quote(addr) + '&output=xml&oe=utf8&sensor=false'
32 xml = urllib.urlopen(url).read()
35 result = re.search(regex, xml, re.M|re.I)
38 return float(result.group(1)),float(result.group(2))
40 raise osv.except_osv(_('Network error'),
41 _('Could not contact geolocation servers, please make sure you have a working internet connection (%s)') % e)
44 class res_partner_grade(osv.osv):
46 _name = 'res.partner.grade'
48 'sequence': fields.integer('Sequence'),
49 'active': fields.boolean('Active'),
50 'name': fields.char('Grade Name', size=32)
53 'active': lambda *args: 1
58 class res_partner(osv.osv):
59 _inherit = "res.partner"
61 'partner_latitude': fields.float('Geo Latitude'),
62 'partner_longitude': fields.float('Geo Longitude'),
63 'date_localization': fields.date('Geo Localization Date'),
64 'partner_weight': fields.integer('Weight',
65 help="Gives the probability to assign a lead to this partner. (0 means no assignation.)"),
66 'opportunity_assigned_ids': fields.one2many('crm.lead', 'partner_assigned_id',\
67 'Assigned Opportunities'),
68 'grade_id': fields.many2one('res.partner.grade', 'Partner Grade')
71 'partner_weight': lambda *args: 0
73 def geo_localize(self, cr, uid, ids, context=None):
74 for partner in self.browse(cr, uid, ids, context=context):
75 if not partner.address:
77 part = partner.address[0]
78 addr = ', '.join(filter(None, [part.street, (part.zip or '')+' '+(part.city or ''), part.state_id and part.state_id.name, part.country_id and part.country_id.name]))
79 result = geo_find(addr.encode('utf8'))
81 self.write(cr, uid, [partner.id], {
82 'partner_latitude': result[0],
83 'partner_longitude': result[1],
84 'date_localization': time.strftime('%Y-%m-%d')
89 class crm_lead(osv.osv):
92 'partner_latitude': fields.float('Geo Latitude'),
93 'partner_longitude': fields.float('Geo Longitude'),
94 'partner_assigned_id': fields.many2one('res.partner', 'Assigned Partner', help="Partner this case has been forwarded/assigned to.", select=True),
95 'date_assign': fields.date('Assignation Date', help="Last date this case was forwarded/assigned to a partner"),
97 def onchange_assign_id(self, cr, uid, ids, partner_assigned_id, context={}):
98 """This function updates the "assignation date" automatically, when manually assign a partner in the geo assign tab
99 @param self: The object pointer
100 @param cr: the current row, from the database cursor,
101 @param uid: the current user’s ID for security checks,
102 @param ids: List of stage’s IDs
103 @stage_id: change state id on run time """
105 if not partner_assigned_id:
106 return {'value':{'date_assign': False}}
108 return {'value':{'date_assign': time.strftime('%Y-%m-%d')}}
110 def assign_partner(self, cr, uid, ids, context=None):
112 for part in self.browse(cr, uid, ids, context=context):
113 if not part.country_id:
115 addr = ', '.join(filter(None, [part.street, (part.zip or '')+' '+(part.city or ''), part.state_id and part.state_id.name, part.country_id and part.country_id.name]))
116 result = geo_find(addr.encode('utf8'))
118 self.write(cr, uid, [part.id], {
119 'partner_latitude': result[0],
120 'partner_longitude': result[1]
123 # 1. first way: in the same country, small area
124 part_ids = self.pool.get('res.partner').search(cr, uid, [
125 ('partner_weight','>',0),
126 ('partner_latitude','>',result[0]-2), ('partner_latitude','<',result[0]+2),
127 ('partner_longitude','>',result[1]-1.5), ('partner_longitude','<',result[1]+1.5),
128 ('country', '=', part.country_id.id),
131 # 2. second way: in the same country, big area
133 part_ids = self.pool.get('res.partner').search(cr, uid, [
134 ('partner_weight','>',0),
135 ('partner_latitude','>',result[0]-4), ('partner_latitude','<',result[0]+4),
136 ('partner_longitude','>',result[1]-3), ('partner_longitude','<',result[1]+3),
137 ('country', '=', part.country_id.id),
140 # 3. third way: other countries, small area
142 part_ids = self.pool.get('res.partner').search(cr, uid, [
143 ('partner_weight','>',0),
144 ('partner_latitude','>',result[0]-2), ('partner_latitude','<',result[0]+2),
145 ('partner_longitude','>',result[1]-1.5), ('partner_longitude','<',result[1]+1.5)
148 # 4. fourth way: other countries, big area
150 part_ids = self.pool.get('res.partner').search(cr, uid, [
151 ('partner_weight','>',0),
152 ('partner_latitude','>',result[0]-4), ('partner_latitude','<',result[0]+4),
153 ('partner_longitude','>',result[1]-3), ('partner_longitude','<',result[1]+3)
156 # 5. fifth way: anywhere in same country
158 # still haven't found any, let's take all partners in the country!
159 part_ids = self.pool.get('res.partner').search(cr, uid, [
160 ('partner_weight','>',0),
161 ('country', '=', part.country_id.id),
164 # 6. sixth way: closest partner whatsoever, just to have at least one result
166 # warning: point() type takes (longitude, latitude) as parameters in this order!
167 cr.execute("""SELECT id, distance
168 FROM (select id, (point(partner_longitude, partner_latitude) <-> point(%s,%s)) AS distance FROM res_partner
169 WHERE partner_longitude is not null
170 AND partner_latitude is not null
171 AND partner_weight > 0) AS d
172 ORDER BY distance LIMIT 1""", (result[1],result[0]))
173 res = cr.dictfetchone()
175 part_ids.append(res['id'])
179 for part2 in self.pool.get('res.partner').browse(cr, uid, part_ids, context=context):
180 total += part2.partner_weight
181 toassign.append( (part2.id, total) )
182 random.shuffle(toassign) # avoid always giving the leads to the first ones in db natural order!
183 mypartner = random.randint(0,total)
186 self.write(cr, uid, [part.id], {'partner_assigned_id': t[0], 'date_assign': time.strftime('%Y-%m-%d')}, context=context)