mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-01 11:10: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:
@ -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:
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user