Fix for BuildItem with zero quantity (#12387) (#12390)

(cherry picked from commit 183e6c37b1)

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
This commit is contained in:
github-actions[bot] 2026-07-14 13:51:46 +10:00 committed by GitHub
parent 83774e126d
commit d7283a5b6e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 58 additions and 0 deletions

View File

@ -1998,6 +1998,12 @@ class BuildItem(InvenTree.models.InvenTreeMetadataModel):
if quantity > item.quantity:
quantity = item.quantity
if quantity <= 0:
# There is nothing to consume or install:
# simply remove this (empty) allocation
self.delete()
return
# Split the allocated stock if there are more available than allocated
if item.quantity > quantity:
item = item.splitStock(quantity, None, user, notes=notes)

View File

@ -707,6 +707,58 @@ class BuildTest(BuildTestBase):
self.line_1.refresh_from_db()
self.assertEqual(self.line_1.consumed, 8)
def test_complete_zero_quantity_allocation(self):
"""A zero-quantity allocation is removed cleanly on completion.
Regression test: completing a BuildItem with quantity=0 (permitted by
the model validators) crashed with an AttributeError, blocking build
completion until the empty allocation was manually removed.
"""
self.build.issue_build()
# An allocation with zero quantity, against an item with stock available
alloc = BuildItem.objects.create(
build_line=self.line_1, stock_item=self.stock_1_2, quantity=0
)
n_items = StockItem.objects.count()
alloc.complete_allocation(user=self.user)
# The empty allocation is deleted, with no stock operations performed
self.assertFalse(BuildItem.objects.filter(pk=alloc.pk).exists())
self.assertEqual(StockItem.objects.count(), n_items)
self.stock_1_2.refresh_from_db()
self.assertEqual(self.stock_1_2.quantity, 100)
self.assertIsNone(self.stock_1_2.consumed_by)
self.line_1.refresh_from_db()
self.assertEqual(self.line_1.consumed, 0)
# An allocation whose stock item has been depleted elsewhere
# is also removed cleanly (allocated quantity clamps to zero)
depleted = StockItem.objects.create(
part=self.sub_part_1, quantity=5, delete_on_deplete=False
)
alloc = BuildItem.objects.create(
build_line=self.line_1, stock_item=depleted, quantity=5
)
depleted.take_stock(5, self.user)
depleted.refresh_from_db()
self.assertEqual(depleted.quantity, 0)
alloc.complete_allocation(user=self.user)
self.assertFalse(BuildItem.objects.filter(pk=alloc.pk).exists())
depleted.refresh_from_db()
self.assertIsNone(depleted.consumed_by)
self.line_1.refresh_from_db()
self.assertEqual(self.line_1.consumed, 0)
def test_complete_with_required_tests(self):
"""Test the prevention completion when a required test is missing feature."""
# with required tests incompleted the save should fail