2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-28 03:26:45 +00:00

Fix build item over-allocation checks (#8235) (#8241)

(cherry picked from commit a1024f1a677391d70cc9227fdac6e523b9002425)

Co-authored-by: Dean <me@dgardiner.net>
This commit is contained in:
github-actions[bot] 2024-10-06 21:13:27 +11:00 committed by GitHub
parent 86111ad9b9
commit 1c6d25ce33
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 80 additions and 6 deletions

View File

@ -1502,12 +1502,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

@ -920,6 +920,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

@ -1189,9 +1189,17 @@ class StockItem(
return False
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']