diff --git a/trytond/CHANGELOG b/trytond/CHANGELOG index f3c1d1e405..e86346a935 100644 --- a/trytond/CHANGELOG +++ b/trytond/CHANGELOG @@ -1,3 +1,4 @@ +* Use no breaking space to format number and date * Fallback to code to format currency without symbol * Add sort method to order like ModelStorage.search * Do not alter argument values of with_transaction decorator (#12108) diff --git a/trytond/trytond/ir/lang.py b/trytond/trytond/ir/lang.py index bc4d9171ac..a7c4a76fc3 100644 --- a/trytond/trytond/ir/lang.py +++ b/trytond/trytond/ir/lang.py @@ -38,6 +38,9 @@ class DeleteDefaultError(UserError): pass +NO_BREAKING_SPACE = '\u00A0' + + class Lang(DeactivableMixin, ModelSQL, ModelView): "Language" __name__ = "ir.lang" @@ -473,7 +476,7 @@ class Lang(DeactivableMixin, ModelSQL, ModelView): formatted, seps = self._group(formatted, monetary=monetary) if seps: formatted = _strip_padding(formatted, seps) - return formatted + return formatted.replace(' ', NO_BREAKING_SPACE) def currency( self, val, currency, symbol=True, grouping=False, digits=None): @@ -528,7 +531,9 @@ class Lang(DeactivableMixin, ModelSQL, ModelView): # this should be the most fitting sign position s = sign + s - return s.replace('<', '').replace('>', '') + return ( + s.replace('<', '').replace('>', '') + .replace(' ', NO_BREAKING_SPACE)) def strftime(self, value, format=None): ''' @@ -561,7 +566,7 @@ class Lang(DeactivableMixin, ModelSQL, ModelView): else: p = self.pm or 'PM' format = format.replace('%p', p) - return value.strftime(format) + return value.strftime(format).replace(' ', NO_BREAKING_SPACE) def format_number(self, value, digits=None, grouping=True, monetary=None): if digits is None: @@ -574,14 +579,13 @@ class Lang(DeactivableMixin, ModelSQL, ModelView): def format_number_symbol(self, value, symbol, digits=None, grouping=True): symbol, position = symbol.get_symbol(value) - separated = ( - value < 0 and self.n_sep_by_space or self.p_sep_by_space) + separated = True s = self.format_number(value, digits, grouping) if position: s = s + (separated and ' ' or '') + symbol else: s = symbol + (separated and ' ' or '') + s - return s + return s.replace(' ', NO_BREAKING_SPACE) class LangConfigStart(ModelView): diff --git a/trytond/trytond/report/report.py b/trytond/trytond/report/report.py index d6e78dcade..aafe26f59a 100644 --- a/trytond/trytond/report/report.py +++ b/trytond/trytond/report/report.py @@ -101,6 +101,8 @@ TIMEDELTA_DEFAULT_CONVERTER['w'] = TIMEDELTA_DEFAULT_CONVERTER['d'] * 7 TIMEDELTA_DEFAULT_CONVERTER['M'] = TIMEDELTA_DEFAULT_CONVERTER['d'] * 30 TIMEDELTA_DEFAULT_CONVERTER['Y'] = TIMEDELTA_DEFAULT_CONVERTER['d'] * 365 +NO_BREAKING_SPACE = '\u00A0' + # For most OS maximum filename is 255 but Excel has a limitation which include # the path of 218. # As on Windows report is most likely to be open from @@ -483,7 +485,7 @@ class Report(URLMixin, PoolBase): # Add space if no time text += ' ' text += ('%.6f' % value)[1:] - return text + return text.replace(' ', NO_BREAKING_SPACE) @classmethod def format_currency( diff --git a/trytond/trytond/tests/test_ir.py b/trytond/trytond/tests/test_ir.py index 4d0087946b..42150e810a 100644 --- a/trytond/trytond/tests/test_ir.py +++ b/trytond/trytond/tests/test_ir.py @@ -153,8 +153,8 @@ class IrTestCase(ModuleTestCase): currency.symbol = None currency.code = 'USD' test_data = [ - (Decimal('10.50'), True, False, None, 'USD 10.50'), - (Decimal('10.50'), True, False, 4, 'USD 10.5000'), + (Decimal('10.50'), True, False, None, 'USD 10.50'), + (Decimal('10.50'), True, False, 4, 'USD 10.5000'), ] for value, symbol, grouping, digits, result in test_data: self.assertEqual( @@ -181,17 +181,17 @@ class IrTestCase(ModuleTestCase): Lang = pool.get('ir.lang') lang = Lang.get('en') test_data = [ - (datetime.date(2016, 8, 3), '%d %B %Y', "03 August 2016"), - (datetime.time(8, 20), '%I:%M %p', "08:20 AM"), + (datetime.date(2016, 8, 3), '%d %B %Y', "03 August 2016"), + (datetime.time(8, 20), '%I:%M %p', "08:20 AM"), (datetime.datetime(2018, 11, 1, 14, 30), '%a %d %b %Y %I:%M %p', - "Thu 01 Nov 2018 02:30 PM"), + "Thu 01 Nov 2018 02:30 PM"), (datetime.date(2018, 11, 1), '%x', "11/01/2018"), (datetime.datetime(2018, 11, 1, 14, 30, 12), - '%x %X', "11/01/2018 14:30:12"), + '%x %X', "11/01/2018 14:30:12"), (datetime.datetime(2018, 11, 1, 14, 30, 12), '%H:%M:%S', "14:30:12"), (datetime.datetime(2018, 11, 1, 14, 30, 12), None, - "11/01/2018 14:30:12"), + "11/01/2018 14:30:12"), ] for date, format_, result in test_data: self.assertEqual(lang.strftime(date, format_), result) @@ -222,8 +222,8 @@ class IrTestCase(ModuleTestCase): unit.get_symbol = Mock() unit.get_symbol.return_value = 'Kg', 1 test_data = [ - (Decimal('10.50'), False, None, '10.50Kg'), - (Decimal('1000.50'), True, 4, '1,000.5000Kg'), + (Decimal('10.50'), False, None, '10.50 Kg'), + (Decimal('1000.50'), True, 4, '1,000.5000 Kg'), ] for value, grouping, digits, result in test_data: self.assertEqual( diff --git a/trytond/trytond/tests/test_report.py b/trytond/trytond/tests/test_report.py index d76068576c..e866df1c2e 100644 --- a/trytond/trytond/tests/test_report.py +++ b/trytond/trytond/tests/test_report.py @@ -19,7 +19,7 @@ class ReportTestCase(unittest.TestCase): "Test format datetime" self.assertEqual(Report.format_datetime( datetime.datetime(2020, 7, 8, 13, 30, 00)), - '07/08/2020 13:30:00') + '07/08/2020 13:30:00') @with_transaction() def test_format_datetime_custom_format(self): @@ -27,7 +27,7 @@ class ReportTestCase(unittest.TestCase): self.assertEqual(Report.format_datetime( datetime.datetime(2020, 7, 8, 13, 30, 00), format='%d %b %Y %I:%M %p'), - "08 Jul 2020 01:30 PM"), + "08 Jul 2020 01:30 PM"), def create_test_format_timedelta(i, in_, out): @@ -42,20 +42,20 @@ def create_test_format_timedelta(i, in_, out): for i, (in_, out) in enumerate([ (None, ''), (datetime.timedelta(), '00:00'), - (datetime.timedelta(days=3, hours=5, minutes=30), '3d 05:30'), - (datetime.timedelta(weeks=48), '11M 6d'), - (datetime.timedelta(weeks=50), '11M 2w 6d'), - (datetime.timedelta(weeks=52), '12M 4d'), + (datetime.timedelta(days=3, hours=5, minutes=30), '3d 05:30'), + (datetime.timedelta(weeks=48), '11M 6d'), + (datetime.timedelta(weeks=50), '11M 2w 6d'), + (datetime.timedelta(weeks=52), '12M 4d'), (datetime.timedelta(days=360), '12M'), - (datetime.timedelta(days=364), '12M 4d'), + (datetime.timedelta(days=364), '12M 4d'), (datetime.timedelta(days=365), '1Y'), - (datetime.timedelta(days=366), '1Y 1d'), + (datetime.timedelta(days=366), '1Y 1d'), (datetime.timedelta(hours=2, minutes=5, seconds=10), '02:05:10'), (datetime.timedelta(minutes=15, microseconds=42), '00:15:00.000042'), - (datetime.timedelta(days=1, microseconds=42), '1d .000042'), + (datetime.timedelta(days=1, microseconds=42), '1d .000042'), (datetime.timedelta(seconds=-1), '-00:00:01'), - (datetime.timedelta(days=-1, hours=-5, minutes=-30), '-1d 05:30'), + (datetime.timedelta(days=-1, hours=-5, minutes=-30), '-1d 05:30'), ]): test_method = create_test_format_timedelta(i, in_, out) setattr(ReportTestCase, test_method.__name__, test_method)