From c5b4911c2d309b200500086d4534c3c1f9c6d509 Mon Sep 17 00:00:00 2001 From: "Pedro M. Baeza" Date: Sun, 3 Apr 2016 16:57:01 +0200 Subject: [PATCH] [IMP] partner_external_map: Better layout + icon + some refactoring --- partner_external_map/README.rst | 42 +- partner_external_map/__init__.py | 4 +- partner_external_map/__openerp__.py | 16 +- .../{ => data}/map_website_data.xml | 14 +- partner_external_map/hooks.py | 33 + partner_external_map/models/__init__.py | 5 + partner_external_map/models/map_website.py | 30 + partner_external_map/models/res_partner.py | 109 + partner_external_map/models/res_users.py | 42 + partner_external_map/partner_external_maps.py | 193 -- partner_external_map/post_install.py | 11 - .../static/description/icon.png | Bin 9455 -> 15961 bytes .../static/description/icon.svg | 2887 +++++++++++++++++ .../{ => views}/map_website_view.xml | 0 .../res_partner_view.xml} | 0 .../res_users_view.xml} | 0 16 files changed, 3145 insertions(+), 241 deletions(-) rename partner_external_map/{ => data}/map_website_data.xml (94%) create mode 100644 partner_external_map/hooks.py create mode 100644 partner_external_map/models/__init__.py create mode 100644 partner_external_map/models/map_website.py create mode 100644 partner_external_map/models/res_partner.py create mode 100644 partner_external_map/models/res_users.py delete mode 100644 partner_external_map/partner_external_maps.py delete mode 100644 partner_external_map/post_install.py create mode 100644 partner_external_map/static/description/icon.svg rename partner_external_map/{ => views}/map_website_view.xml (100%) rename partner_external_map/{partner_view.xml => views/res_partner_view.xml} (100%) rename partner_external_map/{users_view.xml => views/res_users_view.xml} (100%) 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 3a0328b516c4980e8e44cdb63fd945757ddd132d..e839add6d9c80a599bb76d5e73554baa7644498c 100644 GIT binary patch literal 15961 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I 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