From ef5a9612f747089a2c3de333d2c6c4f672f8432f Mon Sep 17 00:00:00 2001 From: arg-odoo Date: Thu, 7 Jul 2022 10:48:38 +0000 Subject: [PATCH] [FIX] base: avoid `KeyError` in `res_partner._display_address` In some case, users use custom `address_format` with keys that do not exists. This lead to errors in odoo and upgrade process. Avoid `KeyError` by using defaultdict with `str` value. upg-361146 upg-361241 ``` Traceback (most recent call last): File "/home/odoo/src/odoo/14.0/odoo/service/server.py", line 1201, in preload_registries registry = Registry.new(dbname, update_module=update_module) File "/home/odoo/src/odoo/14.0/odoo/modules/registry.py", line 89, in new odoo.modules.load_modules(registry._db, force_demo, status, update_module) File "/home/odoo/src/odoo/14.0/odoo/modules/loading.py", line 457, in load_modules force, status, report, loaded_modules, update_module, models_to_check) File "/home/odoo/src/odoo/14.0/odoo/modules/loading.py", line 349, in load_marked_modules perform_checks=perform_checks, models_to_check=models_to_check File "/home/odoo/src/odoo/14.0/odoo/modules/loading.py", line 227, in load_module_graph migrations.migrate_module(package, 'post') File "/home/odoo/src/odoo/14.0/odoo/modules/migration.py", line 180, in migrate_module migrate(self.cr, installed_version) File "/tmp/tmp1bew9m7l/migrations/website/14.0.1.0/post-adapt-footer-data.py", line 69, in migrate address = html_escape(partner._display_address(without_company=True)) File "/home/odoo/src/odoo/14.0/odoo/addons/base/models/res_partner.py", line 950, in _display_address return address_format % args KeyError: 'town_name' ``` closes odoo/odoo#96703 X-original-commit: 81bf1d2101202a4a6cb832004500129905c2d3be Signed-off-by: Christophe Simonis --- odoo/addons/base/models/res_partner.py | 5 +++-- odoo/addons/base/tests/test_res_partner.py | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/odoo/addons/base/models/res_partner.py b/odoo/addons/base/models/res_partner.py index aa7c483e868..b6c557be360 100644 --- a/odoo/addons/base/models/res_partner.py +++ b/odoo/addons/base/models/res_partner.py @@ -10,6 +10,7 @@ import threading import re import requests +from collections import defaultdict from lxml import etree from random import randint from werkzeug import urls @@ -967,13 +968,13 @@ class Partner(models.Model): # get the information that will be injected into the display format # get the address format address_format = self._get_address_format() - args = { + args = defaultdict(str, { 'state_code': self.state_id.code or '', 'state_name': self.state_id.name or '', 'country_code': self.country_id.code or '', 'country_name': self._get_country_name(), 'company_name': self.commercial_company_name or '', - } + }) for field in self._formatting_address_fields(): args[field] = getattr(self, field) or '' if without_company: diff --git a/odoo/addons/base/tests/test_res_partner.py b/odoo/addons/base/tests/test_res_partner.py index d57cbc50064..fe0ebe56ca4 100644 --- a/odoo/addons/base/tests/test_res_partner.py +++ b/odoo/addons/base/tests/test_res_partner.py @@ -182,3 +182,22 @@ class TestPartner(TransactionCase): partner_form.company_type = 'person' partner_form.name = 'Philip' self.assertEqual(partner_form.user_id, test_parent_partner.user_id) + + def test_display_address_missing_key(self): + """ Check _display_address when some keys are missing. As a defaultdict is used, missing keys should be + filled with empty strings. """ + country = self.env["res.country"].create({"name": "TestCountry", "address_format": "%(city)s %(zip)s"}) + partner = self.env["res.partner"].create({ + "name": "TestPartner", + "country_id": country.id, + "city": "TestCity", + "zip": "12345", + }) + before = partner._display_address() + # Manually update the country address_format because placeholders are checked by create + self.env.cr.execute( + "UPDATE res_country SET address_format ='%%(city)s %%(zip)s %%(nothing)s' WHERE id=%s", + [country.id] + ) + self.env["res.country"].invalidate_model() + self.assertEqual(before, partner._display_address().strip())