2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-01-11 21:57:57 +00:00

BuildItem quantity fix (#11108)

* Refactor clean check for BuildItem

* Don't raise an error when saving a BuildItem

* Fix order of operations

* remove debug statements
This commit is contained in:
Oliver
2026-01-09 18:45:44 +11:00
committed by GitHub
parent 02a95ffba8
commit 70fcaa7808

View File

@@ -1743,11 +1743,10 @@ class BuildItem(InvenTree.models.InvenTreeMetadataModel):
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
"""Custom save method for the BuildItem model.""" """Custom save method for the BuildItem model."""
self.clean() self.clean(raise_error=False)
super().save() super().save()
def clean(self): def clean(self, raise_error: bool = True):
"""Check validity of this BuildItem instance. """Check validity of this BuildItem instance.
The following checks are performed: The following checks are performed:
@@ -1771,47 +1770,7 @@ class BuildItem(InvenTree.models.InvenTreeMetadataModel):
) )
) )
# Allocated quantity cannot exceed available stock quantity self.check_allocated_quantity(raise_error=raise_error)
if self.quantity > self.stock_item.quantity:
q = InvenTree.helpers.normalize(self.quantity)
a = InvenTree.helpers.normalize(self.stock_item.quantity)
raise ValidationError({
'quantity': _(
f'Allocated quantity ({q}) must not exceed available stock quantity ({a})'
)
})
# Ensure that we do not 'over allocate' a stock item
available = decimal.Decimal(self.stock_item.quantity)
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()
)
total_allocation = (
build_allocation_count + sales_allocation_count + quantity
)
if total_allocation > available:
raise ValidationError({'quantity': _('Stock item is over-allocated')})
# Allocated quantity must be positive
if self.quantity <= 0:
raise ValidationError({
'quantity': _('Allocation quantity must be greater than zero')
})
# Quantity must be 1 for serialized stock
if self.stock_item.serialized and self.quantity != 1:
raise ValidationError({
'quantity': _('Quantity must be 1 for serialized stock')
})
except stock.models.StockItem.DoesNotExist: except stock.models.StockItem.DoesNotExist:
raise ValidationError('Stock item must be specified') raise ValidationError('Stock item must be specified')
@@ -1873,6 +1832,60 @@ class BuildItem(InvenTree.models.InvenTreeMetadataModel):
'stock_item': _('Selected stock item does not match BOM line') 'stock_item': _('Selected stock item does not match BOM line')
}) })
def check_allocated_quantity(self, raise_error: bool = False):
"""Ensure that the allocated quantity is valid.
Will reduce the allocated quantity if it exceeds available stock.
Arguments:
raise_error: If True, raise ValidationError on failure
Raises:
ValidationError: If the allocated quantity is invalid and raise_error is True
"""
error = None
# Allocated quantity must be positive
if self.quantity <= 0:
self.quantity = 0
error = {'quantity': _('Allocated quantity must be greater than zero')}
# Quantity must be 1 for serialized stock
if self.stock_item.serialized and self.quantity != 1:
self.quantity = 1
raise ValidationError({
'quantity': _('Quantity must be 1 for serialized stock')
})
# Allocated quantity cannot exceed available stock quantity
if self.quantity > self.stock_item.quantity:
q = InvenTree.helpers.normalize(self.quantity)
a = InvenTree.helpers.normalize(self.stock_item.quantity)
self.quantity = self.stock_item.quantity
error = {
'quantity': _(
f'Allocated quantity ({q}) must not exceed available stock quantity ({a})'
)
}
# Ensure that we do not 'over allocate' a stock item
available = decimal.Decimal(self.stock_item.quantity)
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()
)
total_allocation = build_allocation_count + sales_allocation_count + quantity
if total_allocation > available:
error = {'quantity': _('Stock item is over-allocated')}
if error and raise_error:
raise ValidationError(error)
@property @property
def build(self): def build(self):
"""Return the BuildOrder associated with this BuildItem.""" """Return the BuildOrder associated with this BuildItem."""