mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Update required parts for build orders (#5542)
- When a BomItem is created or edited, update any active build orders which use it - Runs as a background task - Fixes https://github.com/inventree/InvenTree/issues/5541
This commit is contained in:
parent
56fdbc00c9
commit
c8021ec319
@ -1186,6 +1186,7 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.
|
|||||||
|
|
||||||
BuildLine.objects.bulk_create(lines)
|
BuildLine.objects.bulk_create(lines)
|
||||||
|
|
||||||
|
if len(lines) > 0:
|
||||||
logger.info(f"Created {len(lines)} BuildLine objects for BuildOrder")
|
logger.info(f"Created {len(lines)} BuildLine objects for BuildOrder")
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
|
@ -24,6 +24,55 @@ import part.models as part_models
|
|||||||
logger = logging.getLogger('inventree')
|
logger = logging.getLogger('inventree')
|
||||||
|
|
||||||
|
|
||||||
|
def update_build_order_lines(bom_item_pk: int):
|
||||||
|
"""Update all BuildOrderLineItem objects which reference a particular BomItem.
|
||||||
|
|
||||||
|
This task is triggered when a BomItem is created or updated.
|
||||||
|
"""
|
||||||
|
|
||||||
|
logger.info(f"Updating build order lines for BomItem {bom_item_pk}")
|
||||||
|
|
||||||
|
bom_item = part_models.BomItem.objects.filter(pk=bom_item_pk).first()
|
||||||
|
|
||||||
|
# If the BomItem has been deleted, there is nothing to do
|
||||||
|
if not bom_item:
|
||||||
|
return
|
||||||
|
|
||||||
|
assemblies = bom_item.get_assemblies()
|
||||||
|
|
||||||
|
# Find all active builds which reference any of the parts
|
||||||
|
builds = build.models.Build.objects.filter(
|
||||||
|
part__in=list(assemblies),
|
||||||
|
status__in=BuildStatusGroups.ACTIVE_CODES
|
||||||
|
)
|
||||||
|
|
||||||
|
# Iterate through each build, and update the relevant line items
|
||||||
|
for bo in builds:
|
||||||
|
# Try to find a matching build order line
|
||||||
|
line = build.models.BuildLine.objects.filter(
|
||||||
|
build=bo,
|
||||||
|
bom_item=bom_item,
|
||||||
|
).first()
|
||||||
|
|
||||||
|
q = bom_item.get_required_quantity(bo.quantity)
|
||||||
|
|
||||||
|
if line:
|
||||||
|
# Ensure quantity is correct
|
||||||
|
if line.quantity != q:
|
||||||
|
line.quantity = q
|
||||||
|
line.save()
|
||||||
|
else:
|
||||||
|
# Create a new line item
|
||||||
|
build.models.BuildLine.objects.create(
|
||||||
|
build=bo,
|
||||||
|
bom_item=bom_item,
|
||||||
|
quantity=q,
|
||||||
|
)
|
||||||
|
|
||||||
|
if builds.count() > 0:
|
||||||
|
logger.info(f"Updated {builds.count()} build orders for part {bom_item.part}")
|
||||||
|
|
||||||
|
|
||||||
def check_build_stock(build: build.models.Build):
|
def check_build_stock(build: build.models.Build):
|
||||||
"""Check the required stock for a newly created build order.
|
"""Check the required stock for a newly created build order.
|
||||||
|
|
||||||
|
@ -3749,6 +3749,18 @@ class BomItem(DataImportMixin, MetadataMixin, models.Model):
|
|||||||
"""Return the list API endpoint URL associated with the BomItem model"""
|
"""Return the list API endpoint URL associated with the BomItem model"""
|
||||||
return reverse('api-bom-list')
|
return reverse('api-bom-list')
|
||||||
|
|
||||||
|
def get_assemblies(self):
|
||||||
|
"""Return a list of assemblies which use this BomItem"""
|
||||||
|
|
||||||
|
assemblies = [self.part]
|
||||||
|
|
||||||
|
if self.inherited:
|
||||||
|
assemblies += list(
|
||||||
|
self.part.get_descendants(include_self=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
return assemblies
|
||||||
|
|
||||||
def get_valid_parts_for_allocation(self, allow_variants=True, allow_substitutes=True):
|
def get_valid_parts_for_allocation(self, allow_variants=True, allow_substitutes=True):
|
||||||
"""Return a list of valid parts which can be allocated against this BomItem.
|
"""Return a list of valid parts which can be allocated against this BomItem.
|
||||||
|
|
||||||
@ -4048,6 +4060,18 @@ class BomItem(DataImportMixin, MetadataMixin, models.Model):
|
|||||||
return "{pmin} to {pmax}".format(pmin=pmin, pmax=pmax)
|
return "{pmin} to {pmax}".format(pmin=pmin, pmax=pmax)
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=BomItem, dispatch_uid='update_bom_build_lines')
|
||||||
|
def update_bom_build_lines(sender, instance, created, **kwargs):
|
||||||
|
"""Update existing build orders when a BomItem is created or edited"""
|
||||||
|
|
||||||
|
if InvenTree.ready.canAppAccessDatabase() and not InvenTree.ready.isImportingData():
|
||||||
|
import build.tasks
|
||||||
|
InvenTree.tasks.offload_task(
|
||||||
|
build.tasks.update_build_order_lines,
|
||||||
|
instance.pk
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=BomItem, dispatch_uid='post_save_bom_item')
|
@receiver(post_save, sender=BomItem, dispatch_uid='post_save_bom_item')
|
||||||
@receiver(post_save, sender=PartSellPriceBreak, dispatch_uid='post_save_sale_price_break')
|
@receiver(post_save, sender=PartSellPriceBreak, dispatch_uid='post_save_sale_price_break')
|
||||||
@receiver(post_save, sender=PartInternalPriceBreak, dispatch_uid='post_save_internal_price_break')
|
@receiver(post_save, sender=PartInternalPriceBreak, dispatch_uid='post_save_internal_price_break')
|
||||||
|
Loading…
Reference in New Issue
Block a user