diff --git a/CHANGELOG.md b/CHANGELOG.md index eee68fb4d6..0bbe434ec4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed +[#11816](https://github.com/inventree/InvenTree/pull/11816) makes the `issued_by` field on the `Build` API read only, and instead sets the `issued_by` field to the current user when a build is created. This change was made to ensure that the `issued_by` field accurately reflects the user who created the build, and to prevent users from setting this field to an arbitrary value when creating or updating a build. + ### Removed ## 1.3.0 - 2026-04-11 diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index 0b03d4790a..476e408b8e 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,11 +1,14 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 479 +INVENTREE_API_VERSION = 480 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v480 -> 2026-04-27 : https://github.com/inventree/InvenTree/pull/11816 + - The "issued_by" field on the Build API endpoint is now read-only, and is automatically set to the current user when a build is created + v479 -> 2026-04-11 : https://github.com/inventree/InvenTree/pull/11723 - POST /api//notifications/readall/ now requires a POST action - POST /api/admin/email/test/ - now returns a 200 on. a successful test diff --git a/src/backend/InvenTree/build/api.py b/src/backend/InvenTree/build/api.py index 232b7adca7..7253f87f95 100644 --- a/src/backend/InvenTree/build/api.py +++ b/src/backend/InvenTree/build/api.py @@ -386,6 +386,20 @@ class BuildList( kwargs['create'] = True return super().get_serializer(*args, **kwargs) + def create(self, request, *args, **kwargs): + """Save user information on order creation.""" + serializer = self.get_serializer(data=self.clean_data(request.data)) + serializer.is_valid(raise_exception=True) + + build = serializer.save() + build.issued_by = request.user + build.save() + + headers = self.get_success_headers(serializer.data) + return Response( + serializer.data, status=status.HTTP_201_CREATED, headers=headers + ) + class BuildDetail(BuildMixin, RetrieveUpdateDestroyAPI): """API endpoint for detail view of a Build object.""" diff --git a/src/backend/InvenTree/build/models.py b/src/backend/InvenTree/build/models.py index 39018c661a..c908bddd24 100644 --- a/src/backend/InvenTree/build/models.py +++ b/src/backend/InvenTree/build/models.py @@ -403,6 +403,14 @@ class Build( related_name='builds_issued', ) + @property + def created_by(self): + """Alias for issued_by field. + + This is used for compatibility with the order models + """ + return self.issued_by + responsible = models.ForeignKey( users.models.Owner, on_delete=models.SET_NULL, diff --git a/src/backend/InvenTree/build/serializers.py b/src/backend/InvenTree/build/serializers.py index 724182c978..9264af7375 100644 --- a/src/backend/InvenTree/build/serializers.py +++ b/src/backend/InvenTree/build/serializers.py @@ -104,7 +104,8 @@ class BuildSerializer( read_only_fields = [ 'completed', 'creation_date', - 'completion_data', + 'issued_by', + 'completion_date', 'status', 'status_text', 'level', diff --git a/src/backend/InvenTree/build/test_api.py b/src/backend/InvenTree/build/test_api.py index e63a313238..b7aa4a9cbd 100644 --- a/src/backend/InvenTree/build/test_api.py +++ b/src/backend/InvenTree/build/test_api.py @@ -568,6 +568,9 @@ class BuildTest(BuildAPITest): self.assertEqual(bo.children.count(), 0) + self.assertIsNotNone(bo.issued_by) + self.assertEqual(bo.issued_by, self.user) + class BuildAllocationTest(BuildAPITest): """Unit tests for allocation of stock items against a build order. diff --git a/src/backend/InvenTree/users/test_api.py b/src/backend/InvenTree/users/test_api.py index c3acd2de0b..20cd9488d8 100644 --- a/src/backend/InvenTree/users/test_api.py +++ b/src/backend/InvenTree/users/test_api.py @@ -54,12 +54,16 @@ class UserAPITests(InvenTreeAPITestCase): url = reverse('api-build-list') response = self.options(url) actions = response.data['actions']['POST'] - issued_by = actions['issued_by'] - self.assertEqual(issued_by['pk_field'], 'pk') - self.assertEqual(issued_by['model'], 'user') - self.assertEqual(issued_by['api_url'], reverse('api-user-list')) - self.assertEqual(issued_by['default'], self.user.pk) + issued_by = actions['issued_by'] + self.assertTrue(issued_by['read_only']) + + project_code = actions['project_code'] + self.assertFalse(project_code['read_only']) + self.assertEqual(project_code['type'], 'related field') + self.assertEqual(project_code['model'], 'projectcode') + self.assertEqual(project_code['api_url'], reverse('api-project-code-list')) + self.assertEqual(project_code['pk_field'], 'pk') def test_user_api(self): """Tests for User API endpoints.""" diff --git a/src/frontend/src/forms/BuildForms.tsx b/src/frontend/src/forms/BuildForms.tsx index 2fc4b447eb..0707f433db 100644 --- a/src/frontend/src/forms/BuildForms.tsx +++ b/src/frontend/src/forms/BuildForms.tsx @@ -8,7 +8,6 @@ import { IconList, IconSitemap, IconTruckDelivery, - IconUser, IconUsersGroup } from '@tabler/icons-react'; import { useEffect, useMemo, useState } from 'react'; @@ -128,12 +127,6 @@ export function useBuildOrderFields({ link: { icon: }, - issued_by: { - icon: , - filters: { - is_active: true - } - }, responsible: { icon: , filters: {