diff --git a/modules/sale_stock_quantity/sale.py b/modules/sale_stock_quantity/sale.py index f972934695..7f55756d0a 100644 --- a/modules/sale_stock_quantity/sale.py +++ b/modules/sale_stock_quantity/sale.py @@ -9,21 +9,12 @@ from operator import attrgetter from trytond.i18n import gettext from trytond.model import ModelView, Workflow, fields from trytond.pool import Pool, PoolMeta -from trytond.tools import grouped_slice +from trytond.tools import grouped_slice, sortable_values from trytond.transaction import Transaction from .exceptions import StockQuantityError, StockQuantityWarning -def sortable_values(func): - def wrapper(*args, **kwargs): - result = list(func(*args, **kwargs)) - for i, value in enumerate(list(result)): - result[i] = (value is None, value) - return result - return wrapper - - class Sale(metaclass=PoolMeta): __name__ = 'sale.sale' diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG index 57989fd0e7..e0ed15b4c6 100644 --- a/trytond/CHANGELOG +++ b/trytond/CHANGELOG @@ -1,3 +1,4 @@ +* Support list of single value in sortable_values tools * Allow resetting password of user via RPC * Support cached_property on object with __slots__ * Allow creating indexes concurrently diff --git a/trytond/doc/ref/tools/misc.rst b/trytond/doc/ref/tools/misc.rst index 4b0caad5a5..fa531aec8b 100644 --- a/trytond/doc/ref/tools/misc.rst +++ b/trytond/doc/ref/tools/misc.rst @@ -56,6 +56,10 @@ Miscellaneous Convert the string for full text if it does not contain any wild cards. +.. function:: sortable_values(func) + + Decorator that makes returned list of values sortable. + .. function:: sql_pairing(x, y) Return an SQL expression that pairs SQL integers x and y. diff --git a/trytond/trytond/tests/test_tools.py b/trytond/trytond/tests/test_tools.py index e26145ed7f..c2ae439f52 100644 --- a/trytond/trytond/tests/test_tools.py +++ b/trytond/trytond/tests/test_tools.py @@ -283,7 +283,7 @@ class ToolsTestCase(unittest.TestCase): "Test hyphenate in slugify" self.assertEqual(slugify('foo bar', hyphenate='_'), 'foo_bar') - def test_sortable_values(self): + def test_sortable_values_couple(self): def key(values): return values @@ -302,6 +302,25 @@ class ToolsTestCase(unittest.TestCase): (('a', 1), ('b', None)), ]) + def test_sortable_values_single(self): + def key(values): + return values + + values = [ + (1, None), + (1, 3), + (1, 2), + ] + + with self.assertRaises(TypeError): + sorted(values, key=key) + self.assertEqual( + sorted(values, key=sortable_values(key)), [ + (1, 2), + (1, 3), + (1, None), + ]) + def test_firstline(self): "Test firstline" for text, result in [ diff --git a/trytond/trytond/tools/misc.py b/trytond/trytond/tools/misc.py index 0529ff88c8..c0fb5cab49 100644 --- a/trytond/trytond/tools/misc.py +++ b/trytond/trytond/tools/misc.py @@ -12,9 +12,9 @@ import types import unicodedata import warnings from array import array -from collections.abc import Sized +from collections.abc import Iterable, Sized from functools import wraps -from itertools import islice, tee, zip_longest +from itertools import chain, islice, tee, zip_longest from sql import Literal from sql.conditionals import Case @@ -286,12 +286,14 @@ def slugify(value, hyphenate='-'): def sortable_values(func): - "Decorator that makes list of couple values sortable" + "Decorator that makes list of values sortable" @wraps(func) def wrapper(*args, **kwargs): result = list(func(*args, **kwargs)) - for i, (name, value) in enumerate(list(result)): - result[i] = (name, value is None, value) + for i, value in enumerate(list(result)): + if not isinstance(value, Iterable): + value = [value] + result[i] = tuple(chain((k is None, k) for k in value)) return result return wrapper