Skip to content

Commit 1c6d25c

Browse files
Fix build item over-allocation checks (#8235) (#8241)
(cherry picked from commit a1024f1) Co-authored-by: Dean <me@dgardiner.net>
1 parent 86111ad commit 1c6d25c

File tree

3 files changed

+80
-6
lines changed

3 files changed

+80
-6
lines changed

src/backend/InvenTree/build/models.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1502,12 +1502,19 @@ def clean(self):
15021502
'quantity': _(f'Allocated quantity ({q}) must not exceed available stock quantity ({a})')
15031503
})
15041504

1505-
# Allocated quantity cannot cause the stock item to be over-allocated
1505+
# Ensure that we do not 'over allocate' a stock item
15061506
available = decimal.Decimal(self.stock_item.quantity)
1507-
allocated = decimal.Decimal(self.stock_item.allocation_count())
15081507
quantity = decimal.Decimal(self.quantity)
1508+
build_allocation_count = decimal.Decimal(self.stock_item.build_allocation_count(
1509+
exclude_allocations={'pk': self.pk}
1510+
))
1511+
sales_allocation_count = decimal.Decimal(self.stock_item.sales_order_allocation_count())
15091512

1510-
if available - allocated + quantity < quantity:
1513+
total_allocation = (
1514+
build_allocation_count + sales_allocation_count + quantity
1515+
)
1516+
1517+
if total_allocation > available:
15111518
raise ValidationError({
15121519
'quantity': _('Stock item is over-allocated')
15131520
})

src/backend/InvenTree/build/test_api.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -920,6 +920,65 @@ def test_fractional_allocation(self):
920920
expected_code=201,
921921
)
922922

923+
class BuildItemTest(BuildAPITest):
924+
"""Unit tests for build items.
925+
926+
For this test, we will be using Build ID=1;
927+
928+
- This points to Part 100 (see fixture data in part.yaml)
929+
- This Part already has a BOM with 4 items (see fixture data in bom.yaml)
930+
- There are no BomItem objects yet created for this build
931+
"""
932+
933+
def setUp(self):
934+
"""Basic operation as part of test suite setup"""
935+
super().setUp()
936+
937+
self.assignRole('build.add')
938+
self.assignRole('build.change')
939+
940+
self.build = Build.objects.get(pk=1)
941+
942+
# Regenerate BuildLine objects
943+
self.build.create_build_line_items()
944+
945+
# Record number of build items which exist at the start of each test
946+
self.n = BuildItem.objects.count()
947+
948+
def test_update_overallocated(self):
949+
"""Test update of overallocated stock items."""
950+
951+
si = StockItem.objects.get(pk=2)
952+
953+
# Find line item
954+
line = self.build.build_lines.all().filter(bom_item__sub_part=si.part).first()
955+
956+
# Set initial stock item quantity
957+
si.quantity = 100
958+
si.save()
959+
960+
# Create build item
961+
bi = BuildItem(
962+
build_line=line,
963+
stock_item=si,
964+
quantity=100
965+
)
966+
bi.save()
967+
968+
# Reduce stock item quantity
969+
si.quantity = 50
970+
si.save()
971+
972+
# Reduce build item quantity
973+
url = reverse('api-build-item-detail', kwargs={'pk': bi.pk})
974+
975+
self.patch(
976+
url,
977+
{
978+
"quantity": 50,
979+
},
980+
expected_code=200,
981+
)
923982

924983
class BuildOverallocationTest(BuildAPITest):
925984
"""Unit tests for over allocation of stock items against a build order.

src/backend/InvenTree/stock/models.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1189,9 +1189,17 @@ def is_allocated(self):
11891189

11901190
return False
11911191

1192-
def build_allocation_count(self):
1193-
"""Return the total quantity allocated to builds."""
1194-
query = self.allocations.aggregate(q=Coalesce(Sum('quantity'), Decimal(0)))
1192+
def build_allocation_count(self, **kwargs):
1193+
"""Return the total quantity allocated to builds, with optional filters."""
1194+
query = self.allocations.all()
1195+
1196+
if filter_allocations := kwargs.get('filter_allocations'):
1197+
query = query.filter(**filter_allocations)
1198+
1199+
if exclude_allocations := kwargs.get('exclude_allocations'):
1200+
query = query.exclude(**exclude_allocations)
1201+
1202+
query = query.aggregate(q=Coalesce(Sum('quantity'), Decimal(0)))
11951203

11961204
total = query['q']
11971205

0 commit comments

Comments
 (0)