From c1bbef1a4d668b0d4af5f1672cd3ef60c5b1ce00 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 29 Oct 2025 16:02:08 +1100 Subject: [PATCH] Format number fix (#10710) * Improvements for format_number func - Prevent accidental rendering in scientific notation * Add multiplier argument to format_number --- .../InvenTree/report/templatetags/report.py | 15 +++++++++++++-- src/backend/InvenTree/report/test_tags.py | 11 +++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/backend/InvenTree/report/templatetags/report.py b/src/backend/InvenTree/report/templatetags/report.py index 7a94d54915..41e6a24445 100644 --- a/src/backend/InvenTree/report/templatetags/report.py +++ b/src/backend/InvenTree/report/templatetags/report.py @@ -607,6 +607,7 @@ def render_html_text(text: str, **kwargs): def format_number( number: Union[int, float, Decimal], decimal_places: Optional[int] = None, + multiplier: Optional[Union[int, float, Decimal]] = None, integer: bool = False, leading: int = 0, separator: Optional[str] = None, @@ -616,16 +617,20 @@ def format_number( Arguments: number: The number to be formatted decimal_places: Number of decimal places to render + multiplier: Optional multiplier to apply to the number before formatting integer: Boolean, whether to render the number as an integer leading: Number of leading zeros (default = 0) separator: Character to use as a thousands separator (default = None) """ try: - number = Decimal(str(number)) + number = Decimal(str(number).strip()) except Exception: # If the number cannot be converted to a Decimal, just return the original value return str(number) + if multiplier is not None: + number *= Decimal(str(multiplier).strip()) + if integer: # Convert to integer number = Decimal(int(number)) @@ -641,7 +646,13 @@ def format_number( pass # Re-encode, and normalize again - value = Decimal(number).normalize() + # Ensure that the output never uses scientific notation + value = Decimal(number) + value = ( + value.quantize(Decimal(1)) + if value == value.to_integral() + else value.normalize() + ) if separator: value = f'{value:,}' diff --git a/src/backend/InvenTree/report/test_tags.py b/src/backend/InvenTree/report/test_tags.py index 1d06ecceba..8c07932520 100644 --- a/src/backend/InvenTree/report/test_tags.py +++ b/src/backend/InvenTree/report/test_tags.py @@ -270,6 +270,14 @@ class ReportTagTest(PartImageTestMixin, InvenTreeTestCase): """Simple tests for number formatting tags.""" fn = report_tags.format_number + self.assertEqual(fn(None), 'None') + + for i in [1, '1', '1.0000', ' 1 ']: + self.assertEqual(fn(i), '1') + + for x in ['10.000000', ' 10 ', 10.000000, 10]: + self.assertEqual(fn(x), '10') + self.assertEqual(fn(1234), '1234') self.assertEqual(fn(1234.5678, decimal_places=2), '1234.57') self.assertEqual(fn(1234.5678, decimal_places=3), '1234.568') @@ -278,6 +286,9 @@ class ReportTagTest(PartImageTestMixin, InvenTreeTestCase): fn(9988776655.4321, integer=True, separator=' '), '9 988 776 655' ) + # Test with multiplier + self.assertEqual(fn(1000, multiplier=1.5), '1500') + # Failure cases self.assertEqual(fn('abc'), 'abc') self.assertEqual(fn(1234.456, decimal_places='a'), '1234.456')