Created
May 7, 2025 15:50
-
-
Save tderick/a1cf4b02d40b2a5e7017f164acd28a2f to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <group> | |
| <span class="o_form_label o_td_label" name="address_name"> | |
| Addresse | |
| </span> | |
| <div class="o_address_format"> | |
| <field name="street" placeholder="Rue..." class="o_address_street"/> | |
| <field name="street2" placeholder="Rue2..." class="o_address_street"/> | |
| <field name="zip_id" options="{'create_name_field': 'city', 'no_open': True, 'no_create': True}" | |
| placeholder="City completion" class="oe_edit_only"/> | |
| <field name="city" placeholder="Cite" class="o_address_city"/> | |
| <field name="state_id" class="o_address_state" placeholder="State" | |
| options="{'no_open': True, 'no_quick_create': True}" | |
| context="{'country_id': country_id, 'default_country_id': country_id, 'zip': zip}"/> | |
| <field name="zip" placeholder="ZIP" class="o_address_zip"/> | |
| <div name="partner_address_country" class="d-flex justify-content-between"> | |
| <field name="country_id" placeholder="Country" class="o_address_country" | |
| options="{"no_open": True, "no_create": True}"/> | |
| </div> | |
| </div> | |
| </group> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from odoo import models, fields, api, _ | |
| from odoo.exceptions import ValidationError | |
| class CrmLeadWizard(models.TransientModel): | |
| _name = 'crm.lead.wizard' | |
| _description = 'Residence Permit Wizard' | |
| street = fields.Char("Rue", required=True) | |
| street2 = fields.Char("Rue 2") | |
| city = fields.Char("Cité", required=True, | |
| compute="_compute_city", readonly=False, store=True) | |
| zip = fields.Char("ZIP", required=True, | |
| compute="_compute_zip", readonly=False, store=True) | |
| zip_id = fields.Many2one( | |
| comodel_name="res.city.zip", | |
| string="ZIP Location", | |
| index=True, | |
| compute="_compute_zip_id", | |
| readonly=False, | |
| store=True, | |
| ) | |
| city_id = fields.Many2one( | |
| "res.city", | |
| index=True, # add index for performance | |
| compute="_compute_city_id", | |
| readonly=False, | |
| store=True, | |
| ) | |
| country_id = fields.Many2one("res.country", | |
| compute="_compute_country_id", readonly=False, store=True | |
| ) | |
| state_id = fields.Many2one("res.country.state", "État", | |
| compute="_compute_state_id", readonly=False, store=True) | |
| country_enforce_cities = fields.Boolean() | |
| # ADDRESS AUTO-COMPLETE | |
| @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"): | |
| 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[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): | |
| if hasattr(super(), "_compute_city_id"): | |
| super()._compute_city_id() # pragma: no cover | |
| for record in self: | |
| if record.zip_id: | |
| record.city_id = record.zip_id.city_id | |
| elif not record.country_enforce_cities: | |
| record.city_id = False | |
| @api.depends("zip_id") | |
| def _compute_city(self): | |
| if hasattr(super(), "_compute_city"): | |
| super()._compute_city() # pragma: no cover | |
| for record in self: | |
| if record.zip_id: | |
| record.city = record.zip_id.city_id.name | |
| @api.depends("zip_id") | |
| def _compute_zip(self): | |
| if hasattr(super(), "_compute_zip"): | |
| super()._compute_zip() # pragma: no cover | |
| for record in self: | |
| if record.zip_id: | |
| record.zip = record.zip_id.name | |
| @api.depends("zip_id", "state_id") | |
| def _compute_country_id(self): | |
| if hasattr(super(), "_compute_country_id"): | |
| super()._compute_country_id() # pragma: no cover | |
| for record in self: | |
| if record.zip_id.city_id.country_id: | |
| record.country_id = record.zip_id.city_id.country_id | |
| elif record.state_id: | |
| record.country_id = record.state_id.country_id | |
| @api.depends("zip_id") | |
| def _compute_state_id(self): | |
| if hasattr(super(), "_compute_state_id"): | |
| super()._compute_state_id() # pragma: no cover | |
| for record in self: | |
| state = record.zip_id.city_id.state_id | |
| 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", "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.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) | |
| ) | |
| 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.city_id != rec.city_id: | |
| raise ValidationError( | |
| _("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") | |
| @api.model | |
| def _address_fields(self): | |
| return super()._address_fields() + ["zip_id"] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment