tryton 6.6.8__py3-none-any.whl → 6.8.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/action/main.py +32 -43
- tryton/bus.py +2 -0
- tryton/client.py +3 -0
- tryton/common/button.py +3 -1
- tryton/common/common.py +55 -43
- tryton/common/datetime_.py +14 -2
- tryton/common/domain_inversion.py +10 -10
- tryton/common/domain_parser.py +5 -2
- tryton/common/popup_menu.py +7 -0
- tryton/common/selection.py +3 -1
- tryton/config.py +22 -5
- tryton/data/locale/bg/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/bg/LC_MESSAGES/tryton.po +45 -39
- tryton/data/locale/ca/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ca/LC_MESSAGES/tryton.po +41 -35
- tryton/data/locale/cs/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/cs/LC_MESSAGES/tryton.po +46 -39
- tryton/data/locale/de/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/de/LC_MESSAGES/tryton.po +41 -35
- tryton/data/locale/es/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/es/LC_MESSAGES/tryton.po +41 -35
- tryton/data/locale/es_419/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/es_419/LC_MESSAGES/tryton.po +171 -167
- tryton/data/locale/et/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/et/LC_MESSAGES/tryton.po +47 -39
- tryton/data/locale/fa/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fa/LC_MESSAGES/tryton.po +46 -38
- tryton/data/locale/fi/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fi/LC_MESSAGES/tryton.po +38 -32
- tryton/data/locale/fr/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/fr/LC_MESSAGES/tryton.po +42 -36
- tryton/data/locale/hu/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/hu/LC_MESSAGES/tryton.po +44 -34
- tryton/data/locale/id/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/id/LC_MESSAGES/tryton.po +40 -34
- tryton/data/locale/it/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/it/LC_MESSAGES/tryton.po +44 -34
- 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 +46 -38
- tryton/data/locale/lt/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/lt/LC_MESSAGES/tryton.po +47 -37
- tryton/data/locale/nl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/nl/LC_MESSAGES/tryton.po +41 -35
- tryton/data/locale/pl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/pl/LC_MESSAGES/tryton.po +45 -35
- tryton/data/locale/pt/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/pt/LC_MESSAGES/tryton.po +46 -38
- tryton/data/locale/ro/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ro/LC_MESSAGES/tryton.po +50 -48
- tryton/data/locale/ru/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/ru/LC_MESSAGES/tryton.po +45 -39
- tryton/data/locale/sl/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/sl/LC_MESSAGES/tryton.po +47 -38
- tryton/data/locale/tr/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/tr/LC_MESSAGES/tryton.po +39 -33
- tryton/data/locale/uk/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/uk/LC_MESSAGES/tryton.po +43 -35
- tryton/data/locale/zh_CN/LC_MESSAGES/tryton.mo +0 -0
- tryton/data/locale/zh_CN/LC_MESSAGES/tryton.po +45 -35
- tryton/data/pixmaps/tryton/tryton-icon.svg +1 -0
- tryton/gui/main.py +54 -61
- tryton/gui/window/dblogin.py +27 -10
- tryton/gui/window/form.py +21 -53
- tryton/gui/window/infobar.py +9 -4
- tryton/gui/window/log.py +95 -0
- tryton/gui/window/view_board/action.py +0 -4
- tryton/gui/window/view_form/model/field.py +36 -14
- tryton/gui/window/view_form/model/record.py +22 -9
- tryton/gui/window/view_form/screen/screen.py +45 -76
- tryton/gui/window/view_form/view/calendar_.py +24 -11
- tryton/gui/window/view_form/view/calendar_gtk/toolbar.py +6 -5
- tryton/gui/window/view_form/view/form.py +14 -5
- tryton/gui/window/view_form/view/form_gtk/many2many.py +10 -1
- tryton/gui/window/view_form/view/form_gtk/many2one.py +1 -0
- tryton/gui/window/view_form/view/form_gtk/one2many.py +7 -7
- tryton/gui/window/view_form/view/form_gtk/textbox.py +0 -2
- tryton/gui/window/view_form/view/form_gtk/widget.py +8 -10
- tryton/gui/window/view_form/view/list_form.py +61 -5
- tryton/gui/window/view_form/view/list_gtk/editabletree.py +13 -3
- tryton/gui/window/view_form/view/list_gtk/widget.py +97 -27
- tryton/gui/window/win_form.py +6 -5
- tryton/rpc.py +13 -15
- tryton/tests/test_common.py +46 -0
- tryton/tests/test_common_domain_parser.py +24 -24
- {tryton-6.6.8.dist-info → tryton-6.8.1.dist-info}/METADATA +6 -6
- {tryton-6.6.8.dist-info → tryton-6.8.1.dist-info}/RECORD +92 -89
- {tryton-6.6.8.data → tryton-6.8.1.data}/scripts/tryton +0 -0
- {tryton-6.6.8.dist-info → tryton-6.8.1.dist-info}/LICENSE +0 -0
- {tryton-6.6.8.dist-info → tryton-6.8.1.dist-info}/WHEEL +0 -0
- {tryton-6.6.8.dist-info → tryton-6.8.1.dist-info}/top_level.txt +0 -0
tryton/gui/window/form.py
CHANGED
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
# this repository contains the full copyright notices and license terms.
|
|
3
3
|
"Form"
|
|
4
4
|
import csv
|
|
5
|
-
import datetime
|
|
6
5
|
import gettext
|
|
7
6
|
import locale
|
|
8
7
|
import os
|
|
@@ -14,8 +13,7 @@ from gi.repository import Gdk, GLib, Gtk
|
|
|
14
13
|
import tryton.common as common
|
|
15
14
|
from tryton import plugins
|
|
16
15
|
from tryton.action import Action
|
|
17
|
-
from tryton.common import
|
|
18
|
-
RPCException, RPCExecute, message, sur, sur_3b, timezoned_date)
|
|
16
|
+
from tryton.common import RPCException, RPCExecute, sur, sur_3b
|
|
19
17
|
from tryton.common.common import selection as selection_
|
|
20
18
|
from tryton.common.popup_menu import popup
|
|
21
19
|
from tryton.common.underline import set_underline
|
|
@@ -23,6 +21,7 @@ from tryton.gui import Main
|
|
|
23
21
|
from tryton.gui.window import Window
|
|
24
22
|
from tryton.gui.window.attachment import Attachment
|
|
25
23
|
from tryton.gui.window.email_ import Email
|
|
24
|
+
from tryton.gui.window.log import Log
|
|
26
25
|
from tryton.gui.window.note import Note
|
|
27
26
|
from tryton.gui.window.revision import Revision
|
|
28
27
|
from tryton.gui.window.view_form.screen import Screen
|
|
@@ -75,7 +74,7 @@ class Form(TabContent):
|
|
|
75
74
|
self.screen.search_filter()
|
|
76
75
|
|
|
77
76
|
self.update_revision()
|
|
78
|
-
self.
|
|
77
|
+
self.set_buttons_sensitive()
|
|
79
78
|
|
|
80
79
|
def get_toolbars(self):
|
|
81
80
|
try:
|
|
@@ -295,37 +294,8 @@ class Form(TabContent):
|
|
|
295
294
|
if not current_record or current_record.id < 0:
|
|
296
295
|
self.info_bar_add(
|
|
297
296
|
_('You have to select one record.'), Gtk.MessageType.INFO)
|
|
298
|
-
return False
|
|
299
|
-
|
|
300
|
-
fields = [
|
|
301
|
-
('id', _('ID:')),
|
|
302
|
-
('create_uid.rec_name', _('Created by:')),
|
|
303
|
-
('create_date', _('Created at:')),
|
|
304
|
-
('write_uid.rec_name', _('Edited by:')),
|
|
305
|
-
('write_date', _('Edited at:')),
|
|
306
|
-
]
|
|
307
|
-
|
|
308
|
-
try:
|
|
309
|
-
data = RPCExecute('model', self.model, 'read', [current_record.id],
|
|
310
|
-
[x[0] for x in fields], context=self.screen.context)[0]
|
|
311
|
-
except RPCException:
|
|
312
297
|
return
|
|
313
|
-
|
|
314
|
-
datetime_format = date_format + ' %H:%M:%S.%f'
|
|
315
|
-
message_str = ''
|
|
316
|
-
for (key, label) in fields:
|
|
317
|
-
value = data
|
|
318
|
-
keys = key.split('.')
|
|
319
|
-
name = keys.pop(-1)
|
|
320
|
-
for key in keys:
|
|
321
|
-
value = value.get(key + '.', {})
|
|
322
|
-
value = (value or {}).get(name, '/')
|
|
323
|
-
if isinstance(value, datetime.datetime):
|
|
324
|
-
value = timezoned_date(value).strftime(datetime_format)
|
|
325
|
-
message_str += '%s %s\n' % (label, value)
|
|
326
|
-
message_str += _('Model:') + ' ' + self.model
|
|
327
|
-
message(message_str)
|
|
328
|
-
return True
|
|
298
|
+
Log(current_record)
|
|
329
299
|
|
|
330
300
|
def sig_revision(self, widget=None):
|
|
331
301
|
if not self.modified_save():
|
|
@@ -374,24 +344,29 @@ class Form(TabContent):
|
|
|
374
344
|
tooltip = self.name
|
|
375
345
|
self.title.set_text(label)
|
|
376
346
|
tooltips.set_tip(self.title, tooltip)
|
|
377
|
-
self.set_buttons_sensitive(
|
|
347
|
+
self.set_buttons_sensitive()
|
|
378
348
|
|
|
379
|
-
def set_buttons_sensitive(self
|
|
349
|
+
def set_buttons_sensitive(self):
|
|
350
|
+
revision = self.screen.context.get('_datetime')
|
|
380
351
|
if not revision:
|
|
381
352
|
access = common.MODELACCESS[self.model]
|
|
353
|
+
modified = self.screen.modified()
|
|
382
354
|
for name, sensitive in [
|
|
383
|
-
('new', access['create']),
|
|
384
|
-
('save',
|
|
355
|
+
('new', access['create'] and not modified),
|
|
356
|
+
('save',
|
|
357
|
+
(access['create'] or access['write'])
|
|
358
|
+
and modified and not self.screen.readonly),
|
|
385
359
|
('remove', access['delete']),
|
|
386
360
|
('copy', access['create']),
|
|
387
361
|
('import', access['create']),
|
|
362
|
+
('action', access['write'] and not self.screen.readonly),
|
|
388
363
|
]:
|
|
389
364
|
if name in self.buttons:
|
|
390
365
|
self.buttons[name].props.sensitive = sensitive
|
|
391
366
|
if name in self.menu_buttons:
|
|
392
367
|
self.menu_buttons[name].props.sensitive = sensitive
|
|
393
368
|
else:
|
|
394
|
-
for name in ['new', 'save', 'remove', 'copy', 'import']:
|
|
369
|
+
for name in ['new', 'save', 'remove', 'copy', 'import', 'action']:
|
|
395
370
|
if name in self.buttons:
|
|
396
371
|
self.buttons[name].props.sensitive = False
|
|
397
372
|
if name in self.menu_buttons:
|
|
@@ -467,7 +442,7 @@ class Form(TabContent):
|
|
|
467
442
|
return
|
|
468
443
|
self.screen.new()
|
|
469
444
|
self.info_bar_clear()
|
|
470
|
-
self.
|
|
445
|
+
self.set_buttons_sensitive()
|
|
471
446
|
|
|
472
447
|
def sig_copy(self, widget=None):
|
|
473
448
|
if not common.MODELACCESS[self.model]['create']:
|
|
@@ -502,14 +477,14 @@ class Form(TabContent):
|
|
|
502
477
|
return
|
|
503
478
|
self.screen.display_prev()
|
|
504
479
|
self.info_bar_clear()
|
|
505
|
-
self.
|
|
480
|
+
self.set_buttons_sensitive()
|
|
506
481
|
|
|
507
482
|
def sig_next(self, widget=None):
|
|
508
483
|
if not self.modified_save():
|
|
509
484
|
return
|
|
510
485
|
self.screen.display_next()
|
|
511
486
|
self.info_bar_clear()
|
|
512
|
-
self.
|
|
487
|
+
self.set_buttons_sensitive()
|
|
513
488
|
|
|
514
489
|
def sig_reload(self, test_modified=True):
|
|
515
490
|
if test_modified:
|
|
@@ -531,7 +506,6 @@ class Form(TabContent):
|
|
|
531
506
|
self.screen.display(set_cursor=set_cursor)
|
|
532
507
|
self.info_bar_clear()
|
|
533
508
|
self.set_buttons_sensitive()
|
|
534
|
-
self.activate_save()
|
|
535
509
|
self.screen.count_tab_domain()
|
|
536
510
|
return True
|
|
537
511
|
|
|
@@ -601,8 +575,7 @@ class Form(TabContent):
|
|
|
601
575
|
selected = len(self.screen.selected_records)
|
|
602
576
|
if selected > 1:
|
|
603
577
|
name += '#%i' % selected
|
|
604
|
-
for button_id in
|
|
605
|
-
'attach'):
|
|
578
|
+
for button_id in ['print', 'relate', 'email', 'open', 'attach']:
|
|
606
579
|
button = self.buttons[button_id]
|
|
607
580
|
can_be_sensitive = getattr(button, '_can_be_sensitive', True)
|
|
608
581
|
if button_id in {'print', 'relate', 'email', 'open'}:
|
|
@@ -612,8 +585,6 @@ class Form(TabContent):
|
|
|
612
585
|
can_be_sensitive |= any(
|
|
613
586
|
b.attrs.get('keyword', 'action') == action_type
|
|
614
587
|
for b in self.screen.get_buttons())
|
|
615
|
-
elif button_id == 'save':
|
|
616
|
-
can_be_sensitive &= not self.screen.readonly
|
|
617
588
|
set_sensitive(button_id, bool(position) and can_be_sensitive)
|
|
618
589
|
set_sensitive('switch', self.screen.number_of_views > 1)
|
|
619
590
|
set_sensitive('remove', self.screen.deletable)
|
|
@@ -629,7 +600,7 @@ class Form(TabContent):
|
|
|
629
600
|
msg = "%s/%s" % (name, common.humanize(size))
|
|
630
601
|
self.status_label.set_text(msg)
|
|
631
602
|
self.info_bar_clear()
|
|
632
|
-
self.
|
|
603
|
+
self.set_buttons_sensitive()
|
|
633
604
|
self.refresh_attachment_preview()
|
|
634
605
|
|
|
635
606
|
def record_modified(self):
|
|
@@ -637,12 +608,12 @@ class Form(TabContent):
|
|
|
637
608
|
# As it is called via idle_add, the form could have been destroyed
|
|
638
609
|
# in the meantime.
|
|
639
610
|
if self.widget_get().props.window:
|
|
640
|
-
self.
|
|
611
|
+
self.set_buttons_sensitive()
|
|
641
612
|
GLib.idle_add(_record_modified)
|
|
642
613
|
self.info_bar_refresh()
|
|
643
614
|
|
|
644
615
|
def record_saved(self):
|
|
645
|
-
self.
|
|
616
|
+
self.set_buttons_sensitive()
|
|
646
617
|
self.refresh_resources()
|
|
647
618
|
|
|
648
619
|
def modified_save(self):
|
|
@@ -693,9 +664,6 @@ class Form(TabContent):
|
|
|
693
664
|
}
|
|
694
665
|
Action.execute(action, data, context=self.screen.local_context)
|
|
695
666
|
|
|
696
|
-
def activate_save(self):
|
|
697
|
-
self.buttons['save'].props.sensitive = self.screen.modified()
|
|
698
|
-
|
|
699
667
|
def sig_win_close(self, widget):
|
|
700
668
|
Main().sig_win_close(widget)
|
|
701
669
|
|
tryton/gui/window/infobar.py
CHANGED
|
@@ -12,9 +12,10 @@ class InfoBar(object):
|
|
|
12
12
|
self.__box = Gtk.VBox()
|
|
13
13
|
self.__box.show()
|
|
14
14
|
self.__messages = set()
|
|
15
|
+
self.__kinds = {}
|
|
15
16
|
return self.__box
|
|
16
17
|
|
|
17
|
-
def info_bar_add(self, message, type_=Gtk.MessageType.ERROR):
|
|
18
|
+
def info_bar_add(self, message, type_=Gtk.MessageType.ERROR, kind=None):
|
|
18
19
|
if not message:
|
|
19
20
|
return
|
|
20
21
|
key = (message, type_)
|
|
@@ -27,15 +28,19 @@ class InfoBar(object):
|
|
|
27
28
|
info_bar.connect('response', self.__response, key)
|
|
28
29
|
info_bar.set_message_type(type_)
|
|
29
30
|
info_bar.show_all()
|
|
31
|
+
self.__kinds[info_bar] = kind
|
|
30
32
|
|
|
31
33
|
def __response(self, widget, response, key):
|
|
32
34
|
self.__messages.add(key)
|
|
33
35
|
self.__box.remove(widget)
|
|
34
36
|
|
|
35
|
-
def info_bar_refresh(self):
|
|
37
|
+
def info_bar_refresh(self, kind=None):
|
|
36
38
|
for child in self.__box.get_children():
|
|
37
|
-
self.
|
|
39
|
+
if self.__kinds[child] == kind:
|
|
40
|
+
self.__box.remove(child)
|
|
41
|
+
del self.__kinds[child]
|
|
38
42
|
|
|
39
43
|
def info_bar_clear(self):
|
|
40
|
-
self.
|
|
44
|
+
for kind in set(self.__kinds.values()):
|
|
45
|
+
self.info_bar_refresh(kind=kind)
|
|
41
46
|
self.__messages.clear()
|
tryton/gui/window/log.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# This file is part of Tryton. The COPYRIGHT file at the top level of
|
|
2
|
+
# this repository contains the full copyright notices and license terms.
|
|
3
|
+
|
|
4
|
+
import gettext
|
|
5
|
+
|
|
6
|
+
from gi.repository import Gtk
|
|
7
|
+
|
|
8
|
+
from tryton.common import RPCException, RPCExecute, timezoned_date
|
|
9
|
+
from tryton.common.underline import set_underline
|
|
10
|
+
from tryton.gui.window.view_form.screen import Screen
|
|
11
|
+
from tryton.gui.window.win_form import WinForm
|
|
12
|
+
|
|
13
|
+
_ = gettext.gettext
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class Log(WinForm):
|
|
17
|
+
|
|
18
|
+
def __init__(self, record):
|
|
19
|
+
self.resource = '%s,%s' % (record.model_name, record.id)
|
|
20
|
+
title = _("Logs (%s)") % record.rec_name()
|
|
21
|
+
|
|
22
|
+
context = record.get_context()
|
|
23
|
+
try:
|
|
24
|
+
log, = RPCExecute(
|
|
25
|
+
'model', record.model_name, 'read', [record.id],
|
|
26
|
+
['create_uid.rec_name', 'create_date',
|
|
27
|
+
'write_uid.rec_name', 'write_date'], context=context)
|
|
28
|
+
except RPCException:
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
date_format = context.get('date_format', '%x')
|
|
32
|
+
datetime_format = date_format + ' %H:%M:%S.%f'
|
|
33
|
+
|
|
34
|
+
grid = Gtk.Grid(
|
|
35
|
+
column_spacing=3, row_spacing=3, border_width=3)
|
|
36
|
+
|
|
37
|
+
entry_model = Gtk.Entry(editable=False)
|
|
38
|
+
entry_model.set_text(record.model_name)
|
|
39
|
+
grid.attach(entry_model, 1, 1, 1, 1)
|
|
40
|
+
label_model = Gtk.Label(
|
|
41
|
+
label=set_underline(_("Model:")),
|
|
42
|
+
use_underline=True, halign=Gtk.Align.END)
|
|
43
|
+
label_model.set_mnemonic_widget(entry_model)
|
|
44
|
+
grid.attach(label_model, 0, 1, 1, 1)
|
|
45
|
+
|
|
46
|
+
entry_id = Gtk.Entry(editable=False)
|
|
47
|
+
entry_id.set_alignment(1)
|
|
48
|
+
entry_id.set_text(str(record.id))
|
|
49
|
+
grid.attach(entry_id, 3, 1, 1, 1)
|
|
50
|
+
label_id = Gtk.Label(
|
|
51
|
+
label=set_underline(_("ID:")),
|
|
52
|
+
use_underline=True, halign=Gtk.Align.END)
|
|
53
|
+
label_id.set_mnemonic_widget(entry_id)
|
|
54
|
+
grid.attach(label_id, 2, 1, 1, 1)
|
|
55
|
+
|
|
56
|
+
for i, (user, user_label, date, date_label) in enumerate([
|
|
57
|
+
('create_uid.', _("Created by:"),
|
|
58
|
+
'create_date', _("Created at:")),
|
|
59
|
+
('write_uid.', _("Last Modified by:"),
|
|
60
|
+
'write_date', _("Last Modified at:"))], 2):
|
|
61
|
+
entry_user = Gtk.Entry(editable=False, width_chars=50)
|
|
62
|
+
user = log.get(user)
|
|
63
|
+
if user:
|
|
64
|
+
user = user.get('rec_name', '')
|
|
65
|
+
entry_user.set_text(user or '')
|
|
66
|
+
grid.attach(entry_user, 1, i, 1, 1)
|
|
67
|
+
label_user = Gtk.Label(
|
|
68
|
+
label=set_underline(user_label),
|
|
69
|
+
use_underline=True, halign=Gtk.Align.END)
|
|
70
|
+
label_user.set_mnemonic_widget(entry_user)
|
|
71
|
+
grid.attach(label_user, 0, i, 1, 1)
|
|
72
|
+
|
|
73
|
+
entry_date = Gtk.Entry(editable=False)
|
|
74
|
+
date = log.get(date)
|
|
75
|
+
if date:
|
|
76
|
+
date = timezoned_date(date).strftime(datetime_format)
|
|
77
|
+
entry_date.set_width_chars(len(date or ''))
|
|
78
|
+
entry_date.set_text(date or '')
|
|
79
|
+
grid.attach(entry_date, 3, i, 1, 1)
|
|
80
|
+
label_date = Gtk.Label(
|
|
81
|
+
label=set_underline(date_label),
|
|
82
|
+
use_underline=True, halign=Gtk.Align.END)
|
|
83
|
+
label_date.set_mnemonic_widget(entry_date)
|
|
84
|
+
grid.attach(label_date, 2, i, 1, 1)
|
|
85
|
+
|
|
86
|
+
grid.show_all()
|
|
87
|
+
|
|
88
|
+
screen = Screen('ir.model.log', domain=[
|
|
89
|
+
('resource', '=', self.resource),
|
|
90
|
+
], mode=['tree', 'form'])
|
|
91
|
+
super().__init__(screen, view_type='tree', title=title)
|
|
92
|
+
screen.search_filter()
|
|
93
|
+
|
|
94
|
+
self.win.vbox.pack_start(grid, expand=False, fill=True, padding=0)
|
|
95
|
+
self.win.vbox.reorder_child(grid, 2)
|
|
@@ -122,10 +122,6 @@ class Action:
|
|
|
122
122
|
self.screen.current_record.cancel()
|
|
123
123
|
WinForm(self.screen, callback, title=self.title.get_text())
|
|
124
124
|
|
|
125
|
-
def set_value(self, mode, model_field):
|
|
126
|
-
self.screen.current_view.set_value()
|
|
127
|
-
return True
|
|
128
|
-
|
|
129
125
|
def display(self):
|
|
130
126
|
self.screen.search_filter(self.screen.screen_container.get_text())
|
|
131
127
|
|
|
@@ -95,8 +95,11 @@ class Field(object):
|
|
|
95
95
|
def validate(self, record, softvalidation=False, pre_validate=None):
|
|
96
96
|
if self.attrs.get('readonly'):
|
|
97
97
|
return True
|
|
98
|
+
state_attrs = self.get_state_attrs(record)
|
|
99
|
+
is_required = bool(int(state_attrs.get('required') or 0))
|
|
100
|
+
is_invisible = bool(int(state_attrs.get('invisible') or 0))
|
|
98
101
|
invalid = False
|
|
99
|
-
|
|
102
|
+
state_attrs['domain_readonly'] = False
|
|
100
103
|
domain = simplify(self.validation_domains(record, pre_validate))
|
|
101
104
|
if not softvalidation:
|
|
102
105
|
if not self.check_required(record):
|
|
@@ -107,8 +110,15 @@ class Field(object):
|
|
|
107
110
|
elif domain == [('id', '=', None)]:
|
|
108
111
|
invalid = 'domain'
|
|
109
112
|
else:
|
|
113
|
+
screen_domain, _ = self.domains_get(record, pre_validate)
|
|
110
114
|
unique, leftpart, value = unique_value(domain)
|
|
111
|
-
|
|
115
|
+
unique_from_screen, _, _ = unique_value(screen_domain)
|
|
116
|
+
if (self._is_empty(record)
|
|
117
|
+
and not is_required
|
|
118
|
+
and not is_invisible
|
|
119
|
+
and not unique_from_screen):
|
|
120
|
+
pass
|
|
121
|
+
elif unique:
|
|
112
122
|
# If the inverted domain is so constraint that only one value
|
|
113
123
|
# is possible we should use it. But we must also pay attention
|
|
114
124
|
# to the fact that the original domain might be a 'OR' domain
|
|
@@ -132,11 +142,10 @@ class Field(object):
|
|
|
132
142
|
setdefault = False
|
|
133
143
|
if setdefault and not pre_validate:
|
|
134
144
|
self.set_client(record, value)
|
|
135
|
-
|
|
136
|
-
domain_readonly)
|
|
145
|
+
state_attrs['domain_readonly'] = domain_readonly
|
|
137
146
|
if not eval_domain(domain, EvalEnvironment(record)):
|
|
138
147
|
invalid = domain
|
|
139
|
-
|
|
148
|
+
state_attrs['invalid'] = invalid
|
|
140
149
|
return not invalid
|
|
141
150
|
|
|
142
151
|
def set(self, record, value):
|
|
@@ -354,6 +363,11 @@ class TimeDeltaField(Field):
|
|
|
354
363
|
class FloatField(Field):
|
|
355
364
|
_default = None
|
|
356
365
|
|
|
366
|
+
def __init__(self, attrs):
|
|
367
|
+
super().__init__(attrs)
|
|
368
|
+
self._digits = {}
|
|
369
|
+
self._symbol = {}
|
|
370
|
+
|
|
357
371
|
def _is_empty(self, record):
|
|
358
372
|
return self.get(record) is None
|
|
359
373
|
|
|
@@ -369,14 +383,18 @@ class FloatField(Field):
|
|
|
369
383
|
digits_name = digits_field.attrs.get('relation')
|
|
370
384
|
digits_id = digits_field.get(record)
|
|
371
385
|
if digits_name and digits_id is not None and digits_id >= 0:
|
|
372
|
-
|
|
373
|
-
digits =
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
386
|
+
if digits_id in self._digits:
|
|
387
|
+
digits = self._digits[digits_id]
|
|
388
|
+
else:
|
|
389
|
+
try:
|
|
390
|
+
digits = RPCExecute(
|
|
391
|
+
'model', digits_name, 'get_digits', digits_id)
|
|
392
|
+
except RPCException:
|
|
393
|
+
logger.warn(
|
|
394
|
+
"Fail to fetch digits for %s,%s",
|
|
395
|
+
digits_name, digits_id)
|
|
396
|
+
return
|
|
397
|
+
self._digits[digits_id] = digits
|
|
380
398
|
else:
|
|
381
399
|
return
|
|
382
400
|
if not digits or any(d is None for d in digits):
|
|
@@ -396,10 +414,14 @@ class FloatField(Field):
|
|
|
396
414
|
symbol_name = symbol_field.attrs.get('relation')
|
|
397
415
|
symbol_id = symbol_field.get(record)
|
|
398
416
|
if symbol_name and symbol_id is not None and symbol_id >= 0:
|
|
417
|
+
if symbol_id in self._symbol:
|
|
418
|
+
return self._symbol[symbol_id]
|
|
399
419
|
try:
|
|
400
|
-
|
|
420
|
+
result = RPCExecute(
|
|
401
421
|
'model', symbol_name, 'get_symbol', symbol_id, sign,
|
|
402
422
|
context=record.get_context())
|
|
423
|
+
self._symbol[symbol_id] = result
|
|
424
|
+
return result
|
|
403
425
|
except RPCException:
|
|
404
426
|
logger.warn(
|
|
405
427
|
"Fail to fetch symbol for %s,%s",
|
|
@@ -44,6 +44,11 @@ class Record:
|
|
|
44
44
|
self.destroyed = False
|
|
45
45
|
|
|
46
46
|
def __getitem__(self, name):
|
|
47
|
+
self.load(name)
|
|
48
|
+
if name != '*':
|
|
49
|
+
return self.group.fields[name]
|
|
50
|
+
|
|
51
|
+
def load(self, name, process_exception=True):
|
|
47
52
|
if not self.destroyed and self.id >= 0 and name not in self._loaded:
|
|
48
53
|
id2record = {
|
|
49
54
|
self.id: self,
|
|
@@ -74,9 +79,13 @@ class Record:
|
|
|
74
79
|
fnames = [fname for fname, field in fields
|
|
75
80
|
if fname not in self._loaded
|
|
76
81
|
and (not views or (views & field.views))]
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
82
|
+
for fname in list(fnames):
|
|
83
|
+
f_attrs = self.group.fields[fname].attrs
|
|
84
|
+
if f_attrs['type'] in {'many2one', 'one2one', 'reference'}:
|
|
85
|
+
fnames.append('%s.rec_name' % fname)
|
|
86
|
+
elif (f_attrs['type'] == 'selection'
|
|
87
|
+
and f_attrs.get('loading', 'lazy') == 'eager'):
|
|
88
|
+
fnames.append('%s:string' % fname)
|
|
80
89
|
if 'rec_name' not in fnames:
|
|
81
90
|
fnames.append('rec_name')
|
|
82
91
|
fnames.extend(['_timestamp', '_write', '_delete'])
|
|
@@ -126,7 +135,8 @@ class Record:
|
|
|
126
135
|
exception = False
|
|
127
136
|
try:
|
|
128
137
|
values = RPCExecute('model', self.model_name, 'read',
|
|
129
|
-
list(id2record.keys()), fnames, context=ctx
|
|
138
|
+
list(id2record.keys()), fnames, context=ctx,
|
|
139
|
+
process_exception=process_exception)
|
|
130
140
|
except RPCException:
|
|
131
141
|
values = [{'id': x} for x in id2record]
|
|
132
142
|
default_values = dict((f, None) for f in fnames)
|
|
@@ -142,8 +152,6 @@ class Record:
|
|
|
142
152
|
for key in record.modified_fields:
|
|
143
153
|
value.pop(key, None)
|
|
144
154
|
record.set(value, modified=False)
|
|
145
|
-
if name != '*':
|
|
146
|
-
return self.group.fields[name]
|
|
147
155
|
|
|
148
156
|
def __repr__(self):
|
|
149
157
|
return '<Record %s@%s at %s>' % (self.id, self.model_name, id(self))
|
|
@@ -240,6 +248,7 @@ class Record:
|
|
|
240
248
|
return (self.deleted
|
|
241
249
|
or self.removed
|
|
242
250
|
or self.exception
|
|
251
|
+
or self.group.readonly
|
|
243
252
|
or not self._write)
|
|
244
253
|
|
|
245
254
|
readonly = property(get_readonly)
|
|
@@ -464,13 +473,17 @@ class Record:
|
|
|
464
473
|
if fieldname == 'rec_name':
|
|
465
474
|
self.value['rec_name'] = value
|
|
466
475
|
continue
|
|
467
|
-
|
|
476
|
+
field = self.group.fields[fieldname]
|
|
477
|
+
if isinstance(field, fields.O2MField):
|
|
468
478
|
later[fieldname] = value
|
|
469
479
|
continue
|
|
470
|
-
if isinstance(
|
|
471
|
-
fields.ReferenceField)):
|
|
480
|
+
if isinstance(field, (fields.M2OField, fields.ReferenceField)):
|
|
472
481
|
related = fieldname + '.'
|
|
473
482
|
self.value[related] = val.get(related) or {}
|
|
483
|
+
elif (isinstance(field, fields.SelectionField)
|
|
484
|
+
and fieldname + ':string' in val):
|
|
485
|
+
related = fieldname + ':string'
|
|
486
|
+
self.value[related] = val[related]
|
|
474
487
|
self.group.fields[fieldname].set(self, value)
|
|
475
488
|
self._loaded.add(fieldname)
|
|
476
489
|
fieldnames.append(fieldname)
|