diff --git a/.travis.yml b/.travis.yml index dddaba92c..6648201bf 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,6 +24,7 @@ install: - git clone https://github.com/OCA/maintainer-quality-tools.git ${HOME}/maintainer-quality-tools - export PATH=${HOME}/maintainer-quality-tools/travis:${PATH} - travis_install_nightly + - pip install pypostcode # example: dependency # - git clone https://github.com/OCA/webkit-tools -b ${VERSION} $HOME/webkit-tools diff --git a/l10n_nl_postcodeapi/README.rst b/l10n_nl_postcodeapi/README.rst new file mode 100644 index 000000000..eca9d5768 --- /dev/null +++ b/l10n_nl_postcodeapi/README.rst @@ -0,0 +1,75 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :alt: License: AGPL-3 + +Auto-completion for Dutch addresses +=================================== +This module contains integration of the excellent and free address completion +service 'PostcodeAPI'. The service allows lookups by zip code and house number, +providing street name and city. The lookups will be triggered in the partner +form views when a zip code or house number is entered or modified. Only +Dutch addresses (which is assumed to include addresses with no country) are +auto-completed. + +More info about the lookup service here: http://www.postcodeapi.nu/ + +Installation +============ +This module depends on the module partner_street_number, which will split +up the street field into separate fields for street name and number. + +This module can be gotten from https://github.com/oca/partner-contact/tree/8.0 + +You also need to have the 'pyPostcode' Python library by Stefan Jansen +installed (https://pypi.python.org/pypi/pyPostcode). + +Configuration +============= +Please enter the API key that you request from PostcodeAPI into the system +parameter 'l10n_nl_postcodeapi.apikey' + +Provinces are autocompleted if a country state with the exact name is found in +the system. A CSV file with the Dutch provinces is included in the data +directory, but not loaded by default. You can import the file manually. + +Compatibility +============= +This module is compatible with OpenERP 8.0. + +Usage +===== + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/176/8.0 + +.. repo_id is available in https://github.com/OCA/maintainer-tools/blob/master/tools/repos_with_ids.txt +.. branch is "8.0" for example + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please 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 +`here `_. + +Credits +======= + +Contributors +------------ + +* Stefan Rijnhart (Therp BV) + +Maintainer +---------- + +.. image:: http://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: http://odoo-community.org + +This module is maintained by the OCA at https://github.com/OCA/l10n-netherlands + +OCA, or the Odoo Community Association, is a nonprofit organization whose mission is to support the collaborative development of Odoo features and promote its widespread use. + +To contribute to this module, please visit http://odoo-community.org. diff --git a/l10n_nl_postcodeapi/__init__.py b/l10n_nl_postcodeapi/__init__.py new file mode 100644 index 000000000..0650744f6 --- /dev/null +++ b/l10n_nl_postcodeapi/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/l10n_nl_postcodeapi/__openerp__.py b/l10n_nl_postcodeapi/__openerp__.py new file mode 100644 index 000000000..e4ffb8ef1 --- /dev/null +++ b/l10n_nl_postcodeapi/__openerp__.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013-2015 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +{ + 'name': 'Integration with PostcodeApi.nu', + 'summary': 'Autocomplete Dutch addresses using PostcodeApi.nu', + 'version': '0.1', + 'author': 'Therp BV,Odoo Community Association (OCA)', + 'category': 'Localization', + 'website': 'https://github.com/OCA/l10n-netherlands', + 'license': 'AGPL-3', + 'depends': ['partner_street_number'], + 'data': [ + 'data/ir_config_parameter.xml', + ], + "external_dependencies": { + 'python': ['pyPostcode'], + } +} diff --git a/l10n_nl_postcodeapi/data/ir_config_parameter.xml b/l10n_nl_postcodeapi/data/ir_config_parameter.xml new file mode 100644 index 000000000..fc4d3f1a1 --- /dev/null +++ b/l10n_nl_postcodeapi/data/ir_config_parameter.xml @@ -0,0 +1,11 @@ + + + + + + l10n_nl_postcodeapi.apikey + Your API key + + + + diff --git a/l10n_nl_postcodeapi/data/res.country.state.csv b/l10n_nl_postcodeapi/data/res.country.state.csv new file mode 100644 index 000000000..3e96051cd --- /dev/null +++ b/l10n_nl_postcodeapi/data/res.country.state.csv @@ -0,0 +1,13 @@ +id,country_id,name,code +,nl,Drenthe,DR +,nl,Flevoland,FL +,nl,Friesland,FR +,nl,Gelderland,GD +,nl,Groningen,GR +,nl,Limburg,LB +,nl,Noord-Brabant,NB +,nl,Noord-Holland,NH +,nl,Overijssel,OV +,nl,Utrecht,UT +,nl,Zeeland,ZH +,nl,Zuid-Holland,ZL diff --git a/l10n_nl_postcodeapi/i18n/l10n_nl_postcodeapi.pot b/l10n_nl_postcodeapi/i18n/l10n_nl_postcodeapi.pot new file mode 100644 index 000000000..33610eace --- /dev/null +++ b/l10n_nl_postcodeapi/i18n/l10n_nl_postcodeapi.pot @@ -0,0 +1,42 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * l10n_nl_postcodeapi +# +msgid "" +msgstr "" + +#. module: l10n_nl_postcodeapi +#: code:addons/l10n_nl_postcodeapi/model/res_partner.py:46 +#, python-format +msgid "" +"Could not verify the connection with the address lookup service (if you want " +"to get rid of this message, please rename or delete the system parameter " +"'l10n_nl_postcodeapi.apikey')." +msgstr "" + +#. module: l10n_nl_postcodeapi +#: code:_description:0 +#: model:ir.model,name:l10n_nl_postcodeapi.model_res_country_state +#, python-format +msgid "Country state" +msgstr "" + +#. module: l10n_nl_postcodeapi +#: code:addons/l10n_nl_postcodeapi/model/res_partner.py:45 +#, python-format +msgid "Error" +msgstr "" + +#. module: l10n_nl_postcodeapi +#: code:_description:0 +#: model:ir.model,name:l10n_nl_postcodeapi.model_res_partner +#, python-format +msgid "Partner" +msgstr "" + +#. module: l10n_nl_postcodeapi +#: code:_description:0 +#: model:ir.model,name:l10n_nl_postcodeapi.model_ir_config_parameter +#, python-format +msgid "ir.config_parameter" +msgstr "" diff --git a/l10n_nl_postcodeapi/i18n/nl.po b/l10n_nl_postcodeapi/i18n/nl.po new file mode 100644 index 000000000..92ec5094e --- /dev/null +++ b/l10n_nl_postcodeapi/i18n/nl.po @@ -0,0 +1,50 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * l10n_nl_postcodeapi +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-11-10 11:03+0000\n" +"PO-Revision-Date: 2014-11-10 11:03+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_nl_postcodeapi +#: code:addons/l10n_nl_postcodeapi/model/res_partner.py:46 +#, python-format +msgid "Could not verify the connection with the address lookup service (if you want to get rid of this message, please rename or delete the system parameter 'l10n_nl_postcodeapi.apikey')." +msgstr "De verbinding met de postcodeserver kon niet gevalideerd worden (als je deze melding niet meer wilt zien, hernoem of verwijder dan de systeemparameter 'l10n_nl_postcodeapi.apikey')." + +#. module: l10n_nl_postcodeapi +#: code:_description:0 +#: model:ir.model,name:l10n_nl_postcodeapi.model_res_country_state +#, python-format +msgid "Country state" +msgstr "Staat" + +#. module: l10n_nl_postcodeapi +#: code:addons/l10n_nl_postcodeapi/model/res_partner.py:45 +#, python-format +msgid "Error" +msgstr "Fout" + +#. module: l10n_nl_postcodeapi +#: code:_description:0 +#: model:ir.model,name:l10n_nl_postcodeapi.model_res_partner +#, python-format +msgid "Partner" +msgstr "Relatie" + +#. module: l10n_nl_postcodeapi +#: code:_description:0 +#: model:ir.model,name:l10n_nl_postcodeapi.model_ir_config_parameter +#, python-format +msgid "ir.config_parameter" +msgstr "ir.config_parameter" + diff --git a/l10n_nl_postcodeapi/models/__init__.py b/l10n_nl_postcodeapi/models/__init__.py new file mode 100644 index 000000000..bfd363357 --- /dev/null +++ b/l10n_nl_postcodeapi/models/__init__.py @@ -0,0 +1,3 @@ +from . import res_partner +from . import ir_config_parameter +from . import res_country_state diff --git a/l10n_nl_postcodeapi/models/ir_config_parameter.py b/l10n_nl_postcodeapi/models/ir_config_parameter.py new file mode 100644 index 000000000..220e7d8be --- /dev/null +++ b/l10n_nl_postcodeapi/models/ir_config_parameter.py @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013-2015 Therp BV (). +# +# @autors: Stefan Rijnhart, Ronald Portier +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp import models, api + + +class IrConfigParameter(models.Model): + _inherit = 'ir.config_parameter' + + @api.model + @api.returns('self', lambda value: value.id) + def create(self, vals): + """ + Clear the postcode provider cache when the API + key is created + """ + if vals.get('key') == 'l10n_nl_postcodeapi.apikey': + partner_obj = self.env['res.partner'] + partner_obj.get_provider_obj.clear_cache(partner_obj) + return super(IrConfigParameter, self).create(vals) + + @api.multi + def write(self, vals): + """ + Clear the postcode provider cache when the API + key is modified + """ + key = 'l10n_nl_postcodeapi.apikey' + if (vals.get('key') == key or + self.search([('id', 'in', self.ids), ('key', '=', key)])): + partner_obj = self.env['res.partner'] + partner_obj.get_provider_obj.clear_cache(partner_obj) + return super(IrConfigParameter, self).write(vals) diff --git a/l10n_nl_postcodeapi/models/res_country_state.py b/l10n_nl_postcodeapi/models/res_country_state.py new file mode 100644 index 000000000..dbe29f6b8 --- /dev/null +++ b/l10n_nl_postcodeapi/models/res_country_state.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013-2015 Therp BV (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp import models, api + + +class ResCountryState(models.Model): + _inherit = 'res.country.state' + + @api.multi + def write(self, vals): + """ + Clear the postcode provider cache when the state + table is altered. + """ + self.env['res.partner'].get_province.clear_cache( + self.env['res.partner']) + return super(ResCountryState, self).write(vals) + + @api.model + @api.returns('self', lambda value: value.id) + def create(self, vals): + """ + Clear the postcode provider cache when the state + table is altered. + """ + self.env['res.partner'].get_province.clear_cache( + self.env['res.partner']) + return super(ResCountryState, self).create(vals) + + @api.multi + def unlink(self): + """ + Clear the postcode provider cache when the state + table is altered. + """ + self.env['res.partner'].get_province.clear_cache( + self.env['res.partner']) + return super(ResCountryState, self).unlink() diff --git a/l10n_nl_postcodeapi/models/res_partner.py b/l10n_nl_postcodeapi/models/res_partner.py new file mode 100644 index 000000000..a6bf1a4b6 --- /dev/null +++ b/l10n_nl_postcodeapi/models/res_partner.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# OpenERP, Open Source Management Solution +# This module copyright (C) 2013-2015 Therp BV (). +# +# @autors: Stefan Rijnhart, Ronald Portier +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of the +# License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from lxml import etree +from openerp import models, api, exceptions, _ +from openerp.tools import ormcache + + +class ResPartner(models.Model): + _inherit = 'res.partner' + + @api.model + @ormcache(skiparg=2) + def get_provider_obj(self): + apikey = self.env['ir.config_parameter'].get_param( + 'l10n_nl_postcodeapi.apikey', '').strip() + if not apikey or apikey == 'Your API key': + return False + from pyPostcode import Api + provider = Api(apikey) + test = provider.getaddress('1053NJ', '334T') + if not test or not test._data: + raise exceptions.Warning( + _('Error'), + _('Could not verify the connection with the address ' + 'lookup service (if you want to get rid of this ' + 'message, please rename or delete the system parameter ' + '\'l10n_nl_postcodeapi.apikey\').')) + return provider + + @api.model + @ormcache(skiparg=2) + def get_province(self, province): + """ Return the province or empty recordset """ + if not province: + return self.env['res.country.state'] + res = self.env['res.country.state'].search([('name', '=', province)]) + return res[0] if res else res + + @api.onchange('zip', 'street_number', 'country_id') + def on_change_zip_street_number(self): + """ + Normalize the zip code, check on the partner's country and + if all is well, request address autocompletion data. + + NB. postal_code is named 'zip' in OpenERP, but is this a reserved + keyword in Python + """ + postal_code = self.zip and self.zip.replace(' ', '') + if (not (postal_code and self.street_number) + or (self.country_id and + self.country_id != self.env.ref('base.nl'))): + return {} + + provider_obj = self.get_provider_obj() + if not provider_obj: + return {} + pc_info = provider_obj.getaddress(postal_code, self.street_number) + if not pc_info or not pc_info._data: + return {} + self.street_name = pc_info._data['street'] + self.city = pc_info._data['town'] + self.state_id = self.get_province(pc_info._province) + + @api.model + def fields_view_get( + self, view_id=None, view_type='form', + toolbar=False, submenu=False): + """ Address fields can be all over the place due to module + interaction. For improved compatibility add the onchange method here, + not in a view.""" + res = super(ResPartner, self).fields_view_get( + view_id=view_id, view_type=view_type, + toolbar=toolbar, submenu=submenu) + if view_type != 'form': + return res + + def inject_onchange(arch): + arch = etree.fromstring(arch) + for field in ['zip', 'street_number', 'country_id']: + for node in arch.xpath('//field[@name="%s"]' % field): + node.attrib['on_change'] = "1" + return etree.tostring(arch, encoding='utf-8') + + res['arch'] = inject_onchange(res['arch']) + # Inject in the embedded contacts view as well + if res['fields'].get('child_ids', {}).get('views', {}).get('form'): + res['fields']['child_ids']['views']['form']['arch'] = \ + inject_onchange( + res['fields']['child_ids']['views']['form']['arch']) + return res diff --git a/l10n_nl_postcodeapi/static/description/icon.png b/l10n_nl_postcodeapi/static/description/icon.png new file mode 100644 index 000000000..a54449ead Binary files /dev/null and b/l10n_nl_postcodeapi/static/description/icon.png differ diff --git a/oca_dependencies.txt b/oca_dependencies.txt new file mode 100644 index 000000000..3839e0144 --- /dev/null +++ b/oca_dependencies.txt @@ -0,0 +1 @@ +partner-contact https://github.com/OCA/partner-contact 8.0