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:
@ -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)
|
||||
|
||||
|
@ -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 }},
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -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):
|
||||
"""
|
||||
|
Reference in New Issue
Block a user