tryton 7.0.21__py3-none-any.whl → 7.2.0__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 +125 -69
- tryton/common/completion.py +2 -2
- tryton/common/domain_inversion.py +1 -2
- tryton/common/domain_parser.py +7 -17
- tryton/common/selection.py +6 -3
- tryton/common/tempfile.py +34 -0
- tryton/config.py +3 -2
- tryton/data/locale/bg/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/bg/LC_MESSAGES/tryton.po +28 -3
- tryton/data/locale/ca/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ca/LC_MESSAGES/tryton.po +33 -5
- tryton/data/locale/cs/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/cs/LC_MESSAGES/tryton.po +28 -3
- tryton/data/locale/de/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/de/LC_MESSAGES/tryton.po +32 -4
- tryton/data/locale/es/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/es/LC_MESSAGES/tryton.po +32 -4
- tryton/data/locale/es_419/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/es_419/LC_MESSAGES/tryton.po +29 -4
- tryton/data/locale/et/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/et/LC_MESSAGES/tryton.po +32 -5
- tryton/data/locale/fa/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fa/LC_MESSAGES/tryton.po +32 -6
- tryton/data/locale/fi/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fi/LC_MESSAGES/tryton.po +28 -3
- tryton/data/locale/fr/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fr/LC_MESSAGES/tryton.po +32 -4
- tryton/data/locale/hu/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/hu/LC_MESSAGES/tryton.po +32 -5
- tryton/data/locale/id/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/id/LC_MESSAGES/tryton.po +30 -3
- tryton/data/locale/it/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/it/LC_MESSAGES/tryton.po +31 -5
- 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 +31 -5
- tryton/data/locale/lt/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/lt/LC_MESSAGES/tryton.po +32 -5
- tryton/data/locale/nl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/nl/LC_MESSAGES/tryton.po +32 -4
- tryton/data/locale/pl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/pl/LC_MESSAGES/tryton.po +32 -5
- tryton/data/locale/pt/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/pt/LC_MESSAGES/tryton.po +31 -5
- tryton/data/locale/ro/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ro/LC_MESSAGES/tryton.po +43 -16
- tryton/data/locale/ru/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ru/LC_MESSAGES/tryton.po +29 -5
- tryton/data/locale/sl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/sl/LC_MESSAGES/tryton.po +32 -4
- tryton/data/locale/tr/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/tr/LC_MESSAGES/tryton.po +28 -3
- tryton/data/locale/uk/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/uk/LC_MESSAGES/tryton.po +32 -5
- tryton/data/locale/zh_CN/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/zh_CN/LC_MESSAGES/tryton.po +32 -4
- 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 +4 -3
- tryton/gui/window/log.py +24 -2
- tryton/gui/window/view_form/model/field.py +56 -62
- tryton/gui/window/view_form/model/group.py +3 -1
- tryton/gui/window/view_form/model/record.py +55 -16
- tryton/gui/window/view_form/screen/screen.py +22 -22
- tryton/gui/window/view_form/view/calendar_gtk/calendar_.py +7 -12
- tryton/gui/window/view_form/view/form.py +4 -14
- tryton/gui/window/view_form/view/form_gtk/binary.py +3 -3
- tryton/gui/window/view_form/view/form_gtk/dictionary.py +33 -27
- tryton/gui/window/view_form/view/form_gtk/document.py +10 -9
- 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/list.py +47 -56
- tryton/gui/window/view_form/view/list_gtk/widget.py +36 -23
- tryton/gui/window/view_form/view/screen_container.py +5 -3
- tryton/gui/window/win_export.py +1 -2
- tryton/gui/window/win_form.py +6 -8
- tryton/gui/window/wizard.py +11 -10
- tryton/jsonrpc.py +41 -27
- tryton/pyson.py +54 -4
- tryton/rpc.py +18 -0
- tryton/tests/test_common_domain_parser.py +0 -8
- {tryton-7.0.21.data → tryton-7.2.0.data}/scripts/tryton +1 -2
- {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/METADATA +6 -20
- {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/RECORD +94 -92
- {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/WHEEL +1 -1
- {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/LICENSE +0 -0
- {tryton-7.0.21.dist-info → tryton-7.2.0.dist-info}/top_level.txt +0 -0
|
@@ -29,6 +29,10 @@ msgstr "določi ime in vrata strežniškega gostitelja"
|
|
|
29
29
|
msgid "disable thread usage"
|
|
30
30
|
msgstr "onemogoči uporabo niti"
|
|
31
31
|
|
|
32
|
+
#, python-format
|
|
33
|
+
msgid "Invalid response id (%s) expected %s"
|
|
34
|
+
msgstr ""
|
|
35
|
+
|
|
32
36
|
#, python-format
|
|
33
37
|
msgid "Unable to set locale %s"
|
|
34
38
|
msgstr "Ni možno nastaviti krajevnih nastavitev %s"
|
|
@@ -130,15 +134,27 @@ msgstr "Prenesi"
|
|
|
130
134
|
msgid "Could not get a session."
|
|
131
135
|
msgstr "Ni mogoče vzpostaviti seje."
|
|
132
136
|
|
|
133
|
-
|
|
137
|
+
#, fuzzy, python-format
|
|
138
|
+
msgid "Error \"%s\". Try again later."
|
|
134
139
|
msgstr "Preveč poslanih zahtev. Poskusite kasneje."
|
|
135
140
|
|
|
136
|
-
msgid "
|
|
137
|
-
msgstr "
|
|
141
|
+
msgid "Too many requests. Try again later."
|
|
142
|
+
msgstr "Preveč poslanih zahtev. Poskusite kasneje."
|
|
138
143
|
|
|
139
144
|
msgid "Not Found."
|
|
140
145
|
msgstr "Ni najdeno."
|
|
141
146
|
|
|
147
|
+
msgid "Reset forgotten password"
|
|
148
|
+
msgstr ""
|
|
149
|
+
|
|
150
|
+
msgid "Send you an email to reset your password."
|
|
151
|
+
msgstr ""
|
|
152
|
+
|
|
153
|
+
msgid ""
|
|
154
|
+
"A request to reset your password has been sent.\n"
|
|
155
|
+
"Please check your mailbox."
|
|
156
|
+
msgstr ""
|
|
157
|
+
|
|
142
158
|
msgid "..."
|
|
143
159
|
msgstr "..."
|
|
144
160
|
|
|
@@ -1085,7 +1101,19 @@ msgstr "Podoba je prevelika."
|
|
|
1085
1101
|
msgid "Copy"
|
|
1086
1102
|
msgstr "Kopiraj"
|
|
1087
1103
|
|
|
1088
|
-
|
|
1104
|
+
#, fuzzy
|
|
1105
|
+
msgid "Copy Row"
|
|
1106
|
+
msgstr "_Kopiraj URL"
|
|
1107
|
+
|
|
1108
|
+
#, fuzzy
|
|
1109
|
+
msgid "Copy Rows"
|
|
1110
|
+
msgstr "_Kopiraj URL"
|
|
1111
|
+
|
|
1112
|
+
msgid "Copy Column"
|
|
1113
|
+
msgstr ""
|
|
1114
|
+
|
|
1115
|
+
#, fuzzy
|
|
1116
|
+
msgid "Paste Rows"
|
|
1089
1117
|
msgstr "Prilepi"
|
|
1090
1118
|
|
|
1091
1119
|
msgid ".."
|
|
Binary file
|
|
@@ -29,6 +29,10 @@ msgstr "Server hostname belirle: port"
|
|
|
29
29
|
msgid "disable thread usage"
|
|
30
30
|
msgstr ""
|
|
31
31
|
|
|
32
|
+
#, python-format
|
|
33
|
+
msgid "Invalid response id (%s) expected %s"
|
|
34
|
+
msgstr ""
|
|
35
|
+
|
|
32
36
|
#, python-format
|
|
33
37
|
msgid "Unable to set locale %s"
|
|
34
38
|
msgstr "Lokal %s ayarlanamadı"
|
|
@@ -131,15 +135,27 @@ msgstr ""
|
|
|
131
135
|
msgid "Could not get a session."
|
|
132
136
|
msgstr ""
|
|
133
137
|
|
|
134
|
-
|
|
138
|
+
#, python-format
|
|
139
|
+
msgid "Error \"%s\". Try again later."
|
|
135
140
|
msgstr ""
|
|
136
141
|
|
|
137
|
-
msgid "
|
|
142
|
+
msgid "Too many requests. Try again later."
|
|
138
143
|
msgstr ""
|
|
139
144
|
|
|
140
145
|
msgid "Not Found."
|
|
141
146
|
msgstr ""
|
|
142
147
|
|
|
148
|
+
msgid "Reset forgotten password"
|
|
149
|
+
msgstr ""
|
|
150
|
+
|
|
151
|
+
msgid "Send you an email to reset your password."
|
|
152
|
+
msgstr ""
|
|
153
|
+
|
|
154
|
+
msgid ""
|
|
155
|
+
"A request to reset your password has been sent.\n"
|
|
156
|
+
"Please check your mailbox."
|
|
157
|
+
msgstr ""
|
|
158
|
+
|
|
143
159
|
msgid "..."
|
|
144
160
|
msgstr "..."
|
|
145
161
|
|
|
@@ -1084,7 +1100,16 @@ msgstr ""
|
|
|
1084
1100
|
msgid "Copy"
|
|
1085
1101
|
msgstr ""
|
|
1086
1102
|
|
|
1087
|
-
msgid "
|
|
1103
|
+
msgid "Copy Row"
|
|
1104
|
+
msgstr ""
|
|
1105
|
+
|
|
1106
|
+
msgid "Copy Rows"
|
|
1107
|
+
msgstr ""
|
|
1108
|
+
|
|
1109
|
+
msgid "Copy Column"
|
|
1110
|
+
msgstr ""
|
|
1111
|
+
|
|
1112
|
+
msgid "Paste Rows"
|
|
1088
1113
|
msgstr ""
|
|
1089
1114
|
|
|
1090
1115
|
msgid ".."
|
|
Binary file
|
|
@@ -26,6 +26,10 @@ msgstr "вкажіть ім'я сервера:порт"
|
|
|
26
26
|
msgid "disable thread usage"
|
|
27
27
|
msgstr ""
|
|
28
28
|
|
|
29
|
+
#, python-format
|
|
30
|
+
msgid "Invalid response id (%s) expected %s"
|
|
31
|
+
msgstr ""
|
|
32
|
+
|
|
29
33
|
#, python-format
|
|
30
34
|
msgid "Unable to set locale %s"
|
|
31
35
|
msgstr "Неможливо встановити локалізацію %s"
|
|
@@ -127,17 +131,28 @@ msgstr "Завантажити"
|
|
|
127
131
|
msgid "Could not get a session."
|
|
128
132
|
msgstr "Не вдалося підключитися до сервера."
|
|
129
133
|
|
|
130
|
-
|
|
134
|
+
#, fuzzy, python-format
|
|
135
|
+
msgid "Error \"%s\". Try again later."
|
|
131
136
|
msgstr "Дуже багато запитів. Спробуйте ще раз пізніше."
|
|
132
137
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
msgstr "Нічого не знайдено."
|
|
138
|
+
msgid "Too many requests. Try again later."
|
|
139
|
+
msgstr "Дуже багато запитів. Спробуйте ще раз пізніше."
|
|
136
140
|
|
|
137
141
|
#, fuzzy
|
|
138
142
|
msgid "Not Found."
|
|
139
143
|
msgstr "Нічого не знайдено."
|
|
140
144
|
|
|
145
|
+
msgid "Reset forgotten password"
|
|
146
|
+
msgstr ""
|
|
147
|
+
|
|
148
|
+
msgid "Send you an email to reset your password."
|
|
149
|
+
msgstr ""
|
|
150
|
+
|
|
151
|
+
msgid ""
|
|
152
|
+
"A request to reset your password has been sent.\n"
|
|
153
|
+
"Please check your mailbox."
|
|
154
|
+
msgstr ""
|
|
155
|
+
|
|
141
156
|
msgid "..."
|
|
142
157
|
msgstr "..."
|
|
143
158
|
|
|
@@ -1090,7 +1105,19 @@ msgstr "Розмір зображення завеликий."
|
|
|
1090
1105
|
msgid "Copy"
|
|
1091
1106
|
msgstr "Копіювати"
|
|
1092
1107
|
|
|
1093
|
-
|
|
1108
|
+
#, fuzzy
|
|
1109
|
+
msgid "Copy Row"
|
|
1110
|
+
msgstr "_Копіювати URL"
|
|
1111
|
+
|
|
1112
|
+
#, fuzzy
|
|
1113
|
+
msgid "Copy Rows"
|
|
1114
|
+
msgstr "_Копіювати URL"
|
|
1115
|
+
|
|
1116
|
+
msgid "Copy Column"
|
|
1117
|
+
msgstr ""
|
|
1118
|
+
|
|
1119
|
+
#, fuzzy
|
|
1120
|
+
msgid "Paste Rows"
|
|
1094
1121
|
msgstr "Вставити"
|
|
1095
1122
|
|
|
1096
1123
|
msgid ".."
|
|
Binary file
|
|
@@ -29,6 +29,10 @@ msgstr "设置服务器主机名:端口号"
|
|
|
29
29
|
msgid "disable thread usage"
|
|
30
30
|
msgstr "禁用线程"
|
|
31
31
|
|
|
32
|
+
#, python-format
|
|
33
|
+
msgid "Invalid response id (%s) expected %s"
|
|
34
|
+
msgstr ""
|
|
35
|
+
|
|
32
36
|
#, python-format
|
|
33
37
|
msgid "Unable to set locale %s"
|
|
34
38
|
msgstr "无法设置 locale %s"
|
|
@@ -129,15 +133,27 @@ msgstr "下载"
|
|
|
129
133
|
msgid "Could not get a session."
|
|
130
134
|
msgstr "无法获取会话。"
|
|
131
135
|
|
|
132
|
-
|
|
136
|
+
#, fuzzy, python-format
|
|
137
|
+
msgid "Error \"%s\". Try again later."
|
|
133
138
|
msgstr "请求太多,请稍后重试."
|
|
134
139
|
|
|
135
|
-
msgid "
|
|
136
|
-
msgstr "
|
|
140
|
+
msgid "Too many requests. Try again later."
|
|
141
|
+
msgstr "请求太多,请稍后重试."
|
|
137
142
|
|
|
138
143
|
msgid "Not Found."
|
|
139
144
|
msgstr "无符合条件的数据."
|
|
140
145
|
|
|
146
|
+
msgid "Reset forgotten password"
|
|
147
|
+
msgstr ""
|
|
148
|
+
|
|
149
|
+
msgid "Send you an email to reset your password."
|
|
150
|
+
msgstr ""
|
|
151
|
+
|
|
152
|
+
msgid ""
|
|
153
|
+
"A request to reset your password has been sent.\n"
|
|
154
|
+
"Please check your mailbox."
|
|
155
|
+
msgstr ""
|
|
156
|
+
|
|
141
157
|
msgid "..."
|
|
142
158
|
msgstr "..."
|
|
143
159
|
|
|
@@ -1083,7 +1099,19 @@ msgstr "图片尺寸过大."
|
|
|
1083
1099
|
msgid "Copy"
|
|
1084
1100
|
msgstr "复制"
|
|
1085
1101
|
|
|
1086
|
-
|
|
1102
|
+
#, fuzzy
|
|
1103
|
+
msgid "Copy Row"
|
|
1104
|
+
msgstr "复制(_C)URL"
|
|
1105
|
+
|
|
1106
|
+
#, fuzzy
|
|
1107
|
+
msgid "Copy Rows"
|
|
1108
|
+
msgstr "复制(_C)URL"
|
|
1109
|
+
|
|
1110
|
+
msgid "Copy Column"
|
|
1111
|
+
msgstr ""
|
|
1112
|
+
|
|
1113
|
+
#, fuzzy
|
|
1114
|
+
msgid "Paste Rows"
|
|
1087
1115
|
msgstr "粘贴"
|
|
1088
1116
|
|
|
1089
1117
|
msgid ".."
|
tryton/device_cookie.py
CHANGED
tryton/gui/main.py
CHANGED
|
@@ -401,7 +401,8 @@ class Main(Gtk.Application):
|
|
|
401
401
|
|
|
402
402
|
RPCExecute('model', 'ir.model', 'global_search', search_text,
|
|
403
403
|
CONFIG['client.limit'], self.menu_screen.model_name,
|
|
404
|
-
context=self.menu_screen.context, callback=set_result
|
|
404
|
+
context=self.menu_screen.context, callback=set_result,
|
|
405
|
+
process_exception=False)
|
|
405
406
|
return False
|
|
406
407
|
|
|
407
408
|
def changed(widget):
|
|
@@ -470,7 +471,7 @@ class Main(Gtk.Application):
|
|
|
470
471
|
favorites = RPCExecute('model',
|
|
471
472
|
self.menu_screen.model_name + '.favorite', 'get',
|
|
472
473
|
process_exception=False)
|
|
473
|
-
except
|
|
474
|
+
except RPCException:
|
|
474
475
|
return False
|
|
475
476
|
for id_, name, icon in favorites:
|
|
476
477
|
menuitem = Gtk.MenuItem(label=name)
|
tryton/gui/window/about.py
CHANGED
tryton/gui/window/dblogin.py
CHANGED
|
@@ -7,8 +7,8 @@ import logging
|
|
|
7
7
|
import os
|
|
8
8
|
import re
|
|
9
9
|
import shutil
|
|
10
|
-
import tempfile
|
|
11
10
|
import threading
|
|
11
|
+
from tempfile import NamedTemporaryFile
|
|
12
12
|
|
|
13
13
|
from gi.repository import GLib, GObject, Gtk
|
|
14
14
|
|
|
@@ -501,7 +501,7 @@ class DBLogin(object):
|
|
|
501
501
|
# reset self.profiles as parsing errors may leave wrong data in
|
|
502
502
|
# the parser
|
|
503
503
|
self.profiles = configparser.ConfigParser()
|
|
504
|
-
with
|
|
504
|
+
with NamedTemporaryFile(
|
|
505
505
|
delete=False, prefix='profiles_', suffix='.cfg',
|
|
506
506
|
dir=config_dir) as temp_file:
|
|
507
507
|
temp_name = temp_file.name
|
tryton/gui/window/email_.py
CHANGED
|
@@ -74,7 +74,7 @@ class EmailEntry(Gtk.Entry):
|
|
|
74
74
|
RPCExecute(
|
|
75
75
|
'model', 'ir.email', 'complete', text, CONFIG['client.limit'],
|
|
76
76
|
process_exception=False, callback=callback)
|
|
77
|
-
except
|
|
77
|
+
except RPCException:
|
|
78
78
|
logger.warning(
|
|
79
79
|
_("Unable to complete email entry"), exc_info=True)
|
|
80
80
|
return False
|
tryton/gui/window/form.py
CHANGED
|
@@ -5,7 +5,6 @@ import csv
|
|
|
5
5
|
import gettext
|
|
6
6
|
import locale
|
|
7
7
|
import os
|
|
8
|
-
import tempfile
|
|
9
8
|
from itertools import zip_longest
|
|
10
9
|
|
|
11
10
|
from gi.repository import Gdk, GLib, Gtk
|
|
@@ -13,7 +12,7 @@ from gi.repository import Gdk, GLib, Gtk
|
|
|
13
12
|
import tryton.common as common
|
|
14
13
|
from tryton import plugins
|
|
15
14
|
from tryton.action import Action
|
|
16
|
-
from tryton.common import RPCException, RPCExecute, sur, sur_3b
|
|
15
|
+
from tryton.common import RPCException, RPCExecute, sur, sur_3b, tempfile
|
|
17
16
|
from tryton.common.common import selection as selection_
|
|
18
17
|
from tryton.common.popup_menu import popup
|
|
19
18
|
from tryton.common.underline import set_underline
|
|
@@ -106,7 +105,9 @@ class Form(TabContent):
|
|
|
106
105
|
def compare(self, model, attributes):
|
|
107
106
|
if not attributes:
|
|
108
107
|
return False
|
|
109
|
-
return (
|
|
108
|
+
return (
|
|
109
|
+
self.screen.view_index == 0
|
|
110
|
+
and self.model == model
|
|
110
111
|
and self.res_id == attributes.get('res_id')
|
|
111
112
|
and self.attributes.get('domain') == attributes.get('domain')
|
|
112
113
|
and self.attributes.get('view_ids') == attributes.get('view_ids')
|
tryton/gui/window/log.py
CHANGED
|
@@ -24,7 +24,8 @@ class Log(WinForm):
|
|
|
24
24
|
log, = RPCExecute(
|
|
25
25
|
'model', record.model_name, 'read', [record.id],
|
|
26
26
|
['create_uid.rec_name', 'create_date',
|
|
27
|
-
'write_uid.rec_name', 'write_date'
|
|
27
|
+
'write_uid.rec_name', 'write_date',
|
|
28
|
+
'xml_id'], context=context)
|
|
28
29
|
except RPCException:
|
|
29
30
|
return
|
|
30
31
|
|
|
@@ -53,11 +54,32 @@ class Log(WinForm):
|
|
|
53
54
|
label_id.set_mnemonic_widget(entry_id)
|
|
54
55
|
grid.attach(label_id, 2, 1, 1, 1)
|
|
55
56
|
|
|
57
|
+
if log.get('xml_id'):
|
|
58
|
+
module, xml_id = log['xml_id'].split('.', 1)
|
|
59
|
+
|
|
60
|
+
entry_module = Gtk.Entry(editable=False)
|
|
61
|
+
entry_module.set_text(module)
|
|
62
|
+
grid.attach(entry_module, 1, 2, 1, 1)
|
|
63
|
+
label_module = Gtk.Label(
|
|
64
|
+
label=set_underline(_("Module:")),
|
|
65
|
+
use_underline=True, halign=Gtk.Align.END)
|
|
66
|
+
label_module.set_mnemonic_widget(entry_module)
|
|
67
|
+
grid.attach(label_module, 0, 2, 1, 1)
|
|
68
|
+
|
|
69
|
+
entry_xml_id = Gtk.Entry(editable=False)
|
|
70
|
+
entry_xml_id.set_text(xml_id)
|
|
71
|
+
grid.attach(entry_xml_id, 3, 2, 1, 1)
|
|
72
|
+
label_xml_id = Gtk.Label(
|
|
73
|
+
label=set_underline(_("XML ID:")),
|
|
74
|
+
use_underline=True, halign=Gtk.Align.END)
|
|
75
|
+
label_xml_id.set_mnemonic_widget(entry_xml_id)
|
|
76
|
+
grid.attach(label_xml_id, 2, 2, 1, 1)
|
|
77
|
+
|
|
56
78
|
for i, (user, user_label, date, date_label) in enumerate([
|
|
57
79
|
('create_uid.', _("Created by:"),
|
|
58
80
|
'create_date', _("Created at:")),
|
|
59
81
|
('write_uid.', _("Last Modified by:"),
|
|
60
|
-
'write_date', _("Last Modified at:"))],
|
|
82
|
+
'write_date', _("Last Modified at:"))], 3):
|
|
61
83
|
entry_user = Gtk.Entry(editable=False, width_chars=50)
|
|
62
84
|
user = log.get(user)
|
|
63
85
|
if user:
|
|
@@ -2,20 +2,21 @@
|
|
|
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
|
|
12
|
-
from pathlib import Path
|
|
13
13
|
|
|
14
14
|
import tryton.common as common
|
|
15
15
|
from tryton.common import (
|
|
16
16
|
EvalEnvironment, RPCException, RPCExecute, concat, domain_inversion,
|
|
17
17
|
eval_domain, extract_reference_models, filter_leaf, inverse_leaf,
|
|
18
|
-
localize_domain, merge, prepare_reference_domain, simplify,
|
|
18
|
+
localize_domain, merge, prepare_reference_domain, simplify, tempfile,
|
|
19
|
+
unique_value)
|
|
19
20
|
from tryton.common.htmltextbuffer import guess_decode
|
|
20
21
|
from tryton.config import CONFIG
|
|
21
22
|
from tryton.pyson import PYSONDecoder
|
|
@@ -619,10 +620,12 @@ class O2MField(Field):
|
|
|
619
620
|
from .group import Group
|
|
620
621
|
parent_name = self.attrs.get('relation_field', '')
|
|
621
622
|
fields = fields or {}
|
|
623
|
+
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,
|
|
628
|
+
context=context,
|
|
626
629
|
parent_datetime_field=self.attrs.get('datetime_field'))
|
|
627
630
|
if not fields and record.model_name == self.attrs['relation']:
|
|
628
631
|
group.fields = record.group.fields
|
|
@@ -698,7 +701,8 @@ class O2MField(Field):
|
|
|
698
701
|
skip={self.attrs.get('relation_field', '')}))
|
|
699
702
|
return result
|
|
700
703
|
|
|
701
|
-
def _set_value(
|
|
704
|
+
def _set_value(
|
|
705
|
+
self, record, value, default=False, modified=False, data=None):
|
|
702
706
|
self._set_default_value(record)
|
|
703
707
|
group = record.value[self.name]
|
|
704
708
|
if value is None:
|
|
@@ -708,23 +712,42 @@ class O2MField(Field):
|
|
|
708
712
|
else:
|
|
709
713
|
mode = 'list values'
|
|
710
714
|
|
|
711
|
-
if mode == 'list values':
|
|
715
|
+
if mode == 'list values' or data:
|
|
712
716
|
context = self.get_context(record)
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
717
|
+
if mode == 'list values':
|
|
718
|
+
fields = set(f for v in value for f in v)
|
|
719
|
+
else:
|
|
720
|
+
fields = functools.reduce(
|
|
721
|
+
operator.or_, (d.keys() for d in data), set())
|
|
722
|
+
field_names = {f for f in fields
|
|
723
|
+
if (f not in group.fields
|
|
724
|
+
and '.' not in f
|
|
725
|
+
and ':' not in f
|
|
726
|
+
and not f.startswith('_'))}
|
|
727
|
+
attr_fields = functools.reduce(
|
|
728
|
+
operator.or_,
|
|
729
|
+
(v['fields'] for v in self.attrs.get('views', {}).values()),
|
|
730
|
+
{})
|
|
731
|
+
fields = {n: attr_fields[n]
|
|
732
|
+
for n in field_names
|
|
733
|
+
if n in attr_fields}
|
|
734
|
+
if to_fetch := (field_names - attr_fields.keys()):
|
|
716
735
|
try:
|
|
717
|
-
fields
|
|
718
|
-
'fields_get', list(
|
|
736
|
+
fields |= RPCExecute('model', self.attrs['relation'],
|
|
737
|
+
'fields_get', list(to_fetch), context=context)
|
|
719
738
|
except RPCException:
|
|
720
739
|
return
|
|
740
|
+
|
|
741
|
+
if fields:
|
|
721
742
|
group.load_fields(fields)
|
|
722
743
|
|
|
723
744
|
if mode == 'list ids':
|
|
724
745
|
records_to_remove = [r for r in group if r.id not in value]
|
|
725
746
|
for record_to_remove in records_to_remove:
|
|
726
747
|
group.remove(record_to_remove, remove=True, modified=False)
|
|
727
|
-
group.load(
|
|
748
|
+
group.load(
|
|
749
|
+
value, modified=modified or default,
|
|
750
|
+
preloaded={v['id']: v for v in (data or [])})
|
|
728
751
|
else:
|
|
729
752
|
for vals in value:
|
|
730
753
|
if 'id' in vals:
|
|
@@ -745,7 +768,7 @@ class O2MField(Field):
|
|
|
745
768
|
# Trigger modified only once
|
|
746
769
|
group.record_modified()
|
|
747
770
|
|
|
748
|
-
def set(self, record, value, _default=False):
|
|
771
|
+
def set(self, record, value, _default=False, preloaded=None):
|
|
749
772
|
group = record.value.get(self.name)
|
|
750
773
|
fields = {}
|
|
751
774
|
if group is not None:
|
|
@@ -765,7 +788,7 @@ class O2MField(Field):
|
|
|
765
788
|
|
|
766
789
|
# Prevent to trigger group-cleared
|
|
767
790
|
group.parent = None
|
|
768
|
-
self._set_value(record, value, default=_default)
|
|
791
|
+
self._set_value(record, value, default=_default, data=preloaded)
|
|
769
792
|
group.parent = record
|
|
770
793
|
|
|
771
794
|
def set_client(self, record, value, force_change=False):
|
|
@@ -1047,56 +1070,26 @@ class ReferenceField(Field):
|
|
|
1047
1070
|
|
|
1048
1071
|
|
|
1049
1072
|
class _FileCache(object):
|
|
1050
|
-
def __init__(self,
|
|
1051
|
-
|
|
1052
|
-
self.path = Path(filename)
|
|
1053
|
-
self.suffixes = {}
|
|
1054
|
-
if data:
|
|
1055
|
-
with open(self.path, 'wb') as fp:
|
|
1056
|
-
fp.write(data)
|
|
1057
|
-
|
|
1058
|
-
@property
|
|
1059
|
-
def data(self):
|
|
1060
|
-
with open(self.path, 'rb') as fp:
|
|
1061
|
-
return fp.read()
|
|
1073
|
+
def __init__(self, path):
|
|
1074
|
+
self.path = path
|
|
1062
1075
|
|
|
1063
1076
|
def __del__(self):
|
|
1064
1077
|
try:
|
|
1065
1078
|
os.remove(self.path)
|
|
1066
1079
|
except IOError:
|
|
1067
1080
|
pass
|
|
1068
|
-
for path in self.suffixes.values():
|
|
1069
|
-
try:
|
|
1070
|
-
os.remove(path)
|
|
1071
|
-
except IOError:
|
|
1072
|
-
pass
|
|
1073
|
-
|
|
1074
|
-
def with_suffix(self, suffix):
|
|
1075
|
-
if suffix in self.suffixes:
|
|
1076
|
-
return self.suffixes[suffix]
|
|
1077
|
-
_, filename = tempfile.mkstemp(prefix='tryton_', suffix=suffix)
|
|
1078
|
-
self.suffixes[suffix] = path = Path(filename)
|
|
1079
|
-
with open(path, 'wb') as fp:
|
|
1080
|
-
fp.write(self.data)
|
|
1081
|
-
return path
|
|
1082
1081
|
|
|
1083
1082
|
|
|
1084
1083
|
class BinaryField(Field):
|
|
1085
1084
|
|
|
1086
1085
|
_default = None
|
|
1087
1086
|
|
|
1088
|
-
def _set_file_cache(self, record, data):
|
|
1089
|
-
if isinstance(data, str):
|
|
1090
|
-
data = data.encode('utf-8')
|
|
1091
|
-
file_cache = _FileCache(data)
|
|
1092
|
-
self.set(record, file_cache)
|
|
1093
|
-
return file_cache
|
|
1094
|
-
|
|
1095
1087
|
def get(self, record):
|
|
1096
1088
|
result = record.value.get(self.name, self._default)
|
|
1097
1089
|
if isinstance(result, _FileCache):
|
|
1098
1090
|
try:
|
|
1099
|
-
result
|
|
1091
|
+
with open(result.path, 'rb') as fp:
|
|
1092
|
+
result = fp.read()
|
|
1100
1093
|
except IOError:
|
|
1101
1094
|
result = self.get_data(record)
|
|
1102
1095
|
return result
|
|
@@ -1105,7 +1098,13 @@ class BinaryField(Field):
|
|
|
1105
1098
|
return self.get(record)
|
|
1106
1099
|
|
|
1107
1100
|
def set_client(self, record, value, force_change=False):
|
|
1108
|
-
|
|
1101
|
+
_, filename = tempfile.mkstemp(prefix='tryton_')
|
|
1102
|
+
data = value or b''
|
|
1103
|
+
if isinstance(data, str):
|
|
1104
|
+
data = data.encode('utf-8')
|
|
1105
|
+
with open(filename, 'wb') as fp:
|
|
1106
|
+
fp.write(data)
|
|
1107
|
+
self.set(record, _FileCache(filename))
|
|
1109
1108
|
self.sig_changed(record)
|
|
1110
1109
|
record.validate(softvalidation=True)
|
|
1111
1110
|
record.set_modified(self.name)
|
|
@@ -1129,20 +1128,15 @@ class BinaryField(Field):
|
|
|
1129
1128
|
[record.id], [self.name], context=context)
|
|
1130
1129
|
except RPCException:
|
|
1131
1130
|
return b''
|
|
1132
|
-
|
|
1131
|
+
_, filename = tempfile.mkstemp(prefix='tryton_')
|
|
1132
|
+
data = values[self.name] or b''
|
|
1133
|
+
if isinstance(data, str):
|
|
1134
|
+
data = data.encode('utf-8')
|
|
1135
|
+
with open(filename, 'wb') as fp:
|
|
1136
|
+
fp.write(data)
|
|
1137
|
+
self.set(record, _FileCache(filename))
|
|
1133
1138
|
return self.get(record)
|
|
1134
1139
|
|
|
1135
|
-
def get_filename(self, record, suffix=None):
|
|
1136
|
-
data = self.get_data(record)
|
|
1137
|
-
file_cache = record.value.get(self.name)
|
|
1138
|
-
if not isinstance(file_cache, _FileCache):
|
|
1139
|
-
file_cache = self._set_file_cache(record, data)
|
|
1140
|
-
if suffix:
|
|
1141
|
-
filename = file_cache.with_suffix(suffix)
|
|
1142
|
-
else:
|
|
1143
|
-
filename = file_cache.path
|
|
1144
|
-
return filename
|
|
1145
|
-
|
|
1146
1140
|
|
|
1147
1141
|
class DictField(Field):
|
|
1148
1142
|
|
|
@@ -1154,10 +1148,10 @@ class DictField(Field):
|
|
|
1154
1148
|
self.keys = {}
|
|
1155
1149
|
|
|
1156
1150
|
def get(self, record):
|
|
1157
|
-
return
|
|
1151
|
+
return super(DictField, self).get(record) or self._default
|
|
1158
1152
|
|
|
1159
1153
|
def get_client(self, record):
|
|
1160
|
-
return super().get_client(record)
|
|
1154
|
+
return super(DictField, self).get_client(record) or self._default
|
|
1161
1155
|
|
|
1162
1156
|
def validation_domains(self, record, pre_validate=None):
|
|
1163
1157
|
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
|