feat(projects): support members and align rate payloads

This commit is contained in:
2026-04-24 22:20:57 +03:30
parent a44995017b
commit e7de587f59
5 changed files with 129 additions and 65 deletions

View File

@@ -95,9 +95,13 @@ class ProjectViewSet(ModelViewSet):
"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
members_data = serializer.validated_data.pop("members", [])
workspace = get_object_or_404(Workspace, id=serializer.validated_data["workspace"], is_deleted=False)
client = get_object_or_404(Client, id=serializer.validated_data.get("client"), is_deleted=False)
client_id = serializer.validated_data.get("client")
client = get_object_or_404(Client, id=client_id, is_deleted=False) if client_id else None
project = create_project(
user=request.user,
workspace=workspace,
@@ -106,6 +110,13 @@ class ProjectViewSet(ModelViewSet):
description=serializer.validated_data.get("description", ""),
color=serializer.validated_data.get("color", "")
)
for member in members_data:
add_project_member(
project=project,
user_id=member["user_id"],
role=member["role"]
)
output_serializer = ProjectSerializer(project)
return Response(output_serializer.data, status=status.HTTP_201_CREATED)
@@ -119,11 +130,41 @@ class ProjectViewSet(ModelViewSet):
serializer = self.get_serializer(data=request.data, partial=partial)
serializer.is_valid(raise_exception=True)
members_data = serializer.validated_data.pop("members", None)
updated_project = update_project(
project=project,
**serializer.validated_data
)
# Full sync of project members if array is provided
if members_data is not None:
current_memberships = {str(m.user_id): m for m in updated_project.memberships.filter(is_deleted=False)}
incoming_users = {str(m['user_id']) for m in members_data}
# Add or Update roles
for member in members_data:
user_id_str = str(member['user_id'])
if user_id_str in current_memberships:
# Reactivate or update role
update_project_member(
current_memberships[user_id_str],
role=member['role'],
is_active=True
)
else:
# Add new member
add_project_member(
project=updated_project,
user_id=member['user_id'],
role=member['role']
)
# Deactivate omitted members
for user_id_str, membership in current_memberships.items():
if user_id_str not in incoming_users:
update_project_member(membership, is_active=False)
output_serializer = ProjectSerializer(updated_project)
return Response(output_serializer.data, status=status.HTTP_200_OK)
@@ -247,12 +288,12 @@ class ProjectRateViewSet(BaseProjectNestedViewSet):
project_id = serializer.validated_data["project_id"]
self.verify_manager_access(project_id)
project = get_object_or_404(Project, id=project_id, is_deleted=False)
rate = create_project_rate(
project=project,
amount=serializer.validated_data["amount"],
currency=serializer.validated_data.get("currency", "USD")
)
project = get_object_or_404(Project, id=project_id, is_deleted=False)
rate = create_project_rate(
project=project,
hourly_rate=serializer.validated_data["hourly_rate"],
currency=serializer.validated_data.get("currency", "USD")
)
return Response(ProjectRateSerializer(rate).data, status=status.HTTP_201_CREATED)
def update(self, request, *args, **kwargs):
@@ -293,13 +334,13 @@ class ProjectUserRateViewSet(BaseProjectNestedViewSet):
project_id = serializer.validated_data["project_id"]
self.verify_manager_access(project_id)
project = get_object_or_404(Project, id=project_id, is_deleted=False)
user_rate = create_project_user_rate(
project=project,
user_id=serializer.validated_data["user_id"],
amount=serializer.validated_data["amount"],
currency=serializer.validated_data.get("currency", "USD")
)
project = get_object_or_404(Project, id=project_id, is_deleted=False)
user_rate = create_project_user_rate(
project=project,
user_id=serializer.validated_data["user_id"],
hourly_rate=serializer.validated_data["hourly_rate"],
currency=serializer.validated_data.get("currency", "USD")
)
return Response(ProjectUserRateSerializer(user_rate).data, status=status.HTTP_201_CREATED)
def update(self, request, *args, **kwargs):