mirror of
https://github.com/inventree/InvenTree.git
synced 2025-11-30 01:10:00 +00:00
Offload stock consume operations (#10856)
- These can be expensive if there are a lot of allocated items - Offload to the background worker
This commit is contained in:
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user