Skip to content

Commit 93368bb

Browse files
authored
Merge branch 'dev' into feature/addotional_project_fields
2 parents 9499c6d + 66be8f6 commit 93368bb

5 files changed

Lines changed: 101 additions & 4 deletions

File tree

invites/serializers.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
from django.apps import apps
12
from rest_framework import serializers
23

34
from invites.models import Invite
5+
from projects.models import Collaborator
46
from projects.serializers import ProjectListSerializer
57
from users.serializers import UserDetailSerializer
68

@@ -18,6 +20,48 @@ class Meta:
1820
"is_accepted",
1921
]
2022

23+
def validate(self, attrs):
24+
project = attrs["project"]
25+
user = attrs["user"]
26+
27+
if project.leader_id == user.id:
28+
raise serializers.ValidationError(
29+
{"user": "Пользователь уже является лидером проекта."}
30+
)
31+
32+
if Collaborator.objects.filter(project=project, user=user).exists():
33+
raise serializers.ValidationError(
34+
{"user": "Пользователь уже состоит в проекте."}
35+
)
36+
37+
if Invite.objects.filter(
38+
project=project, user=user, is_accepted__isnull=True
39+
).exists():
40+
raise serializers.ValidationError(
41+
{"user": "У пользователя уже есть активное приглашение в этот проект."}
42+
)
43+
44+
link = project.program_links.select_related("partner_program").first()
45+
if link:
46+
PartnerProgramUserProfile = apps.get_model(
47+
"partner_programs", "PartnerProgramUserProfile"
48+
)
49+
is_participant = PartnerProgramUserProfile.objects.filter(
50+
user_id=user.id,
51+
partner_program_id=link.partner_program_id,
52+
).exists()
53+
if not is_participant:
54+
raise serializers.ValidationError(
55+
{
56+
"user": (
57+
"Нельзя пригласить пользователя: проект относится к программе, "
58+
"а пользователь не является её участником."
59+
)
60+
}
61+
)
62+
63+
return attrs
64+
2165

2266
class InviteDetailSerializer(serializers.ModelSerializer[Invite]):
2367
user = UserDetailSerializer(many=False, read_only=True)

projects/models.py

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from typing import Optional
22

3+
from django.apps import apps
34
from django.contrib.auth import get_user_model
45
from django.contrib.contenttypes.fields import GenericRelation
6+
from django.core.exceptions import ValidationError
57
from django.core.validators import (
68
MaxLengthValidator,
79
MaxValueValidator,
@@ -310,6 +312,35 @@ class Meta:
310312
)
311313
]
312314

315+
def clean(self):
316+
"""
317+
Если проект привязан к программе, добавлять коллаборатора можно
318+
только если пользователь — участник этой программы.
319+
(Проект привязан максимум к одной программе.)
320+
"""
321+
link = self.project.program_links.select_related("partner_program").first()
322+
if not link:
323+
return
324+
325+
PartnerProgramUserProfile = apps.get_model(
326+
"partner_programs",
327+
"PartnerProgramUserProfile",
328+
)
329+
330+
is_participant = PartnerProgramUserProfile.objects.filter(
331+
user_id=self.user_id,
332+
partner_program_id=link.partner_program_id,
333+
).exists()
334+
335+
if not is_participant:
336+
raise ValidationError(
337+
"Пользователь не является участником программы, к которой относится проект."
338+
)
339+
340+
def save(self, *args, **kwargs):
341+
self.full_clean()
342+
return super().save(*args, **kwargs)
343+
313344

314345
class ProjectNews(models.Model):
315346
"""

projects/permissions.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
from datetime import datetime, timedelta
22

33
from django.utils import timezone
4-
from rest_framework.exceptions import PermissionDenied
4+
from rest_framework.exceptions import PermissionDenied, ValidationError
55
from rest_framework.permissions import SAFE_METHODS, BasePermission
66

7-
from partner_programs.models import PartnerProgramUserProfile
7+
from partner_programs.models import PartnerProgram, PartnerProgramUserProfile
88
from projects.models import Project
99

1010

@@ -189,3 +189,24 @@ def has_object_permission(self, request, view, obj):
189189
and request.user.is_authenticated
190190
and obj.project.leader_id == request.user.id
191191
)
192+
193+
194+
class CanBindProjectToProgram(BasePermission):
195+
message = "Привязать проект к программе может только её участник (или менеджер)."
196+
197+
def has_permission(self, request, view):
198+
program_id = (request.data or {}).get("partner_program_id")
199+
if not program_id:
200+
return True
201+
202+
try:
203+
program = PartnerProgram.objects.get(pk=program_id)
204+
except PartnerProgram.DoesNotExist:
205+
raise ValidationError({"partner_program_id": "Программа не найдена."})
206+
207+
if program.is_manager(request.user):
208+
return True
209+
210+
return PartnerProgramUserProfile.objects.filter(
211+
user=request.user, partner_program=program
212+
).exists()

projects/serializers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ def validate(self, data):
412412

413413
if project.leader != request.user:
414414
raise serializers.ValidationError(
415-
"Только лидер проекта может дублировать его в программу."
415+
{"error": "Только лидер проекта может дублировать его в программу."}
416416
)
417417

418418
try:

projects/views.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from projects.models import Achievement, Collaborator, Project, ProjectGoal, ProjectNews
3434
from projects.pagination import ProjectNewsPagination, ProjectsPagination
3535
from projects.permissions import (
36+
CanBindProjectToProgram,
3637
HasInvolvementInProjectOrReadOnly,
3738
IsNewsAuthorIsProjectLeaderOrReadOnly,
3839
IsProjectLeader,
@@ -661,7 +662,7 @@ def patch(self, request, project_pk: int, user_to_leader_pk: int) -> Response:
661662

662663

663664
class DuplicateProjectView(APIView):
664-
permission_classes = [IsAuthenticated]
665+
permission_classes = [IsAuthenticated, CanBindProjectToProgram]
665666

666667
@swagger_auto_schema(
667668
request_body=ProjectDuplicateRequestSerializer,

0 commit comments

Comments
 (0)