mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-10 23:14:13 +00:00
Build consume stock (#4817)
* Adds "consumed_by" field to the StockItem model. - Points to a BuildOrder instance which "consumed" this stock - Marks item as unavailable - Allows filtering against build order * Allow API filtering * Adds table of "consumed stock items" to build order page * Update stock table to show "consumed by" stock status * Add "consumed_by" link to stock item detail * Optionally add 'buildorder' details to installStockItem method * Update methodology for completing a build item - Instead of deleting stock, mark as "consumed by" * Fix history entry for splitting stock * Bug fix * track "consumed_by" field for tracked items also * Update build docs * Update allocation documentation * Update terminology.md * Unit test updates * Fix conflicting migrations * revert change
This commit is contained in:
@ -332,6 +332,7 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo
|
||||
sales_order=None,
|
||||
belongs_to=None,
|
||||
customer=None,
|
||||
consumed_by=None,
|
||||
is_building=False,
|
||||
status__in=StockStatus.AVAILABLE_CODES
|
||||
)
|
||||
@ -755,6 +756,14 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo
|
||||
related_name='build_outputs',
|
||||
)
|
||||
|
||||
consumed_by = models.ForeignKey(
|
||||
'build.Build', on_delete=models.CASCADE,
|
||||
verbose_name=_('Consumed By'),
|
||||
blank=True, null=True,
|
||||
help_text=_('Build order which consumed this stock item'),
|
||||
related_name='consumed_stock',
|
||||
)
|
||||
|
||||
is_building = models.BooleanField(
|
||||
default=False,
|
||||
)
|
||||
@ -1167,7 +1176,7 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo
|
||||
return self.installed_parts.count()
|
||||
|
||||
@transaction.atomic
|
||||
def installStockItem(self, other_item, quantity, user, notes):
|
||||
def installStockItem(self, other_item, quantity, user, notes, build=None):
|
||||
"""Install another stock item into this stock item.
|
||||
|
||||
Args:
|
||||
@ -1175,6 +1184,7 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo
|
||||
quantity: The quantity of stock to install
|
||||
user: The user performing the operation
|
||||
notes: Any notes associated with the operation
|
||||
build: The BuildOrder to associate with the operation (optional)
|
||||
"""
|
||||
# If the quantity is less than the stock item, split the stock!
|
||||
stock_item = other_item.splitStock(quantity, None, user)
|
||||
@ -1184,16 +1194,22 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo
|
||||
|
||||
# Assign the other stock item into this one
|
||||
stock_item.belongs_to = self
|
||||
stock_item.save()
|
||||
stock_item.consumed_by = build
|
||||
stock_item.save(add_note=False)
|
||||
|
||||
deltas = {
|
||||
'stockitem': self.pk,
|
||||
}
|
||||
|
||||
if build is not None:
|
||||
deltas['buildorder'] = build.pk
|
||||
|
||||
# Add a transaction note to the other item
|
||||
stock_item.add_tracking_entry(
|
||||
StockHistoryCode.INSTALLED_INTO_ASSEMBLY,
|
||||
user,
|
||||
notes=notes,
|
||||
deltas={
|
||||
'stockitem': self.pk,
|
||||
}
|
||||
deltas=deltas,
|
||||
)
|
||||
|
||||
# Add a transaction note to this item (the assembly)
|
||||
@ -1574,7 +1590,6 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo
|
||||
The new item will have a different StockItem ID, while this will remain the same.
|
||||
"""
|
||||
notes = kwargs.get('notes', '')
|
||||
code = kwargs.get('code', StockHistoryCode.SPLIT_FROM_PARENT)
|
||||
|
||||
# Do not split a serialized part
|
||||
if self.serialized:
|
||||
@ -1606,30 +1621,31 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo
|
||||
else:
|
||||
new_stock.location = self.location
|
||||
|
||||
new_stock.save()
|
||||
|
||||
# Copy the transaction history of this part into the new one
|
||||
new_stock.copyHistoryFrom(self)
|
||||
|
||||
# Copy the test results of this part to the new one
|
||||
new_stock.copyTestResultsFrom(self)
|
||||
new_stock.save(add_note=False)
|
||||
|
||||
# Add a new tracking item for the new stock item
|
||||
# Add a stock tracking entry for the newly created item
|
||||
new_stock.add_tracking_entry(
|
||||
code,
|
||||
StockHistoryCode.SPLIT_FROM_PARENT,
|
||||
user,
|
||||
quantity=quantity,
|
||||
notes=notes,
|
||||
location=location,
|
||||
deltas={
|
||||
'stockitem': self.pk,
|
||||
},
|
||||
location=location,
|
||||
}
|
||||
)
|
||||
|
||||
# Copy the test results of this part to the new one
|
||||
new_stock.copyTestResultsFrom(self)
|
||||
|
||||
# Remove the specified quantity from THIS stock item
|
||||
self.take_stock(
|
||||
quantity,
|
||||
user,
|
||||
notes=notes
|
||||
code=StockHistoryCode.SPLIT_CHILD_ITEM,
|
||||
notes=notes,
|
||||
location=location,
|
||||
stockitem=new_stock,
|
||||
)
|
||||
|
||||
# Return a copy of the "new" stock item
|
||||
@ -1798,7 +1814,7 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo
|
||||
return True
|
||||
|
||||
@transaction.atomic
|
||||
def take_stock(self, quantity, user, notes='', code=StockHistoryCode.STOCK_REMOVE):
|
||||
def take_stock(self, quantity, user, notes='', code=StockHistoryCode.STOCK_REMOVE, **kwargs):
|
||||
"""Remove items from stock."""
|
||||
# Cannot remove items from a serialized part
|
||||
if self.serialized:
|
||||
@ -1814,14 +1830,22 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo
|
||||
|
||||
if self.updateQuantity(self.quantity - quantity):
|
||||
|
||||
deltas = {
|
||||
'removed': float(quantity),
|
||||
'quantity': float(self.quantity),
|
||||
}
|
||||
|
||||
if location := kwargs.get('location', None):
|
||||
deltas['location'] = location.pk
|
||||
|
||||
if stockitem := kwargs.get('stockitem', None):
|
||||
deltas['stockitem'] = stockitem.pk
|
||||
|
||||
self.add_tracking_entry(
|
||||
code,
|
||||
user,
|
||||
notes=notes,
|
||||
deltas={
|
||||
'removed': float(quantity),
|
||||
'quantity': float(self.quantity),
|
||||
}
|
||||
deltas=deltas,
|
||||
)
|
||||
|
||||
return True
|
||||
|
Reference in New Issue
Block a user