Fix for "used in" calculation (#4770)

* Simplify query filtering for determining list of parts which require a component to build

* Fix .devcontainer file

(cherry picked from commit d4bd8ea0a9)

* Catch ValueError
This commit is contained in:
Oliver 2023-05-06 00:44:49 +10:00 committed by GitHub
parent d7f75d3ab3
commit 35d04c0357
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -830,7 +830,7 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel)
self.validate_name()
if self.trackable:
for part in self.get_used_in().all():
for part in self.get_used_in():
if not part.trackable:
part.trackable = True
@ -1063,7 +1063,7 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel)
# Now, get a list of outstanding build orders which require this part
builds = BuildModels.Build.objects.filter(
part__in=self.get_used_in().all(),
part__in=self.get_used_in(),
status__in=BuildStatus.ACTIVE_CODES
)
@ -1543,7 +1543,11 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel)
"""
# Cache all *parent* parts
parents = self.get_ancestors(include_self=False)
try:
parents = self.get_ancestors(include_self=False)
except ValueError:
# If get_ancestors() fails, then this part is not saved yet
parents = []
# Case A: This part is directly specified in a BomItem (we always use this case)
query = Q(
@ -1569,50 +1573,40 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel)
return query
def get_used_in_filter(self, include_inherited=True):
"""Return a query filter for all parts that this part is used in.
There are some considerations:
a) This part may be directly specified against a BOM for a part
b) This part may be specifed in a BOM which is then inherited by another part
Note: This function returns a Q object, not an actual queryset.
The Q object is used to filter against a list of Part objects
"""
# This is pretty expensive - we need to traverse multiple variant lists!
# TODO - In the future, could this be improved somehow?
# Keep a set of Part ID values
parts = set()
# First, grab a list of all BomItem objects which "require" this part
bom_items = BomItem.objects.filter(sub_part=self)
for bom_item in bom_items:
# Add the directly referenced part
parts.add(bom_item.part)
# Traverse down the variant tree?
if include_inherited and bom_item.inherited:
part_variants = bom_item.part.get_descendants(include_self=False)
for variant in part_variants:
parts.add(variant)
# Turn into a list of valid IDs (for matching against a Part query)
part_ids = [part.pk for part in parts]
return Q(id__in=part_ids)
def get_used_in(self, include_inherited=True):
"""Return a queryset containing all parts this part is used in.
def get_used_in(self, include_inherited=True, include_substitutes=True):
"""Return a list containing all parts this part is used in.
Includes consideration of inherited BOMs
"""
return Part.objects.filter(self.get_used_in_filter(include_inherited=include_inherited))
# Grab a queryset of all BomItem objects which "require" this part
bom_items = BomItem.objects.filter(
self.get_used_in_bom_item_filter(
include_substitutes=include_substitutes
)
)
# Iterate through the returned items and construct a set of
parts = set()
for bom_item in bom_items:
if bom_item.part in parts:
continue
parts.add(bom_item.part)
# Include inherited BOMs?
if include_inherited and bom_item.inherited:
try:
descendants = bom_item.part.get_descendants(include_self=False)
except ValueError:
# This part is not saved yet
descendants = []
for variant in descendants:
parts.add(variant)
return list(parts)
@property
def has_bom(self):
@ -1642,7 +1636,7 @@ class Part(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, MPTTModel)
@property
def used_in_count(self):
"""Return the number of part BOMs that this part appears in."""
return self.get_used_in().count()
return len(self.get_used_in())
def get_bom_hash(self):
"""Return a checksum hash for the BOM for this part.