diff --git a/src/backend/InvenTree/generic/states/fields.py b/src/backend/InvenTree/generic/states/fields.py index 99717d4884a9..7f67f5c14b50 100644 --- a/src/backend/InvenTree/generic/states/fields.py +++ b/src/backend/InvenTree/generic/states/fields.py @@ -41,7 +41,7 @@ def to_internal_value(self, data): 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 af73fa1b73d3..44e553cacfe0 100644 --- a/src/backend/InvenTree/stock/test_api.py +++ b/src/backend/InvenTree/stock/test_api.py @@ -1004,6 +1004,14 @@ def test_custom_status(self): 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 2e65072853bd..6c2153634308 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 3ed19c8938b1..7ac947826ba6 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 },