mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 21:25:42 +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