diff --git a/src/backend/InvenTree/build/serializers.py b/src/backend/InvenTree/build/serializers.py index efac948b60..2da5c2ee85 100644 --- a/src/backend/InvenTree/build/serializers.py +++ b/src/backend/InvenTree/build/serializers.py @@ -39,6 +39,7 @@ from InvenTree.serializers import ( NotesFieldMixin, enable_filter, ) +from InvenTree.tasks import offload_task from stock.generators import generate_batch_code from stock.models import StockItem, StockLocation from stock.serializers import ( @@ -51,6 +52,7 @@ from users.serializers import OwnerSerializer, UserSerializer from .models import Build, BuildItem, BuildLine from .status_codes import BuildStatus +from .tasks import consume_build_item, consume_build_line class BuildSerializer( @@ -1845,12 +1847,14 @@ class BuildConsumeSerializer(serializers.Serializer): return data + @transaction.atomic def save(self): """Perform the stock consumption step.""" data = self.validated_data request = self.context.get('request') notes = data.get('notes', '') + # We may be passed either a list of BuildItem or BuildLine instances items = data.get('items', []) lines = data.get('lines', []) @@ -1865,25 +1869,23 @@ class BuildConsumeSerializer(serializers.Serializer): # Instead, it gets consumed when the output is completed continue - build_item.complete_allocation( - quantity=quantity, + # Offload a background task to consume this BuildItem + offload_task( + consume_build_item, + build_item.pk, + quantity, notes=notes, - user=request.user if request else None, + user_id=request.user.pk if request else None, ) # Process the provided BuildLine objects for line in lines: build_line = line['build_line'] - # In this case, perform full consumption of all allocated stock - for item in build_line.allocations.all(): - # If the build item is tracked into an output, we do not consume now - # Instead, it gets consumed when the output is completed - if item.install_into: - continue - - item.complete_allocation( - quantity=item.quantity, - notes=notes, - user=request.user if request else None, - ) + # Offload a background task to consume this BuildLine + offload_task( + consume_build_line, + build_line.pk, + notes=notes, + user_id=request.user.pk if request else None, + ) diff --git a/src/backend/InvenTree/build/tasks.py b/src/backend/InvenTree/build/tasks.py index 4c6625741c..fd8e3da1de 100644 --- a/src/backend/InvenTree/build/tasks.py +++ b/src/backend/InvenTree/build/tasks.py @@ -39,6 +39,51 @@ def auto_allocate_build(build_id: int, **kwargs): build_order.auto_allocate_stock(**kwargs) +@tracer.start_as_current_span('consume_build_item') +def consume_build_item( + item_id: str, quantity, notes: str = '', user_id: int | None = None +): + """Consume stock against a particular BuildOrderLineItem allocation.""" + from build.models import BuildItem + + item = BuildItem.objects.filter(pk=item_id).first() + + if not item: + logger.warning( + 'Could not consume stock for BuildItem <%s> - BuildItem does not exist', + item_id, + ) + return + + item.complete_allocation( + quantity=quantity, + notes=notes, + user=User.objects.filter(pk=user_id).first() if user_id else None, + ) + + +@tracer.start_as_current_span('consume_build_line') +def consume_build_line(line_id: int, notes: str = '', user_id: int | None = None): + """Consume stock against a particular BuildOrderLineItem.""" + from build.models import BuildLine + + line_item = BuildLine.objects.filter(pk=line_id).first() + + if not line_item: + logger.warning( + 'Could not consume stock for LineItem <%s> - LineItem does not exist', + line_id, + ) + return + + for item in line_item.allocations.all(): + item.complete_allocation( + quantity=item.quantity, + notes=notes, + user=User.objects.filter(pk=user_id).first() if user_id else None, + ) + + @tracer.start_as_current_span('complete_build_allocations') def complete_build_allocations(build_id: int, user_id: int): """Complete build allocations for a specified BuildOrder.""" diff --git a/src/frontend/src/forms/BuildForms.tsx b/src/frontend/src/forms/BuildForms.tsx index 5d77f50f76..22706a8b50 100644 --- a/src/frontend/src/forms/BuildForms.tsx +++ b/src/frontend/src/forms/BuildForms.tsx @@ -814,7 +814,7 @@ export function useConsumeBuildItemsForm({ url: ApiEndpoints.build_order_consume, pk: buildId, title: t`Consume Stock`, - successMessage: t`Stock items consumed`, + successMessage: t`Stock items scheduled to be consumed`, onFormSuccess: onFormSuccess, size: '80%', fields: consumeFields, @@ -915,7 +915,7 @@ export function useConsumeBuildLinesForm({ url: ApiEndpoints.build_order_consume, pk: buildId, title: t`Consume Stock`, - successMessage: t`Stock items consumed`, + successMessage: t`Stock items scheduled to be consumed`, onFormSuccess: onFormSuccess, fields: consumeFields, initialData: {