tryton 7.4.8__py3-none-any.whl → 7.6.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/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/domain_parser.py +8 -6
- 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 +38 -29
- tryton/gui/window/view_form/model/group.py +4 -4
- tryton/gui/window/view_form/model/record.py +17 -4
- tryton/gui/window/view_form/screen/screen.py +24 -6
- 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 +110 -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 +73 -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.8.dist-info → tryton-7.6.0.dist-info}/METADATA +6 -6
- {tryton-7.4.8.dist-info → tryton-7.6.0.dist-info}/RECORD +122 -120
- {tryton-7.4.8.data → tryton-7.6.0.data}/scripts/tryton +0 -0
- {tryton-7.4.8.dist-info → tryton-7.6.0.dist-info}/WHEEL +0 -0
- {tryton-7.4.8.dist-info → tryton-7.6.0.dist-info}/licenses/LICENSE +0 -0
- {tryton-7.4.8.dist-info → tryton-7.6.0.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():
|
|
@@ -650,7 +692,12 @@ class Form(TabContent):
|
|
|
650
692
|
for dialog in reversed(self.dialogs[:]):
|
|
651
693
|
dialog.destroy()
|
|
652
694
|
modified_save = self.modified_save()
|
|
653
|
-
|
|
695
|
+
can_close = True if modified_save is None else modified_save
|
|
696
|
+
if can_close and self._chat:
|
|
697
|
+
self._chat.unregister()
|
|
698
|
+
self.chat.remove(self._chat.widget)
|
|
699
|
+
self._chat = None
|
|
700
|
+
return can_close
|
|
654
701
|
|
|
655
702
|
def _action(self, action, atype):
|
|
656
703
|
if not self.modified_save():
|
|
@@ -679,7 +726,7 @@ class Form(TabContent):
|
|
|
679
726
|
Main().sig_win_close(widget)
|
|
680
727
|
|
|
681
728
|
def create_toolbar(self, toolbars):
|
|
682
|
-
gtktoolbar = super(
|
|
729
|
+
gtktoolbar = super().create_toolbar(toolbars)
|
|
683
730
|
|
|
684
731
|
attach_btn = self.buttons['attach']
|
|
685
732
|
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:
|
|
@@ -882,7 +888,10 @@ class O2MField(Field):
|
|
|
882
888
|
continue
|
|
883
889
|
record2 = group.get(vals['id'])
|
|
884
890
|
if record2 is not None:
|
|
885
|
-
|
|
891
|
+
to_set = {
|
|
892
|
+
k: v for k, v in vals.items
|
|
893
|
+
if k not in vals_to_set}
|
|
894
|
+
record2.set_on_change(to_set)
|
|
886
895
|
|
|
887
896
|
def validation_domains(self, record, pre_validate=None):
|
|
888
897
|
screen_domain, attr_domain = self.domains_get(record, pre_validate)
|
|
@@ -904,7 +913,7 @@ class O2MField(Field):
|
|
|
904
913
|
if not record2.validate(softvalidation=softvalidation,
|
|
905
914
|
pre_validate=ldomain):
|
|
906
915
|
invalid = 'children'
|
|
907
|
-
test = super(
|
|
916
|
+
test = super().validate(record, softvalidation,
|
|
908
917
|
pre_validate)
|
|
909
918
|
if test and invalid:
|
|
910
919
|
self.get_state_attrs(record)['invalid'] = invalid
|
|
@@ -913,7 +922,7 @@ class O2MField(Field):
|
|
|
913
922
|
|
|
914
923
|
def state_set(self, record, states=('readonly', 'required', 'invisible')):
|
|
915
924
|
self._set_default_value(record)
|
|
916
|
-
super(
|
|
925
|
+
super().state_set(record, states=states)
|
|
917
926
|
|
|
918
927
|
def get_removed_ids(self, record):
|
|
919
928
|
return [x.id for x in record.value[self.name].record_removed]
|
|
@@ -936,7 +945,7 @@ class ReferenceField(Field):
|
|
|
936
945
|
_default = None
|
|
937
946
|
|
|
938
947
|
def _is_empty(self, record):
|
|
939
|
-
result = super(
|
|
948
|
+
result = super()._is_empty(record)
|
|
940
949
|
if not result and (record.value[self.name] is None
|
|
941
950
|
or record.value[self.name][1] < 0):
|
|
942
951
|
result = True
|
|
@@ -978,7 +987,7 @@ class ReferenceField(Field):
|
|
|
978
987
|
rec_name = ''
|
|
979
988
|
record.value.setdefault(self.name + '.', {})['rec_name'] = rec_name
|
|
980
989
|
value = (ref_model, ref_id)
|
|
981
|
-
super(
|
|
990
|
+
super().set_client(record, value,
|
|
982
991
|
force_change=force_change)
|
|
983
992
|
|
|
984
993
|
def set(self, record, value):
|
|
@@ -1013,7 +1022,7 @@ class ReferenceField(Field):
|
|
|
1013
1022
|
record.value.setdefault(self.name + '.', {})['rec_name'] = rec_name
|
|
1014
1023
|
|
|
1015
1024
|
def get_context(self, record, record_context=None, local=False):
|
|
1016
|
-
context = super(
|
|
1025
|
+
context = super().get_context(
|
|
1017
1026
|
record, record_context, local=local)
|
|
1018
1027
|
if self.attrs.get('datetime_field'):
|
|
1019
1028
|
context['_datetime'] = record.get_eval(
|
|
@@ -1024,7 +1033,7 @@ class ReferenceField(Field):
|
|
|
1024
1033
|
if record.parent_name == self.name and record.parent:
|
|
1025
1034
|
return record.parent.model_name, record.parent.get_on_change_value(
|
|
1026
1035
|
skip={record.group.child_name})
|
|
1027
|
-
return super(
|
|
1036
|
+
return super().get_on_change_value(record)
|
|
1028
1037
|
|
|
1029
1038
|
def validation_domains(self, record, pre_validate=None):
|
|
1030
1039
|
screen_domain, attr_domain = self.domains_get(record, pre_validate)
|
|
@@ -1172,7 +1181,7 @@ class DictField(Field):
|
|
|
1172
1181
|
_single_value = False
|
|
1173
1182
|
|
|
1174
1183
|
def __init__(self, attrs):
|
|
1175
|
-
super(
|
|
1184
|
+
super().__init__(attrs)
|
|
1176
1185
|
self.keys = {}
|
|
1177
1186
|
|
|
1178
1187
|
def get(self, record):
|
|
@@ -1231,7 +1240,7 @@ class DictField(Field):
|
|
|
1231
1240
|
return new_keys
|
|
1232
1241
|
|
|
1233
1242
|
def validate(self, record, softvalidation=False, pre_validate=None):
|
|
1234
|
-
valid = super(
|
|
1243
|
+
valid = super().validate(
|
|
1235
1244
|
record, softvalidation, pre_validate)
|
|
1236
1245
|
|
|
1237
1246
|
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
|
|
@@ -437,11 +437,12 @@ class Record:
|
|
|
437
437
|
return ''
|
|
438
438
|
|
|
439
439
|
def validate(self, fields=None, softvalidation=False, pre_validate=None):
|
|
440
|
-
self._check_load(fields)
|
|
441
440
|
res = True
|
|
442
441
|
for field_name, field in list(self.group.fields.items()):
|
|
443
442
|
if fields is not None and field_name not in fields:
|
|
444
443
|
continue
|
|
444
|
+
if not self.get_loaded([field_name]):
|
|
445
|
+
continue
|
|
445
446
|
if field.attrs.get('readonly'):
|
|
446
447
|
continue
|
|
447
448
|
if field_name == self.group.exclude_field:
|
|
@@ -554,7 +555,6 @@ class Record:
|
|
|
554
555
|
else:
|
|
555
556
|
for field in fields:
|
|
556
557
|
self[field]
|
|
557
|
-
self.validate(fields or [])
|
|
558
558
|
|
|
559
559
|
def reset(self, value):
|
|
560
560
|
self.cancel()
|
|
@@ -606,6 +606,7 @@ class Record:
|
|
|
606
606
|
continue
|
|
607
607
|
values.update(self._get_on_change_args(on_change))
|
|
608
608
|
|
|
609
|
+
modified = set(fieldnames)
|
|
609
610
|
if values:
|
|
610
611
|
values['id'] = self.id
|
|
611
612
|
try:
|
|
@@ -625,9 +626,10 @@ class Record:
|
|
|
625
626
|
else:
|
|
626
627
|
for change in changes:
|
|
627
628
|
self.set_on_change(change)
|
|
629
|
+
modified.update(change)
|
|
628
630
|
|
|
629
631
|
notification_fields = common.MODELNOTIFICATION.get(self.model_name)
|
|
630
|
-
if
|
|
632
|
+
if modified & set(notification_fields):
|
|
631
633
|
values = self._get_on_change_args(notification_fields)
|
|
632
634
|
try:
|
|
633
635
|
notifications = RPCExecute(
|
|
@@ -699,6 +701,17 @@ class Record:
|
|
|
699
701
|
except RPCException:
|
|
700
702
|
return
|
|
701
703
|
self.set_on_change(changed)
|
|
704
|
+
notification_fields = common.MODELNOTIFICATION.get(self.model_name)
|
|
705
|
+
if set(field_names) & set(notification_fields):
|
|
706
|
+
values = self._get_on_change_args(notification_fields)
|
|
707
|
+
try:
|
|
708
|
+
notifications = RPCExecute(
|
|
709
|
+
'model', self.model_name, 'on_change_notify', values,
|
|
710
|
+
context=self.get_context())
|
|
711
|
+
except RPCException:
|
|
712
|
+
pass
|
|
713
|
+
else:
|
|
714
|
+
self.group.record_notify(notifications)
|
|
702
715
|
|
|
703
716
|
def autocomplete_with(self, field_name):
|
|
704
717
|
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:
|
|
@@ -486,8 +501,6 @@ class Screen:
|
|
|
486
501
|
return self.__current_record
|
|
487
502
|
|
|
488
503
|
def __set_current_record(self, record):
|
|
489
|
-
if self.__current_record == record and record:
|
|
490
|
-
return
|
|
491
504
|
self.__current_record = record
|
|
492
505
|
if record:
|
|
493
506
|
try:
|
|
@@ -552,7 +565,8 @@ class Screen:
|
|
|
552
565
|
return next_view
|
|
553
566
|
|
|
554
567
|
def switch_view(
|
|
555
|
-
self, view_type=None, view_id=None, creatable=None,
|
|
568
|
+
self, view_type=None, view_id=None, creatable=None,
|
|
569
|
+
searchable=None, display=True):
|
|
556
570
|
if view_id is not None:
|
|
557
571
|
view_id = int(view_id)
|
|
558
572
|
if self.current_view:
|
|
@@ -578,6 +592,9 @@ class Screen:
|
|
|
578
592
|
result &= self.current_view.view_id == view_id
|
|
579
593
|
if creatable is not None:
|
|
580
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
|
|
581
598
|
return result
|
|
582
599
|
for i in range(len(self.views) + len(self.view_to_load)):
|
|
583
600
|
if len(self.view_to_load):
|
|
@@ -616,6 +633,7 @@ class Screen:
|
|
|
616
633
|
else:
|
|
617
634
|
context = self.context
|
|
618
635
|
context['view_tree_width'] = CONFIG['client.save_tree_width']
|
|
636
|
+
context['screen_size'] = get_monitor_size()
|
|
619
637
|
try:
|
|
620
638
|
view = RPCExecute(
|
|
621
639
|
'model', self.model_name, 'fields_view_get', view_id,
|
|
@@ -820,7 +838,7 @@ class Screen:
|
|
|
820
838
|
# to set deleted record as current_record
|
|
821
839
|
self.current_record = None
|
|
822
840
|
# call only once
|
|
823
|
-
|
|
841
|
+
self.group.record_modified()
|
|
824
842
|
|
|
825
843
|
if delete:
|
|
826
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,
|