2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-28 11:36:44 +00:00

Fix build item over-allocation checks (#8235)

This commit is contained in:
Dean 2024-10-06 23:08:40 +13:00 committed by GitHub
parent 7e7cfb8ee1
commit a1024f1a67
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 80 additions and 6 deletions

View File

@ -1606,12 +1606,19 @@ class BuildItem(InvenTree.models.InvenTreeMetadataModel):
'quantity': _(f'Allocated quantity ({q}) must not exceed available stock quantity ({a})')
})
# Allocated quantity cannot cause the stock item to be over-allocated
# Ensure that we do not 'over allocate' a stock item
available = decimal.Decimal(self.stock_item.quantity)
allocated = decimal.Decimal(self.stock_item.allocation_count())
quantity = decimal.Decimal(self.quantity)
build_allocation_count = decimal.Decimal(self.stock_item.build_allocation_count(
exclude_allocations={'pk': self.pk}
))
sales_allocation_count = decimal.Decimal(self.stock_item.sales_order_allocation_count())
if available - allocated + quantity < quantity:
total_allocation = (
build_allocation_count + sales_allocation_count + quantity
)
if total_allocation > available:
raise ValidationError({
'quantity': _('Stock item is over-allocated')
})

View File

@ -993,6 +993,65 @@ class BuildAllocationTest(BuildAPITest):
expected_code=201,
)
class BuildItemTest(BuildAPITest):
"""Unit tests for build items.
For this test, we will be using Build ID=1;
- This points to Part 100 (see fixture data in part.yaml)
- This Part already has a BOM with 4 items (see fixture data in bom.yaml)
- There are no BomItem objects yet created for this build
"""
def setUp(self):
"""Basic operation as part of test suite setup"""
super().setUp()
self.assignRole('build.add')
self.assignRole('build.change')
self.build = Build.objects.get(pk=1)
# Regenerate BuildLine objects
self.build.create_build_line_items()
# Record number of build items which exist at the start of each test
self.n = BuildItem.objects.count()
def test_update_overallocated(self):
"""Test update of overallocated stock items."""
si = StockItem.objects.get(pk=2)
# Find line item
line = self.build.build_lines.all().filter(bom_item__sub_part=si.part).first()
# Set initial stock item quantity
si.quantity = 100
si.save()
# Create build item
bi = BuildItem(
build_line=line,
stock_item=si,
quantity=100
)
bi.save()
# Reduce stock item quantity
si.quantity = 50
si.save()
# Reduce build item quantity
url = reverse('api-build-item-detail', kwargs={'pk': bi.pk})
self.patch(
url,
{
"quantity": 50,
},
expected_code=200,
)
class BuildOverallocationTest(BuildAPITest):
"""Unit tests for over allocation of stock items against a build order.

View File

@ -1190,9 +1190,17 @@ class StockItem(
return self.sales_order_allocations.count() > 0
def build_allocation_count(self):
"""Return the total quantity allocated to builds."""
query = self.allocations.aggregate(q=Coalesce(Sum('quantity'), Decimal(0)))
def build_allocation_count(self, **kwargs):
"""Return the total quantity allocated to builds, with optional filters."""
query = self.allocations.all()
if filter_allocations := kwargs.get('filter_allocations'):
query = query.filter(**filter_allocations)
if exclude_allocations := kwargs.get('exclude_allocations'):
query = query.exclude(**exclude_allocations)
query = query.aggregate(q=Coalesce(Sum('quantity'), Decimal(0)))
total = query['q']