mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-03 14:45:42 +00:00 
			
		
		
		
	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:
		@@ -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.
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user