2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-19 13:35:40 +00:00

Merge remote-tracking branch 'inventree/master' into partial-shipment

# Conflicts:
#	InvenTree/InvenTree/version.py
This commit is contained in:
Oliver
2021-11-16 17:06:38 +11:00
11 changed files with 332 additions and 49 deletions

View File

@ -832,18 +832,6 @@ class PartList(generics.ListCreateAPIView):
queryset = super().filter_queryset(queryset)
# Filter by "uses" query - Limit to parts which use the provided part
uses = params.get('uses', None)
if uses:
try:
uses = Part.objects.get(pk=uses)
queryset = queryset.filter(uses.get_used_in_filter())
except (ValueError, Part.DoesNotExist):
pass
# Exclude specific part ID values?
exclude_id = []
@ -1040,13 +1028,19 @@ class PartParameterTemplateList(generics.ListCreateAPIView):
serializer_class = part_serializers.PartParameterTemplateSerializer
filter_backends = [
DjangoFilterBackend,
filters.OrderingFilter,
filters.SearchFilter,
]
filter_fields = [
'name',
]
search_fields = [
'name',
]
class PartParameterList(generics.ListCreateAPIView):
""" API endpoint for accessing a list of PartParameter objects
@ -1211,6 +1205,54 @@ class BomList(generics.ListCreateAPIView):
except (ValueError, Part.DoesNotExist):
pass
"""
Filter by 'uses'?
Here we pass a part ID and return BOM items for any assemblies which "use" (or "require") that part.
There are multiple ways that an assembly can "use" a sub-part:
A) Directly specifying the sub_part in a BomItem field
B) Specifing a "template" part with inherited=True
C) Allowing variant parts to be substituted
D) Allowing direct substitute parts to be specified
- BOM items which are "inherited" by parts which are variants of the master BomItem
"""
uses = params.get('uses', None)
if uses is not None:
try:
# Extract the part we are interested in
uses_part = Part.objects.get(pk=uses)
# Construct the database query in multiple parts
# A) Direct specification of sub_part
q_A = Q(sub_part=uses_part)
# B) BomItem is inherited and points to a "parent" of this part
parents = uses_part.get_ancestors(include_self=False)
q_B = Q(
inherited=True,
sub_part__in=parents
)
# C) Substitution of variant parts
# TODO
# D) Specification of individual substitutes
# TODO
q = q_A | q_B
queryset = queryset.filter(q)
except (ValueError, Part.DoesNotExist):
pass
if self.include_pricing():
queryset = self.annotate_pricing(queryset)

View File

@ -388,9 +388,7 @@
{% if part.variant_of %}
<li><a class='dropdown-item' href='#' id='bom-duplicate'><span class='fas fa-clone'></span> {% trans "Copy BOM" %}</a></li>
{% endif %}
{% if not part.is_bom_valid %}
<li><a class='dropdown-item' href='#' id='validate-bom'><span class='fas fa-clipboard-check icon-green'></span> {% trans "Validate BOM" %}</a></li>
{% endif %}
</ul>
</div>
@ -649,14 +647,10 @@
// Load the "used in" tab
onPanelLoad("used-in", function() {
loadPartTable('#used-table',
'{% url "api-part-list" %}',
{
params: {
uses: {{ part.pk }},
},
filterTarget: '#filter-list-usedin',
}
loadUsedInTable(
'#used-table',
{{ part.pk }},
);
});

View File

@ -1123,6 +1123,59 @@ class BomItemTest(InvenTreeAPITestCase):
response = self.get(url, expected_code=200)
self.assertEqual(len(response.data), 5)
def test_bom_item_uses(self):
"""
Tests for the 'uses' field
"""
url = reverse('api-bom-list')
# Test that the direct 'sub_part' association works
assemblies = []
for i in range(5):
assy = Part.objects.create(
name=f"Assy_{i}",
description="An assembly made of other parts",
active=True,
assembly=True
)
assemblies.append(assy)
components = []
# Create some sub-components
for i in range(5):
cmp = Part.objects.create(
name=f"Component_{i}",
description="A sub component",
active=True,
component=True
)
for j in range(i):
# Create a BOM item
BomItem.objects.create(
quantity=10,
part=assemblies[j],
sub_part=cmp,
)
components.append(cmp)
response = self.get(
url,
{
'uses': cmp.pk,
},
expected_code=200,
)
self.assertEqual(len(response.data), i)
class PartParameterTest(InvenTreeAPITestCase):
"""