Skip to content

Commit 3a3da47

Browse files
committed
[FIX] mail: adapt the use of sms number
Ensure the correct access to sms number. task-4800230
1 parent 1437fb8 commit 3a3da47

File tree

5 files changed

+50
-54
lines changed

5 files changed

+50
-54
lines changed

addons/sms/models/mail_notification.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class MailNotification(models.Model):
1414
# Used to give links on form view without foreign key. In most cases, you'd want to use sms_id_int or sms_tracker_ids.sms_uuid.
1515
sms_id = fields.Many2one('sms.sms', string='SMS', store=False, compute='_compute_sms_id')
1616
sms_tracker_ids = fields.One2many('sms.tracker', 'mail_notification_id', string="SMS Trackers")
17-
sms_number = fields.Char('SMS Number')
17+
sms_number = fields.Char('SMS Number', groups='base.group_user')
1818
failure_type = fields.Selection(selection_add=[
1919
('sms_number_missing', 'Missing Number'),
2020
('sms_number_format', 'Wrong Number Format'),

odoo/addons/base/models/ir_model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ def _check_manual_name(self, name):
496496

497497

498498
# retrieve field types defined by the framework only (not extensions)
499-
FIELD_TYPES = [(key, key) for key in sorted(fields.Field._by_type__)]
499+
FIELD_TYPES = [(key, key) for key in sorted(fields.Field.by_type)]
500500

501501

502502
class IrModelFields(models.Model):
@@ -2413,7 +2413,7 @@ def _module_data_uninstall(self, modules_to_remove):
24132413
else:
24142414
# the field is shared across registries; don't modify it
24152415
Field = type(field)
2416-
field_ = Field(_base_fields__=(field, Field(prefetch=False)))
2416+
field_ = Field(_base_fields=[field, Field(prefetch=False)])
24172417
add_field(self.env[ir_field.model], ir_field.name, field_)
24182418
field_.setup(model)
24192419
has_shared_field = True

odoo/orm/fields.py

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from odoo.exceptions import AccessError, MissingError
1717
from odoo.tools import Query, SQL, sql
1818
from odoo.tools.constants import PREFETCH_MAX
19-
from odoo.tools.misc import SENTINEL, OrderedSet, ReadonlyDict, Sentinel, unique
19+
from odoo.tools.misc import SENTINEL, OrderedSet, Sentinel, unique
2020

2121
from .domains import NEGATIVE_CONDITION_OPERATORS, Domain
2222
from .utils import COLLECTION_TYPES, SQL_OPERATORS, SUPERUSER_ID, expand_ids
@@ -253,13 +253,13 @@ def _read_group_many2one_field(self, records, domain):
253253
# Company-dependent fields are stored as jsonb (see column_type).
254254
_column_type: tuple[str, str] | None = None
255255

256-
_args__: dict[str, typing.Any] | None = None # the parameters given to __init__()
256+
args: dict[str, typing.Any] | None = None # the parameters given to __init__()
257257
_module: str | None = None # the field's module name
258258
_modules: tuple[str, ...] = () # modules that define this field
259259
_setup_done = True # whether the field is completely set up
260260
_sequence: int # absolute ordering of the field
261-
_base_fields__: tuple[Self, ...] = () # the fields defining self, in override order
262-
_extra_keys__: tuple[str, ...] = () # unknown attributes set on the field
261+
_base_fields: tuple[Self, ...] = () # the fields defining self, in override order
262+
_extra_keys: tuple[str, ...] = () # unknown attributes set on the field
263263
_direct: bool = False # whether self may be used directly (shared)
264264
_toplevel: bool = False # whether self is on the model's registry class
265265

@@ -304,12 +304,12 @@ def _read_group_many2one_field(self, records, domain):
304304
exportable: bool = True
305305

306306
# mapping from type name to field type
307-
_by_type__: dict[str, Field] = {}
307+
by_type: dict[str, Field] = {}
308308

309309
def __init__(self, string: str | Sentinel = SENTINEL, **kwargs):
310310
kwargs['string'] = string
311311
self._sequence = next(_global_seq)
312-
self._args__ = ReadonlyDict({key: val for key, val in kwargs.items() if val is not SENTINEL})
312+
self.args = {key: val for key, val in kwargs.items() if val is not SENTINEL}
313313

314314
def __str__(self):
315315
if not self.name:
@@ -327,7 +327,7 @@ def __init_subclass__(cls):
327327
return
328328

329329
if cls.type:
330-
cls._by_type__.setdefault(cls.type, cls)
330+
cls.by_type.setdefault(cls.type, cls)
331331

332332
# compute class attributes to avoid calling dir() on fields
333333
cls.related_attrs = []
@@ -337,8 +337,6 @@ def __init_subclass__(cls):
337337
cls.related_attrs.append((attr[9:], attr))
338338
elif attr.startswith('_description_'):
339339
cls.description_attrs.append((attr[13:], attr))
340-
cls.related_attrs = tuple(cls.related_attrs)
341-
cls.description_attrs = tuple(cls.description_attrs)
342340

343341
############################################################################
344342
#
@@ -347,33 +345,33 @@ def __init_subclass__(cls):
347345
# The base field setup is done by field.__set_name__(), which determines the
348346
# field's name, model name, module and its parameters.
349347
#
350-
# The dictionary field._args__ gives the parameters passed to the field's
348+
# The dictionary field.args gives the parameters passed to the field's
351349
# constructor. Most parameters have an attribute of the same name on the
352350
# field. The parameters as attributes are assigned by the field setup.
353351
#
354352
# When several definition classes of the same model redefine a given field,
355353
# the field occurrences are "merged" into one new field instantiated at
356354
# runtime on the registry class of the model. The occurrences of the field
357-
# are given to the new field as the parameter '_base_fields__'; it is a list
355+
# are given to the new field as the parameter '_base_fields'; it is a list
358356
# of fields in override order (or reverse MRO).
359357
#
360-
# In order to save memory, a field should avoid having field._args__ and/or
358+
# In order to save memory, a field should avoid having field.args and/or
361359
# many attributes when possible. We call "direct" a field that can be set
362360
# up directly from its definition class. Direct fields are non-related
363361
# fields defined on models, and can be shared across registries. We call
364362
# "toplevel" a field that is put on the model's registry class, and is
365363
# therefore specific to the registry.
366364
#
367365
# Toplevel field are set up once, and are no longer set up from scratch
368-
# after that. Those fields can save memory by discarding field._args__ and
369-
# field._base_fields__ once set up, because those are no longer necessary.
366+
# after that. Those fields can save memory by discarding field.args and
367+
# field._base_fields once set up, because those are no longer necessary.
370368
#
371369
# Non-toplevel non-direct fields are the fields on definition classes that
372370
# may not be shared. In other words, those fields are never used directly,
373371
# and are always recreated as toplevel fields. On those fields, the base
374-
# setup is useless, because only field._args__ is used for setting up other
372+
# setup is useless, because only field.args is used for setting up other
375373
# fields. We therefore skip the base setup for those fields. The only
376-
# attributes of those fields are: '_sequence', '_args__', 'model_name', 'name'
374+
# attributes of those fields are: '_sequence', 'args', 'model_name', 'name'
377375
# and '_module', which makes their __dict__'s size minimal.
378376

379377
def __set_name__(self, owner: type[BaseModel], name: str) -> None:
@@ -393,14 +391,14 @@ def __set_name__(self, owner: type[BaseModel], name: str) -> None:
393391
self._module = owner._module
394392
owner._field_definitions.append(self)
395393

396-
if not self._args__.get('related'):
394+
if not self.args.get('related'):
397395
self._direct = True
398396
if self._direct or self._toplevel:
399397
self._setup_attrs__(owner, name)
400398
if self._toplevel:
401-
# free memory, self._args__ and self._base_fields__ are no longer useful
402-
self.__dict__.pop('_args__', None)
403-
self.__dict__.pop('_base_fields__', None)
399+
# free memory, self.args and self._base_fields are no longer useful
400+
self.__dict__.pop('args', None)
401+
self.__dict__.pop('_base_fields', None)
404402

405403
#
406404
# Setup field parameter attributes
@@ -411,20 +409,21 @@ def _get_attrs(self, model_class: type[BaseModel], name: str) -> dict[str, typin
411409
# determine all inherited field attributes
412410
attrs = {}
413411
modules: list[str] = []
414-
for field in self._args__.get('_base_fields__', ()):
412+
for field in self.args.get('_base_fields', ()):
415413
if not isinstance(self, type(field)):
416414
# 'self' overrides 'field' and their types are not compatible;
417415
# so we ignore all the parameters collected so far
418416
attrs.clear()
419417
modules.clear()
420418
continue
421-
attrs.update(field._args__)
419+
attrs.update(field.args)
422420
if field._module:
423421
modules.append(field._module)
424-
attrs.update(self._args__)
422+
attrs.update(self.args)
425423
if self._module:
426424
modules.append(self._module)
427425

426+
attrs['args'] = self.args
428427
attrs['model_name'] = model_class._name
429428
attrs['name'] = name
430429
attrs['_module'] = modules[-1] if modules else None
@@ -487,9 +486,9 @@ def _setup_attrs__(self, model_class: type[BaseModel], name: str) -> None:
487486
attrs = self._get_attrs(model_class, name)
488487

489488
# determine parameters that must be validated
490-
extra_keys = tuple(key for key in attrs if not hasattr(self, key))
489+
extra_keys = [key for key in attrs if not hasattr(self, key)]
491490
if extra_keys:
492-
attrs['_extra_keys__'] = extra_keys
491+
attrs['_extra_keys'] = extra_keys
493492

494493
self.__dict__.update(attrs)
495494

@@ -521,7 +520,7 @@ def setup(self, model: BaseModel) -> None:
521520
""" Perform the complete setup of a field. """
522521
if not self._setup_done:
523522
# validate field params
524-
for key in self._extra_keys__:
523+
for key in self._extra_keys:
525524
if not model._valid_field_parameter(self, key):
526525
_logger.warning(
527526
"Field %s: unknown parameter %r, if this is an actual"
@@ -636,7 +635,7 @@ def setup_related(self, model: BaseModel) -> None:
636635
if attr not in self.__dict__ and prop.startswith('_related_'):
637636
setattr(self, attr, getattr(field, prop))
638637

639-
for attr in field._extra_keys__:
638+
for attr in field._extra_keys:
640639
if not hasattr(self, attr) and model._valid_field_parameter(self, attr):
641640
setattr(self, attr, getattr(field, attr))
642641

odoo/orm/fields_selection.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import typing
44
from collections import defaultdict
55

6-
from odoo.tools.misc import ReadonlyDict, SENTINEL, Sentinel, merge_sequences
6+
from odoo.tools.misc import SENTINEL, Sentinel, merge_sequences
77
from odoo.tools.sql import pg_varchar
88

99
from .fields import Field, _logger, determine, resolve_mro
@@ -92,19 +92,19 @@ def _get_attrs(self, model_class, name):
9292

9393
def _setup_attrs__(self, model_class, name):
9494
super()._setup_attrs__(model_class, name)
95-
if not self._base_fields__:
95+
if not self._base_fields:
9696
return
9797

9898
# determine selection (applying 'selection_add' extensions) as a dict
9999
values = None
100100

101-
for field in self._base_fields__:
101+
for field in self._base_fields:
102102
# We cannot use field.selection or field.selection_add here
103103
# because those attributes are overridden by ``_setup_attrs__``.
104-
if 'selection' in field._args__:
104+
if 'selection' in field.args:
105105
if self.related:
106106
_logger.warning("%s: selection attribute will be ignored as the field is related", self)
107-
selection = field._args__['selection']
107+
selection = field.args['selection']
108108
if isinstance(selection, (list, tuple)):
109109
if values is not None and list(values) != [kv[0] for kv in selection]:
110110
_logger.warning("%s: selection=%r overrides existing selection; use selection_add instead", self, selection)
@@ -117,17 +117,17 @@ def _setup_attrs__(self, model_class, name):
117117
else:
118118
raise ValueError(f"{self!r}: selection={selection!r} should be a list, a callable or a method name")
119119

120-
if 'selection_add' in field._args__:
120+
if 'selection_add' in field.args:
121121
if self.related:
122122
_logger.warning("%s: selection_add attribute will be ignored as the field is related", self)
123-
selection_add = field._args__['selection_add']
123+
selection_add = field.args['selection_add']
124124
assert isinstance(selection_add, list), \
125125
"%s: selection_add=%r must be a list" % (self, selection_add)
126126
assert values is not None, \
127127
"%s: selection_add=%r on non-list selection %r" % (self, selection_add, self.selection)
128128

129129
values_add = {kv[0]: (kv[1] if len(kv) > 1 else None) for kv in selection_add}
130-
ondelete = field._args__.get('ondelete') or {}
130+
ondelete = field.args.get('ondelete') or {}
131131
new_values = [key for key in values_add if key not in values]
132132
for key in new_values:
133133
ondelete.setdefault(key, 'set null')
@@ -185,13 +185,13 @@ def _selection_modules(self, model):
185185
module = field._module
186186
if not module:
187187
continue
188-
if 'selection' in field._args__:
188+
if 'selection' in field.args:
189189
value_modules.clear()
190-
if isinstance(field._args__['selection'], list):
191-
for value, _label in field._args__['selection']:
190+
if isinstance(field.args['selection'], list):
191+
for value, _label in field.args['selection']:
192192
value_modules[value].add(module)
193-
if 'selection_add' in field._args__:
194-
for value_label in field._args__['selection_add']:
193+
if 'selection_add' in field.args:
194+
for value_label in field.args['selection_add']:
195195
if len(value_label) > 1:
196196
value_modules[value_label[0]].add(module)
197197
return value_modules

odoo/orm/model_classes.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
frozendict,
1919
sql,
2020
)
21-
from odoo.tools.misc import ReadonlyDict
2221

2322
if typing.TYPE_CHECKING:
2423
from odoo.api import Environment
@@ -183,7 +182,7 @@ def add_to_registry(registry: Registry, model_def: type[BaseModel]) -> type[Base
183182
'_inherit_module': {}, # map parent to introducing module
184183
'_inherit_children': OrderedSet(), # names of children models
185184
'_inherits_children': set(), # names of children models
186-
'_fields': ReadonlyDict({}), # populated in _setup()
185+
'_fields': {}, # populated in _setup()
187186
'_table_objects': frozendict(), # populated in _setup()
188187
})
189188

@@ -360,7 +359,7 @@ def _setup(model: BaseModel):
360359
# avoid clashes with inheritance between different models
361360
for name in model_cls._fields:
362361
discardattr(model_cls, name)
363-
model_cls._fields = ReadonlyDict({})
362+
model_cls._fields.clear()
364363

365364
# collect the definitions of each field (base definition + overrides)
366365
definitions = defaultdict(list)
@@ -376,17 +375,17 @@ def _setup(model: BaseModel):
376375
# field is translated to avoid converting its column to varchar
377376
# and losing data
378377
translate = next((
379-
field._args__['translate'] for field in reversed(fields_) if 'translate' in field._args__
378+
field.args['translate'] for field in reversed(fields_) if 'translate' in field.args
380379
), False)
381380
if not translate:
382381
# patch the field definition by adding an override
383382
_logger.debug("Patching %s.%s with translate=True", model_cls._name, name)
384383
fields_.append(type(fields_[0])(translate=True))
385384
if len(fields_) == 1 and fields_[0]._direct and fields_[0].model_name == model_cls._name:
386-
model_cls._fields = ReadonlyDict({**model_cls._fields, name: fields_[0]})
385+
model_cls._fields[name] = fields_[0]
387386
else:
388387
Field = type(fields_[-1])
389-
add_field(model, name, Field(_base_fields__=tuple(fields_)))
388+
add_field(model, name, Field(_base_fields=fields_))
390389

391390
# 2. add manual fields
392391
if model.pool._init_modules:
@@ -554,7 +553,7 @@ def _add_manual_fields(model: BaseModel):
554553
try:
555554
attrs = IrModelFields._instanciate_attrs(field_data)
556555
if attrs:
557-
field = fields.Field._by_type__[field_data['ttype']](**attrs)
556+
field = fields.Field.by_type[field_data['ttype']](**attrs)
558557
add_field(model, name, field)
559558
except Exception:
560559
_logger.exception("Failed to load field %s.%s: skipped", model._name, field_data['name'])
@@ -585,15 +584,13 @@ def add_field(model: BaseModel, name: str, field: Field):
585584
field._toplevel = True
586585
field.__set_name__(model_cls, name)
587586
# add field as an attribute and in model_cls._fields (for reflection)
588-
model_cls._fields = ReadonlyDict({**model_cls._fields, name: field})
587+
model_cls._fields[name] = field
589588

590589

591590
def pop_field(model: BaseModel, name: str) -> Field | None:
592591
""" Remove the field with the given ``name`` from the model class of ``model``. """
593592
model_cls = model.env.registry[model._name]
594-
fields_dict = dict(model_cls._fields)
595-
field = fields_dict.pop(name, None)
596-
model_cls._fields = ReadonlyDict(fields_dict)
593+
field = model_cls._fields.pop(name, None)
597594
discardattr(model_cls, name)
598595
if model_cls._rec_name == name:
599596
# fixup _rec_name and display_name's dependencies

0 commit comments

Comments
 (0)