diff --git a/src/backend/InvenTree/build/serializers.py b/src/backend/InvenTree/build/serializers.py index bdcdaf790e..97edd4350d 100644 --- a/src/backend/InvenTree/build/serializers.py +++ b/src/backend/InvenTree/build/serializers.py @@ -570,6 +570,15 @@ class BuildOutputCompleteSerializer(serializers.Serializer): help_text=_('Location for completed build outputs'), ) + def validate_location(self, location): + """Validate the provided location.""" + if location and location.structural: + raise ValidationError( + _('Structural locations cannot be assigned stock items') + ) + + return location + status_custom_key = StockStatusCustomSerializer(default=StockStatus.OK.value) accept_incomplete_allocation = serializers.BooleanField( diff --git a/src/backend/InvenTree/build/test_api.py b/src/backend/InvenTree/build/test_api.py index 18475c3198..8af5e524e3 100644 --- a/src/backend/InvenTree/build/test_api.py +++ b/src/backend/InvenTree/build/test_api.py @@ -498,6 +498,39 @@ class BuildTest(BuildAPITest): 'This build output has already been completed', str(response.data) ) + def test_complete_build_output_structural_location(self): + """Test that a structural location is rejected by the BuildOutputComplete API. + + Ref: this validation must happen synchronously in the serializer, + before the completion is offloaded to the background worker. + """ + bo = Build.objects.get(pk=1) + + # Create a new build output + create_url = reverse('api-build-output-create', kwargs={'pk': bo.pk}) + response = self.post(create_url, {'quantity': 1}, expected_code=201) + output = StockItem.objects.get(pk=response.data[0]['pk']) + + structural_location = StockLocation.objects.create( + name='Structural location', structural=True + ) + + complete_url = reverse('api-build-output-complete', kwargs={'pk': bo.pk}) + + response = self.post( + complete_url, + {'outputs': [{'output': output.pk}], 'location': structural_location.pk}, + expected_code=400, + ) + + self.assertIn( + 'Structural locations cannot be assigned stock items', str(response.data) + ) + + # None of the outputs should have been completed + output.refresh_from_db() + self.assertTrue(output.is_building) + def test_download_build_orders(self): """Test that we can download a list of build orders via the API.""" required_cols = [