diff --git a/addons/web/static/src/views/utils.js b/addons/web/static/src/views/utils.js index f4b03dc9fcd..34bebefcd01 100644 --- a/addons/web/static/src/views/utils.js +++ b/addons/web/static/src/views/utils.js @@ -266,12 +266,12 @@ export function toStringExpression(str) { } /** - * Generate a unique identifier. + * Generate a unique identifier (64 bits) in hexadecimal. * * @returns {string} */ export function uuid() { - const array = new Uint8Array(16); + const array = new Uint8Array(8); window.crypto.getRandomValues(array); // Uint8Array to hex return [...array].map((b) => b.toString(16).padStart(2, "0")).join(""); diff --git a/odoo/addons/test_new_api/tests/test_properties.py b/odoo/addons/test_new_api/tests/test_properties.py index 9bd8ba86ab5..9e9f2c63861 100644 --- a/odoo/addons/test_new_api/tests/test_properties.py +++ b/odoo/addons/test_new_api/tests/test_properties.py @@ -230,18 +230,20 @@ class PropertiesCase(TransactionCase): }]) self.env.invalidate_all() - with self.assertQueryCount(7): + with self.assertQueryCount(8): messages = self.env['test_new_api.message'].create([{ 'name': 'Test Message', 'discussion': self.discussion_1.id, 'author': self.user.id, 'attributes': [{ - 'name': 'discussion_color_code', - 'string': 'New Label', + # no name, should be automatically generated + 'string': 'Discussion Color code', 'type': 'char', 'default': 'blue', 'value': 'purple', + 'definition_changed': True, }, { + # the name is already set and shouldn't be re-generated 'name': 'moderator_partner_id', 'string': 'Partner', 'type': 'many2one', @@ -254,7 +256,6 @@ class PropertiesCase(TransactionCase): 'discussion': self.discussion_2.id, 'author': self.user.id, 'attributes': [{ - 'name': 'state', 'type': 'selection', 'string': 'Status', 'selection': [ @@ -269,12 +270,18 @@ class PropertiesCase(TransactionCase): self.env.invalidate_all() sql_definition = self._get_sql_definition(self.discussion_1) + self.assertEqual(len(sql_definition), 2) + + # check the generated name + property_color_name = sql_definition[0]['name'] + self.assertTrue(property_color_name, msg="Property name must have been generated") + self.assertEqual(sql_definition, [ { - 'name': 'discussion_color_code', - 'type': 'char', 'string': - 'New Label', 'default': - 'blue' + 'name': property_color_name, + 'default': 'blue', + 'string': 'Discussion Color code', + 'type': 'char', }, { 'name': 'moderator_partner_id', 'type': 'many2one', @@ -284,7 +291,7 @@ class PropertiesCase(TransactionCase): ]) self.assertEqual( - self.discussion_1.attributes_definition[0]['string'], 'New Label', + self.discussion_1.attributes_definition[0]['string'], 'Discussion Color code', msg='Should have updated the definition record') self.assertEqual(len(messages), 2) @@ -293,11 +300,12 @@ class PropertiesCase(TransactionCase): self.assertEqual( sql_properties_1, {'moderator_partner_id': self.partner.id, - 'discussion_color_code': 'purple'}) + property_color_name: 'purple'}) sql_properties_2 = self._get_sql_properties(messages[1]) + status_name = self.discussion_2.attributes_definition[0]['name'] self.assertEqual( sql_properties_2, - {'state': 'draft'}) + {status_name: 'draft'}) properties_values_1 = messages[0].attributes properties_values_2 = messages[1].attributes diff --git a/odoo/fields.py b/odoo/fields.py index 5d820edcbaf..fef6a507c69 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -16,6 +16,7 @@ import enum import itertools import json import logging +import uuid import warnings from markupsafe import Markup @@ -3198,8 +3199,8 @@ class Properties(Field): # names to their corresponding value, like # # { - # '3adf37f3258cfe40f0907d9cbdd7d091': 'red', - # 'aa34746a6851ee4ea1f8d95746e45788': 1337, + # '3adf37f3258cfe40': 'red', + # 'aa34746a6851ee4e': 1337, # } # def convert_to_column(self, value, record, values=None, validate=True): @@ -3254,13 +3255,13 @@ class Properties(Field): # corresponding value, like # # [{ - # 'name': '3adf37f3258cfe40f0907d9cbdd7d091', + # 'name': '3adf37f3258cfe40', # 'string': 'Color Code', # 'type': 'char', # 'default': 'blue', # 'value': 'red', # }, { - # 'name': 'aa34746a6851ee4ea1f8d95746e45788', + # 'name': 'aa34746a6851ee4e', # 'string': 'Partner', # 'type': 'many2one', # 'comodel': 'test_new_api.partner', @@ -3283,13 +3284,13 @@ class Properties(Field): # field values have a display name. # # [{ - # 'name': '3adf37f3258cfe40f0907d9cbdd7d091', + # 'name': '3adf37f3258cfe40', # 'string': 'Color Code', # 'type': 'char', # 'default': 'blue', # 'value': 'red', # }, { - # 'name': 'aa34746a6851ee4ea1f8d95746e45788', + # 'name': 'aa34746a6851ee4e', # 'string': 'Partner', # 'type': 'many2one', # 'comodel': 'test_new_api.partner', @@ -3493,6 +3494,19 @@ class Properties(Field): for many2many_value in property_value ] + @classmethod + def _add_missing_names(cls, values_list): + """Generate new properties name if needed. + + Modify in place "values_list". + + :param values_list: List of properties definition with properties value + """ + for definition in values_list: + if definition.get('definition_changed') and not definition.get('name'): + # keep only the first 64 bits + definition['name'] = str(uuid.uuid4()).replace('-', '')[:16] + @classmethod def _parse_json_types(cls, values_list, env, check_existence=True): """Parse the value stored in the JSON. @@ -3563,13 +3577,13 @@ class Properties(Field): E.G. Input list: [{ - 'name': '3adf37f3258cfe40f0907d9cbdd7d091', + 'name': '3adf37f3258cfe40', 'string': 'Color Code', 'type': 'char', 'default': 'blue', 'value': 'red', }, { - 'name': 'aa34746a6851ee4ea1f8d95746e45788', + 'name': 'aa34746a6851ee4e', 'string': 'Partner', 'type': 'many2one', 'comodel': 'test_new_api.partner', @@ -3578,8 +3592,8 @@ class Properties(Field): Output dict: { - '3adf37f3258cfe40f0907d9cbdd7d091': 'red', - 'aa34746a6851ee4ea1f8d95746e45788': 1337, + '3adf37f3258cfe40': 'red', + 'aa34746a6851ee4e': 1337, } :param values_list: List of properties definition and value @@ -3588,6 +3602,8 @@ class Properties(Field): if not is_list_of(values_list, dict): raise ValueError(f'Wrong properties value {values_list!r}') + cls._add_missing_names(values_list) + dict_value = {} for property_definition in values_list: property_value = property_definition.get('value') @@ -3650,13 +3666,13 @@ class PropertiesDefinition(Field): might contain the name_get of those records (and will be removed). [{ - 'name': '3adf37f3258cfe40f0907d9cbdd7d091', + 'name': '3adf37f3258cfe40', 'string': 'Color Code', 'type': 'char', 'default': 'blue', 'default': 'red', }, { - 'name': 'aa34746a6851ee4ea1f8d95746e45788', + 'name': 'aa34746a6851ee4e', 'string': 'Partner', 'type': 'many2one', 'comodel': 'test_new_api.partner',