diff --git a/partner_external_map/README.rst b/partner_external_map/README.rst index 17bb47786..f55c2f471 100644 --- a/partner_external_map/README.rst +++ b/partner_external_map/README.rst @@ -6,47 +6,52 @@ Partner External Maps ===================== -In the old days of Odoo/OpenERP, back in version 6.1, there was an official *google_map* module ; this module added a *Map* button on the partner form view and, when the user clicked on that button, it would open a new tab on its web browser and go to Google Map with a search on the address of the partner. +In the old days of Odoo/OpenERP, back in version 6.1, there was an official +*google_map* module ; this module added a *Map* button on the partner form view +and, when the user clicked on that button, it would open a new tab on its web +browser and go to Google Map with a search on the address of the partner. This module aims at restoring this feature with several improvements: -* each user can select the map website he wants to use in its preferences - -* there are now two buttons on the partner form view: one to open a regular map on the address of the partner, and another one to open an itinerary map from the start address configured in the preferences of the user to the address of the partner. +* Each user can select the map website he wants to use in its preferences +* There are now two buttons on the partner form view: one to open a regular map + on the address of the partner, and another one to open an itinerary map from + the start address configured in the preferences of the user to the address of + the partner. This module supports several map websites: * `Google Maps ` - * `OpenStreetMap ` - * `Bing Maps ` - * `Here Maps ` - * `MapQuest ` - * `Yahoo! Maps ` -If the module *base_geolocalize* from the official addons is installed on the system, it will use the latitude and longitude to localize the partner (instead of the address) if this information is present on the partner. +If the module *base_geolocalize* from the official addons is installed on the +system, it will use the latitude and longitude to localize the partner (instead +of the address) if this information is present on the partner. Configuration ============= -If you want to create additionnal map websites, go to the menu *Sales > Configuration > Address Book > Localization > Map Websites*. You are invited to send the configuration information of your additionnal map websites to the author of the module, so that the module can be updated with more pre-configured map websites. +If you want to create additionnal map websites, go to the menu +*Sales > Configuration > Address Book > Localization > Map Websites*. You are +invited to send the configuration information of your additionnal map websites +to the author of the module, so that the module can be updated with more +pre-configured map websites. Usage ===== First, you need to configure in your preferences: -* the map website to use for the regular maps, - -* the map website to use for the route maps, - -* the start address for the route maps. +* The map website to use for the regular maps, +* The map website to use for the route maps, +* The start address for the route maps. -Then you can use the two new buttons on the partner form to open a regular map or a route map. +Then you can use the two new buttons on the partner form to open a regular map +or a route map. .. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas :alt: Try me on Runbot @@ -61,7 +66,7 @@ check there if your issue has already been reported. If you spotted it first, help us smashing it by providing a detailed and welcomed `feedback `_. Credits @@ -71,6 +76,7 @@ Contributors ------------ * Alexis de Lattre +* Pedro M. Baeza Maintainer ---------- diff --git a/partner_external_map/__init__.py b/partner_external_map/__init__.py index b951e5bb3..915bc22c8 100644 --- a/partner_external_map/__init__.py +++ b/partner_external_map/__init__.py @@ -1,4 +1,4 @@ # -*- coding: utf-8 -*- -from . import partner_external_maps -from .post_install import set_default_map_settings +from . import models +from .hooks import set_default_map_settings diff --git a/partner_external_map/__openerp__.py b/partner_external_map/__openerp__.py index 2f3da5b38..9b4fa8a05 100644 --- a/partner_external_map/__openerp__.py +++ b/partner_external_map/__openerp__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -# © 2015 Akretion (http://www.akretion.com) +# © 2015 Alexis de Lattre +# © 2016 Pedro M. Baeza # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -# @author Alexis de Lattre { 'name': 'Partner External Maps', @@ -10,14 +10,16 @@ 'license': 'AGPL-3', 'summary': 'Add Map and Map Routing buttons on partner form to ' 'open GMaps, OSM, Bing and others', - 'author': 'Akretion,Odoo Community Association (OCA)', + 'author': 'Akretion, ' + 'Tecnativa, ' + 'Odoo Community Association (OCA)', 'website': 'http://www.akretion.com', 'depends': ['base'], 'data': [ - 'partner_view.xml', - 'map_website_data.xml', - 'map_website_view.xml', - 'users_view.xml', + 'views/res_partner_view.xml', + 'views/map_website_view.xml', + 'data/map_website_data.xml', + 'views/res_users_view.xml', 'security/ir.model.access.csv', ], 'post_init_hook': 'set_default_map_settings', diff --git a/partner_external_map/map_website_data.xml b/partner_external_map/data/map_website_data.xml similarity index 94% rename from partner_external_map/map_website_data.xml rename to partner_external_map/data/map_website_data.xml index 3d8f3efa6..353b1e6e8 100644 --- a/partner_external_map/map_website_data.xml +++ b/partner_external_map/data/map_website_data.xml @@ -1,12 +1,8 @@ - + - - + @@ -59,6 +55,4 @@ https://maps.yahoo.com/directions/?o={START_ADDRESS}&d={DEST_ADDRESS} - - - + diff --git a/partner_external_map/hooks.py b/partner_external_map/hooks.py new file mode 100644 index 000000000..69e360d2f --- /dev/null +++ b/partner_external_map/hooks.py @@ -0,0 +1,33 @@ +# -*- coding: utf-8 -*- +# © 2015 Alexis de Lattre +# © 2016 Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import api, SUPERUSER_ID +import logging + + +logger = logging.getLogger(__name__) + + +def set_default_map_settings(cr, pool): + """Method called as post-install script + The default method on the field can't be used, because it would be executed + before loading map_website_data.xml, so it would not be able to set a + value""" + with api.Environment.manage(): + env = api.Environment(cr, SUPERUSER_ID, {}) + user_model = env['res.users'] + users = user_model.search([]) + logger.info('Updating user settings for maps...') + users.write({ + 'context_map_website_id': user_model._default_map_website().id, + 'context_route_map_website_id': ( + user_model._default_route_map_website().id), + }) + # Update the starting partner this way that is faster + cr.execute(""" + UPDATE res_users + SET context_route_start_partner_id = partner_id + WHERE context_route_start_partner_id IS NULL; + """) diff --git a/partner_external_map/models/__init__.py b/partner_external_map/models/__init__.py new file mode 100644 index 000000000..f727dbdc9 --- /dev/null +++ b/partner_external_map/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + +from . import map_website +from . import res_partner +from . import res_users diff --git a/partner_external_map/models/map_website.py b/partner_external_map/models/map_website.py new file mode 100644 index 000000000..44a11253e --- /dev/null +++ b/partner_external_map/models/map_website.py @@ -0,0 +1,30 @@ +# -*- coding: utf-8 -*- +# © 2015 Alexis de Lattre +# © 2016 Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api, _ + + +class MapWebsite(models.Model): + _name = 'map.website' + _description = 'Map Website' + + name = fields.Char(string='Map Website Name', required=True) + address_url = fields.Char( + string='URL that uses the address', + help="In this URL, {ADDRESS} will be replaced by the address.") + lat_lon_url = fields.Char( + string='URL that uses latitude and longitude', + help="In this URL, {LATITUDE} and {LONGITUDE} will be replaced by " + "the latitude and longitude (requires the module 'base_geolocalize')") + route_address_url = fields.Char( + string='Route URL that uses the addresses', + help="In this URL, {START_ADDRESS} and {DEST_ADDRESS} will be " + "replaced by the start and destination addresses.") + route_lat_lon_url = fields.Char( + string='Route URL that uses latitude and longitude', + help="In this URL, {START_LATITUDE}, {START_LONGITUDE}, " + "{DEST_LATITUDE} and {DEST_LONGITUDE} will be replaced by the " + "latitude and longitude of the start and destination adresses " + "(requires the module 'base_geolocalize').") diff --git a/partner_external_map/models/res_partner.py b/partner_external_map/models/res_partner.py new file mode 100644 index 000000000..e705b190f --- /dev/null +++ b/partner_external_map/models/res_partner.py @@ -0,0 +1,109 @@ +# -*- coding: utf-8 -*- +# © 2015 Alexis de Lattre +# © 2016 Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api, _ +from openerp.exceptions import Warning as UserError +import logging + + +logger = logging.getLogger(__name__) + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + @api.multi + def _address_as_string(self): + self.ensure_one() + addr = [] + if self.street: + addr.append(self.street) + if self.street2: + addr.append(self.street2) + if self.city: + addr.append(self.city) + if self.state_id: + addr.append(self.state_id.name) + if self.country_id: + addr.append(self.country_id.name) + if not addr: + raise UserError(_("Address missing on partner '%s'.") % self.name) + return ' '.join(addr) + + @api.model + def _prepare_url(self, url, replace): + assert url, 'Missing URL' + for key, value in replace.iteritems(): + if not isinstance(value, (str, unicode)): + # for latitude and longitude which are floats + value = unicode(value) + url = url.replace(key, value) + logger.debug('Final URL: %s', url) + return url + + @api.multi + def open_map(self): + map_website = self.env.user.context_map_website_id + if not map_website: + raise UserError( + _('Missing map provider: ' + 'you should set it in your preferences.')) + if (map_website.lat_lon_url and hasattr(self, 'partner_latitude') and + self.partner_latitude and self.partner_longitude): + url = self._prepare_url( + map_website.lat_lon_url, { + '{LATITUDE}': self.partner_latitude, + '{LONGITUDE}': self.partner_longitude}) + else: + if not map_website.address_url: + raise UserError( + _("Missing parameter 'URL that uses the address' " + "for map website '%s'.") % map_website.name) + url = self._prepare_url( + map_website.address_url, + {'{ADDRESS}': self._address_as_string()}) + return { + 'type': 'ir.actions.act_url', + 'url': url, + 'target': 'new', + } + + @api.multi + def open_route_map(self): + if not self.env.user.context_route_map_website_id: + raise UserError( + _('Missing route map website: ' + 'you should set it in your preferences.')) + map_website = self.env.user.context_route_map_website_id + if not self.env.user.context_route_start_partner_id: + raise UserError( + _('Missing start address for route map: ' + 'you should set it in your preferences.')) + start_partner = self.env.user.context_route_start_partner_id + if (map_website.route_lat_lon_url and + hasattr(self, 'partner_latitude') and + self.partner_latitude and self.partner_longitude and + start_partner.partner_latitude and + start_partner.partner_longitude): + url = self._prepare_url( + map_website.route_lat_lon_url, { + '{START_LATITUDE}': start_partner.partner_latitude, + '{START_LONGITUDE}': start_partner.partner_longitude, + '{DEST_LATITUDE}': self.partner_latitude, + '{DEST_LONGITUDE}': self.partner_longitude}) + else: + if not map_website.route_address_url: + raise UserError( + _("Missing route URL that uses the addresses " + "for the map website '%s'") % map_website.name) + url = self._prepare_url( + map_website.route_address_url, { + '{START_ADDRESS}': start_partner._address_as_string(), + '{DEST_ADDRESS}': self._address_as_string()}) + return { + 'type': 'ir.actions.act_url', + 'url': url, + 'target': 'new', + } diff --git a/partner_external_map/models/res_users.py b/partner_external_map/models/res_users.py new file mode 100644 index 000000000..cb01ade4f --- /dev/null +++ b/partner_external_map/models/res_users.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# © 2015 Alexis de Lattre +# © 2016 Pedro M. Baeza +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from openerp import models, fields, api, _ +import logging + +logger = logging.getLogger(__name__) + + +class ResUsers(models.Model): + _inherit = 'res.users' + + @api.model + def _default_map_website(self): + return self.env['map.website'].search([ + '|', ('address_url', '!=', False), ('lat_lon_url', '!=', False)], + limit=1) + + @api.model + def _default_route_map_website(self): + return self.env['map.website'].search([ + '|', ('route_address_url', '!=', False), + ('route_lat_lon_url', '!=', False)], limit=1) + + # begin with context_ to allow user to change it by himself + context_map_website_id = fields.Many2one( + 'map.website', string='Map Website', default=_default_map_website, + domain=['|', ('address_url', '!=', False), + ('lat_lon_url', '!=', False)]) + # We want to give the possibility to the user to have one map provider for + # regular maps and another one for routing + context_route_map_website_id = fields.Many2one( + 'map.website', string='Route Map Website', + domain=['|', ('route_address_url', '!=', False), + ('route_lat_lon_url', '!=', False)], + default=_default_route_map_website, + help="Map provided used when you click on the car icon on the partner " + "form to display an itinerary.") + context_route_start_partner_id = fields.Many2one( + 'res.partner', string='Start Address for Route Map') diff --git a/partner_external_map/partner_external_maps.py b/partner_external_map/partner_external_maps.py deleted file mode 100644 index 405e0d49b..000000000 --- a/partner_external_map/partner_external_maps.py +++ /dev/null @@ -1,193 +0,0 @@ -# -*- coding: utf-8 -*- -# © 2015 Akretion (http://www.akretion.com/) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -# @author: Alexis de Lattre - -from openerp import models, fields, api, _ -from openerp.exceptions import Warning -import logging - -logger = logging.getLogger(__name__) - - -class MapWebsite(models.Model): - _name = 'map.website' - _description = 'Map Website' - - name = fields.Char(string='Map Website Name', required=True) - address_url = fields.Char( - string='URL that uses the address', - help="In this URL, {ADDRESS} will be replaced by the address.") - lat_lon_url = fields.Char( - string='URL that uses latitude and longitude', - help="In this URL, {LATITUDE} and {LONGITUDE} will be replaced by " - "the latitude and longitude (requires the module 'base_geolocalize')") - route_address_url = fields.Char( - string='Route URL that uses the addresses', - help="In this URL, {START_ADDRESS} and {DEST_ADDRESS} will be " - "replaced by the start and destination addresses.") - route_lat_lon_url = fields.Char( - string='Route URL that uses latitude and longitude', - help="In this URL, {START_LATITUDE}, {START_LONGITUDE}, " - "{DEST_LATITUDE} and {DEST_LONGITUDE} will be replaced by the " - "latitude and longitude of the start and destination adresses " - "(requires the module 'base_geolocalize').") - - -class ResUsers(models.Model): - _inherit = 'res.users' - - @api.model - def _default_map_website(self): - map_website = self.env['map.website'].search([ - '|', ('address_url', '!=', False), ('lat_lon_url', '!=', False)], - limit=1) - return map_website - - @api.model - def _default_route_map_website(self): - map_route_website = self.env['map.website'].search([ - '|', - ('route_address_url', '!=', False), - ('route_lat_lon_url', '!=', False)], limit=1) - return map_route_website - - # begin with context_ to allow user to change it by himself - context_map_website_id = fields.Many2one( - 'map.website', string='Map Website', - domain=[ - '|', ('address_url', '!=', False), ('lat_lon_url', '!=', False)], - default=_default_map_website) - # We want to give the possibility to the user to have one map provider for - # regular maps and another one for routing - context_route_map_website_id = fields.Many2one( - 'map.website', string='Route Map Website', - domain=[ - '|', - ('route_address_url', '!=', False), - ('route_lat_lon_url', '!=', False)], - default=_default_route_map_website, - help="Map provided used when you click on the car icon on the partner " - "form to display an itinerary.") - context_route_start_partner_id = fields.Many2one( - 'res.partner', string='Start Address for Route Map') - - @api.model - def _default_map_settings(self): - """Method called from post-install script - I can't use a default method on the field, because it would be executed - before loading map_website_data.xml, so it would not be able to set a - value""" - users = self.env['res.users'].search([]) - map_website = self._default_map_website() - map_route_website = self._default_route_map_website() - logger.info('Updating user settings for maps...') - for user in users: - user.write({ - 'context_map_website_id': map_website.id or False, - 'context_route_map_website_id': map_route_website.id or False, - 'context_route_start_partner_id': user.partner_id.id or False, - }) - - -class ResPartner(models.Model): - _inherit = 'res.partner' - - @api.model - def _address_as_string(self): - addr = [] - if self.street: - addr.append(self.street) - if self.street2: - addr.append(self.street2) - if self.city: - addr.append(self.city) - if self.state_id: - addr.append(self.state_id.name) - if self.country_id: - addr.append(self.country_id.name) - if not addr: - raise Warning( - _("Address missing on partner '%s'.") % self.name) - address = ' '.join(addr) - return address - - @api.model - def _prepare_url(self, url, replace): - assert url, 'Missing URL' - for key, value in replace.iteritems(): - if not isinstance(value, (str, unicode)): - # for latitude and longitude which are floats - value = unicode(value) - url = url.replace(key, value) - logger.debug('Final URL: %s', url) - return url - - @api.multi - def open_map(self): - if not self.env.user.context_map_website_id: - raise Warning( - _('Missing map provider: ' - 'you should set it in your preferences.')) - map_website = self.env.user.context_map_website_id - if ( - map_website.lat_lon_url and - hasattr(self, 'partner_latitude') and - self.partner_latitude and self.partner_longitude): - url = self._prepare_url( - map_website.lat_lon_url, { - '{LATITUDE}': self.partner_latitude, - '{LONGITUDE}': self.partner_longitude}) - else: - if not map_website.address_url: - raise Warning( - _("Missing parameter 'URL that uses the address' " - "for map website '%s'.") % map_website.name) - url = self._prepare_url( - map_website.address_url, - {'{ADDRESS}': self._address_as_string()}) - return { - 'type': 'ir.actions.act_url', - 'url': url, - 'target': 'new', - } - - @api.multi - def open_route_map(self): - if not self.env.user.context_route_map_website_id: - raise Warning( - _('Missing route map website: ' - 'you should set it in your preferences.')) - map_website = self.env.user.context_route_map_website_id - if not self.env.user.context_route_start_partner_id: - raise Warning( - _('Missing start address for route map: ' - 'you should set it in your preferences.')) - start_partner = self.env.user.context_route_start_partner_id - if ( - map_website.route_lat_lon_url and - hasattr(self, 'partner_latitude') and - self.partner_latitude and - self.partner_longitude and - start_partner.partner_latitude and - start_partner.partner_longitude): - url = self._prepare_url( - map_website.route_lat_lon_url, { - '{START_LATITUDE}': start_partner.partner_latitude, - '{START_LONGITUDE}': start_partner.partner_longitude, - '{DEST_LATITUDE}': self.partner_latitude, - '{DEST_LONGITUDE}': self.partner_longitude}) - else: - if not map_website.route_address_url: - raise Warning( - _("Missing route URL that uses the addresses " - "for the map website '%s'") % map_website.name) - url = self._prepare_url( - map_website.route_address_url, { - '{START_ADDRESS}': start_partner._address_as_string(), - '{DEST_ADDRESS}': self._address_as_string()}) - return { - 'type': 'ir.actions.act_url', - 'url': url, - 'target': 'new', - } diff --git a/partner_external_map/post_install.py b/partner_external_map/post_install.py deleted file mode 100644 index 059d0d321..000000000 --- a/partner_external_map/post_install.py +++ /dev/null @@ -1,11 +0,0 @@ -# -*- coding: utf-8 -*- -# © 2015 Akretion (http://www.akretion.com) -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -# @author: Alexis de Lattre - -from openerp import SUPERUSER_ID - - -def set_default_map_settings(cr, pool): - pool['res.users']._default_map_settings(cr, SUPERUSER_ID) - return diff --git a/partner_external_map/static/description/icon.png b/partner_external_map/static/description/icon.png index 3a0328b51..e839add6d 100644 Binary files a/partner_external_map/static/description/icon.png and b/partner_external_map/static/description/icon.png differ diff --git a/partner_external_map/static/description/icon.svg b/partner_external_map/static/description/icon.svg new file mode 100644 index 000000000..a72f876b1 --- /dev/null +++ b/partner_external_map/static/description/icon.svg @@ -0,0 +1,2887 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 010110010011010110010011 + 010110010011010110010011 + + + + + diff --git a/partner_external_map/map_website_view.xml b/partner_external_map/views/map_website_view.xml similarity index 100% rename from partner_external_map/map_website_view.xml rename to partner_external_map/views/map_website_view.xml diff --git a/partner_external_map/partner_view.xml b/partner_external_map/views/res_partner_view.xml similarity index 100% rename from partner_external_map/partner_view.xml rename to partner_external_map/views/res_partner_view.xml diff --git a/partner_external_map/users_view.xml b/partner_external_map/views/res_users_view.xml similarity index 100% rename from partner_external_map/users_view.xml rename to partner_external_map/views/res_users_view.xml