mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
[Bug] Fix for create_child_builds (#8399)
* Fix for create_child_builds - Account for concurrency between multiple worker processes - Ensure db commits are atomic - Add random delays between build creation * Check for existing build order * Initially force task off to background worker * Revert force_async change
This commit is contained in:
parent
feefa60a8f
commit
913a05cf45
@ -180,6 +180,7 @@ class BuildSerializer(NotesFieldMixin, DataImportExportSerializerMixin, InvenTre
|
||||
|
||||
return reference
|
||||
|
||||
@transaction.atomic
|
||||
def create(self, validated_data):
|
||||
"""Save the Build object."""
|
||||
|
||||
@ -192,7 +193,7 @@ class BuildSerializer(NotesFieldMixin, DataImportExportSerializerMixin, InvenTre
|
||||
InvenTree.tasks.offload_task(
|
||||
build.tasks.create_child_builds,
|
||||
build_order.pk,
|
||||
group='build',
|
||||
group='build'
|
||||
)
|
||||
|
||||
return build_order
|
||||
|
@ -1,11 +1,15 @@
|
||||
"""Background task definitions for the BuildOrder app."""
|
||||
|
||||
import logging
|
||||
import random
|
||||
import time
|
||||
|
||||
from datetime import timedelta
|
||||
from decimal import Decimal
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.template.loader import render_to_string
|
||||
from django.db import transaction
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from allauth.account.models import EmailAddress
|
||||
@ -198,27 +202,49 @@ def create_child_builds(build_id: int) -> None:
|
||||
|
||||
assembly_items = build_order.part.get_bom_items().filter(sub_part__assembly=True)
|
||||
|
||||
for item in assembly_items:
|
||||
quantity = item.quantity * build_order.quantity
|
||||
# Random delay, to reduce likelihood of race conditions from multiple build orders being created simultaneously
|
||||
time.sleep(random.random())
|
||||
|
||||
sub_order = build_models.Build.objects.create(
|
||||
part=item.sub_part,
|
||||
quantity=quantity,
|
||||
title=build_order.title,
|
||||
batch=build_order.batch,
|
||||
parent=build_order,
|
||||
target_date=build_order.target_date,
|
||||
sales_order=build_order.sales_order,
|
||||
issued_by=build_order.issued_by,
|
||||
responsible=build_order.responsible,
|
||||
)
|
||||
with transaction.atomic():
|
||||
# Atomic transaction to ensure that all child build orders are created together, or not at all
|
||||
# This is critical to prevent duplicate child build orders being created (e.g. if the task is re-run)
|
||||
|
||||
# Offload the child build order creation to the background task queue
|
||||
InvenTree.tasks.offload_task(
|
||||
create_child_builds,
|
||||
sub_order.pk,
|
||||
group='build'
|
||||
)
|
||||
sub_build_ids = []
|
||||
|
||||
for item in assembly_items:
|
||||
quantity = item.quantity * build_order.quantity
|
||||
|
||||
|
||||
# Check if the child build order has already been created
|
||||
if build_models.Build.objects.filter(
|
||||
part=item.sub_part,
|
||||
parent=build_order,
|
||||
quantity=quantity,
|
||||
status__in=BuildStatusGroups.ACTIVE_CODES
|
||||
).exists():
|
||||
continue
|
||||
|
||||
sub_order = build_models.Build.objects.create(
|
||||
part=item.sub_part,
|
||||
quantity=quantity,
|
||||
title=build_order.title,
|
||||
batch=build_order.batch,
|
||||
parent=build_order,
|
||||
target_date=build_order.target_date,
|
||||
sales_order=build_order.sales_order,
|
||||
issued_by=build_order.issued_by,
|
||||
responsible=build_order.responsible,
|
||||
)
|
||||
|
||||
sub_build_ids.append(sub_order.pk)
|
||||
|
||||
for pk in sub_build_ids:
|
||||
# Offload the child build order creation to the background task queue
|
||||
InvenTree.tasks.offload_task(
|
||||
create_child_builds,
|
||||
pk,
|
||||
group='build'
|
||||
)
|
||||
|
||||
|
||||
def notify_overdue_build_order(bo: build_models.Build):
|
||||
|
Loading…
x
Reference in New Issue
Block a user