2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-29 20:16:44 +00:00

Improved unit testing for BomItem

- tests for allowing variant parts
- tests for allowing substitutes
This commit is contained in:
Oliver 2021-10-13 23:18:26 +11:00
parent 0f8c279aa2
commit f3074e8f34
2 changed files with 263 additions and 54 deletions

View File

@ -2337,18 +2337,29 @@ class BomItem(models.Model):
""" """
Return a queryset filter for selecting StockItems which match this BomItem Return a queryset filter for selecting StockItems which match this BomItem
- Allow stock from all directly specified substitute parts
- If allow_variants is True, allow all part variants - If allow_variants is True, allow all part variants
""" """
# Target part # List of parts we allow
part = self.sub_part part_ids = set()
part_ids.add(self.sub_part.pk)
# Variant parts (if allowed)
if self.allow_variants: if self.allow_variants:
variants = part.get_descendants(include_self=True) variants = self.sub_part.get_descendants(include_self=False)
return Q(part__in=[v.pk for v in variants])
else: for v in variants:
return Q(part=part) part_ids.add(v.pk)
# Direct substitute parts
for sub in self.substitutes.all():
part_ids.add(sub.part.pk)
# Return a list of Part ID values which can be filtered against
return Q(part__in=[pk for pk in part_ids])
def save(self, *args, **kwargs): def save(self, *args, **kwargs):

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals
import PIL import PIL
@ -11,7 +12,8 @@ from InvenTree.api_tester import InvenTreeAPITestCase
from InvenTree.status_codes import StockStatus from InvenTree.status_codes import StockStatus
from part.models import Part, PartCategory from part.models import Part, PartCategory
from stock.models import StockItem from part.models import BomItem, BomItemSubstitute
from stock.models import StockItem, StockLocation
from company.models import Company from company.models import Company
from common.models import InvenTreeSetting from common.models import InvenTreeSetting
@ -273,53 +275,6 @@ class PartAPITest(InvenTreeAPITestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(len(response.data), 3) self.assertEqual(len(response.data), 3)
def test_get_bom_list(self):
""" There should be 4 BomItem objects in the database """
url = reverse('api-bom-list')
response = self.client.get(url, format='json')
self.assertEqual(len(response.data), 5)
def test_get_bom_detail(self):
# Get the detail for a single BomItem
url = reverse('api-bom-item-detail', kwargs={'pk': 3})
response = self.client.get(url, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(int(float(response.data['quantity'])), 25)
# Increase the quantity
data = response.data
data['quantity'] = 57
data['note'] = 'Added a note'
response = self.client.patch(url, data, format='json')
# Check that the quantity was increased and a note added
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(int(float(response.data['quantity'])), 57)
self.assertEqual(response.data['note'], 'Added a note')
def test_add_bom_item(self):
url = reverse('api-bom-list')
data = {
'part': 100,
'sub_part': 4,
'quantity': 777,
}
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
# Now try to create a BomItem which points to a non-assembly part (should fail)
data['part'] = 3
response = self.client.post(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
# TODO - Now try to create a BomItem which references itself
data['part'] = 2
data['sub_part'] = 2
response = self.client.post(url, data, format='json')
def test_test_templates(self): def test_test_templates(self):
url = reverse('api-part-test-template-list') url = reverse('api-part-test-template-list')
@ -926,6 +881,249 @@ class PartAPIAggregationTest(InvenTreeAPITestCase):
self.assertEqual(data['stock_item_count'], 105) self.assertEqual(data['stock_item_count'], 105)
class BomItemTest(InvenTreeAPITestCase):
"""
Unit tests for the BomItem API
"""
fixtures = [
'category',
'part',
'location',
'stock',
'bom',
'company',
]
roles = [
'part.add',
'part.change',
'part.delete',
]
def setUp(self):
super().setUp()
def test_bom_list(self):
"""
Tests for the BomItem list endpoint
"""
# How many BOM items currently exist in the database?
n = BomItem.objects.count()
url = reverse('api-bom-list')
response = self.get(url, expected_code=200)
self.assertEqual(len(response.data), n)
# Now, filter by part
response = self.get(
url,
data={
'part': 100,
},
expected_code=200
)
print("results:", len(response.data))
def test_get_bom_detail(self):
"""
Get the detail view for a single BomItem object
"""
url = reverse('api-bom-item-detail', kwargs={'pk': 3})
response = self.get(url, expected_code=200)
self.assertEqual(int(float(response.data['quantity'])), 25)
# Increase the quantity
data = response.data
data['quantity'] = 57
data['note'] = 'Added a note'
response = self.patch(url, data, expected_code=200)
self.assertEqual(int(float(response.data['quantity'])), 57)
self.assertEqual(response.data['note'], 'Added a note')
def test_add_bom_item(self):
"""
Test that we can create a new BomItem via the API
"""
url = reverse('api-bom-list')
data = {
'part': 100,
'sub_part': 4,
'quantity': 777,
}
self.post(url, data, expected_code=201)
# Now try to create a BomItem which references itself
data['part'] = 100
data['sub_part'] = 100
self.client.post(url, data, expected_code=400)
def test_variants(self):
"""
Tests for BomItem use with variants
"""
stock_url = reverse('api-stock-list')
# BOM item we are interested in
bom_item = BomItem.objects.get(pk=1)
bom_item.allow_variants = True
bom_item.save()
# sub part that the BOM item points to
sub_part = bom_item.sub_part
sub_part.is_template = True
sub_part.save()
# How many stock items are initially available for this part?
response = self.get(
stock_url,
{
'bom_item': bom_item.pk,
},
expected_code=200
)
n_items = len(response.data)
self.assertEqual(n_items, 2)
loc = StockLocation.objects.get(pk=1)
# Now we will create some variant parts and stock
for ii in range(5):
# Create a variant part!
variant = Part.objects.create(
name=f"Variant_{ii}",
description="A variant part",
component=True,
variant_of=sub_part
)
variant.save()
Part.objects.rebuild()
# Create some stock items for this new part
for jj in range(ii):
StockItem.objects.create(
part=variant,
location=loc,
quantity=100
)
# Keep track of running total
n_items += jj
# Now, there should be more stock items available!
response = self.get(
stock_url,
{
'bom_item': bom_item.pk,
},
expected_code=200
)
self.assertEqual(len(response.data), n_items)
# Now, disallow variant parts in the BomItem
bom_item.allow_variants = False
bom_item.save()
# There should now only be 2 stock items available again
response = self.get(
stock_url,
{
'bom_item': bom_item.pk,
},
expected_code=200
)
self.assertEqual(len(response.data), 2)
def test_substitutes(self):
"""
Tests for BomItem substitutes
"""
url = reverse('api-bom-substitute-list')
stock_url = reverse('api-stock-list')
# Initially we have no substitute parts
response = self.get(url, expected_code=200)
self.assertEqual(len(response.data), 0)
# BOM item we are interested in
bom_item = BomItem.objects.get(pk=1)
# Filter stock items which can be assigned against this stock item
response = self.get(
stock_url,
{
"bom_item": bom_item.pk,
},
expected_code=200
)
n_items = len(response.data)
loc = StockLocation.objects.get(pk=1)
# Let's make some!
for ii in range(5):
sub_part = Part.objects.create(
name=f"Substitute {ii}",
description="A substitute part",
component=True,
is_template=False,
assembly=False
)
# Create a new StockItem for this Part
StockItem.objects.create(
part=sub_part,
quantity=1000,
location=loc,
)
# Now, create an "alternative" for the BOM Item
BomItemSubstitute.objects.create(
bom_item=bom_item,
part=sub_part
)
# We should be able to filter the API list to just return this new part
response = self.get(url, data={'part': sub_part.pk}, expected_code=200)
self.assertEqual(len(response.data), 1)
# We should also have more stock available to allocate against this BOM item!
response = self.get(
stock_url,
{
"bom_item": bom_item.pk,
},
expected_code=200
)
self.assertEqual(len(response.data), n_items + ii + 1)
# There should now be 5 substitute parts available in the database
response = self.get(url, expected_code=200)
self.assertEqual(len(response.data), 5)
class PartParameterTest(InvenTreeAPITestCase): class PartParameterTest(InvenTreeAPITestCase):
""" """
Tests for the ParParameter API Tests for the ParParameter API