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.
- tryton/__init__.py +1 -1
- tryton/cache.py +34 -0
- tryton/common/common.py +137 -69
- tryton/common/completion.py +2 -2
- tryton/common/datetime_.py +3 -1
- tryton/common/domain_inversion.py +10 -7
- tryton/common/domain_parser.py +17 -7
- tryton/common/selection.py +6 -3
- tryton/common/tempfile.py +34 -0
- tryton/config.py +4 -5
- tryton/data/locale/bg/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/bg/LC_MESSAGES/tryton.po +42 -4
- tryton/data/locale/ca/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ca/LC_MESSAGES/tryton.po +47 -8
- tryton/data/locale/cs/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/cs/LC_MESSAGES/tryton.po +42 -4
- tryton/data/locale/de/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/de/LC_MESSAGES/tryton.po +45 -6
- tryton/data/locale/es/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/es/LC_MESSAGES/tryton.po +46 -7
- tryton/data/locale/es_419/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/es_419/LC_MESSAGES/tryton.po +43 -5
- tryton/data/locale/et/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/et/LC_MESSAGES/tryton.po +46 -6
- tryton/data/locale/fa/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fa/LC_MESSAGES/tryton.po +46 -7
- tryton/data/locale/fi/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fi/LC_MESSAGES/tryton.po +41 -4
- tryton/data/locale/fr/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fr/LC_MESSAGES/tryton.po +46 -7
- tryton/data/locale/hu/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/hu/LC_MESSAGES/tryton.po +46 -6
- tryton/data/locale/id/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/id/LC_MESSAGES/tryton.po +43 -4
- tryton/data/locale/it/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/it/LC_MESSAGES/tryton.po +45 -6
- tryton/data/locale/ja_JP/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/lo/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/lo/LC_MESSAGES/tryton.po +45 -6
- tryton/data/locale/lt/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/lt/LC_MESSAGES/tryton.po +46 -6
- tryton/data/locale/nl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/nl/LC_MESSAGES/tryton.po +46 -7
- tryton/data/locale/pl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/pl/LC_MESSAGES/tryton.po +84 -60
- tryton/data/locale/pt/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/pt/LC_MESSAGES/tryton.po +45 -6
- tryton/data/locale/ro/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ro/LC_MESSAGES/tryton.po +57 -17
- tryton/data/locale/ru/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ru/LC_MESSAGES/tryton.po +43 -6
- tryton/data/locale/sl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/sl/LC_MESSAGES/tryton.po +46 -5
- tryton/data/locale/tr/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/tr/LC_MESSAGES/tryton.po +41 -4
- tryton/data/locale/uk/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/uk/LC_MESSAGES/tryton.po +46 -6
- tryton/data/locale/zh_CN/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/zh_CN/LC_MESSAGES/tryton.po +46 -5
- tryton/device_cookie.py +1 -1
- tryton/gui/main.py +3 -2
- tryton/gui/window/about.py +1 -1
- tryton/gui/window/dblogin.py +2 -2
- tryton/gui/window/email_.py +1 -1
- tryton/gui/window/form.py +6 -4
- tryton/gui/window/log.py +24 -2
- tryton/gui/window/view_form/model/field.py +84 -34
- tryton/gui/window/view_form/model/group.py +3 -1
- tryton/gui/window/view_form/model/record.py +64 -15
- tryton/gui/window/view_form/screen/screen.py +83 -46
- tryton/gui/window/view_form/view/calendar_gtk/calendar_.py +15 -9
- tryton/gui/window/view_form/view/form.py +6 -12
- tryton/gui/window/view_form/view/form_gtk/char.py +5 -6
- tryton/gui/window/view_form/view/form_gtk/dictionary.py +37 -24
- tryton/gui/window/view_form/view/form_gtk/document.py +9 -10
- tryton/gui/window/view_form/view/form_gtk/many2many.py +17 -7
- tryton/gui/window/view_form/view/form_gtk/many2one.py +21 -13
- tryton/gui/window/view_form/view/form_gtk/one2many.py +25 -6
- tryton/gui/window/view_form/view/form_gtk/state_widget.py +6 -2
- tryton/gui/window/view_form/view/graph_gtk/graph.py +3 -1
- tryton/gui/window/view_form/view/list.py +68 -35
- tryton/gui/window/view_form/view/list_gtk/editabletree.py +2 -1
- tryton/gui/window/view_form/view/list_gtk/widget.py +58 -23
- tryton/gui/window/view_form/view/screen_container.py +3 -5
- tryton/gui/window/win_export.py +1 -2
- tryton/gui/window/win_form.py +9 -7
- tryton/gui/window/win_import.py +9 -4
- tryton/gui/window/wizard.py +13 -10
- tryton/jsonrpc.py +46 -28
- tryton/plugins/__init__.py +5 -3
- tryton/pyson.py +55 -5
- tryton/rpc.py +18 -0
- tryton/tests/test_common_domain_parser.py +8 -0
- tryton/translate.py +5 -2
- {tryton-7.0.6.data → tryton-7.2.13.data}/scripts/tryton +8 -7
- {tryton-7.0.6.dist-info → tryton-7.2.13.dist-info}/METADATA +6 -6
- {tryton-7.0.6.dist-info → tryton-7.2.13.dist-info}/RECORD +100 -98
- {tryton-7.0.6.dist-info → tryton-7.2.13.dist-info}/WHEEL +1 -1
- {tryton-7.0.6.dist-info → tryton-7.2.13.dist-info}/LICENSE +0 -0
- {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,
|
|
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(
|
|
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
|
-
|
|
715
|
-
|
|
716
|
-
|
|
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
|
|
719
|
-
'fields_get', list(
|
|
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(
|
|
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,
|
|
1052
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
1179
|
+
return (super().get(record) or self._default).copy()
|
|
1130
1180
|
|
|
1131
1181
|
def get_client(self, record):
|
|
1132
|
-
return super(
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
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
|
-
|
|
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
|
-
|
|
693
|
-
|
|
694
|
-
|
|
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)
|