From ed2da62a46963d4202880441812f72dc684249e2 Mon Sep 17 00:00:00 2001 From: Matthias Mair Date: Sat, 24 Aug 2024 01:18:09 +0200 Subject: [PATCH] Fix state changes on stock items (#7976) * Revert changes from https://github.com/inventree/InvenTree/pull/7965 * Add error handling for wrong key * Add e2e test case for error condition Fixes #7964 * Better code code / flow * [BUG] Order of states in schema descriptions is not stable Fixes #7977 --- .../InvenTree/generic/states/fields.py | 32 ++++++++++++------- src/backend/InvenTree/stock/test_api.py | 8 +++++ .../templates/js/translated/stock.js | 2 +- src/frontend/src/forms/StockForms.tsx | 2 +- 4 files changed, 30 insertions(+), 14 deletions(-) diff --git a/src/backend/InvenTree/generic/states/fields.py b/src/backend/InvenTree/generic/states/fields.py index 99717d4884..7f67f5c14b 100644 --- a/src/backend/InvenTree/generic/states/fields.py +++ b/src/backend/InvenTree/generic/states/fields.py @@ -41,7 +41,7 @@ class CustomChoiceField(serializers.ChoiceField): if self.is_custom: return logical.key return logical.logical_key - except ObjectDoesNotExist: + except (ObjectDoesNotExist, Exception): raise serializers.ValidationError('Invalid choice') def get_field_info(self, field, field_info): @@ -145,24 +145,32 @@ class InvenTreeCustomStatusSerializerMixin: def update(self, instance, validated_data): """Ensure the custom field is updated if the leader was changed.""" self.gather_custom_fields() + # Mirror values from leader to follower for field in self._custom_fields_leader: + follower_field_name = f'{field}_custom_key' if ( field in self.initial_data and self.instance and self.initial_data[field] - != getattr(self.instance, f'{field}_custom_key', None) + != getattr(self.instance, follower_field_name, None) ): - setattr(self.instance, f'{field}_custom_key', self.initial_data[field]) + setattr(self.instance, follower_field_name, self.initial_data[field]) + + # Mirror values from follower to leader for field in self._custom_fields_follower: - if ( - field in validated_data - and field.replace('_custom_key', '') not in self.initial_data - ): - reference = get_logical_value( - validated_data[field], - self.fields[field].choice_mdl._meta.model_name, - ) - validated_data[field.replace('_custom_key', '')] = reference.logical_key + leader_field_name = field.replace('_custom_key', '') + if field in validated_data and leader_field_name not in self.initial_data: + try: + reference = get_logical_value( + validated_data[field], + self.fields[field].choice_mdl._meta.model_name, + ) + validated_data[leader_field_name] = reference.logical_key + except (ObjectDoesNotExist, Exception): + if validated_data[field] in self.fields[leader_field_name].choices: + validated_data[leader_field_name] = validated_data[field] + else: + raise serializers.ValidationError('Invalid choice') return super().update(instance, validated_data) def to_representation(self, instance): diff --git a/src/backend/InvenTree/stock/test_api.py b/src/backend/InvenTree/stock/test_api.py index af73fa1b73..44e553cacf 100644 --- a/src/backend/InvenTree/stock/test_api.py +++ b/src/backend/InvenTree/stock/test_api.py @@ -1004,6 +1004,14 @@ class CustomStockItemStatusTest(StockAPITestCase): self.assertEqual(response.data['status'], self.status.logical_key) self.assertEqual(response.data['status_custom_key'], self.status.logical_key) + # Test case with wrong key + response = self.patch( + reverse('api-stock-detail', kwargs={'pk': pk}), + {'status_custom_key': 23456789}, + expected_code=400, + ) + self.assertIn('Invalid choice', str(response.data)) + def test_options(self): """Test the StockItem OPTIONS endpoint to contain custom StockStatuses.""" response = self.options(self.list_url) diff --git a/src/backend/InvenTree/templates/js/translated/stock.js b/src/backend/InvenTree/templates/js/translated/stock.js index 2e65072853..6c21536343 100644 --- a/src/backend/InvenTree/templates/js/translated/stock.js +++ b/src/backend/InvenTree/templates/js/translated/stock.js @@ -380,7 +380,7 @@ function stockItemFields(options={}) { batch: { icon: 'fa-layer-group', }, - status: {}, + status_custom_key: {}, expiry_date: { icon: 'fa-calendar-alt', }, diff --git a/src/frontend/src/forms/StockForms.tsx b/src/frontend/src/forms/StockForms.tsx index 3ed19c8938..7ac947826b 100644 --- a/src/frontend/src/forms/StockForms.tsx +++ b/src/frontend/src/forms/StockForms.tsx @@ -138,7 +138,7 @@ export function useStockFields({ value: batchCode, onValueChange: (value) => setBatchCode(value) }, - status: {}, + status_custom_key: {}, expiry_date: { // TODO: icon },