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

[bug] State change fixes (#10832)

* Fix for setting custom status

* Fix for setting custom status when receiving stock items

* Allow caching for set_status

* Updated code and unit tests
This commit is contained in:
Oliver
2025-11-15 07:42:48 +11:00
committed by GitHub
parent af4d9efd1d
commit aa9958bf11
6 changed files with 58 additions and 18 deletions

View File

@@ -309,13 +309,23 @@ class StatusCodeMixin:
return status is not None and status == self.get_custom_status()
def set_status(self, status: int) -> bool:
"""Set the status code for this object."""
def set_status(self, status: int, custom_values=None) -> bool:
"""Set the status code for this object.
Arguments:
status: The status code to set
custom_values: Optional list of custom values to consider (can be used to avoid DB queries)
"""
if not self.status_class:
raise NotImplementedError('Status class not defined')
base_values = self.status_class.values()
custom_value_set = self.status_class.custom_values()
custom_value_set = (
self.status_class.custom_values()
if custom_values is None
else custom_values
)
custom_field = f'{self.STATUS_FIELD}_custom_key'

View File

@@ -976,6 +976,9 @@ class PurchaseOrder(TotalPriceMixin, Order):
# Prefetch line item objects for DB efficiency
line_items_ids = [item['line_item'].pk for item in items]
# Cache the custom status options for the StockItem model
custom_stock_status_values = stock.models.StockItem.STATUS_CLASS.custom_values()
line_items = PurchaseOrderLineItem.objects.filter(
pk__in=line_items_ids
).prefetch_related('part', 'part__part', 'order')
@@ -1050,7 +1053,6 @@ class PurchaseOrder(TotalPriceMixin, Order):
'supplier_part': supplier_part,
'purchase_order': self,
'purchase_price': purchase_price,
'status': item.get('status', StockStatus.OK.value),
'location': stock_location,
'quantity': 1 if serialize else stock_quantity,
'batch': item.get('batch_code', ''),
@@ -1059,6 +1061,9 @@ class PurchaseOrder(TotalPriceMixin, Order):
'packaging': item.get('packaging') or supplier_part.packaging,
}
# Extract the "status" field
status = item.get('status', StockStatus.OK.value)
# Check linked build order
# This is for receiving against an *external* build order
if build_order := line.build_order:
@@ -1099,11 +1104,14 @@ class PurchaseOrder(TotalPriceMixin, Order):
# Now, create the new stock items
if serialize:
stock_items.extend(
stock.models.StockItem._create_serial_numbers(
serials=serials, **stock_data
)
new_items = stock.models.StockItem._create_serial_numbers(
serials=serials, **stock_data
)
for item in new_items:
item.set_status(status, custom_values=custom_stock_status_values)
stock_items.append(item)
else:
new_item = stock.models.StockItem(
**stock_data,
@@ -1115,10 +1123,11 @@ class PurchaseOrder(TotalPriceMixin, Order):
rght=2,
)
new_item.set_status(status, custom_values=custom_stock_status_values)
if barcode:
new_item.assign_barcode(barcode_data=barcode, save=False)
# new_item.save()
bulk_create_items.append(new_item)
# Update the line item quantity

View File

@@ -1226,6 +1226,8 @@ class PurchaseOrderReceiveTest(OrderTest):
def test_receive_large_quantity(self):
"""Test receipt of a large number of items."""
from stock.status_codes import StockStatus
sp = SupplierPart.objects.first()
# Create a new order
@@ -1256,7 +1258,12 @@ class PurchaseOrderReceiveTest(OrderTest):
url,
{
'items': [
{'line_item': line.pk, 'quantity': line.quantity} for line in lines
{
'line_item': line.pk,
'quantity': line.quantity,
'status': StockStatus.QUARANTINED.value,
}
for line in lines
],
'location': location.pk,
},
@@ -1269,6 +1276,7 @@ class PurchaseOrderReceiveTest(OrderTest):
for item in response:
self.assertEqual(item['purchase_order'], po.pk)
self.assertEqual(item['status'], StockStatus.QUARANTINED)
# Check that the order has been completed
po.refresh_from_db()

View File

@@ -827,7 +827,7 @@ class StockItem(
super().save(*args, **kwargs)
# If user information is provided, and no existing note exists, create one!
if user and add_note and self.tracking_info.count() == 0:
if add_note and self.tracking_info.count() == 0:
tracking_info = {'status': self.status}
self.add_tracking_entry(

View File

@@ -1060,12 +1060,19 @@ class StockChangeStatusSerializer(serializers.Serializer):
# Instead of performing database updates for each item,
# perform bulk database updates (much more efficient)
# Pre-cache the custom status values (to reduce DB hits)
custom_status_codes = StockItem.STATUS_CLASS.custom_values()
for item in items:
# Ignore items which are already in the desired status
if item.compare_status(status):
continue
item.set_status(status)
# Careful check for custom status codes also
if item.compare_status(status):
custom_status = item.get_custom_status()
if status == custom_status or custom_status is None:
continue
item.set_status(status, custom_values=custom_status_codes)
item.save(add_note=False)
# Create a new transaction note for each item

View File

@@ -1701,12 +1701,18 @@ class StockItemTest(StockAPITestCase):
prt = Part.objects.first()
# Number of items to create
N_ITEMS = 10
# Create a bunch of items
items = [StockItem.objects.create(part=prt, quantity=10) for _ in range(10)]
items = [
StockItem.objects.create(part=prt, quantity=10) for _ in range(N_ITEMS)
]
for item in items:
item.refresh_from_db()
self.assertEqual(item.status, StockStatus.OK.value)
self.assertEqual(item.tracking_info.count(), 1)
data = {
'items': [item.pk for item in items],
@@ -1719,10 +1725,10 @@ class StockItemTest(StockAPITestCase):
for item in items:
item.refresh_from_db()
self.assertEqual(item.status, StockStatus.DAMAGED.value)
self.assertEqual(item.tracking_info.count(), 1)
self.assertEqual(item.tracking_info.count(), 2)
# Same test, but with one item unchanged
items[0].status = StockStatus.ATTENTION.value
items[0].set_status(StockStatus.ATTENTION.value)
items[0].save()
data['status'] = StockStatus.ATTENTION.value
@@ -1732,7 +1738,7 @@ class StockItemTest(StockAPITestCase):
for item in items:
item.refresh_from_db()
self.assertEqual(item.status, StockStatus.ATTENTION.value)
self.assertEqual(item.tracking_info.count(), 2)
self.assertEqual(item.tracking_info.count(), 3)
tracking = item.tracking_info.last()
self.assertEqual(tracking.tracking_type, StockHistoryCode.EDITED.value)