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
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# this repository contains the full copyright notices and license terms.
|
|
3
3
|
import datetime
|
|
4
4
|
import gettext
|
|
5
|
+
import logging
|
|
5
6
|
import os
|
|
6
7
|
import webbrowser
|
|
7
8
|
from functools import partial, wraps
|
|
@@ -31,6 +32,7 @@ from tryton.gui.window.win_form import WinForm
|
|
|
31
32
|
from tryton.gui.window.win_search import WinSearch
|
|
32
33
|
|
|
33
34
|
_ = gettext.gettext
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
34
36
|
|
|
35
37
|
COLORS = {n: v for n, v in zip(
|
|
36
38
|
['muted', 'success', 'warning', 'danger'],
|
|
@@ -50,6 +52,23 @@ def send_keys(renderer, editable, position, treeview):
|
|
|
50
52
|
editable.connect('changed', changed)
|
|
51
53
|
|
|
52
54
|
|
|
55
|
+
def catch_errors(error_value=_('#ERROR')):
|
|
56
|
+
def decorator(func):
|
|
57
|
+
@wraps(func)
|
|
58
|
+
def wrapper(self, record):
|
|
59
|
+
if record.exception:
|
|
60
|
+
return error_value
|
|
61
|
+
try:
|
|
62
|
+
return func(self, record)
|
|
63
|
+
except Exception:
|
|
64
|
+
logger.error(
|
|
65
|
+
f"Error calling {func.__name__} for {self} with {record}",
|
|
66
|
+
exc_info=True)
|
|
67
|
+
return error_value
|
|
68
|
+
return wrapper
|
|
69
|
+
return decorator
|
|
70
|
+
|
|
71
|
+
|
|
53
72
|
def realized(func):
|
|
54
73
|
|
|
55
74
|
@wraps(func)
|
|
@@ -143,7 +162,7 @@ class Cell(object):
|
|
|
143
162
|
if not store:
|
|
144
163
|
store = self.view.treeview.get_model()
|
|
145
164
|
record = store.get_value(iter_, 0)
|
|
146
|
-
field = record[self.attrs['name']]
|
|
165
|
+
field = record.group.fields[self.attrs['name']]
|
|
147
166
|
return record, field
|
|
148
167
|
|
|
149
168
|
def _set_visual(self, cell, record):
|
|
@@ -330,9 +349,9 @@ class GenericText(Cell):
|
|
|
330
349
|
callback=None):
|
|
331
350
|
raise NotImplementedError
|
|
332
351
|
|
|
352
|
+
@catch_errors()
|
|
333
353
|
def get_textual_value(self, record):
|
|
334
|
-
|
|
335
|
-
return ''
|
|
354
|
+
record.load(self.attrs['name'], process_exception=False)
|
|
336
355
|
return record[self.attrs['name']].get_client(record)
|
|
337
356
|
|
|
338
357
|
def value_from_text(self, record, text, callback=None):
|
|
@@ -396,9 +415,9 @@ class Int(GenericText):
|
|
|
396
415
|
return [self.renderer_suffix]
|
|
397
416
|
return []
|
|
398
417
|
|
|
418
|
+
@catch_errors()
|
|
399
419
|
def get_textual_value(self, record):
|
|
400
|
-
|
|
401
|
-
return ''
|
|
420
|
+
record.load(self.attrs['name'], process_exception=False)
|
|
402
421
|
return record[self.attrs['name']].get_client(
|
|
403
422
|
record, factor=self.factor, grouping=self.grouping)
|
|
404
423
|
|
|
@@ -464,9 +483,9 @@ class Date(GenericText):
|
|
|
464
483
|
else:
|
|
465
484
|
return '%x'
|
|
466
485
|
|
|
486
|
+
@catch_errors()
|
|
467
487
|
def get_textual_value(self, record):
|
|
468
|
-
|
|
469
|
-
return ''
|
|
488
|
+
record.load(self.attrs['name'], process_exception=False)
|
|
470
489
|
value = record[self.attrs['name']].get_client(record)
|
|
471
490
|
if value:
|
|
472
491
|
return value.strftime(self.renderer.props.format)
|
|
@@ -487,9 +506,9 @@ class Time(Date):
|
|
|
487
506
|
else:
|
|
488
507
|
return '%X'
|
|
489
508
|
|
|
509
|
+
@catch_errors()
|
|
490
510
|
def get_textual_value(self, record):
|
|
491
|
-
|
|
492
|
-
return ''
|
|
511
|
+
record.load(self.attrs['name'], process_exception=False)
|
|
493
512
|
value = record[self.attrs['name']].get_client(record)
|
|
494
513
|
if value is not None:
|
|
495
514
|
if isinstance(value, datetime.datetime):
|
|
@@ -549,7 +568,9 @@ class Binary(GenericText):
|
|
|
549
568
|
def suffixes(self):
|
|
550
569
|
return [self.renderer_save, self.renderer_select]
|
|
551
570
|
|
|
571
|
+
@catch_errors()
|
|
552
572
|
def get_textual_value(self, record):
|
|
573
|
+
record.load(self.attrs['name'], process_exception=False)
|
|
553
574
|
field = record[self.attrs['name']]
|
|
554
575
|
if hasattr(field, 'get_size'):
|
|
555
576
|
size = field.get_size(record)
|
|
@@ -604,6 +625,11 @@ class _BinaryIcon(Cell):
|
|
|
604
625
|
def view(self):
|
|
605
626
|
return self.binary.view
|
|
606
627
|
|
|
628
|
+
@catch_errors(False)
|
|
629
|
+
def _fetch_data(self, record):
|
|
630
|
+
record.fetch(self.attrs['name'], process_exception=False)
|
|
631
|
+
return True
|
|
632
|
+
|
|
607
633
|
|
|
608
634
|
class _BinarySave(_BinaryIcon):
|
|
609
635
|
icon_name = 'tryton-save'
|
|
@@ -631,6 +657,9 @@ class _BinarySave(_BinaryIcon):
|
|
|
631
657
|
@CellCache.cache
|
|
632
658
|
def setter(self, column, cell, store, iter_, user_data=None):
|
|
633
659
|
record, field = self._get_record_field_from_iter(iter_, store)
|
|
660
|
+
if not self._fetch_data(record):
|
|
661
|
+
cell.set_property('visible', False)
|
|
662
|
+
return
|
|
634
663
|
if hasattr(field, 'get_size'):
|
|
635
664
|
size = field.get_size(record)
|
|
636
665
|
else:
|
|
@@ -671,6 +700,9 @@ class _BinarySelect(_BinaryIcon):
|
|
|
671
700
|
@CellCache.cache
|
|
672
701
|
def setter(self, column, cell, store, iter_, user_data=None):
|
|
673
702
|
record, field = self._get_record_field_from_iter(iter_, store)
|
|
703
|
+
if not self._fetch_data(record):
|
|
704
|
+
cell.set_property('visible', False)
|
|
705
|
+
return
|
|
674
706
|
if hasattr(field, 'get_size'):
|
|
675
707
|
size = field.get_size(record)
|
|
676
708
|
else:
|
|
@@ -710,6 +742,9 @@ class _BinaryOpen(_BinarySave):
|
|
|
710
742
|
def setter(self, column, cell, store, iter_, user_data=None):
|
|
711
743
|
super().setter(column, cell, store, iter_)
|
|
712
744
|
record, field = self._get_record_field_from_iter(iter_, store)
|
|
745
|
+
if not self._fetch_data(record):
|
|
746
|
+
cell.set_property('visible', False)
|
|
747
|
+
return
|
|
713
748
|
filename_field = record.group.fields.get(self.attrs.get('filename'))
|
|
714
749
|
filename = filename_field.get(record)
|
|
715
750
|
if not filename:
|
|
@@ -731,22 +766,33 @@ class Image(GenericText):
|
|
|
731
766
|
@CellCache.cache
|
|
732
767
|
def setter(self, column, cell, store, iter_, user_data=None):
|
|
733
768
|
record, field = self._get_record_field_from_iter(iter_, store)
|
|
769
|
+
value = self._get_data(record)
|
|
770
|
+
if value is None:
|
|
771
|
+
cell.set_property('pixbuf', None)
|
|
772
|
+
return
|
|
773
|
+
pixbuf = data2pixbuf(value)
|
|
774
|
+
if pixbuf:
|
|
775
|
+
pixbuf = common.resize_pixbuf(pixbuf, self.width, self.height)
|
|
776
|
+
cell.set_property('pixbuf', pixbuf)
|
|
777
|
+
self._set_visual(cell, record)
|
|
778
|
+
|
|
779
|
+
@catch_errors(None)
|
|
780
|
+
def _get_data(self, record):
|
|
781
|
+
record.load(self.attrs['name'], process_exception=False)
|
|
782
|
+
field = record[self.attrs['name']]
|
|
734
783
|
value = field.get_client(record)
|
|
735
784
|
if isinstance(value, int):
|
|
736
785
|
if value > CONFIG['image.max_size']:
|
|
737
786
|
value = None
|
|
738
787
|
else:
|
|
739
788
|
value = field.get_data(record)
|
|
740
|
-
|
|
741
|
-
if pixbuf:
|
|
742
|
-
pixbuf = common.resize_pixbuf(pixbuf, self.width, self.height)
|
|
743
|
-
cell.set_property('pixbuf', pixbuf)
|
|
744
|
-
self._set_visual(cell, record)
|
|
789
|
+
return value
|
|
745
790
|
|
|
791
|
+
@catch_errors()
|
|
746
792
|
def get_textual_value(self, record):
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
return str(
|
|
793
|
+
record.load(self.attrs['name'], process_exception=False)
|
|
794
|
+
field = record[self.attrs['name']]
|
|
795
|
+
return str(field.get_size(record))
|
|
750
796
|
|
|
751
797
|
|
|
752
798
|
class M2O(GenericText):
|
|
@@ -974,9 +1020,11 @@ class O2O(M2O):
|
|
|
974
1020
|
class O2M(GenericText):
|
|
975
1021
|
align = 0.5
|
|
976
1022
|
|
|
1023
|
+
@catch_errors()
|
|
977
1024
|
def get_textual_value(self, record):
|
|
978
|
-
|
|
979
|
-
|
|
1025
|
+
record.load(self.attrs['name'], process_exception=False)
|
|
1026
|
+
field = record[self.attrs['name']]
|
|
1027
|
+
return '( ' + str(len(field.get_eval(record))) + ' )'
|
|
980
1028
|
|
|
981
1029
|
def value_from_text(self, record, text, callback=None):
|
|
982
1030
|
if callback:
|
|
@@ -1045,16 +1093,24 @@ class Selection(GenericText, SelectionMixin, PopdownMixin):
|
|
|
1045
1093
|
if 'renderer' not in kwargs:
|
|
1046
1094
|
kwargs['renderer'] = CellRendererCombo
|
|
1047
1095
|
super(Selection, self).__init__(*args, **kwargs)
|
|
1048
|
-
self.
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1096
|
+
if self.view and self.view.editable:
|
|
1097
|
+
self.init_selection()
|
|
1098
|
+
# Use a variable let Python holding reference when calling
|
|
1099
|
+
# set_property
|
|
1100
|
+
model = self.get_popdown_model(self.selection)[0]
|
|
1101
|
+
self.renderer.set_property('model', model)
|
|
1102
|
+
self.renderer.set_property('text-column', 0)
|
|
1053
1103
|
|
|
1054
1104
|
def get_value(self, record, field):
|
|
1055
1105
|
return field.get(record)
|
|
1056
1106
|
|
|
1107
|
+
@catch_errors()
|
|
1057
1108
|
def get_textual_value(self, record):
|
|
1109
|
+
related = self.attrs['name'] + ':string'
|
|
1110
|
+
if not self.view.editable and related in record.value:
|
|
1111
|
+
return record.value[related]
|
|
1112
|
+
|
|
1113
|
+
record.load(self.attrs['name'], process_exception=False)
|
|
1058
1114
|
field = record[self.attrs['name']]
|
|
1059
1115
|
self.update_selection(record, field)
|
|
1060
1116
|
value = self.get_value(record, field)
|
|
@@ -1119,7 +1175,8 @@ class MultiSelection(GenericText, SelectionMixin):
|
|
|
1119
1175
|
|
|
1120
1176
|
def __init__(self, *args, **kwargs):
|
|
1121
1177
|
super().__init__(*args, **kwargs)
|
|
1122
|
-
self.
|
|
1178
|
+
if self.view and self.view.editable:
|
|
1179
|
+
self.init_selection()
|
|
1123
1180
|
|
|
1124
1181
|
@realized
|
|
1125
1182
|
@CellCache.cache
|
|
@@ -1131,7 +1188,13 @@ class MultiSelection(GenericText, SelectionMixin):
|
|
|
1131
1188
|
if callback:
|
|
1132
1189
|
callback()
|
|
1133
1190
|
|
|
1191
|
+
@catch_errors()
|
|
1134
1192
|
def get_textual_value(self, record):
|
|
1193
|
+
related = self.attrs['name'] + ':string'
|
|
1194
|
+
if not self.view.editable and related in record.value:
|
|
1195
|
+
return ";".join(record.value[related])
|
|
1196
|
+
|
|
1197
|
+
record.load(self.attrs['name'], process_exception=False)
|
|
1135
1198
|
field = record[self.attrs['name']]
|
|
1136
1199
|
self.update_selection(record, field)
|
|
1137
1200
|
selection = dict(self.selection)
|
|
@@ -1180,6 +1243,7 @@ class Reference(M2O):
|
|
|
1180
1243
|
_, value = value.split(',')
|
|
1181
1244
|
return int(value)
|
|
1182
1245
|
|
|
1246
|
+
@catch_errors()
|
|
1183
1247
|
def get_textual_value(self, record):
|
|
1184
1248
|
value = super().get_textual_value(record)
|
|
1185
1249
|
if value:
|
|
@@ -1221,8 +1285,11 @@ class Dict(GenericText):
|
|
|
1221
1285
|
super().setter(column, cell, store, iter_, user_data=None)
|
|
1222
1286
|
cell.props.editable = False
|
|
1223
1287
|
|
|
1288
|
+
@catch_errors()
|
|
1224
1289
|
def get_textual_value(self, record):
|
|
1225
|
-
|
|
1290
|
+
record.load(self.attrs['name'], process_exception=False)
|
|
1291
|
+
field = record[self.attrs['name']]
|
|
1292
|
+
return '(%s)' % len(field.get_client(record))
|
|
1226
1293
|
|
|
1227
1294
|
|
|
1228
1295
|
class ProgressBar(Cell):
|
|
@@ -1264,8 +1331,11 @@ class ProgressBar(Cell):
|
|
|
1264
1331
|
callback=None):
|
|
1265
1332
|
raise NotImplementedError
|
|
1266
1333
|
|
|
1334
|
+
@catch_errors()
|
|
1267
1335
|
def get_textual_value(self, record):
|
|
1268
|
-
|
|
1336
|
+
record.load(self.attrs['name'], process_exception=False)
|
|
1337
|
+
field = record[self.attrs['name']]
|
|
1338
|
+
return field.get_client(record, factor=100) or ''
|
|
1269
1339
|
|
|
1270
1340
|
def value_from_text(self, record, text, callback=None):
|
|
1271
1341
|
field = record[self.attrs['name']]
|
tryton/gui/window/win_form.py
CHANGED
|
@@ -21,7 +21,7 @@ _ = gettext.gettext
|
|
|
21
21
|
class WinForm(NoModal, InfoBar):
|
|
22
22
|
"Form window"
|
|
23
23
|
|
|
24
|
-
def __init__(self, screen, callback, view_type='form',
|
|
24
|
+
def __init__(self, screen, callback=None, view_type='form',
|
|
25
25
|
new=False, many=0, domain=None, context=None,
|
|
26
26
|
save_current=False, title='', rec_name=None):
|
|
27
27
|
tooltips = common.Tooltips()
|
|
@@ -345,7 +345,7 @@ class WinForm(NoModal, InfoBar):
|
|
|
345
345
|
win.show()
|
|
346
346
|
|
|
347
347
|
def record_message(self, position, size, *args):
|
|
348
|
-
self.
|
|
348
|
+
self.set_buttons_sensitive()
|
|
349
349
|
if self.view_type != 'tree':
|
|
350
350
|
return
|
|
351
351
|
name = '_'
|
|
@@ -380,10 +380,10 @@ class WinForm(NoModal, InfoBar):
|
|
|
380
380
|
self.label.set_text(line)
|
|
381
381
|
|
|
382
382
|
def record_modified(self, *args):
|
|
383
|
-
self.
|
|
383
|
+
self.set_buttons_sensitive()
|
|
384
384
|
self.info_bar_refresh()
|
|
385
385
|
|
|
386
|
-
def
|
|
386
|
+
def set_buttons_sensitive(self):
|
|
387
387
|
modified = self.screen.modified()
|
|
388
388
|
# Keep sensible as change could have been trigger by a Many2One edition
|
|
389
389
|
sensitive = modified or self.but_ok.props.sensitive
|
|
@@ -448,7 +448,8 @@ class WinForm(NoModal, InfoBar):
|
|
|
448
448
|
result = False
|
|
449
449
|
else:
|
|
450
450
|
result = response_id not in cancel_responses
|
|
451
|
-
self.callback
|
|
451
|
+
if self.callback:
|
|
452
|
+
self.callback(result)
|
|
452
453
|
self.destroy()
|
|
453
454
|
|
|
454
455
|
def new(self):
|
tryton/rpc.py
CHANGED
|
@@ -45,12 +45,12 @@ context_reset()
|
|
|
45
45
|
def db_list(host, port):
|
|
46
46
|
try:
|
|
47
47
|
connection = ServerProxy(host, port)
|
|
48
|
-
|
|
48
|
+
logger.info('common.db.list()')
|
|
49
49
|
result = connection.common.db.list()
|
|
50
|
-
|
|
50
|
+
logger.debug('%r', result)
|
|
51
51
|
return result
|
|
52
52
|
except Fault as exception:
|
|
53
|
-
|
|
53
|
+
logger.debug(exception.faultCode)
|
|
54
54
|
if exception.faultCode == str(HTTPStatus.FORBIDDEN.value):
|
|
55
55
|
return []
|
|
56
56
|
else:
|
|
@@ -60,13 +60,12 @@ def db_list(host, port):
|
|
|
60
60
|
def server_version(host, port):
|
|
61
61
|
try:
|
|
62
62
|
connection = ServerProxy(host, port)
|
|
63
|
-
|
|
64
|
-
'common.server.version(None, None)')
|
|
63
|
+
logger.info('common.server.version(None, None)')
|
|
65
64
|
result = connection.common.server.version()
|
|
66
|
-
|
|
65
|
+
logger.debug('%r', result)
|
|
67
66
|
return result
|
|
68
67
|
except Exception as e:
|
|
69
|
-
|
|
68
|
+
logger.exception(e)
|
|
70
69
|
return None
|
|
71
70
|
|
|
72
71
|
|
|
@@ -75,10 +74,10 @@ def authentication_services(host, port):
|
|
|
75
74
|
connection = ServerProxy(host, port)
|
|
76
75
|
logger.info('common.authentication.services()')
|
|
77
76
|
services = connection.common.authentication.services()
|
|
78
|
-
logger.debug(
|
|
77
|
+
logger.debug('%r', services)
|
|
79
78
|
return connection.url, services
|
|
80
79
|
except Exception as e:
|
|
81
|
-
logger.
|
|
80
|
+
logger.exception(e)
|
|
82
81
|
return '', []
|
|
83
82
|
|
|
84
83
|
|
|
@@ -119,10 +118,9 @@ def login(parameters):
|
|
|
119
118
|
language = CONFIG['client.lang']
|
|
120
119
|
parameters['device_cookie'] = device_cookie.get()
|
|
121
120
|
connection = ServerProxy(hostname, port, database)
|
|
122
|
-
|
|
123
|
-
% (username, 'x' * 10, language))
|
|
121
|
+
logger.info('common.db.login(%s, %s, %s)', username, 'x' * 10, language)
|
|
124
122
|
result = connection.common.db.login(username, parameters, language)
|
|
125
|
-
|
|
123
|
+
logger.debug('%r', result)
|
|
126
124
|
_USER = result[0]
|
|
127
125
|
session = ':'.join(map(str, [username] + result))
|
|
128
126
|
if CONNECTION is not None:
|
|
@@ -137,7 +135,7 @@ def logout():
|
|
|
137
135
|
global CONNECTION, _USER
|
|
138
136
|
if CONNECTION is not None:
|
|
139
137
|
try:
|
|
140
|
-
|
|
138
|
+
logger.info('common.db.logout()')
|
|
141
139
|
with CONNECTION() as conn:
|
|
142
140
|
conn.common.db.logout()
|
|
143
141
|
except (Fault, socket.error, http.client.CannotSendRequest):
|
|
@@ -154,12 +152,12 @@ def execute(*args):
|
|
|
154
152
|
try:
|
|
155
153
|
name = '.'.join(args[:3])
|
|
156
154
|
args = args[3:]
|
|
157
|
-
|
|
155
|
+
logger.info('%s%r', name, args)
|
|
158
156
|
with CONNECTION() as conn:
|
|
159
157
|
result = getattr(conn, name)(*args)
|
|
160
158
|
except (http.client.CannotSendRequest, socket.error) as exception:
|
|
161
159
|
raise TrytonServerUnavailable(*exception.args)
|
|
162
|
-
|
|
160
|
+
logger.debug('%r', result)
|
|
163
161
|
return result
|
|
164
162
|
|
|
165
163
|
|
|
@@ -0,0 +1,46 @@
|
|
|
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
|
+
from unittest import TestCase
|
|
5
|
+
|
|
6
|
+
from tryton.common import humanize
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Humanize(TestCase):
|
|
10
|
+
"Test humanize"
|
|
11
|
+
|
|
12
|
+
def test_humanize(self):
|
|
13
|
+
"Test humanize"
|
|
14
|
+
for value, text in [
|
|
15
|
+
(0, '0'),
|
|
16
|
+
(1, '1'),
|
|
17
|
+
(5, '5'),
|
|
18
|
+
(10, '10'),
|
|
19
|
+
(50, '50'),
|
|
20
|
+
(100, '100'),
|
|
21
|
+
(1000, '1000'),
|
|
22
|
+
(1001, '1k'),
|
|
23
|
+
(1500, '1.5k'),
|
|
24
|
+
(1000000, '1000k'),
|
|
25
|
+
(1000001, '1M'),
|
|
26
|
+
(1010000, '1.01M'),
|
|
27
|
+
(10**33, '1000Q'),
|
|
28
|
+
(0.1, '0.1'),
|
|
29
|
+
(0.5, '0.5'),
|
|
30
|
+
(0.01, '0.01'),
|
|
31
|
+
(0.05, '0.05'),
|
|
32
|
+
(0.001, '1m'),
|
|
33
|
+
(0.0001, '0.1m'),
|
|
34
|
+
(0.000001, '1µ'),
|
|
35
|
+
(0.0000015, '1.5µ'),
|
|
36
|
+
(0.00000105, '1.05µ'),
|
|
37
|
+
(0.000001001, '1µ'),
|
|
38
|
+
(10**-33, '0.001q'),
|
|
39
|
+
]:
|
|
40
|
+
with self.subTest(value=value):
|
|
41
|
+
self.assertEqual(humanize(value), text)
|
|
42
|
+
if value:
|
|
43
|
+
value *= -1
|
|
44
|
+
text = '-' + text
|
|
45
|
+
with self.subTest(value=value):
|
|
46
|
+
self.assertEqual(humanize(value), text)
|
|
@@ -529,31 +529,31 @@ class DomainParserTestCase(TestCase):
|
|
|
529
529
|
double_null_ = ('e', None, None)
|
|
530
530
|
for value, result in (
|
|
531
531
|
(['a'], ['a']),
|
|
532
|
-
(['a', '
|
|
533
|
-
(['a', '
|
|
534
|
-
(['a', 'b', '
|
|
535
|
-
(['a', '
|
|
532
|
+
(['a', '|', 'b'], [['OR', 'a', 'b']]),
|
|
533
|
+
(['a', '|', 'b', '|', 'c'], [['OR', ['OR', 'a', 'b'], 'c']]),
|
|
534
|
+
(['a', 'b', '|', 'c'], ['a', ['OR', 'b', 'c']]),
|
|
535
|
+
(['a', '|', 'b', 'c'], [['OR', 'a', 'b'], 'c']),
|
|
536
536
|
(['a', iter(['b', 'c'])], ['a', ['b', 'c']]),
|
|
537
537
|
(['a', iter(['b', 'c']), 'd'], ['a', ['b', 'c'], 'd']),
|
|
538
|
-
(['a', '
|
|
539
|
-
(['a', '
|
|
538
|
+
(['a', '|', iter(['b', 'c'])], [['OR', 'a', ['b', 'c']]]),
|
|
539
|
+
(['a', '|', iter(['b', 'c']), 'd'],
|
|
540
540
|
[['OR', 'a', ['b', 'c']], 'd']),
|
|
541
|
-
(['a', iter(['b', 'c']), '
|
|
541
|
+
(['a', iter(['b', 'c']), '|', 'd'],
|
|
542
542
|
['a', ['OR', ['b', 'c'], 'd']]),
|
|
543
|
-
(['a', '
|
|
543
|
+
(['a', '|', iter(['b', '|', 'c'])],
|
|
544
544
|
[['OR', 'a', [['OR', 'b', 'c']]]]),
|
|
545
|
-
(['
|
|
546
|
-
(['
|
|
547
|
-
(['a', iter(['
|
|
548
|
-
(['a', '
|
|
549
|
-
(['
|
|
550
|
-
(['
|
|
551
|
-
(['
|
|
552
|
-
(['a', iter(['b', '
|
|
553
|
-
([a, iter([b, ('
|
|
554
|
-
(['a', iter(['b', '
|
|
545
|
+
(['|'], []),
|
|
546
|
+
(['|', 'a'], ['a']),
|
|
547
|
+
(['a', iter(['|', 'b'])], ['a', ['b']]),
|
|
548
|
+
(['a', '|', '|', 'b'], [['OR', 'a', 'b']]),
|
|
549
|
+
(['|', '|', 'a'], ['a']),
|
|
550
|
+
(['|', '|', 'a', 'b'], ['a', 'b']),
|
|
551
|
+
(['|', '|', 'a', '|', 'b'], [['OR', 'a', 'b']]),
|
|
552
|
+
(['a', iter(['b', '|', 'c'])], ['a', [['OR', 'b', 'c']]]),
|
|
553
|
+
([a, iter([b, ('|',), c])], [a, [['OR', b, c]]]),
|
|
554
|
+
(['a', iter(['b', '|'])], ['a', [['OR', 'b']]]),
|
|
555
555
|
([null_], [null_]),
|
|
556
|
-
([null_, '
|
|
556
|
+
([null_, '|', double_null_], [['OR', null_, double_null_]]),
|
|
557
557
|
):
|
|
558
558
|
self.assertEqual(
|
|
559
559
|
rlist(operatorize(iter(value))), result,
|
|
@@ -696,14 +696,14 @@ class DomainParserTestCase(TestCase):
|
|
|
696
696
|
dom.string(['OR',
|
|
697
697
|
('name', 'ilike', '%Doe%'),
|
|
698
698
|
('name', 'ilike', '%Jane%')]),
|
|
699
|
-
'Name: Doe
|
|
699
|
+
'Name: Doe | Name: Jane')
|
|
700
700
|
self.assertEqual(
|
|
701
701
|
dom.string([
|
|
702
702
|
('name', 'ilike', '%Doe%'),
|
|
703
703
|
['OR',
|
|
704
704
|
('name', 'ilike', '%John%'),
|
|
705
705
|
('name', 'ilike', '%Jane%')]]),
|
|
706
|
-
'Name: Doe (Name: John
|
|
706
|
+
'Name: Doe (Name: John | Name: Jane)')
|
|
707
707
|
self.assertEqual(dom.string([]), '')
|
|
708
708
|
self.assertEqual(
|
|
709
709
|
dom.string([('surname', 'ilike', '%Doe%')]), '"(Sur)Name": Doe')
|
|
@@ -1121,10 +1121,10 @@ class DomainParserTestCase(TestCase):
|
|
|
1121
1121
|
self.assertEqual(list(dom.completion('Name: !=foo')), [])
|
|
1122
1122
|
self.assertEqual(list(dom.completion('')), ['Name: '])
|
|
1123
1123
|
self.assertEqual(list(dom.completion(' ')), ['', 'Name: '])
|
|
1124
|
-
self.assertEqual(list(dom.completion('Name: foo
|
|
1124
|
+
self.assertEqual(list(dom.completion('Name: foo |')), ['Name: foo'])
|
|
1125
1125
|
self.assertEqual(
|
|
1126
|
-
list(dom.completion('Name: foo (Name: foo
|
|
1127
|
-
['Name: foo (Name: foo
|
|
1126
|
+
list(dom.completion('Name: foo (Name: foo | N')),
|
|
1127
|
+
['Name: foo (Name: foo | Name: '])
|
|
1128
1128
|
|
|
1129
1129
|
def test_completion_many2one(self):
|
|
1130
1130
|
"Test completion many2one"
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: tryton
|
|
3
|
-
Version: 6.
|
|
3
|
+
Version: 6.8.1
|
|
4
4
|
Summary: Tryton desktop client
|
|
5
5
|
Home-page: http://www.tryton.org/
|
|
6
|
-
Download-URL: http://downloads.tryton.org/6.
|
|
6
|
+
Download-URL: http://downloads.tryton.org/6.8/
|
|
7
7
|
Author: Tryton
|
|
8
|
-
Author-email:
|
|
8
|
+
Author-email: foundation@tryton.org
|
|
9
9
|
License: GPL-3
|
|
10
10
|
Project-URL: Bug Tracker, https://bugs.tryton.org/
|
|
11
11
|
Project-URL: Documentation, https://docs.tryton.org/
|
|
12
12
|
Project-URL: Forum, https://www.tryton.org/forum
|
|
13
|
-
Project-URL: Source Code, https://
|
|
13
|
+
Project-URL: Source Code, https://code.tryton.org/tryton
|
|
14
14
|
Keywords: business application ERP
|
|
15
15
|
Platform: any
|
|
16
16
|
Classifier: Development Status :: 5 - Production/Stable
|
|
@@ -42,12 +42,12 @@ Classifier: Natural Language :: Japanese
|
|
|
42
42
|
Classifier: Natural Language :: Ukrainian
|
|
43
43
|
Classifier: Operating System :: OS Independent
|
|
44
44
|
Classifier: Programming Language :: Python :: 3
|
|
45
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
46
45
|
Classifier: Programming Language :: Python :: 3.8
|
|
47
46
|
Classifier: Programming Language :: Python :: 3.9
|
|
48
47
|
Classifier: Programming Language :: Python :: 3.10
|
|
48
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
49
49
|
Classifier: Topic :: Office/Business
|
|
50
|
-
Requires-Python: >=3.
|
|
50
|
+
Requires-Python: >=3.8
|
|
51
51
|
License-File: LICENSE
|
|
52
52
|
Requires-Dist: pycairo
|
|
53
53
|
Requires-Dist: python-dateutil
|