2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-01 03:00:54 +00:00

[bug] Custom state fix (#9858)

* Set status correctly when returning from customer

* Fix for stock item status change

- Reduced set of changes from #9781

* Handle API updates

* Fix variable shadowing

* More intelligent comparison

* Remove debug statement
This commit is contained in:
Oliver
2025-06-26 07:42:06 +10:00
committed by GitHub
parent 1a51c4a3dd
commit bc2dbfa0c2
5 changed files with 47 additions and 21 deletions

View File

@ -301,6 +301,13 @@ class StatusCodeMixin:
"""Return the custom status code for this object.""" """Return the custom status code for this object."""
return getattr(self, f'{self.STATUS_FIELD}_custom_key', None) return getattr(self, f'{self.STATUS_FIELD}_custom_key', None)
def compare_status(self, status: int) -> bool:
"""Determine if the current status matches the provided status code."""
if status == self.get_status():
return True
return status is not None and status == self.get_custom_status()
def set_status(self, status: int) -> bool: def set_status(self, status: int) -> bool:
"""Set the status code for this object.""" """Set the status code for this object."""
if not self.status_class: if not self.status_class:

View File

@ -1067,6 +1067,11 @@ class StockList(DataExportViewMixin, StockApiMixin, ListCreateDestroyAPIView):
# Do this regardless of results above # Do this regardless of results above
data.pop('use_pack_size', None) data.pop('use_pack_size', None)
# Extract 'status' flag from data
status_raw = data.pop('status', None)
status_custom = data.pop('status_custom_key', None)
status_value = status_custom or status_raw
# Assign serial numbers for a trackable part # Assign serial numbers for a trackable part
if serial_numbers: if serial_numbers:
if not part.trackable: if not part.trackable:
@ -1130,10 +1135,14 @@ class StockList(DataExportViewMixin, StockApiMixin, ListCreateDestroyAPIView):
tracking = [] tracking = []
for item in items: for item in items:
if status_value and not item.compare_status(status_value):
item.set_status(status_value)
item.save()
if entry := item.add_tracking_entry( if entry := item.add_tracking_entry(
StockHistoryCode.CREATED, StockHistoryCode.CREATED,
user, user,
deltas={'status': item.status}, deltas={'status': status_value},
location=location, location=location,
quantity=float(item.quantity), quantity=float(item.quantity),
commit=False, commit=False,
@ -1152,6 +1161,10 @@ class StockList(DataExportViewMixin, StockApiMixin, ListCreateDestroyAPIView):
# Create a single StockItem object # Create a single StockItem object
# Note: This automatically creates a tracking entry # Note: This automatically creates a tracking entry
item = serializer.save() item = serializer.save()
if status_value and not item.compare_status(status_value):
item.set_status(status_value)
item.save(user=user) item.save(user=user)
response_data = serializer.data response_data = serializer.data

View File

@ -1332,9 +1332,10 @@ class StockItem(
self.sales_order = None self.sales_order = None
self.location = location self.location = location
if status := kwargs.get('status'): if status := kwargs.pop('status', None):
self.status = status if not self.compare_status(status):
tracking_info['status'] = status self.set_status(status)
tracking_info['status'] = status
self.save() self.save()
@ -2240,7 +2241,7 @@ class StockItem(
status = kwargs.pop('status', None) or kwargs.pop('status_custom_key', None) status = kwargs.pop('status', None) or kwargs.pop('status_custom_key', None)
if status and status != self.status: if status and not self.compare_status(status):
self.set_status(status) self.set_status(status)
tracking_info['status'] = status tracking_info['status'] = status
@ -2325,7 +2326,7 @@ class StockItem(
status = kwargs.pop('status', None) or kwargs.pop('status_custom_key', None) status = kwargs.pop('status', None) or kwargs.pop('status_custom_key', None)
if status and status != self.status: if status and not self.compare_status(status):
self.set_status(status) self.set_status(status)
tracking_info['status'] = status tracking_info['status'] = status
@ -2388,7 +2389,7 @@ class StockItem(
status = kwargs.pop('status', None) or kwargs.pop('status_custom_key', None) status = kwargs.pop('status', None) or kwargs.pop('status_custom_key', None)
if status and status != self.status: if status and not self.compare_status(status):
self.set_status(status) self.set_status(status)
tracking_info['status'] = status tracking_info['status'] = status
@ -2442,7 +2443,7 @@ class StockItem(
status = kwargs.pop('status', None) or kwargs.pop('status_custom_key', None) status = kwargs.pop('status', None) or kwargs.pop('status_custom_key', None)
if status and status != self.status: if status and not self.compare_status(status):
self.set_status(status) self.set_status(status)
deltas['status'] = status deltas['status'] = status

View File

@ -521,7 +521,17 @@ class StockItemSerializer(
"""Custom update method to pass the user information through to the instance.""" """Custom update method to pass the user information through to the instance."""
instance._user = self.context['user'] instance._user = self.context['user']
return super().update(instance, validated_data) status_custom_key = validated_data.pop('status_custom_key', None)
status = validated_data.pop('status', None)
instance = super().update(instance, validated_data=validated_data)
if status_code := status_custom_key or status:
if not instance.compare_status(status_code):
instance.set_status(status_code)
instance.save()
return instance
@staticmethod @staticmethod
def annotate_queryset(queryset): def annotate_queryset(queryset):
@ -1123,7 +1133,6 @@ class StockChangeStatusSerializer(serializers.Serializer):
note = data.get('note', '') note = data.get('note', '')
items_to_update = []
transaction_notes = [] transaction_notes = []
deltas = {'status': status} deltas = {'status': status}
@ -1135,12 +1144,11 @@ class StockChangeStatusSerializer(serializers.Serializer):
for item in items: for item in items:
# Ignore items which are already in the desired status # Ignore items which are already in the desired status
if item.status == status: if item.compare_status(status):
continue continue
item.updated = now item.set_status(status)
item.status = status item.save(add_note=False)
items_to_update.append(item)
# Create a new transaction note for each item # Create a new transaction note for each item
transaction_notes.append( transaction_notes.append(
@ -1154,10 +1162,7 @@ class StockChangeStatusSerializer(serializers.Serializer):
) )
) )
# Update status # Create tracking entries
StockItem.objects.bulk_update(items_to_update, ['status', 'updated'])
# Create entries
StockItemTracking.objects.bulk_create(transaction_notes) StockItemTracking.objects.bulk_create(transaction_notes)

View File

@ -11,7 +11,7 @@ from users.models import Owner
class StockViewTestCase(InvenTreeTestCase): class StockViewTestCase(InvenTreeTestCase):
"""Mixin for Stockview tests.""" """Mixin for StockView tests."""
fixtures = ['category', 'part', 'company', 'location', 'supplier_part', 'stock'] fixtures = ['category', 'part', 'company', 'location', 'supplier_part', 'stock']
@ -46,7 +46,7 @@ class StockOwnershipTest(StockViewTestCase):
"""Helper function to get response to API change.""" """Helper function to get response to API change."""
return self.client.patch( return self.client.patch(
reverse('api-stock-detail', args=(self.test_item_id,)), reverse('api-stock-detail', args=(self.test_item_id,)),
{'status': StockStatus.DAMAGED.value}, {'status_custom_key': StockStatus.DAMAGED.value},
content_type='application/json', content_type='application/json',
) )
@ -96,7 +96,7 @@ class StockOwnershipTest(StockViewTestCase):
self.assertTrue(location.check_ownership(self.user)) # Owner is group -> True self.assertTrue(location.check_ownership(self.user)) # Owner is group -> True
self.assertContains( self.assertContains(
self.assert_api_change(), self.assert_api_change(),
f'"status":{StockStatus.DAMAGED.value}', f'"status_custom_key":{StockStatus.DAMAGED.value}',
status_code=200, status_code=200,
) )