mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	Build source location (#10220)
* Display build source location * Fix docstring * Enhance build availability filter - Take build source location into account - Improve pre-fetch * Enhance type annotations
This commit is contained in:
		| @@ -1519,7 +1519,10 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali | ||||
|             'allocations', | ||||
|             'allocations__stock_item', | ||||
|             'allocations__stock_item__part', | ||||
|             'allocations__stock_item__supplier_part', | ||||
|             'allocations__stock_item__supplier_part__manufacturer_part', | ||||
|             'allocations__stock_item__location', | ||||
|             'allocations__stock_item__tags', | ||||
|             'bom_item', | ||||
|             'bom_item__part', | ||||
|             'bom_item__sub_part', | ||||
| @@ -1603,6 +1606,8 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali | ||||
|                 location__rght__lte=location.rght, | ||||
|                 location__level__gte=location.level, | ||||
|             ) | ||||
|         else: | ||||
|             location = None | ||||
|  | ||||
|         # Annotate the "in_production" quantity | ||||
|         queryset = queryset.annotate( | ||||
| @@ -1623,10 +1628,10 @@ class BuildLineSerializer(DataImportExportSerializerMixin, InvenTreeModelSeriali | ||||
|                 reference=ref, filter=stock_filter | ||||
|             ), | ||||
|             allocated_to_sales_orders=part.filters.annotate_sales_order_allocations( | ||||
|                 reference=ref | ||||
|                 reference=ref, location=location | ||||
|             ), | ||||
|             allocated_to_build_orders=part.filters.annotate_build_order_allocations( | ||||
|                 reference=ref | ||||
|                 reference=ref, location=location | ||||
|             ), | ||||
|         ) | ||||
|  | ||||
|   | ||||
| @@ -42,7 +42,7 @@ from build.status_codes import BuildStatusGroups | ||||
| from order.status_codes import PurchaseOrderStatusGroups, SalesOrderStatusGroups | ||||
|  | ||||
|  | ||||
| def annotate_in_production_quantity(reference=''): | ||||
| def annotate_in_production_quantity(reference: str = '') -> QuerySet: | ||||
|     """Annotate the 'in production' quantity for each part in a queryset. | ||||
|  | ||||
|     - Sum the 'quantity' field for all stock items which are 'in production' for each part. | ||||
| @@ -63,7 +63,7 @@ def annotate_in_production_quantity(reference=''): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def annotate_scheduled_to_build_quantity(reference: str = ''): | ||||
| def annotate_scheduled_to_build_quantity(reference: str = '') -> QuerySet: | ||||
|     """Annotate the 'scheduled to build' quantity for each part in a queryset. | ||||
|  | ||||
|     - This is total scheduled quantity for all build orders which are 'active' | ||||
| @@ -91,7 +91,7 @@ def annotate_scheduled_to_build_quantity(reference: str = ''): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def annotate_on_order_quantity(reference: str = ''): | ||||
| def annotate_on_order_quantity(reference: str = '') -> QuerySet: | ||||
|     """Annotate the 'on order' quantity for each part in a queryset. | ||||
|  | ||||
|     Sum the 'remaining quantity' of each line item for any open purchase orders for each part: | ||||
| @@ -137,7 +137,7 @@ def annotate_on_order_quantity(reference: str = ''): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def annotate_total_stock(reference: str = '', filter: Q = None): | ||||
| def annotate_total_stock(reference: str = '', filter: Q = None) -> QuerySet: | ||||
|     """Annotate 'total stock' quantity against a queryset. | ||||
|  | ||||
|     - This function calculates the 'total stock' for a given part | ||||
| @@ -161,7 +161,7 @@ def annotate_total_stock(reference: str = '', filter: Q = None): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def annotate_build_order_requirements(reference: str = ''): | ||||
| def annotate_build_order_requirements(reference: str = '') -> QuerySet: | ||||
|     """Annotate the total quantity of each part required for build orders. | ||||
|  | ||||
|     - Only interested in 'active' build orders | ||||
| @@ -179,20 +179,30 @@ def annotate_build_order_requirements(reference: str = ''): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def annotate_build_order_allocations(reference: str = ''): | ||||
| def annotate_build_order_allocations(reference: str = '', location=None) -> QuerySet: | ||||
|     """Annotate the total quantity of each part allocated to build orders. | ||||
|  | ||||
|     - This function calculates the total part quantity allocated to open build orders | ||||
|     - Finds all build order allocations for each part (using the provided filter) | ||||
|     - Aggregates the 'allocated quantity' for each relevant build order allocation item | ||||
|  | ||||
|     Args: | ||||
|     Arguments: | ||||
|         reference: The relationship reference of the part from the current model | ||||
|         build_filter: Q object which defines how to filter the allocation items | ||||
|         location: If provided, only allocated stock items from this location are considered | ||||
|     """ | ||||
|     # Build filter only returns 'active' build orders | ||||
|     build_filter = Q(build_line__build__status__in=BuildStatusGroups.ACTIVE_CODES) | ||||
|  | ||||
|     if location is not None: | ||||
|         # Filter by location (including any child locations) | ||||
|  | ||||
|         build_filter &= Q( | ||||
|             stock_item__location__tree_id=location.tree_id, | ||||
|             stock_item__location__lft__gte=location.lft, | ||||
|             stock_item__location__rght__lte=location.rght, | ||||
|             stock_item__location__level__gte=location.level, | ||||
|         ) | ||||
|  | ||||
|     return Coalesce( | ||||
|         SubquerySum( | ||||
|             f'{reference}stock_items__allocations__quantity', filter=build_filter | ||||
| @@ -202,7 +212,7 @@ def annotate_build_order_allocations(reference: str = ''): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def annotate_sales_order_requirements(reference: str = ''): | ||||
| def annotate_sales_order_requirements(reference: str = '') -> QuerySet: | ||||
|     """Annotate the total quantity of each part required for sales orders. | ||||
|  | ||||
|     - Only interested in 'active' sales orders | ||||
| @@ -222,16 +232,16 @@ def annotate_sales_order_requirements(reference: str = ''): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def annotate_sales_order_allocations(reference: str = ''): | ||||
| def annotate_sales_order_allocations(reference: str = '', location=None) -> QuerySet: | ||||
|     """Annotate the total quantity of each part allocated to sales orders. | ||||
|  | ||||
|     - This function calculates the total part quantity allocated to open sales orders" | ||||
|     - Finds all sales order allocations for each part (using the provided filter) | ||||
|     - Aggregates the 'allocated quantity' for each relevant sales order allocation item | ||||
|  | ||||
|     Args: | ||||
|     Arguments: | ||||
|         reference: The relationship reference of the part from the current model | ||||
|         order_filter: Q object which defines how to filter the allocation items | ||||
|         location: If provided, only allocated stock items from this location are considered | ||||
|     """ | ||||
|     # Order filter only returns incomplete shipments for open orders | ||||
|     order_filter = Q( | ||||
| @@ -239,6 +249,16 @@ def annotate_sales_order_allocations(reference: str = ''): | ||||
|         shipment__shipment_date=None, | ||||
|     ) | ||||
|  | ||||
|     if location is not None: | ||||
|         # Filter by location (including any child locations) | ||||
|  | ||||
|         order_filter &= Q( | ||||
|             item__location__tree_id=location.tree_id, | ||||
|             item__location__lft__gte=location.lft, | ||||
|             item__location__rght__lte=location.rght, | ||||
|             item__location__level__gte=location.level, | ||||
|         ) | ||||
|  | ||||
|     return Coalesce( | ||||
|         SubquerySum( | ||||
|             f'{reference}stock_items__sales_order_allocations__quantity', | ||||
| @@ -249,7 +269,7 @@ def annotate_sales_order_allocations(reference: str = ''): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def variant_stock_query(reference: str = '', filter: Q = None): | ||||
| def variant_stock_query(reference: str = '', filter: Q = None) -> QuerySet: | ||||
|     """Create a queryset to retrieve all stock items for variant parts under the specified part. | ||||
|  | ||||
|     - Useful for annotating a queryset with aggregated information about variant parts | ||||
| @@ -270,7 +290,7 @@ def variant_stock_query(reference: str = '', filter: Q = None): | ||||
|     ).filter(stock_filter) | ||||
|  | ||||
|  | ||||
| def annotate_variant_quantity(subquery: Q, reference: str = 'quantity'): | ||||
| def annotate_variant_quantity(subquery: Q, reference: str = 'quantity') -> QuerySet: | ||||
|     """Create a subquery annotation for all variant part stock items on the given parent query. | ||||
|  | ||||
|     Args: | ||||
| @@ -290,7 +310,7 @@ def annotate_variant_quantity(subquery: Q, reference: str = 'quantity'): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def annotate_category_parts(): | ||||
| def annotate_category_parts() -> QuerySet: | ||||
|     """Construct a queryset annotation which returns the number of parts in a particular category. | ||||
|  | ||||
|     - Includes parts in subcategories also | ||||
| @@ -317,7 +337,7 @@ def annotate_category_parts(): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def annotate_default_location(reference=''): | ||||
| def annotate_default_location(reference: str = '') -> QuerySet: | ||||
|     """Construct a queryset that finds the closest default location in the part's category tree. | ||||
|  | ||||
|     If the part's category has its own default_location, this is returned. | ||||
| @@ -340,7 +360,7 @@ def annotate_default_location(reference=''): | ||||
|     ) | ||||
|  | ||||
|  | ||||
| def annotate_sub_categories(): | ||||
| def annotate_sub_categories() -> QuerySet: | ||||
|     """Construct a queryset annotation which returns the number of subcategories for each provided category.""" | ||||
|     subquery = part.models.PartCategory.objects.filter( | ||||
|         tree_id=OuterRef('tree_id'), | ||||
| @@ -504,7 +524,9 @@ def annotate_bom_item_can_build(queryset: QuerySet, reference: str = '') -> Quer | ||||
| PARAMETER_FILTER_OPERATORS: list[str] = ['gt', 'gte', 'lt', 'lte', 'ne', 'icontains'] | ||||
|  | ||||
|  | ||||
| def filter_by_parameter(queryset, template_id: int, value: str, func: str = ''): | ||||
| def filter_by_parameter( | ||||
|     queryset: QuerySet, template_id: int, value: str, func: str = '' | ||||
| ) -> QuerySet: | ||||
|     """Filter the given queryset by a given template parameter. | ||||
|  | ||||
|     Parts which do not have a value for the given parameter are excluded. | ||||
| @@ -598,7 +620,9 @@ def filter_by_parameter(queryset, template_id: int, value: str, func: str = ''): | ||||
|         return queryset.filter(q).distinct() | ||||
|  | ||||
|  | ||||
| def order_by_parameter(queryset, template_id: int, ascending=True): | ||||
| def order_by_parameter( | ||||
|     queryset: QuerySet, template_id: int, ascending: bool = True | ||||
| ) -> QuerySet: | ||||
|     """Order the given queryset by a given template parameter. | ||||
|  | ||||
|     Parts which do not have a value for the given parameter are ordered last. | ||||
|   | ||||
		Reference in New Issue
	
	Block a user