tryton 7.4.10__py3-none-any.whl → 7.6.1__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/bus.py +113 -69
- tryton/chat.py +179 -0
- tryton/client.py +7 -0
- tryton/common/__init__.py +15 -11
- tryton/common/button.py +1 -1
- tryton/common/cellrendererfloat.py +1 -1
- tryton/common/cellrenderertext.py +2 -2
- tryton/common/common.py +91 -19
- tryton/common/environment.py +2 -2
- tryton/common/number_entry.py +12 -6
- tryton/common/selection.py +1 -1
- tryton/data/locale/bg/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/bg/LC_MESSAGES/tryton.po +26 -16
- tryton/data/locale/ca/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ca/LC_MESSAGES/tryton.po +29 -18
- tryton/data/locale/cs/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/cs/LC_MESSAGES/tryton.po +28 -16
- tryton/data/locale/de/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/de/LC_MESSAGES/tryton.po +27 -18
- tryton/data/locale/es/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/es/LC_MESSAGES/tryton.po +25 -16
- tryton/data/locale/es_419/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/es_419/LC_MESSAGES/tryton.po +26 -16
- tryton/data/locale/et/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/et/LC_MESSAGES/tryton.po +28 -18
- tryton/data/locale/fa/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fa/LC_MESSAGES/tryton.po +28 -18
- tryton/data/locale/fi/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fi/LC_MESSAGES/tryton.po +25 -16
- tryton/data/locale/fr/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fr/LC_MESSAGES/tryton.po +25 -16
- tryton/data/locale/hu/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/hu/LC_MESSAGES/tryton.po +28 -18
- tryton/data/locale/id/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/id/LC_MESSAGES/tryton.po +23 -16
- tryton/data/locale/it/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/it/LC_MESSAGES/tryton.po +29 -18
- 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 +26 -18
- tryton/data/locale/lt/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/lt/LC_MESSAGES/tryton.po +30 -18
- tryton/data/locale/nl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/nl/LC_MESSAGES/tryton.po +25 -16
- tryton/data/locale/pl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/pl/LC_MESSAGES/tryton.po +31 -18
- tryton/data/locale/pt/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/pt/LC_MESSAGES/tryton.po +148 -177
- tryton/data/locale/ro/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ro/LC_MESSAGES/tryton.po +32 -19
- tryton/data/locale/ru/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ru/LC_MESSAGES/tryton.po +28 -16
- tryton/data/locale/sl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/sl/LC_MESSAGES/tryton.po +33 -18
- tryton/data/locale/tr/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/tr/LC_MESSAGES/tryton.po +25 -16
- tryton/data/locale/uk/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/uk/LC_MESSAGES/tryton.po +31 -18
- tryton/data/locale/zh_CN/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/zh_CN/LC_MESSAGES/tryton.po +27 -18
- tryton/data/pixmaps/tryton/tryton-chat.svg +1 -0
- tryton/data/pixmaps/tryton/tryton-note.svg +1 -4
- tryton/gui/window/attachment.py +2 -2
- tryton/gui/window/board.py +1 -1
- tryton/gui/window/form.py +57 -10
- tryton/gui/window/note.py +2 -2
- tryton/gui/window/tabcontent.py +8 -1
- tryton/gui/window/view_board/action.py +1 -1
- tryton/gui/window/view_form/model/field.py +34 -28
- tryton/gui/window/view_form/model/group.py +4 -4
- tryton/gui/window/view_form/model/record.py +19 -4
- tryton/gui/window/view_form/screen/screen.py +24 -4
- tryton/gui/window/view_form/view/calendar_gtk/calendar_.py +1 -1
- tryton/gui/window/view_form/view/calendar_gtk/toolbar.py +1 -1
- tryton/gui/window/view_form/view/form.py +2 -1
- tryton/gui/window/view_form/view/form_gtk/binary.py +3 -3
- tryton/gui/window/view_form/view/form_gtk/calendar_.py +4 -4
- tryton/gui/window/view_form/view/form_gtk/char.py +42 -5
- tryton/gui/window/view_form/view/form_gtk/checkbox.py +3 -3
- tryton/gui/window/view_form/view/form_gtk/dictionary.py +53 -15
- tryton/gui/window/view_form/view/form_gtk/float.py +3 -7
- tryton/gui/window/view_form/view/form_gtk/image.py +4 -4
- tryton/gui/window/view_form/view/form_gtk/integer.py +1 -1
- tryton/gui/window/view_form/view/form_gtk/many2many.py +3 -4
- tryton/gui/window/view_form/view/form_gtk/many2one.py +2 -2
- tryton/gui/window/view_form/view/form_gtk/multiselection.py +3 -3
- tryton/gui/window/view_form/view/form_gtk/one2many.py +11 -8
- tryton/gui/window/view_form/view/form_gtk/progressbar.py +2 -2
- tryton/gui/window/view_form/view/form_gtk/pyson.py +3 -3
- tryton/gui/window/view_form/view/form_gtk/reference.py +4 -4
- tryton/gui/window/view_form/view/form_gtk/richtextbox.py +5 -5
- tryton/gui/window/view_form/view/form_gtk/selection.py +3 -3
- tryton/gui/window/view_form/view/form_gtk/state_widget.py +8 -6
- tryton/gui/window/view_form/view/form_gtk/textbox.py +4 -4
- tryton/gui/window/view_form/view/form_gtk/timedelta.py +3 -3
- tryton/gui/window/view_form/view/form_gtk/url.py +2 -2
- tryton/gui/window/view_form/view/form_gtk/widget.py +1 -1
- tryton/gui/window/view_form/view/graph_gtk/bar.py +7 -7
- tryton/gui/window/view_form/view/graph_gtk/graph.py +2 -2
- tryton/gui/window/view_form/view/graph_gtk/line.py +5 -5
- tryton/gui/window/view_form/view/graph_gtk/pie.py +2 -2
- tryton/gui/window/view_form/view/list.py +107 -52
- tryton/gui/window/view_form/view/list_gtk/editabletree.py +2 -2
- tryton/gui/window/view_form/view/list_gtk/widget.py +22 -20
- tryton/gui/window/view_form/view/screen_container.py +13 -1
- tryton/gui/window/win_csv.py +2 -2
- tryton/gui/window/win_export.py +9 -7
- tryton/gui/window/win_form.py +74 -39
- tryton/gui/window/win_import.py +5 -6
- tryton/gui/window/wizard.py +11 -11
- tryton/jsonrpc.py +2 -2
- tryton/plugins/__init__.py +0 -1
- tryton/pyson.py +18 -18
- tryton/rpc.py +7 -5
- {tryton-7.4.10.dist-info → tryton-7.6.1.dist-info}/METADATA +5 -5
- {tryton-7.4.10.dist-info → tryton-7.6.1.dist-info}/RECORD +121 -120
- tryton/gui/window/view_form/view/list_gtk/generictreemodel.py +0 -426
- {tryton-7.4.10.data → tryton-7.6.1.data}/scripts/tryton +0 -0
- {tryton-7.4.10.dist-info → tryton-7.6.1.dist-info}/WHEEL +0 -0
- {tryton-7.4.10.dist-info → tryton-7.6.1.dist-info}/licenses/LICENSE +0 -0
- {tryton-7.4.10.dist-info → tryton-7.6.1.dist-info}/top_level.txt +0 -0
tryton/gui/window/form.py
CHANGED
|
@@ -12,6 +12,7 @@ from gi.repository import Gdk, GLib, Gtk
|
|
|
12
12
|
import tryton.common as common
|
|
13
13
|
from tryton import plugins
|
|
14
14
|
from tryton.action import Action
|
|
15
|
+
from tryton.chat import Chat
|
|
15
16
|
from tryton.common import RPCException, RPCExecute, sur, sur_3b, tempfile
|
|
16
17
|
from tryton.common.common import selection as selection_
|
|
17
18
|
from tryton.common.popup_menu import popup
|
|
@@ -36,7 +37,7 @@ class Form(TabContent):
|
|
|
36
37
|
"Form"
|
|
37
38
|
|
|
38
39
|
def __init__(self, model, res_id=None, name='', **attributes):
|
|
39
|
-
super(
|
|
40
|
+
super().__init__(**attributes)
|
|
40
41
|
|
|
41
42
|
self.model = model
|
|
42
43
|
self.res_id = res_id
|
|
@@ -88,6 +89,10 @@ class Form(TabContent):
|
|
|
88
89
|
def create_tabcontent(self):
|
|
89
90
|
super().create_tabcontent()
|
|
90
91
|
|
|
92
|
+
self.sidebar = Gtk.VBox()
|
|
93
|
+
self.sidebar.show()
|
|
94
|
+
self.main.pack2(self.sidebar, resize=False, shrink=True)
|
|
95
|
+
|
|
91
96
|
self.attachment_preview = Gtk.Viewport()
|
|
92
97
|
self.attachment_preview.set_shadow_type(Gtk.ShadowType.NONE)
|
|
93
98
|
self.attachment_preview.show()
|
|
@@ -97,7 +102,13 @@ class Form(TabContent):
|
|
|
97
102
|
Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
|
98
103
|
scrolledwindow.add(self.attachment_preview)
|
|
99
104
|
scrolledwindow.set_size_request(300, -1)
|
|
100
|
-
self.
|
|
105
|
+
self.sidebar.pack_start(
|
|
106
|
+
scrolledwindow, expand=True, fill=True, padding=0)
|
|
107
|
+
|
|
108
|
+
self.chat = Gtk.HBox()
|
|
109
|
+
self.chat.props.width_request = 300
|
|
110
|
+
self._chat = None
|
|
111
|
+
self.sidebar.pack_start(self.chat, expand=True, fill=True, padding=0)
|
|
101
112
|
|
|
102
113
|
def widget_get(self):
|
|
103
114
|
return self.screen.widget
|
|
@@ -186,7 +197,7 @@ class Form(TabContent):
|
|
|
186
197
|
vbox.set_margin_start(4)
|
|
187
198
|
hbox = Gtk.HBox(homogeneous=False, spacing=0)
|
|
188
199
|
hbox.set_halign(Gtk.Align.CENTER)
|
|
189
|
-
vbox.
|
|
200
|
+
vbox.pack_end(hbox, expand=False, fill=True, padding=0)
|
|
190
201
|
hbox.set_border_width(2)
|
|
191
202
|
tooltips = common.Tooltips()
|
|
192
203
|
|
|
@@ -247,6 +258,24 @@ class Form(TabContent):
|
|
|
247
258
|
self.attachment_screen.current_record = group[0]
|
|
248
259
|
self.attachment_screen.display()
|
|
249
260
|
|
|
261
|
+
def sig_chat(self, widget=None):
|
|
262
|
+
button = self.buttons['chat']
|
|
263
|
+
if widget != button:
|
|
264
|
+
if button.props.sensitive:
|
|
265
|
+
button.props.active = True
|
|
266
|
+
return
|
|
267
|
+
|
|
268
|
+
if button.get_active():
|
|
269
|
+
self._chat = Chat(self.screen.current_reference)
|
|
270
|
+
self.chat.pack_start(self._chat.widget, True, True, padding=3)
|
|
271
|
+
self.chat.show_all()
|
|
272
|
+
self._chat.refresh()
|
|
273
|
+
else:
|
|
274
|
+
self.chat.hide()
|
|
275
|
+
self.chat.remove(self._chat.widget)
|
|
276
|
+
self._chat.unregister()
|
|
277
|
+
self._chat = None
|
|
278
|
+
|
|
250
279
|
def sig_note(self, widget=None):
|
|
251
280
|
record = self.screen.current_record
|
|
252
281
|
if not record or record.id < 0:
|
|
@@ -556,9 +585,11 @@ class Form(TabContent):
|
|
|
556
585
|
self.buttons['copy_url'].props.active = True
|
|
557
586
|
|
|
558
587
|
def sig_search(self, widget):
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
588
|
+
if not self.modified_save():
|
|
589
|
+
return
|
|
590
|
+
self.screen.switch_view(searchable=True, display=False)
|
|
591
|
+
self.screen.display()
|
|
592
|
+
self.screen.screen_container.grab_focus()
|
|
562
593
|
|
|
563
594
|
def action_popup(self, widget):
|
|
564
595
|
button, = widget.get_children()
|
|
@@ -583,8 +614,11 @@ class Form(TabContent):
|
|
|
583
614
|
has_views = self.screen.number_of_views > 1
|
|
584
615
|
if selected > 1:
|
|
585
616
|
name += '#%i' % selected
|
|
586
|
-
for button_id in [
|
|
587
|
-
|
|
617
|
+
for button_id in [
|
|
618
|
+
'print', 'relate', 'email', 'open', 'attach', 'chat']:
|
|
619
|
+
button = self.buttons.get(button_id)
|
|
620
|
+
if not button:
|
|
621
|
+
continue
|
|
588
622
|
can_be_sensitive = getattr(button, '_can_be_sensitive', True)
|
|
589
623
|
if button_id in {'print', 'relate', 'email', 'open'}:
|
|
590
624
|
action_type = button_id
|
|
@@ -613,6 +647,14 @@ class Form(TabContent):
|
|
|
613
647
|
self.info_bar_clear()
|
|
614
648
|
self.set_buttons_sensitive()
|
|
615
649
|
self.refresh_attachment_preview()
|
|
650
|
+
if self._chat:
|
|
651
|
+
self._chat.unregister()
|
|
652
|
+
self.chat.remove(self._chat.widget)
|
|
653
|
+
if self.screen.current_reference:
|
|
654
|
+
self._chat = Chat(self.screen.current_reference)
|
|
655
|
+
self.chat.add(self._chat.widget)
|
|
656
|
+
self.chat.show_all()
|
|
657
|
+
self._chat.refresh()
|
|
616
658
|
|
|
617
659
|
def record_modified(self):
|
|
618
660
|
def _record_modified():
|
|
@@ -653,7 +695,12 @@ class Form(TabContent):
|
|
|
653
695
|
for dialog in reversed(self.dialogs[:]):
|
|
654
696
|
dialog.destroy()
|
|
655
697
|
modified_save = self.modified_save()
|
|
656
|
-
|
|
698
|
+
can_close = True if modified_save is None else modified_save
|
|
699
|
+
if can_close and self._chat:
|
|
700
|
+
self._chat.unregister()
|
|
701
|
+
self.chat.remove(self._chat.widget)
|
|
702
|
+
self._chat = None
|
|
703
|
+
return can_close
|
|
657
704
|
|
|
658
705
|
def _action(self, action, atype):
|
|
659
706
|
if not self.modified_save():
|
|
@@ -682,7 +729,7 @@ class Form(TabContent):
|
|
|
682
729
|
Main().sig_win_close(widget)
|
|
683
730
|
|
|
684
731
|
def create_toolbar(self, toolbars):
|
|
685
|
-
gtktoolbar = super(
|
|
732
|
+
gtktoolbar = super().create_toolbar(toolbars)
|
|
686
733
|
|
|
687
734
|
attach_btn = self.buttons['attach']
|
|
688
735
|
attach_btn.drag_dest_set(
|
tryton/gui/window/note.py
CHANGED
|
@@ -18,13 +18,13 @@ class Note(WinForm):
|
|
|
18
18
|
screen = Screen('ir.note', domain=[
|
|
19
19
|
('resource', '=', self.resource),
|
|
20
20
|
], mode=['tree', 'form'])
|
|
21
|
-
super(
|
|
21
|
+
super().__init__(screen, self.callback, view_type='tree',
|
|
22
22
|
title=title)
|
|
23
23
|
screen.search_filter()
|
|
24
24
|
|
|
25
25
|
def destroy(self):
|
|
26
26
|
self.prev_view.save_width()
|
|
27
|
-
super(
|
|
27
|
+
super().destroy()
|
|
28
28
|
|
|
29
29
|
def callback(self, result):
|
|
30
30
|
if result:
|
tryton/gui/window/tabcontent.py
CHANGED
|
@@ -37,7 +37,7 @@ class ToolbarItem(object):
|
|
|
37
37
|
class TabContent(InfoBar):
|
|
38
38
|
|
|
39
39
|
def __init__(self, **attributes):
|
|
40
|
-
super(
|
|
40
|
+
super().__init__()
|
|
41
41
|
self.attributes = attributes.copy()
|
|
42
42
|
|
|
43
43
|
@property
|
|
@@ -118,6 +118,13 @@ class TabContent(InfoBar):
|
|
|
118
118
|
tooltip=_("Add a note to the record"),
|
|
119
119
|
icon_name='tryton-note',
|
|
120
120
|
accel_path='<tryton>/Form/Notes'),
|
|
121
|
+
ToolbarItem(
|
|
122
|
+
id='chat' if self.model in common.MODELCHAT else None,
|
|
123
|
+
label=_("C_hat"),
|
|
124
|
+
tooltip=_("Chat on the record"),
|
|
125
|
+
icon_name='tryton-chat',
|
|
126
|
+
accel_path='<tryton>/Form/Chat',
|
|
127
|
+
toggle=True),
|
|
121
128
|
ToolbarItem(
|
|
122
129
|
id='action',
|
|
123
130
|
label=_("_Actions..."),
|
|
@@ -228,7 +228,7 @@ class CharField(Field):
|
|
|
228
228
|
super().set(record, value)
|
|
229
229
|
|
|
230
230
|
def get(self, record):
|
|
231
|
-
return super(
|
|
231
|
+
return super().get(record) or self._default
|
|
232
232
|
|
|
233
233
|
def set_client(self, record, value, force_change=False):
|
|
234
234
|
if isinstance(value, bytes):
|
|
@@ -301,11 +301,11 @@ class DateTimeField(Field):
|
|
|
301
301
|
value = datetime.datetime.combine(value, time)
|
|
302
302
|
if value:
|
|
303
303
|
value = common.untimezoned_date(value)
|
|
304
|
-
super(
|
|
304
|
+
super().set_client(record, value,
|
|
305
305
|
force_change=force_change)
|
|
306
306
|
|
|
307
307
|
def get_client(self, record):
|
|
308
|
-
value = super(
|
|
308
|
+
value = super().get_client(record)
|
|
309
309
|
if value:
|
|
310
310
|
return common.timezoned_date(value)
|
|
311
311
|
|
|
@@ -325,7 +325,7 @@ class DateField(Field):
|
|
|
325
325
|
if isinstance(value, datetime.datetime):
|
|
326
326
|
assert value.time() == datetime.time()
|
|
327
327
|
value = value.date()
|
|
328
|
-
super(
|
|
328
|
+
super().set_client(record, value,
|
|
329
329
|
force_change=force_change)
|
|
330
330
|
|
|
331
331
|
def date_format(self, record):
|
|
@@ -343,7 +343,7 @@ class TimeField(Field):
|
|
|
343
343
|
def set_client(self, record, value, force_change=False):
|
|
344
344
|
if isinstance(value, datetime.datetime):
|
|
345
345
|
value = value.time()
|
|
346
|
-
super(
|
|
346
|
+
super().set_client(record, value,
|
|
347
347
|
force_change=force_change)
|
|
348
348
|
|
|
349
349
|
def time_format(self, record):
|
|
@@ -363,11 +363,11 @@ class TimeDeltaField(Field):
|
|
|
363
363
|
def set_client(self, record, value, force_change=False):
|
|
364
364
|
if isinstance(value, str):
|
|
365
365
|
value = common.timedelta.parse(value, self.converter(record.group))
|
|
366
|
-
super(
|
|
366
|
+
super().set_client(
|
|
367
367
|
record, value, force_change=force_change)
|
|
368
368
|
|
|
369
369
|
def get_client(self, record):
|
|
370
|
-
value = super(
|
|
370
|
+
value = super().get_client(record)
|
|
371
371
|
return common.timedelta.format(value, self.converter(record.group))
|
|
372
372
|
|
|
373
373
|
|
|
@@ -408,10 +408,16 @@ class FloatField(Field):
|
|
|
408
408
|
self._digits[digits_id] = digits
|
|
409
409
|
else:
|
|
410
410
|
return
|
|
411
|
-
if not digits
|
|
411
|
+
if not digits:
|
|
412
412
|
return
|
|
413
413
|
shift = int(round(math.log(abs(factor), 10)))
|
|
414
|
-
|
|
414
|
+
int_size = digits[0]
|
|
415
|
+
if int_size is not None:
|
|
416
|
+
int_size += shift
|
|
417
|
+
dec_size = digits[1]
|
|
418
|
+
if dec_size is not None:
|
|
419
|
+
dec_size -= shift
|
|
420
|
+
return (int_size, dec_size)
|
|
415
421
|
|
|
416
422
|
def get_symbol(self, record, symbol):
|
|
417
423
|
if record and symbol in record.group.fields:
|
|
@@ -462,7 +468,7 @@ class FloatField(Field):
|
|
|
462
468
|
|
|
463
469
|
def set_client(self, record, value, force_change=False, factor=1):
|
|
464
470
|
value = self.apply_factor(record, self.convert(value), factor)
|
|
465
|
-
super(
|
|
471
|
+
super().set_client(record, value,
|
|
466
472
|
force_change=force_change)
|
|
467
473
|
|
|
468
474
|
def get_client(self, record, factor=1, grouping=True):
|
|
@@ -472,7 +478,7 @@ class FloatField(Field):
|
|
|
472
478
|
d = value * factor
|
|
473
479
|
if not isinstance(d, Decimal):
|
|
474
480
|
d = Decimal(repr(d))
|
|
475
|
-
if digits:
|
|
481
|
+
if digits and digits[1] is not None:
|
|
476
482
|
p = int(digits[1])
|
|
477
483
|
elif d == d.to_integral_value():
|
|
478
484
|
p = 0
|
|
@@ -495,11 +501,11 @@ class NumericField(FloatField):
|
|
|
495
501
|
_convert = Decimal
|
|
496
502
|
|
|
497
503
|
def set_client(self, record, value, force_change=False, factor=1):
|
|
498
|
-
return super(
|
|
504
|
+
return super().set_client(record, value,
|
|
499
505
|
force_change=force_change, factor=Decimal(str(factor)))
|
|
500
506
|
|
|
501
507
|
def get_client(self, record, factor=1, grouping=True):
|
|
502
|
-
return super(
|
|
508
|
+
return super().get_client(record,
|
|
503
509
|
factor=Decimal(str(factor)), grouping=grouping)
|
|
504
510
|
|
|
505
511
|
|
|
@@ -508,11 +514,11 @@ class IntegerField(FloatField):
|
|
|
508
514
|
_convert = int
|
|
509
515
|
|
|
510
516
|
def set_client(self, record, value, force_change=False, factor=1):
|
|
511
|
-
return super(
|
|
517
|
+
return super().set_client(record, value,
|
|
512
518
|
force_change=force_change, factor=int(factor))
|
|
513
519
|
|
|
514
520
|
def get_client(self, record, factor=1, grouping=True):
|
|
515
|
-
return super(
|
|
521
|
+
return super().get_client(
|
|
516
522
|
record, factor=int(factor), grouping=grouping)
|
|
517
523
|
|
|
518
524
|
|
|
@@ -522,7 +528,7 @@ class BooleanField(Field):
|
|
|
522
528
|
|
|
523
529
|
def set_client(self, record, value, force_change=False):
|
|
524
530
|
value = bool(value)
|
|
525
|
-
super(
|
|
531
|
+
super().set_client(record, value,
|
|
526
532
|
force_change=force_change)
|
|
527
533
|
|
|
528
534
|
def get(self, record):
|
|
@@ -561,7 +567,7 @@ class M2OField(Field):
|
|
|
561
567
|
if value and value < 0 and self.name != record.parent_name:
|
|
562
568
|
value, rec_name = None, ''
|
|
563
569
|
record.value.setdefault(self.name + '.', {})['rec_name'] = rec_name
|
|
564
|
-
super(
|
|
570
|
+
super().set_client(record, value,
|
|
565
571
|
force_change=force_change)
|
|
566
572
|
|
|
567
573
|
def set(self, record, value):
|
|
@@ -577,7 +583,7 @@ class M2OField(Field):
|
|
|
577
583
|
record.value[self.name] = value
|
|
578
584
|
|
|
579
585
|
def get_context(self, record, record_context=None, local=False):
|
|
580
|
-
context = super(
|
|
586
|
+
context = super().get_context(
|
|
581
587
|
record, record_context=record_context, local=local)
|
|
582
588
|
if self.attrs.get('datetime_field'):
|
|
583
589
|
context['_datetime'] = record.get_eval(
|
|
@@ -597,7 +603,7 @@ class M2OField(Field):
|
|
|
597
603
|
if record.parent_name == self.name and record.parent:
|
|
598
604
|
return record.parent.get_on_change_value(
|
|
599
605
|
skip={record.group.child_name})
|
|
600
|
-
return super(
|
|
606
|
+
return super().get_on_change_value(record)
|
|
601
607
|
|
|
602
608
|
|
|
603
609
|
class O2OField(M2OField):
|
|
@@ -613,7 +619,7 @@ class O2MField(Field):
|
|
|
613
619
|
_single_value = False
|
|
614
620
|
|
|
615
621
|
def __init__(self, attrs):
|
|
616
|
-
super(
|
|
622
|
+
super().__init__(attrs)
|
|
617
623
|
|
|
618
624
|
def _set_default_value(self, record, fields=None):
|
|
619
625
|
if record.value.get(self.name) is not None:
|
|
@@ -908,7 +914,7 @@ class O2MField(Field):
|
|
|
908
914
|
if not record2.validate(softvalidation=softvalidation,
|
|
909
915
|
pre_validate=ldomain):
|
|
910
916
|
invalid = 'children'
|
|
911
|
-
test = super(
|
|
917
|
+
test = super().validate(record, softvalidation,
|
|
912
918
|
pre_validate)
|
|
913
919
|
if test and invalid:
|
|
914
920
|
self.get_state_attrs(record)['invalid'] = invalid
|
|
@@ -917,7 +923,7 @@ class O2MField(Field):
|
|
|
917
923
|
|
|
918
924
|
def state_set(self, record, states=('readonly', 'required', 'invisible')):
|
|
919
925
|
self._set_default_value(record)
|
|
920
|
-
super(
|
|
926
|
+
super().state_set(record, states=states)
|
|
921
927
|
|
|
922
928
|
def get_removed_ids(self, record):
|
|
923
929
|
return [x.id for x in record.value[self.name].record_removed]
|
|
@@ -940,7 +946,7 @@ class ReferenceField(Field):
|
|
|
940
946
|
_default = None
|
|
941
947
|
|
|
942
948
|
def _is_empty(self, record):
|
|
943
|
-
result = super(
|
|
949
|
+
result = super()._is_empty(record)
|
|
944
950
|
if not result and (record.value[self.name] is None
|
|
945
951
|
or record.value[self.name][1] < 0):
|
|
946
952
|
result = True
|
|
@@ -982,7 +988,7 @@ class ReferenceField(Field):
|
|
|
982
988
|
rec_name = ''
|
|
983
989
|
record.value.setdefault(self.name + '.', {})['rec_name'] = rec_name
|
|
984
990
|
value = (ref_model, ref_id)
|
|
985
|
-
super(
|
|
991
|
+
super().set_client(record, value,
|
|
986
992
|
force_change=force_change)
|
|
987
993
|
|
|
988
994
|
def set(self, record, value):
|
|
@@ -1017,7 +1023,7 @@ class ReferenceField(Field):
|
|
|
1017
1023
|
record.value.setdefault(self.name + '.', {})['rec_name'] = rec_name
|
|
1018
1024
|
|
|
1019
1025
|
def get_context(self, record, record_context=None, local=False):
|
|
1020
|
-
context = super(
|
|
1026
|
+
context = super().get_context(
|
|
1021
1027
|
record, record_context, local=local)
|
|
1022
1028
|
if self.attrs.get('datetime_field'):
|
|
1023
1029
|
context['_datetime'] = record.get_eval(
|
|
@@ -1028,7 +1034,7 @@ class ReferenceField(Field):
|
|
|
1028
1034
|
if record.parent_name == self.name and record.parent:
|
|
1029
1035
|
return record.parent.model_name, record.parent.get_on_change_value(
|
|
1030
1036
|
skip={record.group.child_name})
|
|
1031
|
-
return super(
|
|
1037
|
+
return super().get_on_change_value(record)
|
|
1032
1038
|
|
|
1033
1039
|
def validation_domains(self, record, pre_validate=None):
|
|
1034
1040
|
screen_domain, attr_domain = self.domains_get(record, pre_validate)
|
|
@@ -1176,7 +1182,7 @@ class DictField(Field):
|
|
|
1176
1182
|
_single_value = False
|
|
1177
1183
|
|
|
1178
1184
|
def __init__(self, attrs):
|
|
1179
|
-
super(
|
|
1185
|
+
super().__init__(attrs)
|
|
1180
1186
|
self.keys = {}
|
|
1181
1187
|
|
|
1182
1188
|
def get(self, record):
|
|
@@ -1235,7 +1241,7 @@ class DictField(Field):
|
|
|
1235
1241
|
return new_keys
|
|
1236
1242
|
|
|
1237
1243
|
def validate(self, record, softvalidation=False, pre_validate=None):
|
|
1238
|
-
valid = super(
|
|
1244
|
+
valid = super().validate(
|
|
1239
1245
|
record, softvalidation, pre_validate)
|
|
1240
1246
|
|
|
1241
1247
|
if self.attrs.get('readonly'):
|
|
@@ -15,7 +15,7 @@ class Group(list):
|
|
|
15
15
|
def __init__(self, model_name, fields, ids=None, parent=None,
|
|
16
16
|
parent_name='', child_name='', context=None, domain=None,
|
|
17
17
|
readonly=False, parent_datetime_field=None):
|
|
18
|
-
super(
|
|
18
|
+
super().__init__()
|
|
19
19
|
if domain is None:
|
|
20
20
|
domain = []
|
|
21
21
|
self.__domain = domain
|
|
@@ -101,7 +101,7 @@ class Group(list):
|
|
|
101
101
|
record.next[id(self)] = self.__getitem__(pos)
|
|
102
102
|
else:
|
|
103
103
|
record.next[id(self)] = None
|
|
104
|
-
super(
|
|
104
|
+
super().insert(pos, record)
|
|
105
105
|
self.__id2record[record.id] = record
|
|
106
106
|
if not self.lock_signal:
|
|
107
107
|
self._group_list_changed('record-added', record, pos)
|
|
@@ -111,7 +111,7 @@ class Group(list):
|
|
|
111
111
|
if self.__len__() >= 1:
|
|
112
112
|
self.__getitem__(self.__len__() - 1).next[id(self)] = record
|
|
113
113
|
record.next[id(self)] = None
|
|
114
|
-
super(
|
|
114
|
+
super().append(record)
|
|
115
115
|
self.__id2record[record.id] = record
|
|
116
116
|
if not self.lock_signal:
|
|
117
117
|
self._group_list_changed(
|
|
@@ -126,7 +126,7 @@ class Group(list):
|
|
|
126
126
|
else:
|
|
127
127
|
self.__getitem__(idx - 1).next[id(self)] = None
|
|
128
128
|
self._group_list_changed('record-removed', record, idx)
|
|
129
|
-
super(
|
|
129
|
+
super().remove(record)
|
|
130
130
|
del self.__id2record[record.id]
|
|
131
131
|
record.destroy()
|
|
132
132
|
|
|
@@ -43,7 +43,7 @@ class Record:
|
|
|
43
43
|
id = -1
|
|
44
44
|
|
|
45
45
|
def __init__(self, model_name, obj_id, group=None):
|
|
46
|
-
super(
|
|
46
|
+
super().__init__()
|
|
47
47
|
self.model_name = model_name
|
|
48
48
|
if obj_id is None:
|
|
49
49
|
self.id = Record.id
|
|
@@ -303,6 +303,8 @@ class Record:
|
|
|
303
303
|
self.reload(fields)
|
|
304
304
|
|
|
305
305
|
def get_loaded(self, fields=None):
|
|
306
|
+
if self.id < 0:
|
|
307
|
+
return True
|
|
306
308
|
if fields is None:
|
|
307
309
|
fields = self.group.fields.keys()
|
|
308
310
|
return set(fields) <= (self._loaded | set(self.modified_fields))
|
|
@@ -437,11 +439,12 @@ class Record:
|
|
|
437
439
|
return ''
|
|
438
440
|
|
|
439
441
|
def validate(self, fields=None, softvalidation=False, pre_validate=None):
|
|
440
|
-
self._check_load(fields)
|
|
441
442
|
res = True
|
|
442
443
|
for field_name, field in list(self.group.fields.items()):
|
|
443
444
|
if fields is not None and field_name not in fields:
|
|
444
445
|
continue
|
|
446
|
+
if not self.get_loaded([field_name]):
|
|
447
|
+
continue
|
|
445
448
|
if field.attrs.get('readonly'):
|
|
446
449
|
continue
|
|
447
450
|
if field_name == self.group.exclude_field:
|
|
@@ -554,7 +557,6 @@ class Record:
|
|
|
554
557
|
else:
|
|
555
558
|
for field in fields:
|
|
556
559
|
self[field]
|
|
557
|
-
self.validate(fields or [])
|
|
558
560
|
|
|
559
561
|
def reset(self, value):
|
|
560
562
|
self.cancel()
|
|
@@ -606,6 +608,7 @@ class Record:
|
|
|
606
608
|
continue
|
|
607
609
|
values.update(self._get_on_change_args(on_change))
|
|
608
610
|
|
|
611
|
+
modified = set(fieldnames)
|
|
609
612
|
if values:
|
|
610
613
|
values['id'] = self.id
|
|
611
614
|
try:
|
|
@@ -625,9 +628,10 @@ class Record:
|
|
|
625
628
|
else:
|
|
626
629
|
for change in changes:
|
|
627
630
|
self.set_on_change(change)
|
|
631
|
+
modified.update(change)
|
|
628
632
|
|
|
629
633
|
notification_fields = common.MODELNOTIFICATION.get(self.model_name)
|
|
630
|
-
if
|
|
634
|
+
if modified & set(notification_fields):
|
|
631
635
|
values = self._get_on_change_args(notification_fields)
|
|
632
636
|
try:
|
|
633
637
|
notifications = RPCExecute(
|
|
@@ -699,6 +703,17 @@ class Record:
|
|
|
699
703
|
except RPCException:
|
|
700
704
|
return
|
|
701
705
|
self.set_on_change(changed)
|
|
706
|
+
notification_fields = common.MODELNOTIFICATION.get(self.model_name)
|
|
707
|
+
if set(field_names) & set(notification_fields):
|
|
708
|
+
values = self._get_on_change_args(notification_fields)
|
|
709
|
+
try:
|
|
710
|
+
notifications = RPCExecute(
|
|
711
|
+
'model', self.model_name, 'on_change_notify', values,
|
|
712
|
+
context=self.get_context())
|
|
713
|
+
except RPCException:
|
|
714
|
+
pass
|
|
715
|
+
else:
|
|
716
|
+
self.group.record_notify(notifications)
|
|
702
717
|
|
|
703
718
|
def autocomplete_with(self, field_name):
|
|
704
719
|
for fieldname, fieldinfo in self.group.fields.items():
|
|
@@ -16,8 +16,8 @@ from gi.repository import GLib, Gtk
|
|
|
16
16
|
|
|
17
17
|
from tryton.action import Action
|
|
18
18
|
from tryton.common import (
|
|
19
|
-
MODELACCESS, RPCContextReload, RPCException, RPCExecute,
|
|
20
|
-
sur, warning)
|
|
19
|
+
MODELACCESS, RPCContextReload, RPCException, RPCExecute, get_monitor_size,
|
|
20
|
+
node_attributes, sur, warning)
|
|
21
21
|
from tryton.common.domain_inversion import canonicalize
|
|
22
22
|
from tryton.common.domain_parser import DomainParser
|
|
23
23
|
from tryton.config import CONFIG
|
|
@@ -162,6 +162,12 @@ class Screen:
|
|
|
162
162
|
def count_limit(self):
|
|
163
163
|
return self.limit * 100 + self.offset
|
|
164
164
|
|
|
165
|
+
@property
|
|
166
|
+
def current_reference(self):
|
|
167
|
+
if self.current_record and self.current_record.id > 0:
|
|
168
|
+
return f"{self.model_name},{self.current_record.id}"
|
|
169
|
+
return None
|
|
170
|
+
|
|
165
171
|
def search_active(self, active=True):
|
|
166
172
|
if active and not self.parent:
|
|
167
173
|
self.screen_container.show_filter()
|
|
@@ -178,6 +184,7 @@ class Screen:
|
|
|
178
184
|
if view_id not in self.fields_view_tree:
|
|
179
185
|
context = self.context
|
|
180
186
|
context['view_tree_width'] = CONFIG['client.save_tree_width']
|
|
187
|
+
context['screen_size'] = get_monitor_size()
|
|
181
188
|
try:
|
|
182
189
|
self.fields_view_tree[view_id] = view_tree = RPCExecute(
|
|
183
190
|
'model', self.model_name, 'fields_view_get', False, 'tree',
|
|
@@ -429,6 +436,7 @@ class Screen:
|
|
|
429
436
|
for name, views in fields_views.items():
|
|
430
437
|
self.__group.fields[name].views.update(views)
|
|
431
438
|
self.__group.exclude_field = self.exclude_field
|
|
439
|
+
self.__group.readonly = self.__readonly
|
|
432
440
|
|
|
433
441
|
group = property(__get_group, __set_group)
|
|
434
442
|
|
|
@@ -446,12 +454,17 @@ class Screen:
|
|
|
446
454
|
for window in self.windows:
|
|
447
455
|
if hasattr(window, 'record_modified'):
|
|
448
456
|
window.record_modified()
|
|
457
|
+
if self.parent:
|
|
458
|
+
for screen in self.parent.group.screens:
|
|
459
|
+
screen.record_modified(display=display)
|
|
449
460
|
if display:
|
|
450
461
|
self.display()
|
|
451
462
|
|
|
452
463
|
def record_notify(self, notifications):
|
|
464
|
+
notified = False
|
|
453
465
|
for window in self.windows:
|
|
454
466
|
if isinstance(window, InfoBar):
|
|
467
|
+
notified = True
|
|
455
468
|
window.info_bar_refresh('notification')
|
|
456
469
|
for type_, message in notifications:
|
|
457
470
|
type_ = {
|
|
@@ -460,6 +473,8 @@ class Screen:
|
|
|
460
473
|
'error': Gtk.MessageType.ERROR,
|
|
461
474
|
}.get(type_, Gtk.MessageType.WARNING)
|
|
462
475
|
window.info_bar_add(message, type_, 'notification')
|
|
476
|
+
if not notified and self.group.parent:
|
|
477
|
+
self.group.parent.group.record_notify(notifications)
|
|
463
478
|
|
|
464
479
|
def record_message(self, position, size, max_size, record_id):
|
|
465
480
|
for window in self.windows:
|
|
@@ -550,7 +565,8 @@ class Screen:
|
|
|
550
565
|
return next_view
|
|
551
566
|
|
|
552
567
|
def switch_view(
|
|
553
|
-
self, view_type=None, view_id=None, creatable=None,
|
|
568
|
+
self, view_type=None, view_id=None, creatable=None,
|
|
569
|
+
searchable=None, display=True):
|
|
554
570
|
if view_id is not None:
|
|
555
571
|
view_id = int(view_id)
|
|
556
572
|
if self.current_view:
|
|
@@ -576,6 +592,9 @@ class Screen:
|
|
|
576
592
|
result &= self.current_view.view_id == view_id
|
|
577
593
|
if creatable is not None:
|
|
578
594
|
result &= self.current_view.creatable == creatable
|
|
595
|
+
if searchable is not None:
|
|
596
|
+
result &= (self.current_view.view_type in {
|
|
597
|
+
'tree', 'graph', 'calendar'}) == searchable
|
|
579
598
|
return result
|
|
580
599
|
for i in range(len(self.views) + len(self.view_to_load)):
|
|
581
600
|
if len(self.view_to_load):
|
|
@@ -614,6 +633,7 @@ class Screen:
|
|
|
614
633
|
else:
|
|
615
634
|
context = self.context
|
|
616
635
|
context['view_tree_width'] = CONFIG['client.save_tree_width']
|
|
636
|
+
context['screen_size'] = get_monitor_size()
|
|
617
637
|
try:
|
|
618
638
|
view = RPCExecute(
|
|
619
639
|
'model', self.model_name, 'fields_view_get', view_id,
|
|
@@ -818,7 +838,7 @@ class Screen:
|
|
|
818
838
|
# to set deleted record as current_record
|
|
819
839
|
self.current_record = None
|
|
820
840
|
# call only once
|
|
821
|
-
|
|
841
|
+
self.group.record_modified()
|
|
822
842
|
|
|
823
843
|
if delete:
|
|
824
844
|
for record in records:
|
|
@@ -17,7 +17,7 @@ class Calendar_(goocalendar.Calendar):
|
|
|
17
17
|
'Calendar'
|
|
18
18
|
|
|
19
19
|
def __init__(self, attrs, view, fields, event_store=None):
|
|
20
|
-
super(
|
|
20
|
+
super().__init__(
|
|
21
21
|
event_store, attrs.get('mode', 'month'))
|
|
22
22
|
self.props.selected_border_color = _colors[1]
|
|
23
23
|
if hasattr(self.props, 'selected_text_color'):
|
|
@@ -17,7 +17,7 @@ from tryton.gui.window.code_scanner import CodeScanner
|
|
|
17
17
|
from . import View, XMLViewParser
|
|
18
18
|
from .form_gtk.binary import Binary
|
|
19
19
|
from .form_gtk.calendar_ import Date, DateTime, Time
|
|
20
|
-
from .form_gtk.char import Char, Password
|
|
20
|
+
from .form_gtk.char import Char, Color, Password
|
|
21
21
|
from .form_gtk.checkbox import CheckBox
|
|
22
22
|
from .form_gtk.dictionary import DictWidget
|
|
23
23
|
from .form_gtk.document import Document
|
|
@@ -167,6 +167,7 @@ class FormXMLViewParser(XMLViewParser):
|
|
|
167
167
|
'boolean': CheckBox,
|
|
168
168
|
'callto': CallTo,
|
|
169
169
|
'char': Char,
|
|
170
|
+
'color': Color,
|
|
170
171
|
'date': Date,
|
|
171
172
|
'datetime': DateTime,
|
|
172
173
|
'dict': DictWidget,
|