diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index 1839b2cd09..7f83a19e2e 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 = 552 +INVENTREE_API_VERSION = 553 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v553 -> 2026-10-04 : https://github.com/inventree/InvenTree/pull/12965 + - Fix user creation with 'group_ids' via the User API endpoint + v552 -> 2026-10-02 : https://github.com/inventree/InvenTree/pull/12967 - Fix transitions endpoint schema to indicate that it returns a list diff --git a/src/backend/InvenTree/users/serializers.py b/src/backend/InvenTree/users/serializers.py index 404f8f94cd..91da686115 100644 --- a/src/backend/InvenTree/users/serializers.py +++ b/src/backend/InvenTree/users/serializers.py @@ -336,6 +336,9 @@ class ExtendedUserSerializer(UserSerializer): read_only_fields = [*UserSerializer.Meta.read_only_fields, 'groups'] + # 'group_ids' is a write-only serializer field which does not exist on the model + SKIP_CREATE_FIELDS = ['group_ids'] + groups = GroupSerializer(many=True, read_only=True) # Write-only field, for updating the groups associated with the user @@ -529,8 +532,14 @@ class UserCreateSerializer(ExtendedUserSerializer): base_url = get_base_url() + # Extract the groups to assign to the new user + groups = validated_data.pop('group_ids', None) + instance = super().create(validated_data) + if groups is not None: + instance.groups.set(groups) + # Create the EmailAddress entry for the user if instance.email: EmailAddress.objects.create( diff --git a/src/backend/InvenTree/users/test_api.py b/src/backend/InvenTree/users/test_api.py index 3088d7a55c..4644ef23f8 100644 --- a/src/backend/InvenTree/users/test_api.py +++ b/src/backend/InvenTree/users/test_api.py @@ -154,6 +154,43 @@ class UserAPITests(InvenTreeAPITestCase): self.assertEqual(response.data['username'], 'Superuser') self.assertEqual(response.data['is_superuser'], True) + def test_user_create_with_groups(self): + """Test that groups can be assigned when creating a new user via the API.""" + url = reverse('api-user-list') + + group_a = Group.objects.create(name='Group A') + group_b = Group.objects.create(name='Group B') + + self.user.is_staff = True + self.user.save() + self.assignRole('admin.add') + + data = { + 'username': 'grouped', + 'first_name': 'Grouped', + 'last_name': 'User', + 'email': 'grouped@example.org', + 'group_ids': [group_a.pk, group_b.pk], + } + + response = self.post(url, data=data, expected_code=201) + + self.assertEqual(response.data['username'], 'grouped') + + group_names = {g['name'] for g in response.data['groups']} + self.assertEqual(group_names, {'Group A', 'Group B'}) + + user = User.objects.get(username='grouped') + self.assertEqual(set(user.groups.all()), {group_a, group_b}) + + # Creating a user without specifying groups must still work + response = self.post( + url, + data={**data, 'username': 'ungrouped', 'group_ids': []}, + expected_code=201, + ) + self.assertEqual(response.data['groups'], []) + def test_user_detail(self): """Test the UserDetail API endpoint.""" user = User.objects.first()