|
|
@ -2,6 +2,8 @@ |
|
|
|
# Copyright 2018 Tecnativa - Pedro M. Baeza |
|
|
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). |
|
|
|
|
|
|
|
from lxml import etree |
|
|
|
|
|
|
|
from odoo import _, api, fields, models |
|
|
|
from odoo.exceptions import ValidationError |
|
|
|
|
|
|
@ -16,9 +18,6 @@ class ResPartner(models.Model): |
|
|
|
compute="_compute_zip_id", |
|
|
|
readonly=False, |
|
|
|
store=True, |
|
|
|
domain="[('city_id', '=?', city_id), " |
|
|
|
"('city_id.country_id', '=?', country_id), " |
|
|
|
"('city_id.state_id', '=?', state_id)]", |
|
|
|
) |
|
|
|
city_id = fields.Many2one( |
|
|
|
index=True, # add index for performance |
|
|
@ -33,17 +32,24 @@ class ResPartner(models.Model): |
|
|
|
) |
|
|
|
state_id = fields.Many2one(compute="_compute_state_id", readonly=False, store=True) |
|
|
|
|
|
|
|
@api.depends("state_id", "country_id") |
|
|
|
@api.depends("state_id", "country_id", "city_id", "zip") |
|
|
|
def _compute_zip_id(self): |
|
|
|
"""Empty the zip auto-completion field if data mismatch when on UI.""" |
|
|
|
for record in self.filtered("zip_id"): |
|
|
|
for field in ["state_id", "country_id"]: |
|
|
|
fields_map = { |
|
|
|
"zip": "name", |
|
|
|
"city_id": "city_id", |
|
|
|
"state_id": "state_id", |
|
|
|
"country_id": "country_id", |
|
|
|
} |
|
|
|
for rec_field, zip_field in fields_map.items(): |
|
|
|
if ( |
|
|
|
record[field] |
|
|
|
and record[field] != record._origin[field] |
|
|
|
and record[field] != record.zip_id.city_id[field] |
|
|
|
record[rec_field] |
|
|
|
and record[rec_field] != record._origin[rec_field] |
|
|
|
and record[rec_field] != record.zip_id[zip_field] |
|
|
|
): |
|
|
|
record.zip_id = False |
|
|
|
break |
|
|
|
|
|
|
|
@api.depends("zip_id") |
|
|
|
def _compute_city_id(self): |
|
|
@ -90,28 +96,53 @@ class ResPartner(models.Model): |
|
|
|
if state and record.state_id != state: |
|
|
|
record.state_id = record.zip_id.city_id.state_id |
|
|
|
|
|
|
|
@api.constrains("zip_id", "country_id", "city_id", "state_id") |
|
|
|
@api.constrains("zip_id", "country_id", "city_id", "state_id", "zip") |
|
|
|
def _check_zip(self): |
|
|
|
if self.env.context.get("skip_check_zip"): |
|
|
|
return |
|
|
|
for rec in self: |
|
|
|
if not rec.zip_id: |
|
|
|
continue |
|
|
|
if rec.zip_id.city_id.state_id != rec.state_id: |
|
|
|
if rec.zip_id.city_id.country_id != rec.country_id: |
|
|
|
raise ValidationError( |
|
|
|
_("The state of the partner %s differs from that in " "location %s") |
|
|
|
_("The country of the partner %s differs from that in location %s") |
|
|
|
% (rec.name, rec.zip_id.name) |
|
|
|
) |
|
|
|
if rec.zip_id.city_id.country_id != rec.country_id: |
|
|
|
if rec.zip_id.city_id.state_id != rec.state_id: |
|
|
|
raise ValidationError( |
|
|
|
_( |
|
|
|
"The country of the partner %s differs from that in " |
|
|
|
"location %s" |
|
|
|
) |
|
|
|
_("The state of the partner %s differs from that in location %s") |
|
|
|
% (rec.name, rec.zip_id.name) |
|
|
|
) |
|
|
|
if rec.zip_id.city_id != rec.city_id: |
|
|
|
raise ValidationError( |
|
|
|
_("The city of partner %s differs from that in " "location %s") |
|
|
|
_("The city of partner %s differs from that in location %s") |
|
|
|
% (rec.name, rec.zip_id.name) |
|
|
|
) |
|
|
|
if rec.zip_id.name != rec.zip: |
|
|
|
raise ValidationError( |
|
|
|
_("The zip of the partner %s differs from that in location %s") |
|
|
|
% (rec.name, rec.zip_id.name) |
|
|
|
) |
|
|
|
|
|
|
|
def _zip_id_domain(self): |
|
|
|
return """ |
|
|
|
[ |
|
|
|
("city_id", "=?", city_id), |
|
|
|
("city_id.country_id", "=?", country_id), |
|
|
|
("city_id.state_id", "=?", state_id), |
|
|
|
] |
|
|
|
""" |
|
|
|
|
|
|
|
@api.model |
|
|
|
def _fields_view_get_address(self, arch): |
|
|
|
# We want to use a domain that requires city_id to be on the view |
|
|
|
# but we can't add it directly there, otherwise _fields_view_get_address |
|
|
|
# in base_address_city won't do its magic, as it immediately returns |
|
|
|
# if city_id is already in there. On the other hand, if city_id is not in the |
|
|
|
# views, odoo won't let us use it in zip_id's domain. |
|
|
|
# For this reason we need to set the domain here. |
|
|
|
arch = super()._fields_view_get_address(arch) |
|
|
|
doc = etree.fromstring(arch) |
|
|
|
for node in doc.xpath("//field[@name='zip_id']"): |
|
|
|
node.attrib["domain"] = self._zip_id_domain() |
|
|
|
return etree.tostring(doc, encoding="unicode") |