diff --git a/addons/test_event_full/tests/test_performance.py b/addons/test_event_full/tests/test_performance.py index 0bc17efb951..76e2e808847 100644 --- a/addons/test_event_full/tests/test_performance.py +++ b/addons/test_event_full/tests/test_performance.py @@ -52,7 +52,7 @@ class TestEventPerformance(EventPerformanceCase): batch_size = 20 # simple without type involved + website - with freeze_time(self.reference_now), self.assertQueryCount(event_user=3236): # tef 3173 / com 3172 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=3377): # tef 3275 / com 3274 self.env.cr._now = self.reference_now # force create_date to check schedulers event_values = [ dict(self.event_base_vals, @@ -60,7 +60,7 @@ class TestEventPerformance(EventPerformanceCase): ) for x in range(batch_size) ] - self.env['event.event'].create(event_values) + self.env['event.event'].with_context(lang='en_US').create(event_values) @users('event_user') @warmup @@ -89,7 +89,7 @@ class TestEventPerformance(EventPerformanceCase): event_type = self.env['event.type'].browse(self.test_event_type.ids) # complex with type + website - with freeze_time(self.reference_now), self.assertQueryCount(event_user=3340): # tef 3277 / com 3276 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=3481): # tef 3379 / com 3378 self.env.cr._now = self.reference_now # force create_date to check schedulers event_values = [ dict(self.event_base_vals, @@ -97,7 +97,7 @@ class TestEventPerformance(EventPerformanceCase): ) for x in range(batch_size) ] - self.env['event.event'].create(event_values) + self.env['event.event'].with_context(lang='en_US').create(event_values) @users('event_user') @@ -128,7 +128,7 @@ class TestEventPerformance(EventPerformanceCase): has_social = 'social_menu' in self.env['event.event'] # otherwise view may crash in enterprise # no type, website - with freeze_time(self.reference_now), self.assertQueryCount(event_user=427): # tef 379 / com 380 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=447): # tef 379 / com 427 self.env.cr._now = self.reference_now # force create_date to check schedulers # Require for `website_menu` to be visible #
@@ -150,7 +150,7 @@ class TestEventPerformance(EventPerformanceCase): has_social = 'social_menu' in self.env['event.event'] # otherwise view may crash in enterprise # type and website - with freeze_time(self.reference_now), self.assertQueryCount(event_user=450): # tef 402 / com 408 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=470): # tef 402 / com 450 self.env.cr._now = self.reference_now # force create_date to check schedulers # Require for `website_menu` to be visible #
@@ -181,13 +181,13 @@ class TestEventPerformance(EventPerformanceCase): def test_event_create_single_notype_website(self): """ Test a single event creation """ # simple without type involved + website - with freeze_time(self.reference_now), self.assertQueryCount(event_user=234): # tef 228 / com 227 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=242): # tef 228 / com 234 self.env.cr._now = self.reference_now # force create_date to check schedulers event_values = dict( self.event_base_vals, website_menu=True ) - self.env['event.event'].create([event_values]) + self.env['event.event'].with_context(lang='en_US').create([event_values]) @users('event_user') @warmup @@ -212,13 +212,13 @@ class TestEventPerformance(EventPerformanceCase): event_type = self.env['event.type'].browse(self.test_event_type.ids) # complex with type + website - with freeze_time(self.reference_now), self.assertQueryCount(event_user=262): # tef 256 / com 255 + with freeze_time(self.reference_now), self.assertQueryCount(event_user=271): # tef 256 / com 262 self.env.cr._now = self.reference_now # force create_date to check schedulers event_values = dict( self.event_base_vals, event_type_id=event_type.id, ) - self.env['event.event'].create([event_values]) + self.env['event.event'].with_context(lang='en_US').create([event_values]) @tagged('event_performance', 'registration_performance', 'post_install', '-at_install') diff --git a/odoo/addons/base/models/res_users.py b/odoo/addons/base/models/res_users.py index fbb0e2167bd..6883d1efd92 100644 --- a/odoo/addons/base/models/res_users.py +++ b/odoo/addons/base/models/res_users.py @@ -693,12 +693,19 @@ class Users(models.Model): for name, key in name_to_key.items() } - # ensure the language is set and is compatible with the web client - lang = context.get('lang') or (request and request.default_lang()) or DEFAULT_LANG - if lang == 'ar_AR': - context['lang'] = 'ar' - if lang in babel.core.LOCALE_ALIASES: - context['lang'] = babel.core.LOCALE_ALIASES[lang] + # ensure lang is set and available + # context > request > company > english > any lang installed + langs = [code for code, _ in self.env['res.lang'].get_installed()] + lang = context.get('lang') + if lang not in langs: + lang = request.best_lang if request else None + if lang not in langs: + lang = self.env.user.company_id.partner_id.lang + if lang not in langs: + lang = DEFAULT_LANG + if lang not in langs: + lang = langs[0] if langs else DEFAULT_LANG + context['lang'] = lang # ensure uid is set context['uid'] = self.env.uid diff --git a/odoo/addons/base/tests/test_res_users.py b/odoo/addons/base/tests/test_res_users.py index c52d67f616e..cd4a472de11 100644 --- a/odoo/addons/base/tests/test_res_users.py +++ b/odoo/addons/base/tests/test_res_users.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. +from types import SimpleNamespace +from unittest.mock import patch from odoo.addons.base.models.res_users import is_selection_groups, get_selection_groups, name_selection_groups from odoo.exceptions import UserError -from odoo.tests.common import TransactionCase, Form, tagged +from odoo.tests.common import Form, TransactionCase, new_test_user, tagged from odoo.tools import mute_logger @@ -188,6 +190,37 @@ class TestUsers(TransactionCase): self.assertTrue(portal_partner_2.exists(), 'Should have kept the partner') self.assertEqual(asked_deletion_2.state, 'fail', 'Should have marked the deletion as failed') + def test_context_get_lang(self): + self.env['res.lang'].with_context(active_test=False).search([ + ('code', 'in', ['fr_FR', 'es_ES', 'de_DE', 'en_US']) + ]).write({'active': True}) + + user = new_test_user(self.env, 'jackoneill') + user = user.with_user(user) + user.lang = 'fr_FR' + + company = user.company_id.partner_id.sudo() + company.lang = 'de_DE' + + request = SimpleNamespace() + request.best_lang = 'es_ES' + request_patch = patch('odoo.addons.base.models.res_users.request', request) + self.addCleanup(request_patch.stop) + request_patch.start() + + self.assertEqual(user.context_get()['lang'], 'fr_FR') + self.env.registry.clear_caches() + user.lang = False + + self.assertEqual(user.context_get()['lang'], 'es_ES') + self.env.registry.clear_caches() + request_patch.stop() + + self.assertEqual(user.context_get()['lang'], 'de_DE') + self.env.registry.clear_caches() + company.lang = False + + self.assertEqual(user.context_get()['lang'], 'en_US') @tagged('post_install', '-at_install') class TestUsers2(TransactionCase): diff --git a/odoo/http.py b/odoo/http.py index 50845e36130..3feb5002e02 100644 --- a/odoo/http.py +++ b/odoo/http.py @@ -1372,6 +1372,22 @@ class Request: _cr = cr + @lazy_property + def best_lang(self): + lang = self.httprequest.accept_languages.best + if not lang: + return None + + try: + code, territory, _, _ = babel.core.parse_locale(lang, sep='-') + if territory: + lang = f'{code}_{territory}' + else: + lang = babel.core.LOCALE_ALIASES[code] + return lang + except (ValueError, KeyError): + return None + # ===================================================== # Helpers # ===================================================== @@ -1434,19 +1450,7 @@ class Request: :returns: Preferred language if specified or 'en_US' :rtype: str """ - lang = self.httprequest.accept_languages.best - if not lang: - return DEFAULT_LANG - - try: - code, territory, _, _ = babel.core.parse_locale(lang, sep='-') - if territory: - lang = f'{code}_{territory}' - else: - lang = babel.core.LOCALE_ALIASES[code] - return lang - except (ValueError, KeyError): - return DEFAULT_LANG + return self.best_lang or DEFAULT_LANG def get_http_params(self): """