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.

Files changed (122) hide show
  1. tryton/__init__.py +1 -1
  2. tryton/bus.py +113 -69
  3. tryton/chat.py +179 -0
  4. tryton/client.py +7 -0
  5. tryton/common/__init__.py +15 -11
  6. tryton/common/button.py +1 -1
  7. tryton/common/cellrendererfloat.py +1 -1
  8. tryton/common/cellrenderertext.py +2 -2
  9. tryton/common/common.py +91 -19
  10. tryton/common/environment.py +2 -2
  11. tryton/common/number_entry.py +12 -6
  12. tryton/common/selection.py +1 -1
  13. tryton/data/locale/bg/LC_MESSAGES/tryton.mo +0 -0
  14. tryton/data/locale/bg/LC_MESSAGES/tryton.po +26 -16
  15. tryton/data/locale/ca/LC_MESSAGES/tryton.mo +0 -0
  16. tryton/data/locale/ca/LC_MESSAGES/tryton.po +29 -18
  17. tryton/data/locale/cs/LC_MESSAGES/tryton.mo +0 -0
  18. tryton/data/locale/cs/LC_MESSAGES/tryton.po +28 -16
  19. tryton/data/locale/de/LC_MESSAGES/tryton.mo +0 -0
  20. tryton/data/locale/de/LC_MESSAGES/tryton.po +27 -18
  21. tryton/data/locale/es/LC_MESSAGES/tryton.mo +0 -0
  22. tryton/data/locale/es/LC_MESSAGES/tryton.po +25 -16
  23. tryton/data/locale/es_419/LC_MESSAGES/tryton.mo +0 -0
  24. tryton/data/locale/es_419/LC_MESSAGES/tryton.po +26 -16
  25. tryton/data/locale/et/LC_MESSAGES/tryton.mo +0 -0
  26. tryton/data/locale/et/LC_MESSAGES/tryton.po +28 -18
  27. tryton/data/locale/fa/LC_MESSAGES/tryton.mo +0 -0
  28. tryton/data/locale/fa/LC_MESSAGES/tryton.po +28 -18
  29. tryton/data/locale/fi/LC_MESSAGES/tryton.mo +0 -0
  30. tryton/data/locale/fi/LC_MESSAGES/tryton.po +25 -16
  31. tryton/data/locale/fr/LC_MESSAGES/tryton.mo +0 -0
  32. tryton/data/locale/fr/LC_MESSAGES/tryton.po +25 -16
  33. tryton/data/locale/hu/LC_MESSAGES/tryton.mo +0 -0
  34. tryton/data/locale/hu/LC_MESSAGES/tryton.po +28 -18
  35. tryton/data/locale/id/LC_MESSAGES/tryton.mo +0 -0
  36. tryton/data/locale/id/LC_MESSAGES/tryton.po +23 -16
  37. tryton/data/locale/it/LC_MESSAGES/tryton.mo +0 -0
  38. tryton/data/locale/it/LC_MESSAGES/tryton.po +29 -18
  39. tryton/data/locale/ja_JP/LC_MESSAGES/tryton.mo +0 -0
  40. tryton/data/locale/lo/LC_MESSAGES/tryton.mo +0 -0
  41. tryton/data/locale/lo/LC_MESSAGES/tryton.po +26 -18
  42. tryton/data/locale/lt/LC_MESSAGES/tryton.mo +0 -0
  43. tryton/data/locale/lt/LC_MESSAGES/tryton.po +30 -18
  44. tryton/data/locale/nl/LC_MESSAGES/tryton.mo +0 -0
  45. tryton/data/locale/nl/LC_MESSAGES/tryton.po +25 -16
  46. tryton/data/locale/pl/LC_MESSAGES/tryton.mo +0 -0
  47. tryton/data/locale/pl/LC_MESSAGES/tryton.po +31 -18
  48. tryton/data/locale/pt/LC_MESSAGES/tryton.mo +0 -0
  49. tryton/data/locale/pt/LC_MESSAGES/tryton.po +148 -177
  50. tryton/data/locale/ro/LC_MESSAGES/tryton.mo +0 -0
  51. tryton/data/locale/ro/LC_MESSAGES/tryton.po +32 -19
  52. tryton/data/locale/ru/LC_MESSAGES/tryton.mo +0 -0
  53. tryton/data/locale/ru/LC_MESSAGES/tryton.po +28 -16
  54. tryton/data/locale/sl/LC_MESSAGES/tryton.mo +0 -0
  55. tryton/data/locale/sl/LC_MESSAGES/tryton.po +33 -18
  56. tryton/data/locale/tr/LC_MESSAGES/tryton.mo +0 -0
  57. tryton/data/locale/tr/LC_MESSAGES/tryton.po +25 -16
  58. tryton/data/locale/uk/LC_MESSAGES/tryton.mo +0 -0
  59. tryton/data/locale/uk/LC_MESSAGES/tryton.po +31 -18
  60. tryton/data/locale/zh_CN/LC_MESSAGES/tryton.mo +0 -0
  61. tryton/data/locale/zh_CN/LC_MESSAGES/tryton.po +27 -18
  62. tryton/data/pixmaps/tryton/tryton-chat.svg +1 -0
  63. tryton/data/pixmaps/tryton/tryton-note.svg +1 -4
  64. tryton/gui/window/attachment.py +2 -2
  65. tryton/gui/window/board.py +1 -1
  66. tryton/gui/window/form.py +57 -10
  67. tryton/gui/window/note.py +2 -2
  68. tryton/gui/window/tabcontent.py +8 -1
  69. tryton/gui/window/view_board/action.py +1 -1
  70. tryton/gui/window/view_form/model/field.py +34 -28
  71. tryton/gui/window/view_form/model/group.py +4 -4
  72. tryton/gui/window/view_form/model/record.py +19 -4
  73. tryton/gui/window/view_form/screen/screen.py +24 -4
  74. tryton/gui/window/view_form/view/calendar_gtk/calendar_.py +1 -1
  75. tryton/gui/window/view_form/view/calendar_gtk/toolbar.py +1 -1
  76. tryton/gui/window/view_form/view/form.py +2 -1
  77. tryton/gui/window/view_form/view/form_gtk/binary.py +3 -3
  78. tryton/gui/window/view_form/view/form_gtk/calendar_.py +4 -4
  79. tryton/gui/window/view_form/view/form_gtk/char.py +42 -5
  80. tryton/gui/window/view_form/view/form_gtk/checkbox.py +3 -3
  81. tryton/gui/window/view_form/view/form_gtk/dictionary.py +53 -15
  82. tryton/gui/window/view_form/view/form_gtk/float.py +3 -7
  83. tryton/gui/window/view_form/view/form_gtk/image.py +4 -4
  84. tryton/gui/window/view_form/view/form_gtk/integer.py +1 -1
  85. tryton/gui/window/view_form/view/form_gtk/many2many.py +3 -4
  86. tryton/gui/window/view_form/view/form_gtk/many2one.py +2 -2
  87. tryton/gui/window/view_form/view/form_gtk/multiselection.py +3 -3
  88. tryton/gui/window/view_form/view/form_gtk/one2many.py +11 -8
  89. tryton/gui/window/view_form/view/form_gtk/progressbar.py +2 -2
  90. tryton/gui/window/view_form/view/form_gtk/pyson.py +3 -3
  91. tryton/gui/window/view_form/view/form_gtk/reference.py +4 -4
  92. tryton/gui/window/view_form/view/form_gtk/richtextbox.py +5 -5
  93. tryton/gui/window/view_form/view/form_gtk/selection.py +3 -3
  94. tryton/gui/window/view_form/view/form_gtk/state_widget.py +8 -6
  95. tryton/gui/window/view_form/view/form_gtk/textbox.py +4 -4
  96. tryton/gui/window/view_form/view/form_gtk/timedelta.py +3 -3
  97. tryton/gui/window/view_form/view/form_gtk/url.py +2 -2
  98. tryton/gui/window/view_form/view/form_gtk/widget.py +1 -1
  99. tryton/gui/window/view_form/view/graph_gtk/bar.py +7 -7
  100. tryton/gui/window/view_form/view/graph_gtk/graph.py +2 -2
  101. tryton/gui/window/view_form/view/graph_gtk/line.py +5 -5
  102. tryton/gui/window/view_form/view/graph_gtk/pie.py +2 -2
  103. tryton/gui/window/view_form/view/list.py +107 -52
  104. tryton/gui/window/view_form/view/list_gtk/editabletree.py +2 -2
  105. tryton/gui/window/view_form/view/list_gtk/widget.py +22 -20
  106. tryton/gui/window/view_form/view/screen_container.py +13 -1
  107. tryton/gui/window/win_csv.py +2 -2
  108. tryton/gui/window/win_export.py +9 -7
  109. tryton/gui/window/win_form.py +74 -39
  110. tryton/gui/window/win_import.py +5 -6
  111. tryton/gui/window/wizard.py +11 -11
  112. tryton/jsonrpc.py +2 -2
  113. tryton/plugins/__init__.py +0 -1
  114. tryton/pyson.py +18 -18
  115. tryton/rpc.py +7 -5
  116. {tryton-7.4.10.dist-info → tryton-7.6.1.dist-info}/METADATA +5 -5
  117. {tryton-7.4.10.dist-info → tryton-7.6.1.dist-info}/RECORD +121 -120
  118. tryton/gui/window/view_form/view/list_gtk/generictreemodel.py +0 -426
  119. {tryton-7.4.10.data → tryton-7.6.1.data}/scripts/tryton +0 -0
  120. {tryton-7.4.10.dist-info → tryton-7.6.1.dist-info}/WHEEL +0 -0
  121. {tryton-7.4.10.dist-info → tryton-7.6.1.dist-info}/licenses/LICENSE +0 -0
  122. {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(Form, self).__init__(**attributes)
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.main.pack2(scrolledwindow, resize=False, shrink=True)
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.pack_start(hbox, expand=False, fill=True, padding=0)
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
- search_container = self.screen.screen_container
560
- if hasattr(search_container, 'search_entry'):
561
- search_container.search_entry.grab_focus()
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 ['print', 'relate', 'email', 'open', 'attach']:
587
- button = self.buttons[button_id]
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
- return True if modified_save is None else modified_save
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(Form, self).create_toolbar(toolbars)
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(Note, self).__init__(screen, self.callback, view_type='tree',
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(Note, self).destroy()
27
+ super().destroy()
28
28
 
29
29
  def callback(self, result):
30
30
  if result:
@@ -37,7 +37,7 @@ class ToolbarItem(object):
37
37
  class TabContent(InfoBar):
38
38
 
39
39
  def __init__(self, **attributes):
40
- super(TabContent, self).__init__()
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..."),
@@ -19,7 +19,7 @@ _ = gettext.gettext
19
19
  class Action:
20
20
 
21
21
  def __init__(self, view, attrs=None):
22
- super(Action, self).__init__()
22
+ super().__init__()
23
23
  self.name = attrs['name']
24
24
  self.view = view
25
25
 
@@ -228,7 +228,7 @@ class CharField(Field):
228
228
  super().set(record, value)
229
229
 
230
230
  def get(self, record):
231
- return super(CharField, self).get(record) or self._default
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(DateTimeField, self).set_client(record, value,
304
+ super().set_client(record, value,
305
305
  force_change=force_change)
306
306
 
307
307
  def get_client(self, record):
308
- value = super(DateTimeField, self).get_client(record)
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(DateField, self).set_client(record, value,
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(TimeField, self).set_client(record, value,
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(TimeDeltaField, self).set_client(
366
+ super().set_client(
367
367
  record, value, force_change=force_change)
368
368
 
369
369
  def get_client(self, record):
370
- value = super(TimeDeltaField, self).get_client(record)
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 or any(d is None for d in digits):
411
+ if not digits:
412
412
  return
413
413
  shift = int(round(math.log(abs(factor), 10)))
414
- return (digits[0] + shift, digits[1] - shift)
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(FloatField, self).set_client(record, value,
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(NumericField, self).set_client(record, value,
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(NumericField, self).get_client(record,
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(IntegerField, self).set_client(record, value,
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(IntegerField, self).get_client(
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(BooleanField, self).set_client(record, value,
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(M2OField, self).set_client(record, value,
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(M2OField, self).get_context(
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(M2OField, self).get_on_change_value(record)
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(O2MField, self).__init__(attrs)
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(O2MField, self).validate(record, softvalidation,
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(O2MField, self).state_set(record, states=states)
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(ReferenceField, self)._is_empty(record)
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(ReferenceField, self).set_client(record, value,
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(ReferenceField, self).get_context(
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(ReferenceField, self).get_on_change_value(record)
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(DictField, self).__init__(attrs)
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(DictField, self).validate(
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(Group, self).__init__()
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(Group, self).insert(pos, record)
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(Group, self).append(record)
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(Group, self).remove(record)
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(Record, self).__init__()
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 set(fieldnames) & set(notification_fields):
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, node_attributes,
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, display=True):
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
- record.set_modified()
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(Calendar_, self).__init__(
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'):
@@ -16,7 +16,7 @@ _ = gettext.gettext
16
16
  class Toolbar(Gtk.Toolbar):
17
17
 
18
18
  def __init__(self, goocalendar):
19
- super(Toolbar, self).__init__()
19
+ super().__init__()
20
20
  self.goocalendar = goocalendar
21
21
  self.accel_group = Main().accel_group
22
22
 
@@ -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,