mirror of
https://github.com/inventree/InvenTree.git
synced 2026-11-14 12:32:44 +00:00
Fix 'get_required_quantity' method (#13002)
- Ensure a Decimal is returned in all cases - Fix type hinting - Updated unit tests
This commit is contained in:
1 parent
788b291a21
commit
c3917c343b
2 files changed
+25
-4
No files matched your search
@@ -4316,11 +4316,11 @@ class BomItem(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel):
|
||||
|
||||
return int(Decimal(available_stock) / n)
|
||||
|
||||
def get_required_quantity(self, build_quantity: float) -> float:
|
||||
def get_required_quantity(self, build_quantity: Decimal | float | int) -> Decimal:
|
||||
"""Calculate the required part quantity, based on the supplied build_quantity.
|
||||
|
||||
Arguments:
|
||||
build_quantity: Number of assemblies to build
|
||||
build_quantity: Number of assemblies to build (converted to Decimal)
|
||||
|
||||
Returns:
|
||||
Production quantity required for this component
|
||||
@@ -4330,6 +4330,10 @@ class BomItem(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel):
|
||||
and piece_count indicates how many pieces are needed.
|
||||
Total material = quantity x piece_count x build_quantity.
|
||||
"""
|
||||
# Convert via str to avoid binary float artefacts (e.g. 0.1 -> 0.1000000000000000055...)
|
||||
if not isinstance(build_quantity, Decimal):
|
||||
build_quantity = Decimal(str(build_quantity))
|
||||
|
||||
# Base quantity requirement (quantity is per-piece, piece_count is number of pieces)
|
||||
required = self.quantity * self.piece_count * build_quantity
|
||||
|
||||
@@ -4356,8 +4360,7 @@ class BomItem(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel):
|
||||
try:
|
||||
round_up = Decimal(self.rounding_multiple)
|
||||
value = Decimal(required)
|
||||
value = math.ceil(value / round_up) * round_up
|
||||
required = float(value)
|
||||
required = math.ceil(value / round_up) * round_up
|
||||
except InvalidOperation:
|
||||
log_error('bom_item.get_required_quantity')
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Unit tests for the BomItem model."""
|
||||
|
||||
import hashlib
|
||||
from decimal import Decimal
|
||||
from unittest import mock
|
||||
|
||||
import django.core.exceptions as django_exceptions
|
||||
@@ -112,6 +113,20 @@ class BomItemTest(TestCase):
|
||||
self.assertEqual(item.get_required_quantity(1), 8) # 3 + 5 = 8
|
||||
self.assertEqual(item.get_required_quantity(10), 35) # 30 + 5 = 35
|
||||
|
||||
def test_required_quantity_input_types(self):
|
||||
"""Test that get_required_quantity accepts int, float and Decimal inputs."""
|
||||
item = BomItem.objects.get(pk=4)
|
||||
|
||||
for value, expected in [
|
||||
(10, Decimal('30')),
|
||||
(Decimal('2.5'), Decimal('7.5')),
|
||||
(2.5, Decimal('7.5')),
|
||||
(0.1, Decimal('0.3')),
|
||||
]:
|
||||
result = item.get_required_quantity(value)
|
||||
self.assertIsInstance(result, Decimal)
|
||||
self.assertEqual(result, expected)
|
||||
|
||||
def test_round_up(self):
|
||||
"""Test the 'rounding_multiple' attribute."""
|
||||
item = BomItem.objects.get(pk=4)
|
||||
@@ -143,6 +158,9 @@ class BomItemTest(TestCase):
|
||||
item.get_required_quantity(100), 306
|
||||
) # 3 x 100 = 300, rounded up to nearest multiple of 17
|
||||
|
||||
# The rounded value must remain a Decimal (not a float)
|
||||
self.assertIsInstance(item.get_required_quantity(10), Decimal)
|
||||
|
||||
# Next, let's create a new Build order
|
||||
bo = build.models.Build.objects.create(
|
||||
part=item.part, quantity=21, reference='BO-9999', title='Test Build Order'
|
||||
|
||||
Reference in new issue
Block a user