Skip to content

Commit e6c5e25

Browse files
authored
Merge pull request #195 from pneumaticapp/backend/users/45646__implementing_hierarchy_users
45646 backend [ users ] Implementing a hierarchy of users
2 parents e2d2fb6 + f9be4e6 commit e6c5e25

54 files changed

Lines changed: 3560 additions & 396 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

backend/src/accounts/messages.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,3 +110,5 @@
110110
MSG_A_0052 = _('The vacation is not active.')
111111
MSG_A_0053 = _('The status is already active.')
112112
MSG_A_0054 = _('The end date must be after the start date.')
113+
MSG_A_0055 = _('A user cannot be their own manager.')
114+
MSG_A_0056 = _('This assignment would create a circular management hierarchy.')
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 2.2 on 2026-04-01 13:05
2+
3+
from django.conf import settings
4+
from django.db import migrations, models
5+
import django.db.models.deletion
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('accounts', '0142_vacation_fields'),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name='user',
17+
name='manager',
18+
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subordinates', to=settings.AUTH_USER_MODEL, verbose_name='Manager'),
19+
),
20+
]

backend/src/accounts/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,14 @@ class Meta:
450450
)
451451
is_admin = models.BooleanField(default=True)
452452
is_account_owner = models.BooleanField(default=False)
453+
manager = models.ForeignKey(
454+
'self',
455+
on_delete=models.SET_NULL,
456+
null=True,
457+
blank=True,
458+
related_name='subordinates',
459+
verbose_name='Manager',
460+
)
453461

454462
notify_about_tasks = models.BooleanField(default=True)
455463
is_digest_subscriber = models.BooleanField(default=True)

backend/src/accounts/serializers/user.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
UserListInviteSerializer,
3232
)
3333
from src.generics.fields import (
34+
AccountPrimaryKeyRelatedField,
3435
CommaSeparatedListField,
3536
DateFormatField,
3637
RelatedListField,
@@ -87,6 +88,8 @@ class Meta:
8788
'invite',
8889
'groups',
8990
'password',
91+
'manager_id',
92+
'subordinates_ids',
9093
'vacation',
9194
)
9295
read_only_fields = (
@@ -111,6 +114,18 @@ class Meta:
111114
date_fmt = DateFormatField(required=False)
112115
invite = serializers.SerializerMethodField(allow_null=True, read_only=True)
113116
password = serializers.CharField(write_only=True, required=False)
117+
manager_id = AccountPrimaryKeyRelatedField(
118+
queryset=UserModel.objects,
119+
required=False,
120+
allow_null=True,
121+
source='manager',
122+
)
123+
subordinates_ids = AccountPrimaryKeyRelatedField(
124+
many=True,
125+
queryset=UserModel.objects,
126+
required=False,
127+
source='subordinates',
128+
)
114129
vacation = VacationSerializer(read_only=True)
115130

116131
def get_invite(self, instance: UserModel):
@@ -282,8 +297,16 @@ class Meta:
282297
'photo',
283298
'is_admin',
284299
'is_account_owner',
300+
'manager_id',
301+
'subordinates_ids',
285302
)
286303

304+
subordinates_ids = serializers.PrimaryKeyRelatedField(
305+
many=True,
306+
read_only=True,
307+
source='subordinates',
308+
)
309+
287310

288311
class VacationActivateSerializer(
289312
CustomValidationErrorMixin,

0 commit comments

Comments
 (0)