tryton 7.0.6__py3-none-any.whl → 7.2.13__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of tryton might be problematic. Click here for more details.

Files changed (100) hide show
  1. tryton/__init__.py +1 -1
  2. tryton/cache.py +34 -0
  3. tryton/common/common.py +137 -69
  4. tryton/common/completion.py +2 -2
  5. tryton/common/datetime_.py +3 -1
  6. tryton/common/domain_inversion.py +10 -7
  7. tryton/common/domain_parser.py +17 -7
  8. tryton/common/selection.py +6 -3
  9. tryton/common/tempfile.py +34 -0
  10. tryton/config.py +4 -5
  11. tryton/data/locale/bg/LC_MESSAGES/tryton.mo +0 -0
  12. tryton/data/locale/bg/LC_MESSAGES/tryton.po +42 -4
  13. tryton/data/locale/ca/LC_MESSAGES/tryton.mo +0 -0
  14. tryton/data/locale/ca/LC_MESSAGES/tryton.po +47 -8
  15. tryton/data/locale/cs/LC_MESSAGES/tryton.mo +0 -0
  16. tryton/data/locale/cs/LC_MESSAGES/tryton.po +42 -4
  17. tryton/data/locale/de/LC_MESSAGES/tryton.mo +0 -0
  18. tryton/data/locale/de/LC_MESSAGES/tryton.po +45 -6
  19. tryton/data/locale/es/LC_MESSAGES/tryton.mo +0 -0
  20. tryton/data/locale/es/LC_MESSAGES/tryton.po +46 -7
  21. tryton/data/locale/es_419/LC_MESSAGES/tryton.mo +0 -0
  22. tryton/data/locale/es_419/LC_MESSAGES/tryton.po +43 -5
  23. tryton/data/locale/et/LC_MESSAGES/tryton.mo +0 -0
  24. tryton/data/locale/et/LC_MESSAGES/tryton.po +46 -6
  25. tryton/data/locale/fa/LC_MESSAGES/tryton.mo +0 -0
  26. tryton/data/locale/fa/LC_MESSAGES/tryton.po +46 -7
  27. tryton/data/locale/fi/LC_MESSAGES/tryton.mo +0 -0
  28. tryton/data/locale/fi/LC_MESSAGES/tryton.po +41 -4
  29. tryton/data/locale/fr/LC_MESSAGES/tryton.mo +0 -0
  30. tryton/data/locale/fr/LC_MESSAGES/tryton.po +46 -7
  31. tryton/data/locale/hu/LC_MESSAGES/tryton.mo +0 -0
  32. tryton/data/locale/hu/LC_MESSAGES/tryton.po +46 -6
  33. tryton/data/locale/id/LC_MESSAGES/tryton.mo +0 -0
  34. tryton/data/locale/id/LC_MESSAGES/tryton.po +43 -4
  35. tryton/data/locale/it/LC_MESSAGES/tryton.mo +0 -0
  36. tryton/data/locale/it/LC_MESSAGES/tryton.po +45 -6
  37. tryton/data/locale/ja_JP/LC_MESSAGES/tryton.mo +0 -0
  38. tryton/data/locale/lo/LC_MESSAGES/tryton.mo +0 -0
  39. tryton/data/locale/lo/LC_MESSAGES/tryton.po +45 -6
  40. tryton/data/locale/lt/LC_MESSAGES/tryton.mo +0 -0
  41. tryton/data/locale/lt/LC_MESSAGES/tryton.po +46 -6
  42. tryton/data/locale/nl/LC_MESSAGES/tryton.mo +0 -0
  43. tryton/data/locale/nl/LC_MESSAGES/tryton.po +46 -7
  44. tryton/data/locale/pl/LC_MESSAGES/tryton.mo +0 -0
  45. tryton/data/locale/pl/LC_MESSAGES/tryton.po +84 -60
  46. tryton/data/locale/pt/LC_MESSAGES/tryton.mo +0 -0
  47. tryton/data/locale/pt/LC_MESSAGES/tryton.po +45 -6
  48. tryton/data/locale/ro/LC_MESSAGES/tryton.mo +0 -0
  49. tryton/data/locale/ro/LC_MESSAGES/tryton.po +57 -17
  50. tryton/data/locale/ru/LC_MESSAGES/tryton.mo +0 -0
  51. tryton/data/locale/ru/LC_MESSAGES/tryton.po +43 -6
  52. tryton/data/locale/sl/LC_MESSAGES/tryton.mo +0 -0
  53. tryton/data/locale/sl/LC_MESSAGES/tryton.po +46 -5
  54. tryton/data/locale/tr/LC_MESSAGES/tryton.mo +0 -0
  55. tryton/data/locale/tr/LC_MESSAGES/tryton.po +41 -4
  56. tryton/data/locale/uk/LC_MESSAGES/tryton.mo +0 -0
  57. tryton/data/locale/uk/LC_MESSAGES/tryton.po +46 -6
  58. tryton/data/locale/zh_CN/LC_MESSAGES/tryton.mo +0 -0
  59. tryton/data/locale/zh_CN/LC_MESSAGES/tryton.po +46 -5
  60. tryton/device_cookie.py +1 -1
  61. tryton/gui/main.py +3 -2
  62. tryton/gui/window/about.py +1 -1
  63. tryton/gui/window/dblogin.py +2 -2
  64. tryton/gui/window/email_.py +1 -1
  65. tryton/gui/window/form.py +6 -4
  66. tryton/gui/window/log.py +24 -2
  67. tryton/gui/window/view_form/model/field.py +84 -34
  68. tryton/gui/window/view_form/model/group.py +3 -1
  69. tryton/gui/window/view_form/model/record.py +64 -15
  70. tryton/gui/window/view_form/screen/screen.py +83 -46
  71. tryton/gui/window/view_form/view/calendar_gtk/calendar_.py +15 -9
  72. tryton/gui/window/view_form/view/form.py +6 -12
  73. tryton/gui/window/view_form/view/form_gtk/char.py +5 -6
  74. tryton/gui/window/view_form/view/form_gtk/dictionary.py +37 -24
  75. tryton/gui/window/view_form/view/form_gtk/document.py +9 -10
  76. tryton/gui/window/view_form/view/form_gtk/many2many.py +17 -7
  77. tryton/gui/window/view_form/view/form_gtk/many2one.py +21 -13
  78. tryton/gui/window/view_form/view/form_gtk/one2many.py +25 -6
  79. tryton/gui/window/view_form/view/form_gtk/state_widget.py +6 -2
  80. tryton/gui/window/view_form/view/graph_gtk/graph.py +3 -1
  81. tryton/gui/window/view_form/view/list.py +68 -35
  82. tryton/gui/window/view_form/view/list_gtk/editabletree.py +2 -1
  83. tryton/gui/window/view_form/view/list_gtk/widget.py +58 -23
  84. tryton/gui/window/view_form/view/screen_container.py +3 -5
  85. tryton/gui/window/win_export.py +1 -2
  86. tryton/gui/window/win_form.py +9 -7
  87. tryton/gui/window/win_import.py +9 -4
  88. tryton/gui/window/wizard.py +13 -10
  89. tryton/jsonrpc.py +46 -28
  90. tryton/plugins/__init__.py +5 -3
  91. tryton/pyson.py +55 -5
  92. tryton/rpc.py +18 -0
  93. tryton/tests/test_common_domain_parser.py +8 -0
  94. tryton/translate.py +5 -2
  95. {tryton-7.0.6.data → tryton-7.2.13.data}/scripts/tryton +8 -7
  96. {tryton-7.0.6.dist-info → tryton-7.2.13.dist-info}/METADATA +6 -6
  97. {tryton-7.0.6.dist-info → tryton-7.2.13.dist-info}/RECORD +100 -98
  98. {tryton-7.0.6.dist-info → tryton-7.2.13.dist-info}/WHEEL +1 -1
  99. {tryton-7.0.6.dist-info → tryton-7.2.13.dist-info}/LICENSE +0 -0
  100. {tryton-7.0.6.dist-info → tryton-7.2.13.dist-info}/top_level.txt +0 -0
@@ -2,19 +2,22 @@
2
2
  # this repository contains the full copyright notices and license terms.
3
3
  import datetime
4
4
  import decimal
5
+ import functools
5
6
  import locale
6
7
  import logging
7
8
  import math
9
+ import operator
8
10
  import os
9
- import tempfile
10
11
  from decimal import Decimal
11
12
  from itertools import chain
13
+ from pathlib import Path
12
14
 
13
15
  import tryton.common as common
14
16
  from tryton.common import (
15
17
  EvalEnvironment, RPCException, RPCExecute, concat, domain_inversion,
16
18
  eval_domain, extract_reference_models, filter_leaf, inverse_leaf,
17
- localize_domain, merge, prepare_reference_domain, simplify, unique_value)
19
+ localize_domain, merge, prepare_reference_domain, simplify, tempfile,
20
+ unique_value)
18
21
  from tryton.common.htmltextbuffer import guess_decode
19
22
  from tryton.config import CONFIG
20
23
  from tryton.pyson import PYSONDecoder
@@ -618,12 +621,10 @@ class O2MField(Field):
618
621
  from .group import Group
619
622
  parent_name = self.attrs.get('relation_field', '')
620
623
  fields = fields or {}
621
- context = record.expr_eval(self.attrs.get('context', {}))
622
624
  group = Group(self.attrs['relation'], fields,
623
625
  parent=record,
624
626
  parent_name=parent_name,
625
627
  child_name=self.name,
626
- context=context,
627
628
  parent_datetime_field=self.attrs.get('datetime_field'))
628
629
  if not fields and record.model_name == self.attrs['relation']:
629
630
  group.fields = record.group.fields
@@ -699,7 +700,8 @@ class O2MField(Field):
699
700
  skip={self.attrs.get('relation_field', '')}))
700
701
  return result
701
702
 
702
- def _set_value(self, record, value, default=False, modified=False):
703
+ def _set_value(
704
+ self, record, value, default=False, modified=False, data=None):
703
705
  self._set_default_value(record)
704
706
  group = record.value[self.name]
705
707
  if value is None:
@@ -709,23 +711,42 @@ class O2MField(Field):
709
711
  else:
710
712
  mode = 'list values'
711
713
 
712
- if mode == 'list values':
714
+ if mode == 'list values' or data:
713
715
  context = self.get_context(record)
714
- field_names = set(f for v in value for f in v
715
- if f not in group.fields and '.' not in f)
716
- if field_names:
716
+ if mode == 'list values':
717
+ fields = set(f for v in value for f in v)
718
+ else:
719
+ fields = functools.reduce(
720
+ operator.or_, (d.keys() for d in data), set())
721
+ field_names = {f for f in fields
722
+ if (f not in group.fields
723
+ and '.' not in f
724
+ and ':' not in f
725
+ and not f.startswith('_'))}
726
+ attr_fields = functools.reduce(
727
+ operator.or_,
728
+ (v['fields'] for v in self.attrs.get('views', {}).values()),
729
+ {})
730
+ fields = {n: attr_fields[n]
731
+ for n in field_names
732
+ if n in attr_fields}
733
+ if to_fetch := (field_names - attr_fields.keys()):
717
734
  try:
718
- fields = RPCExecute('model', self.attrs['relation'],
719
- 'fields_get', list(field_names), context=context)
735
+ fields |= RPCExecute('model', self.attrs['relation'],
736
+ 'fields_get', list(to_fetch), context=context)
720
737
  except RPCException:
721
738
  return
739
+
740
+ if fields:
722
741
  group.load_fields(fields)
723
742
 
724
743
  if mode == 'list ids':
725
744
  records_to_remove = [r for r in group if r.id not in value]
726
745
  for record_to_remove in records_to_remove:
727
746
  group.remove(record_to_remove, remove=True, modified=False)
728
- group.load(value, modified=modified or default)
747
+ group.load(
748
+ value, modified=modified or default,
749
+ preloaded={v['id']: v for v in (data or [])})
729
750
  else:
730
751
  for vals in value:
731
752
  if 'id' in vals:
@@ -746,7 +767,7 @@ class O2MField(Field):
746
767
  # Trigger modified only once
747
768
  group.record_modified()
748
769
 
749
- def set(self, record, value, _default=False):
770
+ def set(self, record, value, _default=False, preloaded=None):
750
771
  group = record.value.get(self.name)
751
772
  fields = {}
752
773
  if group is not None:
@@ -766,7 +787,7 @@ class O2MField(Field):
766
787
 
767
788
  # Prevent to trigger group-cleared
768
789
  group.parent = None
769
- self._set_value(record, value, default=_default)
790
+ self._set_value(record, value, default=_default, data=preloaded)
770
791
  group.parent = record
771
792
 
772
793
  def set_client(self, record, value, force_change=False):
@@ -1048,26 +1069,56 @@ class ReferenceField(Field):
1048
1069
 
1049
1070
 
1050
1071
  class _FileCache(object):
1051
- def __init__(self, path):
1052
- self.path = path
1072
+ def __init__(self, data=None):
1073
+ _, filename = tempfile.mkstemp(prefix='tryton_')
1074
+ self.path = Path(filename)
1075
+ self.suffixes = {}
1076
+ if data:
1077
+ with open(self.path, 'wb') as fp:
1078
+ fp.write(data)
1079
+
1080
+ @property
1081
+ def data(self):
1082
+ with open(self.path, 'rb') as fp:
1083
+ return fp.read()
1053
1084
 
1054
1085
  def __del__(self):
1055
1086
  try:
1056
1087
  os.remove(self.path)
1057
1088
  except IOError:
1058
1089
  pass
1090
+ for path in self.suffixes.values():
1091
+ try:
1092
+ os.remove(path)
1093
+ except IOError:
1094
+ pass
1095
+
1096
+ def with_suffix(self, suffix):
1097
+ if suffix in self.suffixes:
1098
+ return self.suffixes[suffix]
1099
+ _, filename = tempfile.mkstemp(prefix='tryton_', suffix=suffix)
1100
+ self.suffixes[suffix] = path = Path(filename)
1101
+ with open(path, 'wb') as fp:
1102
+ fp.write(self.data)
1103
+ return path
1059
1104
 
1060
1105
 
1061
1106
  class BinaryField(Field):
1062
1107
 
1063
1108
  _default = None
1064
1109
 
1110
+ def _set_file_cache(self, record, data):
1111
+ if isinstance(data, str):
1112
+ data = data.encode('utf-8')
1113
+ file_cache = _FileCache(data)
1114
+ self.set(record, file_cache)
1115
+ return file_cache
1116
+
1065
1117
  def get(self, record):
1066
1118
  result = record.value.get(self.name, self._default)
1067
1119
  if isinstance(result, _FileCache):
1068
1120
  try:
1069
- with open(result.path, 'rb') as fp:
1070
- result = fp.read()
1121
+ result = result.data
1071
1122
  except IOError:
1072
1123
  result = self.get_data(record)
1073
1124
  return result
@@ -1076,13 +1127,7 @@ class BinaryField(Field):
1076
1127
  return self.get(record)
1077
1128
 
1078
1129
  def set_client(self, record, value, force_change=False):
1079
- _, filename = tempfile.mkstemp(prefix='tryton_')
1080
- data = value or b''
1081
- if isinstance(data, str):
1082
- data = data.encode('utf-8')
1083
- with open(filename, 'wb') as fp:
1084
- fp.write(data)
1085
- self.set(record, _FileCache(filename))
1130
+ self._set_file_cache(record, value or b'')
1086
1131
  self.sig_changed(record)
1087
1132
  record.validate(softvalidation=True)
1088
1133
  record.set_modified(self.name)
@@ -1106,15 +1151,20 @@ class BinaryField(Field):
1106
1151
  [record.id], [self.name], context=context)
1107
1152
  except RPCException:
1108
1153
  return b''
1109
- _, filename = tempfile.mkstemp(prefix='tryton_')
1110
- data = values[self.name] or b''
1111
- if isinstance(data, str):
1112
- data = data.encode('utf-8')
1113
- with open(filename, 'wb') as fp:
1114
- fp.write(data)
1115
- self.set(record, _FileCache(filename))
1154
+ self._set_file_cache(record, values[self.name] or b'')
1116
1155
  return self.get(record)
1117
1156
 
1157
+ def get_filename(self, record, suffix=None):
1158
+ data = self.get_data(record)
1159
+ file_cache = record.value.get(self.name)
1160
+ if not isinstance(file_cache, _FileCache):
1161
+ file_cache = self._set_file_cache(record, data)
1162
+ if suffix:
1163
+ filename = file_cache.with_suffix(suffix)
1164
+ else:
1165
+ filename = file_cache.path
1166
+ return filename
1167
+
1118
1168
 
1119
1169
  class DictField(Field):
1120
1170
 
@@ -1126,10 +1176,10 @@ class DictField(Field):
1126
1176
  self.keys = {}
1127
1177
 
1128
1178
  def get(self, record):
1129
- return super(DictField, self).get(record) or self._default
1179
+ return (super().get(record) or self._default).copy()
1130
1180
 
1131
1181
  def get_client(self, record):
1132
- return super(DictField, self).get_client(record) or self._default
1182
+ return super().get_client(record)
1133
1183
 
1134
1184
  def validation_domains(self, record, pre_validate=None):
1135
1185
  screen_domain, attr_domain = self.domains_get(record, pre_validate)
@@ -239,7 +239,7 @@ class Group(list):
239
239
  return []
240
240
  return list({}.fromkeys(res))
241
241
 
242
- def load(self, ids, modified=False, position=-1):
242
+ def load(self, ids, modified=False, position=-1, preloaded=None):
243
243
  if not ids:
244
244
  return True
245
245
 
@@ -256,6 +256,8 @@ class Group(list):
256
256
  else:
257
257
  self.insert(position, new_record)
258
258
  position += 1
259
+ if preloaded and (already_loaded := preloaded.get(id)):
260
+ new_record.set(already_loaded, modified=False, validate=False)
259
261
  new_records.append(new_record)
260
262
 
261
263
  # Remove previously removed or deleted records
@@ -1,6 +1,8 @@
1
1
  # This file is part of Tryton. The COPYRIGHT file at the top level of
2
2
  # this repository contains the full copyright notices and license terms.
3
+ import functools
3
4
  import logging
5
+ import operator
4
6
 
5
7
  import tryton.common as common
6
8
  from tryton.common import RPCException, RPCExecute
@@ -12,6 +14,30 @@ from . import field as fields
12
14
  logger = logging.getLogger(__name__)
13
15
 
14
16
 
17
+ def get_x2m_sub_fields(f_attrs, prefix):
18
+ if f_attrs.get('visible') and f_attrs.get('views'):
19
+ sub_fields = functools.reduce(
20
+ operator.or_,
21
+ (v.get('fields', {}) for v in f_attrs['views'].values()),
22
+ {})
23
+ x2m_sub_fields = []
24
+ for s_field, f_def in sub_fields.items():
25
+ x2m_sub_fields.append(f"{prefix}.{s_field}")
26
+ if f_def['type'] in {'many2one', 'one2one', 'reference'}:
27
+ x2m_sub_fields.append(f"{prefix}.{s_field}.rec_name")
28
+ elif f_def['type'] in {'selection', 'multiselection'}:
29
+ x2m_sub_fields.append(f"{prefix}.{s_field}:string")
30
+ elif f_def['type'] in {'one2many', 'many2many'}:
31
+ x2m_sub_fields.extend(
32
+ get_x2m_sub_fields(f_def, f"{prefix}.{s_field}"))
33
+ x2m_sub_fields.extend(
34
+ f"{prefix}.{f}"
35
+ for f in ['_timestamp', '_write', '_delete'])
36
+ return x2m_sub_fields
37
+ else:
38
+ return []
39
+
40
+
15
41
  class Record:
16
42
 
17
43
  id = -1
@@ -73,12 +99,15 @@ class Record:
73
99
  fields = ((fname, field)
74
100
  for fname, field in self.group.fields.items()
75
101
  if field.attrs.get('loading', 'eager') == 'eager')
102
+ views_operator = set.issubset
76
103
  else:
77
104
  fields = self.group.fields.items()
105
+ views_operator = set.intersection
78
106
 
79
107
  fnames = [fname for fname, field in fields
80
108
  if fname not in self._loaded
81
- and (not views or (views & field.views))]
109
+ and (not views or views_operator(views, field.views))]
110
+ related_read_limit = 0
82
111
  for fname in list(fnames):
83
112
  f_attrs = self.group.fields[fname].attrs
84
113
  if f_attrs['type'] in {'many2one', 'one2one', 'reference'}:
@@ -86,6 +115,11 @@ class Record:
86
115
  elif (f_attrs['type'] in {'selection', 'multiselection'}
87
116
  and f_attrs.get('loading', 'lazy') == 'eager'):
88
117
  fnames.append('%s:string' % fname)
118
+ elif f_attrs['type'] in {'many2many', 'one2many'}:
119
+ sub_fields = get_x2m_sub_fields(f_attrs, fname)
120
+ fnames.extend(sub_fields)
121
+ if sub_fields:
122
+ related_read_limit += len(sub_fields)
89
123
  if 'rec_name' not in fnames:
90
124
  fnames.append('rec_name')
91
125
  fnames.extend(['_timestamp', '_write', '_delete'])
@@ -132,21 +166,25 @@ class Record:
132
166
  ctx.update(dict(('%s.%s' % (self.model_name, fname), 'size')
133
167
  for fname, field in self.group.fields.items()
134
168
  if field.attrs['type'] == 'binary' and fname in fnames))
135
- exception = False
169
+ if related_read_limit:
170
+ ctx['related_read_limit'] = (
171
+ CONFIG['client.limit'] // min(related_read_limit, 10))
136
172
  try:
137
173
  values = RPCExecute('model', self.model_name, 'read',
138
174
  list(id2record.keys()), fnames, context=ctx,
139
175
  process_exception=process_exception)
140
176
  except RPCException:
141
- values = [{'id': x} for x in id2record]
142
- default_values = dict((f, None) for f in fnames)
143
- for value in values:
144
- value.update(default_values)
145
- self.exception = exception = True
177
+ for record in id2record.values():
178
+ record.exception = True
179
+ if process_exception:
180
+ values = [{'id': x} for x in id2record]
181
+ default_values = {f: None for f in fnames if f != 'id'}
182
+ for value in values:
183
+ value.update(default_values)
184
+ else:
185
+ raise
146
186
  id2value = dict((value['id'], value) for value in values)
147
187
  for id, record in id2record.items():
148
- if not record.exception:
149
- record.exception = exception
150
188
  value = id2value.get(id)
151
189
  if record and not record.destroyed and value:
152
190
  for key in record.modified_fields:
@@ -375,6 +413,7 @@ class Record:
375
413
  return self.id
376
414
 
377
415
  def default_get(self, defaults=None):
416
+ vals = {}
378
417
  if len(self.group.fields):
379
418
  context = self.get_context()
380
419
  if defaults is not None:
@@ -384,7 +423,7 @@ class Record:
384
423
  vals = RPCExecute('model', self.model_name, 'default_get',
385
424
  list(self.group.fields.keys()), context=context)
386
425
  except RPCException:
387
- return
426
+ return vals
388
427
  if (self.parent
389
428
  and self.parent_name in self.group.fields):
390
429
  parent_field = self.group.fields[self.parent_name]
@@ -496,7 +535,9 @@ class Record:
496
535
  self._loaded.add(fieldname)
497
536
  fieldnames.append(fieldname)
498
537
  for fieldname, value in later.items():
499
- self.group.fields[fieldname].set(self, value)
538
+ if isinstance(
539
+ field := self.group.fields[fieldname], fields.O2MField):
540
+ field.set(self, value, preloaded=val.get(f"{fieldname}."))
500
541
  self._loaded.add(fieldname)
501
542
  fieldnames.append(fieldname)
502
543
  if validate:
@@ -576,6 +617,7 @@ class Record:
576
617
  values.update(self._get_on_change_args(on_change))
577
618
 
578
619
  if values:
620
+ values['id'] = self.id
579
621
  try:
580
622
  if len(fieldnames) == 1 or 'id' not in values:
581
623
  changes = []
@@ -637,6 +679,7 @@ class Record:
637
679
  'on_change_with_' + fieldname,
638
680
  values, context=self.get_context()))
639
681
  else:
682
+ values['id'] = self.id
640
683
  changed = RPCExecute(
641
684
  'model', self.model_name, 'on_change_with',
642
685
  values, list(fieldnames), context=self.get_context())
@@ -659,6 +702,7 @@ class Record:
659
702
  'on_change_with_' + fieldname,
660
703
  values, context=self.get_context()))
661
704
  else:
705
+ values['id'] = self.id
662
706
  changed = RPCExecute(
663
707
  'model', self.model_name, 'on_change_with',
664
708
  values, list(later), context=self.get_context())
@@ -680,7 +724,8 @@ class Record:
680
724
  try:
681
725
  res = RPCExecute(
682
726
  'model', self.model_name,
683
- 'autocomplete_' + fieldname, args, context=self.get_context())
727
+ 'autocomplete_' + fieldname, args, context=self.get_context(),
728
+ process_exception=False)
684
729
  except RPCException:
685
730
  # ensure res is a list
686
731
  res = []
@@ -689,9 +734,13 @@ class Record:
689
734
  def on_scan_code(self, code, depends):
690
735
  depends = self.expr_eval(depends)
691
736
  values = self._get_on_change_args(depends)
692
- changes = RPCExecute(
693
- 'model', self.model_name, 'on_scan_code', values, code,
694
- context=self.get_context(), process_exception=False)
737
+ values['id'] = self.id
738
+ try:
739
+ changes = RPCExecute(
740
+ 'model', self.model_name, 'on_scan_code', values, code,
741
+ context=self.get_context(), process_exception=False)
742
+ except RPCException:
743
+ changes = []
695
744
  self.set_on_change(changes)
696
745
  self.set_modified()
697
746
  return bool(changes)