From f4f38c4c595375e8d6055585547490d8c7ca764f Mon Sep 17 00:00:00 2001 From: Aitor Bouzas Date: Mon, 8 Oct 2018 17:04:28 +0200 Subject: [PATCH] [MIG] base_location: Migration to 12.0 This module has now been refactored to be more consistent with what base_address_city offers to the location management. Added dependency to contacts so that I could change the menu location for cities / zip management. Now, every res.city record has a relation One2many to res.city.zip (old res.better.zip). This way, every zip has a realted city too. Zips can be searched through city code, zip or city name (same as before). Modified tests and deleted not needed tests. Added sql contraints so that zips and cities are unique within it's country / state / city. --- base_location/README.rst | 106 ++-- base_location/__init__.py | 1 - base_location/__openerp__.py | 24 +- base_location/demo/better_zip.xml | 8 - base_location/demo/res_city_zip.xml | 13 + base_location/models/__init__.py | 9 +- base_location/models/better_zip.py | 108 ---- base_location/models/res_city.py | 19 + base_location/models/res_city_zip.py | 40 ++ .../models/{company.py => res_company.py} | 46 +- .../models/{partner.py => res_partner.py} | 60 ++- base_location/models/state.py | 11 - base_location/readme/CONFIGURE.rst | 10 + base_location/readme/CONTRIBUTORS.rst | 9 + base_location/readme/CREDITS.rst | 1 + base_location/readme/DESCRIPTION.rst | 5 + base_location/readme/USAGE.rst | 3 + base_location/security/ir.model.access.csv | 3 +- base_location/static/description/index.html | 463 ++++++++++++++++++ base_location/tests/__init__.py | 1 - base_location/tests/test_base_location.py | 394 +++++++-------- base_location/views/better_zip_view.xml | 85 ---- base_location/views/res_city_view.xml | 63 +++ base_location/views/res_city_zip_view.xml | 56 +++ ...{company_view.xml => res_company_view.xml} | 0 base_location/views/res_country_view.xml | 2 +- ...{partner_view.xml => res_partner_view.xml} | 1 - base_location/views/state_view.xml | 26 - 28 files changed, 998 insertions(+), 569 deletions(-) delete mode 100644 base_location/demo/better_zip.xml create mode 100644 base_location/demo/res_city_zip.xml delete mode 100644 base_location/models/better_zip.py create mode 100644 base_location/models/res_city.py create mode 100644 base_location/models/res_city_zip.py rename base_location/models/{company.py => res_company.py} (62%) rename base_location/models/{partner.py => res_partner.py} (55%) delete mode 100644 base_location/models/state.py create mode 100644 base_location/readme/CONFIGURE.rst create mode 100644 base_location/readme/CONTRIBUTORS.rst create mode 100644 base_location/readme/CREDITS.rst create mode 100644 base_location/readme/DESCRIPTION.rst create mode 100644 base_location/readme/USAGE.rst create mode 100644 base_location/static/description/index.html delete mode 100644 base_location/views/better_zip_view.xml create mode 100644 base_location/views/res_city_view.xml create mode 100644 base_location/views/res_city_zip_view.xml rename base_location/views/{company_view.xml => res_company_view.xml} (100%) rename base_location/views/{partner_view.xml => res_partner_view.xml} (99%) delete mode 100644 base_location/views/state_view.xml diff --git a/base_location/README.rst b/base_location/README.rst index 160dbf7c0..33b6fe83c 100644 --- a/base_location/README.rst +++ b/base_location/README.rst @@ -1,33 +1,54 @@ -.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg - :target: https://www.gnu.org/licenses/agpl-3.0-standalone.html - :alt: License: AGPL-3 +==================================== +Location management (aka Better ZIP) +==================================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fpartner--contact-lightgray.png?logo=github + :target: https://github.com/OCA/partner-contact/tree/12.0/base_location + :alt: OCA/partner-contact +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/partner-contact-12-0/partner-contact-12-0-base_location + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/134/12.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module introduces a zip model that allows you to manage locations in a better way. + +The zips will allow the users to complete automatically all address-related fields by just filling the zip. -======================= -Enhanced ZIP management -======================= - -This module introduces a better zip/npa management system. +Also allows different search filters. -It enables zip, city, state and country auto-completion on partners and companies. +**Table of contents** -Also allows different search filters. +.. contents:: + :local: Configuration ============= -#. Activate the developer mode in *Settings*. -#. Go to *Settings / Technical / Locations Management / Locations*. -#. Create a new Location. +#. Go to *Contacts / Configuration / Localization / Cities*. +#. Create a new City. + +#. Go to *Contacts / Configuration / Localization / Zips*. +#. Create a new Zip and relate it to the city (you can also create the Zip from the City). or, with module 'Contacts Directory' installed: #. Go to *Contacts / Configuration / Localization / Countries*. #. Locate the desired country. -#. Press on the button 'Locations'. - -or, -#. Go to *Contacts / Configuration / Localization / Fed. States* -#. Locate the desired state. -#. Enter the desired Locations. +#. Press on the button 'Cities' / 'Zips'. Usage ===== @@ -36,52 +57,59 @@ Usage #. Fill the field *Location completion* #. Information about country, state, city and zip will be filled automatically - - -.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas - :alt: Try me on Runbot - :target: https://runbot.odoo-community.org/runbot/134/11.0 - 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 smash it by providing detailed and welcomed feedback. +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 `_. + +Do not contact contributors directly about support or help with technical issues. Credits ======= -Images ------- +Authors +~~~~~~~ -* Icon park: `Icon http://icon-park.com/icon/location-map-pin-orange3/` +* Camptocamp +* ACYSOS S.L. +* Alejandro Santana +* Tecnativa +* AdaptiveCity Contributors ------------- +~~~~~~~~~~~~ * Nicolas Bessi (Camptocamp) * Ignacio Ibeas (Acysos S.L.) -* Pedro M. Baeza +* Pedro M. Baeza * Alejandro Santana * Sandy Carter * Yannick Vaucher * Francesco Apruzzese * Dave Lasley +* Aitor Bouzas + +Other credits +~~~~~~~~~~~~~ + +* Icon park: `Icon http://icon-park.com/icon/location-map-pin-orange3/` +Maintainers +~~~~~~~~~~~ -Maintainer ----------- +This module is maintained by the OCA. .. image:: https://odoo-community.org/logo.png :alt: Odoo Community Association :target: https://odoo-community.org -This module is maintained by the OCA. - 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 https://odoo-community.org. +This module is part of the `OCA/partner-contact `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/base_location/__init__.py b/base_location/__init__.py index e2aed6e67..69f7babdf 100644 --- a/base_location/__init__.py +++ b/base_location/__init__.py @@ -1,4 +1,3 @@ -# Copyright 2016 Nicolas Bessi, Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import models diff --git a/base_location/__openerp__.py b/base_location/__openerp__.py index 1d4810eba..532856707 100644 --- a/base_location/__openerp__.py +++ b/base_location/__openerp__.py @@ -4,26 +4,30 @@ { 'name': 'Location management (aka Better ZIP)', - 'version': '11.0.1.0.1', + 'version': '12.0.1.0.0', 'depends': [ - 'base_address_city' + 'base_address_city', + 'contacts', ], 'author': "Camptocamp," "ACYSOS S.L.," "Alejandro Santana," "Tecnativa," + "AdaptiveCity," "Odoo Community Association (OCA)", 'license': "AGPL-3", 'summary': '''Enhanced zip/npa management system''', - 'website': 'http://www.camptocamp.com', - 'data': ['views/better_zip_view.xml', - 'views/state_view.xml', - 'views/res_country_view.xml', - 'views/company_view.xml', - 'views/partner_view.xml', - 'security/ir.model.access.csv'], + 'website': 'https://github.com/OCA/partner-contact', + 'data': [ + 'security/ir.model.access.csv', + 'views/res_city_zip_view.xml', + 'views/res_city_view.xml', + 'views/res_country_view.xml', + 'views/res_company_view.xml', + 'views/res_partner_view.xml', + ], 'demo': [ - 'demo/better_zip.xml', + 'demo/res_city_zip.xml', ], 'installable': True, 'auto_install': False, diff --git a/base_location/demo/better_zip.xml b/base_location/demo/better_zip.xml deleted file mode 100644 index 0c7f3b9ee..000000000 --- a/base_location/demo/better_zip.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - 1000 - Brussels - - - diff --git a/base_location/demo/res_city_zip.xml b/base_location/demo/res_city_zip.xml new file mode 100644 index 000000000..e940e49f1 --- /dev/null +++ b/base_location/demo/res_city_zip.xml @@ -0,0 +1,13 @@ + + + + + Brussels + + + + + 1000 + + + diff --git a/base_location/models/__init__.py b/base_location/models/__init__.py index 3f7a893a5..1fb23520f 100644 --- a/base_location/models/__init__.py +++ b/base_location/models/__init__.py @@ -1,7 +1,6 @@ -# Copyright 2016 Nicolas Bessi, Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from . import better_zip -from . import partner -from . import state -from . import company +from . import res_city_zip +from . import res_partner +from . import res_company +from . import res_city diff --git a/base_location/models/better_zip.py b/base_location/models/better_zip.py deleted file mode 100644 index 991ffa6c8..000000000 --- a/base_location/models/better_zip.py +++ /dev/null @@ -1,108 +0,0 @@ -# Copyright 2016 Nicolas Bessi, Camptocamp SA -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import api, fields, models, _ -from odoo.exceptions import ValidationError - - -class BetterZip(models.Model): - '''City/locations completion object''' - - _name = "res.better.zip" - _description = __doc__ - _order = "name asc" - - name = fields.Char('ZIP') - code = fields.Char( - 'City Code', - size=64, - help="The official code for the city" - ) - city = fields.Char('City', required=True) - city_id = fields.Many2one( - 'res.city', - 'City', - ) - state_id = fields.Many2one( - 'res.country.state', - 'State', - ) - country_id = fields.Many2one('res.country', 'Country') - enforce_cities = fields.Boolean( - related='country_id.enforce_cities', - readonly=True, - ) - latitude = fields.Float() - longitude = fields.Float() - - @api.multi - @api.depends('name', 'city', 'state_id', 'country_id') - def name_get(self): - result = [] - for rec in self: - name = [] - if rec.name: - name.append('%(name)s' % {'name': rec.name}) - name.append('%(name)s' % {'name': rec.city}) - if rec.state_id: - name.append('%(name)s' % {'name': rec.state_id.name}) - if rec.country_id: - name.append('%(name)s' % {'name': rec.country_id.name}) - result.append((rec.id, ", ".join(name))) - return result - - @api.model - def name_search(self, name='', args=None, operator='ilike', limit=100): - args = list(args or []) - args += ['|', ('city', operator, name), - '|', ('name', operator, name), ('code', operator, name)] - recs = self.search(args, limit=limit) - return recs.name_get() - - @api.onchange('country_id') - def _onchange_country_id(self): - if self.state_id.country_id != self.country_id: - self.state_id = False - if self.city_id.country_id != self.country_id: - self.city_id = False - if self.country_id: - domain = [('country_id', '=', self.country_id.id)] - else: - domain = [] - return { - 'domain': { - 'state_id': domain, - 'city_id': domain, - } - } - - @api.onchange('city_id') - def _onchange_city_id(self): - if self.city_id: - self.city = self.city_id.name - self.country_id = self.city_id.country_id - self.state_id = self.city_id.state_id - - @api.onchange('state_id') - def _onchange_state_id(self): - if self.state_id: - self.country_id = self.state_id.country_id - - @api.constrains('state_id', 'country_id', 'city_id') - def constrains_country(self): - for rec in self: - if rec.state_id and rec.state_id.country_id != \ - rec.country_id: - raise ValidationError(_( - "The country of the state differs from the country in " - "location %s") % rec.name) - if rec.city_id and rec.city_id.country_id \ - != rec.country_id: - raise ValidationError(_( - "The country of the city differs from the country in " - "location %s") % rec.name) - if rec.city_id and rec.city_id.state_id \ - != rec.state_id: - raise ValidationError(_( - "The state of the city differs from the state in " - "location %s") % rec.name) diff --git a/base_location/models/res_city.py b/base_location/models/res_city.py new file mode 100644 index 000000000..629268b5d --- /dev/null +++ b/base_location/models/res_city.py @@ -0,0 +1,19 @@ +# Copyright 2018 Aitor Bouzas +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class City(models.Model): + _inherit = 'res.city' + + zip_ids = fields.One2many('res.city.zip', 'city_id', + string="Zips in this city") + + _sql_constraints = [ + ('name_state_country_uniq', + 'UNIQUE(name, state_id, country_id)', + 'You already have a city with that name in the same state.' + 'The city must have a unique name within ' + 'it\'s state and it\'s country'), + ] diff --git a/base_location/models/res_city_zip.py b/base_location/models/res_city_zip.py new file mode 100644 index 000000000..12c679a96 --- /dev/null +++ b/base_location/models/res_city_zip.py @@ -0,0 +1,40 @@ +# Copyright 2016 Nicolas Bessi, Camptocamp SA +# Copyright 2018 Aitor Bouzas +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class ResCityZip(models.Model): + """City/locations completion object""" + + _name = "res.city.zip" + _description = __doc__ + _order = "name asc" + _rec_name = "display_name" + + name = fields.Char('ZIP', required=True) + city_id = fields.Many2one( + 'res.city', + 'City', + required=True, + ) + display_name = fields.Char(compute='_compute_new_display_name', + store=True, index=True) + + _sql_constraints = [ + ('name_city_uniq', 'UNIQUE(name, city_id)', + 'You already have a zip with that code in the same city. ' + 'The zip code must be unique within it\'s city'), + ] + + @api.multi + @api.depends('name', 'city_id') + def _compute_new_display_name(self): + for rec in self: + name = [rec.name, rec.city_id.name] + if rec.city_id.state_id: + name.append(rec.city_id.state_id.name) + if rec.city_id.country_id: + name.append(rec.city_id.country_id.name) + rec.display_name = ", ".join(name) diff --git a/base_location/models/company.py b/base_location/models/res_company.py similarity index 62% rename from base_location/models/company.py rename to base_location/models/res_company.py index 1016a1d23..f78642aa9 100644 --- a/base_location/models/company.py +++ b/base_location/models/res_company.py @@ -8,38 +8,39 @@ from odoo import models, fields, api class ResCompany(models.Model): _inherit = 'res.company' + # In order to keep the same logic used in Odoo, fields must be computed + # and inversed, not related. This way we can ensure that it works + # correctly on changes and that inconsistencies cannot happen. + # When you make the fields related, the constrains added in res.partner + # will fail, because when you change the city_id in the company, you are + # effectively changing it in the partner. The constrains on the partner + # are evaluated before the inverse methods update the other fields (city, + # etc..) so we need them to ensure consistency. + # As a conclusion, address fields are very related to each other. + # Either you make them all related to the partner in company, or you + # don't for all of them. Mixing both approaches produces inconsistencies. + city_id = fields.Many2one( 'res.city', compute='_compute_address', inverse='_inverse_city_id', - string="City" + string="City ID" ) zip_id = fields.Many2one( - 'res.better.zip', + 'res.city.zip', string='ZIP Location', compute='_compute_address', inverse='_inverse_zip_id', oldname="better_zip_id", help='Use the city name or the zip code to search the location', ) - # In order to keep the same logic used in odoo, fields must be computed - # and inversed, not related. This way, we can ensure that it works - # correctly on changes and inconsistencies cannot happen. - # When you make the fields related, the constrains added in res.partner - # will fail. because when you change the city_id in the company, you are - # effectively changing it in the partner. The constrains on the partner - # are evaluated before the inverse methods update the other fields (city, - # etc..). And we need constrains in the partner to ensure consistency. - # So, as a conclusion, address fields are very related to each other. - # Either you make them all related to the partner in company, or you - # don't for all of them. But mixing methods produces inconsistencies. country_enforce_cities = fields.Boolean( related='country_id.enforce_cities' ) def _get_company_address_fields(self, partner): - res = super(ResCompany, self)._get_company_address_fields(partner) + res = super()._get_company_address_fields(partner) res['city_id'] = partner.city_id res['zip_id'] = partner.zip_id return res @@ -55,16 +56,15 @@ class ResCompany(models.Model): @api.onchange('zip_id') def _onchange_zip_id(self): if self.zip_id: - self.zip = self.zip_id.name - self.city_id = self.zip_id.city_id - self.city = self.zip_id.city - self.country_id = self.zip_id.country_id - if self.country_id.enforce_cities: - self.state_id = self.city_id.state_id - else: - self.state_id = self.zip_id.state_id + self.update({ + 'zip': self.zip_id.name, + 'city_id': self.zip_id.city_id, + 'city': self.zip_id.city_id.name, + 'country_id': self.zip_id.city_id.country_id, + 'state_id': self.zip_id.city_id.state_id, + }) @api.onchange('state_id') - def onchange_state_id(self): + def _onchange_state_id(self): if self.state_id.country_id: self.country_id = self.state_id.country_id.id diff --git a/base_location/models/partner.py b/base_location/models/res_partner.py similarity index 55% rename from base_location/models/partner.py rename to base_location/models/res_partner.py index befddd1ff..273dc3027 100644 --- a/base_location/models/partner.py +++ b/base_location/models/res_partner.py @@ -8,16 +8,19 @@ from odoo.exceptions import ValidationError class ResPartner(models.Model): _inherit = 'res.partner' - zip_id = fields.Many2one('res.better.zip', 'ZIP Location') + + zip_id = fields.Many2one('res.city.zip', 'ZIP Location') @api.onchange('city_id') def _onchange_city_id(self): if not self.zip_id: - super(ResPartner, self)._onchange_city_id() + super()._onchange_city_id() if self.zip_id and self.city_id != self.zip_id.city_id: - self.zip_id = False - self.zip = False - self.city = False + self.update({ + 'zip_id': False, + 'zip': False, + 'city': False, + }) if self.city_id: return { 'domain': { @@ -26,38 +29,37 @@ class ResPartner(models.Model): } return {'domain': {'zip_id': []}} - @api.onchange('state_id') - def _onchange_state_id(self): - if self.zip_id and self.state_id != self.zip_id.state_id: - self.zip_id = False - self.zip = False - self.city = False - @api.onchange('country_id') def _onchange_country_id(self): - res = super(ResPartner, self)._onchange_country_id() - if self.zip_id and self.zip_id.country_id != self.country_id: + res = super()._onchange_country_id() + if self.zip_id and self.zip_id.city_id.country_id != self.country_id: self.zip_id = False return res @api.onchange('zip_id') def _onchange_zip_id(self): if self.zip_id: - self.country_id = self.zip_id.country_id - if self.country_id.enforce_cities: - self.city_id = self.zip_id.city_id - self.zip = self.zip_id.name - self.state_id = self.zip_id.state_id - self.city = self.zip_id.city + vals = { + 'city_id': self.zip_id.city_id, + 'zip': self.zip_id.name, + 'city': self.zip_id.city_id.name, + } + if self.zip_id.city_id.country_id: + vals.update({'country_id': self.zip_id.city_id.country_id}) + if self.zip_id.city_id.state_id: + vals.update({'state_id': self.zip_id.city_id.state_id}) + self.update(vals) @api.constrains('zip_id', 'country_id', 'city_id', 'state_id') def _check_zip(self): - for rec in self.filtered('zip_id'): - if rec.zip_id.state_id != rec.state_id: + for rec in self: + if not rec.zip_id: + continue + if rec.zip_id.city_id.state_id != rec.state_id: raise ValidationError(_( "The state of the partner %s differs from that in " "location %s") % (rec.name, rec.zip_id.name)) - if rec.zip_id.country_id != rec.country_id: + if rec.zip_id.city_id.country_id != rec.country_id: raise ValidationError(_( "The country of the partner %s differs from that in " "location %s") % (rec.name, rec.zip_id.name)) @@ -67,6 +69,14 @@ class ResPartner(models.Model): "location %s") % (rec.name, rec.zip_id.name)) @api.onchange('state_id') - def onchange_state_id(self): + def _onchange_state_id(self): + vals = {} if self.state_id.country_id: - self.country_id = self.state_id.country_id.id + vals.update({'country_id': self.state_id.country_id}) + if self.zip_id and self.state_id != self.zip_id.city_id.state_id: + vals.update({ + 'zip_id': False, + 'zip': False, + 'city': False, + }) + self.update(vals) diff --git a/base_location/models/state.py b/base_location/models/state.py deleted file mode 100644 index 5161a34a6..000000000 --- a/base_location/models/state.py +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright 2016 Nicolas Bessi, Camptocamp SA -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). - -from odoo import models, fields - - -class ResCountryState(models.Model): - - _inherit = 'res.country.state' - - better_zip_ids = fields.One2many('res.better.zip', 'state_id', 'Cities') diff --git a/base_location/readme/CONFIGURE.rst b/base_location/readme/CONFIGURE.rst new file mode 100644 index 000000000..32d87344b --- /dev/null +++ b/base_location/readme/CONFIGURE.rst @@ -0,0 +1,10 @@ +#. Go to *Contacts / Configuration / Localization / Cities*. +#. Create a new City. + +#. Go to *Contacts / Configuration / Localization / Zips*. +#. Create a new Zip and relate it to the city (you can also create the Zip from the City). + +or, with module 'Contacts Directory' installed: +#. Go to *Contacts / Configuration / Localization / Countries*. +#. Locate the desired country. +#. Press on the button 'Cities' / 'Zips'. diff --git a/base_location/readme/CONTRIBUTORS.rst b/base_location/readme/CONTRIBUTORS.rst new file mode 100644 index 000000000..b90283688 --- /dev/null +++ b/base_location/readme/CONTRIBUTORS.rst @@ -0,0 +1,9 @@ +* Nicolas Bessi (Camptocamp) +* Ignacio Ibeas (Acysos S.L.) +* Pedro M. Baeza +* Alejandro Santana +* Sandy Carter +* Yannick Vaucher +* Francesco Apruzzese +* Dave Lasley +* Aitor Bouzas diff --git a/base_location/readme/CREDITS.rst b/base_location/readme/CREDITS.rst new file mode 100644 index 000000000..e84ac4aed --- /dev/null +++ b/base_location/readme/CREDITS.rst @@ -0,0 +1 @@ +* Icon park: `Icon http://icon-park.com/icon/location-map-pin-orange3/` diff --git a/base_location/readme/DESCRIPTION.rst b/base_location/readme/DESCRIPTION.rst new file mode 100644 index 000000000..c4afc713f --- /dev/null +++ b/base_location/readme/DESCRIPTION.rst @@ -0,0 +1,5 @@ +This module introduces a zip model that allows you to manage locations in a better way. + +The zips will allow the users to complete automatically all address-related fields by just filling the zip. + +Also allows different search filters. diff --git a/base_location/readme/USAGE.rst b/base_location/readme/USAGE.rst new file mode 100644 index 000000000..bc8bfc983 --- /dev/null +++ b/base_location/readme/USAGE.rst @@ -0,0 +1,3 @@ +#. Access a partner record +#. Fill the field *Location completion* +#. Information about country, state, city and zip will be filled automatically diff --git a/base_location/security/ir.model.access.csv b/base_location/security/ir.model.access.csv index 5ef6c8b46..e0699ce7b 100644 --- a/base_location/security/ir.model.access.csv +++ b/base_location/security/ir.model.access.csv @@ -1,3 +1,2 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"ir_model_access_betterzip0","res_better_zip group_user_all","model_res_better_zip",base.group_user,1,0,0,0 -"ir_model_access_betterzip1","res_better_zip group_user","model_res_better_zip","base.group_partner_manager",1,1,1,1 +"ir_model_access_cityzip0","res_city_zip group_user","model_res_city_zip","base.group_partner_manager",1,1,1,1 diff --git a/base_location/static/description/index.html b/base_location/static/description/index.html new file mode 100644 index 000000000..2ed60fbde --- /dev/null +++ b/base_location/static/description/index.html @@ -0,0 +1,463 @@ + + + + + + +Location management (aka Better ZIP) + + + +
+

Location management (aka Better ZIP)

+ + +

Beta License: AGPL-3 OCA/partner-contact Translate me on Weblate Try me on Runbot

+

This module introduces a zip model that allows you to manage locations in a better way.

+

The zips will allow the users to complete automatically all address-related fields by just filling the zip.

+

Also allows different search filters.

+

Table of contents

+ +
+

Configuration

+
    +
  1. Go to Contacts / Configuration / Localization / Cities.
  2. +
  3. Create a new City.
  4. +
  5. Go to Contacts / Configuration / Localization / Zips.
  6. +
  7. Create a new Zip and relate it to the city (you can also create the Zip from the City).
  8. +
+

or, with module ‘Contacts Directory’ installed: +#. Go to Contacts / Configuration / Localization / Countries. +#. Locate the desired country. +#. Press on the button ‘Cities’ / ‘Zips’.

+
+
+

Usage

+
    +
  1. Access a partner record
  2. +
  3. Fill the field Location completion
  4. +
  5. Information about country, state, city and zip will be filled automatically
  6. +
+
+
+

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.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • Camptocamp
  • +
  • ACYSOS S.L.
  • +
  • Alejandro Santana
  • +
  • Tecnativa
  • +
  • AdaptiveCity
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+
    +
  • Icon park: Icon http://icon-park.com/icon/location-map-pin-orange3/
  • +
+
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

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.

+

This module is part of the OCA/partner-contact project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/base_location/tests/__init__.py b/base_location/tests/__init__.py index 372da9775..9816762b0 100644 --- a/base_location/tests/__init__.py +++ b/base_location/tests/__init__.py @@ -1,4 +1,3 @@ -# Copyright 2015 Yannick Vaucher, Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import test_base_location diff --git a/base_location/tests/test_base_location.py b/base_location/tests/test_base_location.py index b86817bcb..eef4aad61 100644 --- a/base_location/tests/test_base_location.py +++ b/base_location/tests/test_base_location.py @@ -1,94 +1,119 @@ # Copyright 2015 Yannick Vaucher, Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo.tests.common import TransactionCase from odoo.exceptions import ValidationError +from odoo.tests import tagged, common +from odoo.tools.misc import mute_logger +import psycopg2 + + +@tagged('post_install', '-at_install') +class TestBaseLocation(common.SavepointCase): + + @classmethod + def setUpClass(cls): + super().setUpClass() + country_obj = cls.env['res.country.state'] + city_obj = cls.env['res.city'] + zip_obj = cls.env['res.city.zip'] + cls.partner_obj = cls.env['res.partner'] + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.state_vd = country_obj.create({ + 'name': 'Vaud', + 'code': 'VD', + 'country_id': cls.env.ref('base.ch').id, + }) + cls.env.ref('base.es').write({ + 'enforce_cities': True + }) + cls.company = cls.env.ref('base.main_company') - -class TestBaseLocation(TransactionCase): - - def test_onchange_better_zip_state_id(self): - """ Test onchange on res.better.zip """ - usa_MA = self.env.ref('base.state_us_34') - better_zip1 = self.env['res.better.zip'].new(self.values_better_zip1()) - better_zip1.state_id = usa_MA - better_zip1._onchange_state_id() - self.assertEqual(better_zip1.country_id, usa_MA.country_id) - - def test_onchange_better_zip_city_id(self): - better_zip2 = self.env['res.better.zip'].new(self.values_better_zip2()) - better_zip2.city_id = self.city_madrid - better_zip2._onchange_city_id() - self.assertEqual(better_zip2.city, self.city_madrid.name) - - def test_onchange_better_zip_country_id(self): - better_zip1 = self.env['res.better.zip'].new(self.values_better_zip1()) - better_zip1.country_id = self.env.ref('base.es') - better_zip1._onchange_country_id() - self.assertFalse(better_zip1.state_id) - - def test_onchange_better_zip_none(self): - better_zip1 = self.env['res.better.zip'].new(self.values_better_zip1()) - better_zip1.country_id = False - better_zip1._onchange_country_id() - self.assertFalse(better_zip1.state_id) + cls.state_bcn = country_obj.create({ + 'name': 'Barcelona', + 'code': '08', + 'country_id': cls.env.ref('base.es').id, + }) + cls.state_madrid = country_obj.create({ + 'name': 'Madrid', + 'code': '28', + 'country_id': cls.env.ref('base.es').id, + }) + cls.city_bcn = city_obj.create({ + 'name': 'Barcelona', + 'state_id': cls.state_bcn.id, + 'country_id': cls.env.ref('base.es').id, + }) + cls.city_madrid = city_obj.create({ + 'name': 'Madrid', + 'state_id': cls.state_madrid.id, + 'country_id': cls.env.ref('base.es').id, + }) + cls.city_lausanne = city_obj.create({ + 'name': 'Lausanne', + 'state_id': cls.state_vd.id, + 'country_id': cls.env.ref('base.ch').id, + }) + cls.lausanne = zip_obj.create({ + 'name': '666', + 'city_id': cls.city_lausanne.id, + }) + cls.barcelona = zip_obj.create({ + 'name': '444', + 'city_id': cls.city_bcn.id, + }) def test_onchange_partner_city_completion(self): - partner1 = self.env['res.partner'].new({ + """Test that partner data is filled accodingly""" + partner1 = self.partner_obj.new({ 'name': 'Camptocamp', }) - better_zip2 = self.env['res.better.zip'].create( - self.values_better_zip2()) - better_zip2.country_id.enforce_cities = True - partner1.zip_id = better_zip2 + self.barcelona.city_id.country_id.enforce_cities = True + partner1.zip_id = self.barcelona partner1._onchange_zip_id() - self.assertEqual(partner1.zip, better_zip2.name) - self.assertEqual(partner1.city, better_zip2.city) - self.assertEqual(partner1.state_id, better_zip2.state_id) - self.assertEqual(partner1.country_id, better_zip2.country_id) + self.assertEqual(partner1.zip, self.barcelona.name) + self.assertEqual(partner1.city, self.barcelona.city_id.name) + self.assertEqual(partner1.state_id, self.barcelona.city_id.state_id) + self.assertEqual(partner1.country_id, + self.barcelona.city_id.country_id) def test_onchange_company_city_completion(self): + """Test that company data is filled accodingly""" company = self.env['res.company'].new({'name': 'Test'}) - better_zip1 = self.env['res.better.zip'].create( - self.values_better_zip1()) - company.zip_id = better_zip1 + company.zip_id = self.lausanne company._onchange_zip_id() - self.assertEqual(company.zip, better_zip1.name) - self.assertEqual(company.city, better_zip1.city) - self.assertEqual(company.state_id, better_zip1.state_id) - self.assertEqual(company.country_id, better_zip1.country_id) + self.assertEqual(company.zip, self.lausanne.name) + self.assertEqual(company.city, self.lausanne.city_id.name) + self.assertEqual(company.state_id, self.lausanne.city_id.state_id) + self.assertEqual(company.country_id, self.lausanne.city_id.country_id) def test_company_address_fields(self): - better_zip1 = self.env['res.better.zip'].create( - self.values_better_zip1() - ) + """Test if the partner address fields changes when + changing the ones from the company""" company = self.env['res.company'].create({ 'name': 'Test', }) self.assertTrue(company.partner_id) company.partner_id.write({ - 'zip_id': better_zip1.id, - 'state_id': better_zip1.state_id.id, - 'country_id': better_zip1.country_id.id, - 'city_id': better_zip1.city_id.id, - 'city': better_zip1.city, - 'zip': better_zip1.name, + 'zip_id': self.lausanne.id, + 'state_id': self.lausanne.city_id.state_id.id, + 'country_id': self.lausanne.city_id.country_id.id, + 'city_id': self.lausanne.city_id.id, + 'city': self.lausanne.city_id.name, + 'zip': self.lausanne.name, }) company._compute_address() self.assertEqual(company.zip_id, company.partner_id.zip_id) self.assertEqual(company.city_id, company.partner_id.city_id) def test_company_address_fields_inverse(self): - better_zip2 = self.env['res.better.zip'].create( - self.values_better_zip2() - ) + """Test inverse fields from res.company""" company = self.env['res.company'].new({ 'name': 'Test', - 'partner_id': self.env['res.partner'].new({}).id + 'partner_id': self.partner_obj.new({}).id # Partner must be initiated in order to be filled }) company.update({ - 'zip_id': better_zip2.id, + 'zip_id': self.barcelona.id, }) company._inverse_city_id() company._inverse_zip_id() @@ -96,112 +121,76 @@ class TestBaseLocation(TransactionCase): self.assertEqual(company.city_id, company.partner_id.city_id) def test_onchange_company_city_id_completion(self): + """Test city auto-completion when changing zip in a company""" company = self.env['res.company'].new({'name': 'Test'}) - better_zip2 = self.env['res.better.zip'].create( - self.values_better_zip2()) - company.zip_id = better_zip2 + company.zip_id = self.barcelona company._onchange_zip_id() - self.assertEqual(company.city_id, better_zip2.city_id) - - def test_constrains_better_zip_01(self): - better_zip1 = self.env['res.better.zip'].create( - self.values_better_zip1()) - better_zip2 = self.env['res.better.zip'].create( - self.values_better_zip2()) - - better_zip1.city_id = self.city_lausanne - with self.assertRaises(ValidationError): - better_zip2.city_id = better_zip1.city_id - - def test_constrains_better_zip_02(self): - better_zip1 = self.env['res.better.zip'].create( - self.values_better_zip1()) - better_zip2 = self.env['res.better.zip'].create( - self.values_better_zip2()) - - with self.assertRaises(ValidationError): - better_zip2.country_id = better_zip1.country_id - - def test_constrains_better_zip_03(self): - better_zip1 = self.env['res.better.zip'].create( - self.values_better_zip1()) - better_zip2 = self.env['res.better.zip'].create( - self.values_better_zip2()) - - with self.assertRaises(ValidationError): - better_zip2.state_id = better_zip1.state_id - - def test_constrains_better_zip_04(self): - better_zip2 = self.env['res.better.zip'].create( - self.values_better_zip2()) - - with self.assertRaises(ValidationError): - better_zip2.city_id = self.city_madrid + self.assertEqual(company.city_id, self.barcelona.city_id) def test_constrains_partner_01(self): - better_zip2 = self.env['res.better.zip'].create( - self.values_better_zip2()) + """Test partner 1 constraints""" with self.assertRaises(ValidationError): - self.env['res.partner'].create({ + self.partner_obj.create({ 'name': 'P1', - 'zip_id': better_zip2.id, + 'zip_id': self.barcelona.id, }) - def test_constrains_partner_02(self): - better_zip2 = self.env['res.better.zip'].create( - self.values_better_zip2()) - partner = self.env['res.partner'].create({ + def test_constrains_partner_country(self): + """Test partner country constraints""" + partner = self.partner_obj.create({ 'name': 'P1', - 'zip_id': better_zip2.id, - 'country_id': better_zip2.country_id.id, - 'state_id': better_zip2.state_id.id, - 'city_id': better_zip2.city_id.id, + 'zip_id': self.barcelona.id, + 'country_id': self.barcelona.city_id.country_id.id, + 'state_id': self.barcelona.city_id.state_id.id, + 'city_id': self.barcelona.city_id.id, }) with self.assertRaises(ValidationError): partner.country_id = self.ref('base.ch') - with self.assertRaises(ValidationError): - partner.state_id = self.state_vd.id, + def test_constrains_partner_state(self): + """Test partner state constraints""" + partner = self.partner_obj.create({ + 'name': 'P1', + 'zip_id': self.barcelona.id, + 'country_id': self.barcelona.city_id.country_id.id, + 'state_id': self.barcelona.city_id.state_id.id, + 'city_id': self.barcelona.city_id.id, + }) with self.assertRaises(ValidationError): - partner.city_id = self.city_lausanne + partner.state_id = self.state_vd.id - def values_better_zip1(self): - return { - 'name': 1000, - 'city': 'Lausanne', - 'state_id': self.state_vd.id, - 'country_id': self.ref('base.ch'), - } + def test_constrains_partner_city(self): + """Test partner city constraints""" + partner = self.partner_obj.create({ + 'name': 'P1', + 'zip_id': self.barcelona.id, + 'country_id': self.barcelona.city_id.country_id.id, + 'state_id': self.barcelona.city_id.state_id.id, + 'city_id': self.barcelona.city_id.id, + }) - def values_better_zip2(self): - return { - 'city_id': self.city_bcn.id, - 'city': self.city_bcn.name, - 'state_id': self.state_bcn.id, - 'country_id': self.ref('base.es'), - } + with self.assertRaises(ValidationError): + partner.city_id = self.city_lausanne def test_partner_onchange_country(self): - country_es = self.browse_ref('base.es') + """Test partner onchange country_id""" + country_es = self.env.ref('base.es') country_es.enforce_cities = True - better_zip1 = self.env['res.better.zip'].create( - self.values_better_zip1()) - partner = self.env['res.partner'].new({ + partner = self.partner_obj.new({ 'name': 'TEST', - 'zip_id': better_zip1.id + 'zip_id': self.lausanne.id }) partner.country_id = country_es partner._onchange_country_id() self.assertFalse(partner.zip_id) def test_partner_onchange_city(self): - better_zip1 = self.env['res.better.zip'].create( - self.values_better_zip1()) - partner = self.env['res.partner'].new({ + """Test partner onchange city_id""" + partner = self.partner_obj.new({ 'name': 'TEST', - 'zip_id': better_zip1.id + 'zip_id': self.lausanne.id }) self.city_bcn.country_id.enforce_cities = False partner.city_id = self.city_bcn @@ -212,119 +201,78 @@ class TestBaseLocation(TransactionCase): self.assertFalse(res['domain']['zip_id']) def test_partner_onchange_state(self): - better_zip1 = self.env['res.better.zip'].create( - self.values_better_zip1()) - partner = self.env['res.partner'].new({ + """Test partner onchange state_id""" + partner = self.partner_obj.new({ 'name': 'TEST', - 'zip_id': better_zip1.id + 'zip_id': self.lausanne.id }) partner.state_id = self.state_bcn partner._onchange_state_id() self.assertFalse(partner.zip_id) + self.assertEqual(partner.country_id, partner.state_id.country_id) + + def test_company_onchange_state(self): + """Test company onchange state_id""" + self.company.state_id = self.state_bcn + self.company._onchange_state_id() + self.assertEqual(self.company.country_id, + self.company.state_id.country_id) def test_display_name(self): - better_zip1 = self.env['res.better.zip'].create( - self.values_better_zip1()) + """Test if the display_name is stored and computed properly""" self.assertEqual( - better_zip1.display_name, '1000, Lausanne, Vaud, '+self.browse_ref( + self.lausanne.display_name, + '666, Lausanne, Vaud, ' + self.browse_ref( 'base.ch' ).name ) - def test_name_search___can_find_using_city_name_or_zip_or_code(self): - barcelona_data = { - 'city_id': self.city_bcn.id, - 'city': self.city_bcn.name, - 'name': '444', - 'code': 'BA', - 'state_id': self.state_bcn.id, - 'country_id': self.ref('base.es'), - } + def test_name_search(self): + """Test that zips can be searched through both the name of the + city or the zip code""" madrid_data = { 'city_id': self.city_madrid.id, - 'city': self.city_madrid.name, 'name': '555', - 'code': 'MD', - 'state_id': self.state_madrid.id, - 'country_id': self.ref('base.es'), - } - lausanne_data = { - 'city_id': self.city_lausanne.id, - 'city': self.city_lausanne.name, - 'name': '666', - 'code': 'LA', - 'state_id': self.state_vd.id, - 'country_id': self.ref('base.ch'), } - barcelona = self.env['res.better.zip'].create(barcelona_data) - madrid = self.env['res.better.zip'].create(madrid_data) - lausanne = self.env['res.better.zip'].create(lausanne_data) + madrid = self.env['res.city.zip'].create(madrid_data) - found_recs = self.env['res.better.zip'].name_search(name='444') + found_recs = self.env['res.city.zip'].name_search(name='444') self.assertEqual(len(found_recs), 1) - self.assertEqual(found_recs[0][0], barcelona.id) - found_recs = self.env['res.better.zip'].name_search(name='Barcelona') + self.assertEqual(found_recs[0][0], self.barcelona.id) + found_recs = self.env['res.city.zip'].name_search(name='Barcelona') self.assertEqual(len(found_recs), 1) - self.assertEqual(found_recs[0][0], barcelona.id) - found_recs = self.env['res.better.zip'].name_search(name='BA') - self.assertEqual(len(found_recs), 1) - self.assertEqual(found_recs[0][0], barcelona.id) + self.assertEqual(found_recs[0][0], self.barcelona.id) - found_recs = self.env['res.better.zip'].name_search(name='555') - self.assertEqual(len(found_recs), 1) - self.assertEqual(found_recs[0][0], madrid.id) - found_recs = self.env['res.better.zip'].name_search(name='Madrid') + found_recs = self.env['res.city.zip'].name_search(name='555') self.assertEqual(len(found_recs), 1) self.assertEqual(found_recs[0][0], madrid.id) - found_recs = self.env['res.better.zip'].name_search(name='MD') + found_recs = self.env['res.city.zip'].name_search(name='Madrid') self.assertEqual(len(found_recs), 1) self.assertEqual(found_recs[0][0], madrid.id) - found_recs = self.env['res.better.zip'].name_search(name='666') + found_recs = self.env['res.city.zip'].name_search(name='666') self.assertEqual(len(found_recs), 1) - self.assertEqual(found_recs[0][0], lausanne.id) - found_recs = self.env['res.better.zip'].name_search(name='Lausanne') + self.assertEqual(found_recs[0][0], self.lausanne.id) + found_recs = self.env['res.city.zip'].name_search(name='Lausanne') self.assertEqual(len(found_recs), 1) - self.assertEqual(found_recs[0][0], lausanne.id) - found_recs = self.env['res.better.zip'].name_search(name='LA') - self.assertEqual(len(found_recs), 1) - self.assertEqual(found_recs[0][0], lausanne.id) - - def setUp(self): - super(TestBaseLocation, self).setUp() - self.state_vd = self.env['res.country.state'].create({ - 'name': 'Vaud', - 'code': 'VD', - 'country_id': self.ref('base.ch'), - }) - self.env['res.country'].browse(self.ref('base.es')).write({ - 'enforce_cities': True - }) - self.company = self.env.ref('base.main_company') + self.assertEqual(found_recs[0][0], self.lausanne.id) + + def test_zip_ql_constraints(self): + """Test UNIQUE name within it's area for zips""" + with self.assertRaises( + psycopg2.IntegrityError), mute_logger('odoo.sql_db'): + self.env['res.city.zip'].create({ + 'name': '666', + 'city_id': self.city_lausanne.id, + }) - self.state_bcn = self.env['res.country.state'].create({ - 'name': 'Barcelona', - 'code': '08', - 'country_id': self.ref('base.es'), - }) - self.state_madrid = self.env['res.country.state'].create({ - 'name': 'Madrid', - 'code': '28', - 'country_id': self.ref('base.es'), - }) - self.city_bcn = self.env['res.city'].create({ - 'name': 'Barcelona', - 'state_id': self.state_bcn.id, - 'country_id': self.ref('base.es'), - }) - self.city_madrid = self.env['res.city'].create({ - 'name': 'Madrid', - 'state_id': self.state_madrid.id, - 'country_id': self.ref('base.es'), - }) - self.city_lausanne = self.env['res.city'].create({ - 'name': 'Lausanne', - 'state_id': self.state_vd.id, - 'country_id': self.ref('base.ch'), - }) + def test_city_sql_contraint(self): + """Test UNIQUE name within it's area for cities""" + with self.assertRaises( + psycopg2.IntegrityError), mute_logger('odoo.sql_db'): + self.env['res.city'].create({ + 'name': 'Barcelona', + 'state_id': self.state_bcn.id, + 'country_id': self.ref('base.es'), + }) diff --git a/base_location/views/better_zip_view.xml b/base_location/views/better_zip_view.xml deleted file mode 100644 index 207c88e92..000000000 --- a/base_location/views/better_zip_view.xml +++ /dev/null @@ -1,85 +0,0 @@ - - - - - res.better.zip.form - res.better.zip - -
- - - - - - - - - - - - - -
-
-
- - - res.better.zip.tree - res.better.zip - - - - - - - - - - - - - res.better.zip.select - res.better.zip - - - - - - - - - - - - - - - - - Locations - res.better.zip - form - tree,form - - - - - - - - -
diff --git a/base_location/views/res_city_view.xml b/base_location/views/res_city_view.xml new file mode 100644 index 000000000..5ad6fb9aa --- /dev/null +++ b/base_location/views/res_city_view.xml @@ -0,0 +1,63 @@ + + + + + res.city + + + + + + + 1 + + + + + + + + + res.city + +
+ + + + + + + + + + +
+
+
+ + + Cities + ir.actions.act_window + res.city + form + tree,form + + + Display and manage the list of all cities that can be assigned to + your partner records. Note that an option can be set on each country separately + to enforce any address of it to have a city in this list. + + + + + +
diff --git a/base_location/views/res_city_zip_view.xml b/base_location/views/res_city_zip_view.xml new file mode 100644 index 000000000..3eeb5f744 --- /dev/null +++ b/base_location/views/res_city_zip_view.xml @@ -0,0 +1,56 @@ + + + + + res.city.zip.form + res.city.zip + +
+ + + + +
+
+
+ + + res.city.zip.tree + res.city.zip + + + + + + + + + + res.city.zip.select + res.city.zip + + + + + + + + + + Locations + res.city.zip + form + tree,form + + + + + + +
diff --git a/base_location/views/company_view.xml b/base_location/views/res_company_view.xml similarity index 100% rename from base_location/views/company_view.xml rename to base_location/views/res_company_view.xml diff --git a/base_location/views/res_country_view.xml b/base_location/views/res_country_view.xml index cbf0f3ff3..0292f52ab 100644 --- a/base_location/views/res_country_view.xml +++ b/base_location/views/res_country_view.xml @@ -22,7 +22,7 @@ icon="fa-globe" type="action" context="{'default_country_id': active_id, 'search_default_country_id': active_id}" - string="Locations"> + string="Zips"> diff --git a/base_location/views/partner_view.xml b/base_location/views/res_partner_view.xml similarity index 99% rename from base_location/views/partner_view.xml rename to base_location/views/res_partner_view.xml index 1e8a016e5..e10fbd474 100644 --- a/base_location/views/partner_view.xml +++ b/base_location/views/res_partner_view.xml @@ -23,4 +23,3 @@ - diff --git a/base_location/views/state_view.xml b/base_location/views/state_view.xml deleted file mode 100644 index cbd4f72aa..000000000 --- a/base_location/views/state_view.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - view_country_state_form2 - res.country.state - - - - - - - - - - - - - - - -