mirror of
https://github.com/inventree/InvenTree.git
synced 2025-10-22 00:47:38 +00:00
Report maths tweaks (#10604)
* Maths tags updates - Allow maths operations on non-float values - Add tests for Decimal values - Add tests for Money values - Convert string values to floating point * Add modulo tag
This commit is contained in:
@@ -103,7 +103,7 @@ def filter_db_model(model_name: str, **kwargs) -> Optional[QuerySet]:
|
||||
def getindex(container: list, index: int) -> Any:
|
||||
"""Return the value contained at the specified index of the list.
|
||||
|
||||
This function is provideed to get around template rendering limitations.
|
||||
This function is provided to get around template rendering limitations.
|
||||
|
||||
Arguments:
|
||||
container: A python list object
|
||||
@@ -413,28 +413,54 @@ def internal_link(link, text) -> str:
|
||||
return mark_safe(f'<a href="{url}">{text}</a>')
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def add(x: float, y: float, *args, **kwargs) -> float:
|
||||
"""Add two numbers together."""
|
||||
return float(x) + float(y)
|
||||
def destringify(value: Any) -> Any:
|
||||
"""Convert a string value into a float.
|
||||
|
||||
- If the value is a string, attempt to convert it to a float.
|
||||
- If conversion fails, return the original string.
|
||||
- If the value is not a string, return it unchanged.
|
||||
|
||||
The purpose of this function is to provide "seamless" math operations in templates,
|
||||
where numeric values may be provided as strings, or converted to strings during template rendering.
|
||||
"""
|
||||
if isinstance(value, str):
|
||||
value = value.strip()
|
||||
try:
|
||||
return float(value)
|
||||
except ValueError:
|
||||
return value
|
||||
|
||||
return value
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def subtract(x: float, y: float) -> float:
|
||||
"""Subtract one number from another."""
|
||||
return float(x) - float(y)
|
||||
def add(x: Any, y: Any) -> Any:
|
||||
"""Add two numbers (or number like values) together."""
|
||||
return destringify(x) + destringify(y)
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def multiply(x: float, y: float) -> float:
|
||||
"""Multiply two numbers together."""
|
||||
return float(x) * float(y)
|
||||
def subtract(x: Any, y: Any) -> Any:
|
||||
"""Subtract one number (or number-like value) from another."""
|
||||
return destringify(x) - destringify(y)
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def divide(x: float, y: float) -> float:
|
||||
"""Divide one number by another."""
|
||||
return float(x) / float(y)
|
||||
def multiply(x: Any, y: Any) -> Any:
|
||||
"""Multiply two numbers (or number-like values) together."""
|
||||
return destringify(x) * destringify(y)
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def divide(x: Any, y: Any) -> Any:
|
||||
"""Divide one number (or number-like value) by another."""
|
||||
return destringify(x) / destringify(y)
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def modulo(x: Any, y: Any) -> Any:
|
||||
"""Calculate the modulo of one number (or number-like value) by another."""
|
||||
return destringify(x) % destringify(y)
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
|
@@ -203,9 +203,58 @@ class ReportTagTest(PartImageTestMixin, InvenTreeTestCase):
|
||||
self.assertEqual(report_tags.multiply('-2', 4), -8.0)
|
||||
self.assertEqual(report_tags.divide(100, 5), 20)
|
||||
|
||||
self.assertEqual(report_tags.modulo(10, 3), 1)
|
||||
self.assertEqual(report_tags.modulo('10', '4'), 2)
|
||||
|
||||
with self.assertRaises(ZeroDivisionError):
|
||||
report_tags.divide(100, 0)
|
||||
|
||||
def test_maths_tags_with_strings(self):
|
||||
"""Tests for mathematical operator tags with string inputs."""
|
||||
self.assertEqual(report_tags.add('10', '20'), 30)
|
||||
self.assertEqual(report_tags.subtract('50.5', '20.2'), 30.3)
|
||||
self.assertEqual(report_tags.multiply(3.0000000000000, '7'), 21)
|
||||
self.assertEqual(report_tags.divide('100.0', '4'), 25.0)
|
||||
|
||||
def test_maths_tags_with_decimal(self):
|
||||
"""Tests for mathematical operator tags with Decimal inputs."""
|
||||
from decimal import Decimal
|
||||
|
||||
self.assertEqual(
|
||||
report_tags.add(Decimal('1.1'), Decimal('2.2')), Decimal('3.3')
|
||||
)
|
||||
self.assertEqual(
|
||||
report_tags.subtract(Decimal('5.5'), Decimal('2.2')), Decimal('3.3')
|
||||
)
|
||||
self.assertEqual(report_tags.multiply(Decimal('3.0'), 4), Decimal('12.0'))
|
||||
self.assertEqual(
|
||||
report_tags.divide(Decimal('10.0'), Decimal('2.000')), Decimal('5.0')
|
||||
)
|
||||
|
||||
def test_maths_tags_with_money(self):
|
||||
"""Tests for mathematical operator tags with Money inputs."""
|
||||
m1 = Money(100, 'USD')
|
||||
m2 = Money(50, 'USD')
|
||||
|
||||
self.assertEqual(report_tags.add(m1, m2), Money(150, 'USD'))
|
||||
self.assertEqual(report_tags.subtract(m1, m2), Money(50, 'USD'))
|
||||
self.assertEqual(report_tags.multiply(m2, 3), Money(150, 'USD'))
|
||||
self.assertEqual(report_tags.divide(m1, '4'), Money(25, 'USD'))
|
||||
|
||||
def test_maths_tags_invalid(self):
|
||||
"""Tests for mathematical operator tags with invalid inputs."""
|
||||
with self.assertRaises(TypeError):
|
||||
report_tags.add('abc', 10)
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
report_tags.subtract(50, 'xyz')
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
report_tags.multiply('foo', 'bar')
|
||||
|
||||
with self.assertRaises(TypeError):
|
||||
report_tags.divide('100', 'baz')
|
||||
|
||||
def test_number_tags(self):
|
||||
"""Simple tests for number formatting tags."""
|
||||
fn = report_tags.format_number
|
||||
|
Reference in New Issue
Block a user