mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
(cherry picked from commit a1024f1a677391d70cc9227fdac6e523b9002425) Co-authored-by: Dean <me@dgardiner.net>
This commit is contained in:
parent
86111ad9b9
commit
1c6d25ce33
@ -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')
|
||||
})
|
||||
|
@ -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.
|
||||
|
@ -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']
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user