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

@@ -6,12 +6,18 @@ from apps.projects.models import (
ProjectRate,
ProjectUserRate,
)
from core.serializers.mini import UserMiniSerializer
class ProjectMemberInputSerializer(serializers.Serializer):
user_id = serializers.UUIDField()
role = serializers.ChoiceField(choices=ProjectMembership.Role.choices, default=ProjectMembership.Role.MEMBER)
class ProjectSerializer(BaseModelSerializer):
"""
Serializer for retrieving and representing project details.
"""
my_role = serializers.SerializerMethodField()
members = serializers.SerializerMethodField()
class Meta:
model = Project
fields = BaseModelSerializer.Meta.fields + (
@@ -21,50 +27,61 @@ class ProjectSerializer(BaseModelSerializer):
"description",
"is_archived",
"color",
"my_role",
"members",
)
read_only_fields = fields
def get_my_role(self, obj):
request = self.context.get("request")
if not request or not request.user.is_authenticated:
return None
membership = obj.memberships.filter(user=request.user, is_active=True, is_deleted=False).first()
return getattr(membership, "role", None)
def get_members(self, obj):
"""
Returns active project members in the response
"""
active_memberships = obj.memberships.filter(is_active=True, is_deleted=False)
return ProjectMembershipSerializer(active_memberships, many=True).data
def to_representation(self, instance):
representation = super().to_representation(instance)
if instance.client:
representation['client'] = {
'id': instance.client.id,
'name': instance.client.name
}
return representation
class ProjectCreateSerializer(serializers.Serializer):
"""
Serializer for validating input data during project creation.
We use a standard Serializer here to decouple validation from the model,
keeping business logic in the service layer.
"""
workspace = serializers.UUIDField()
name = serializers.CharField(max_length=255)
client = serializers.UUIDField(required=False, allow_null=True)
description = serializers.CharField(required=False, allow_blank=True, default="")
color = serializers.CharField(max_length=7, required=False, allow_blank=True, default="")
members = ProjectMemberInputSerializer(many=True, required=False)
class ProjectUpdateSerializer(serializers.Serializer):
"""
Serializer for validating input data during project updates.
"""
name = serializers.CharField(max_length=255, required=False)
clien = serializers.UUIDField(required=False, allow_null=True)
client = serializers.UUIDField(required=False, allow_null=True)
description = serializers.CharField(required=False, allow_blank=True)
color = serializers.CharField(max_length=7, required=False, allow_blank=True)
is_archived = serializers.BooleanField(required=False)
members = ProjectMemberInputSerializer(many=True, required=False)
class ProjectMembershipSerializer(BaseModelSerializer):
user_details = UserMiniSerializer(read_only=True)
class Meta:
model = ProjectMembership
fields = BaseModelSerializer.Meta.fields + (
"project",
"user",
"user_details",
"role",
"is_active",
)
@@ -93,15 +110,15 @@ class ProjectRateSerializer(BaseModelSerializer):
read_only_fields = fields
class ProjectRateCreateSerializer(serializers.Serializer):
project_id = serializers.UUIDField()
amount = serializers.DecimalField(max_digits=10, decimal_places=2)
currency = serializers.CharField(max_length=3, default="USD")
class ProjectRateUpdateSerializer(serializers.Serializer):
amount = serializers.DecimalField(max_digits=10, decimal_places=2, required=False)
currency = serializers.CharField(max_length=3, required=False)
class ProjectRateCreateSerializer(serializers.Serializer):
project_id = serializers.UUIDField()
hourly_rate = serializers.DecimalField(max_digits=10, decimal_places=2)
currency = serializers.CharField(max_length=3, default="USD")
class ProjectRateUpdateSerializer(serializers.Serializer):
hourly_rate = serializers.DecimalField(max_digits=10, decimal_places=2, required=False)
currency = serializers.CharField(max_length=3, required=False)
class ProjectUserRateSerializer(BaseModelSerializer):
@@ -116,13 +133,13 @@ class ProjectUserRateSerializer(BaseModelSerializer):
read_only_fields = fields
class ProjectUserRateCreateSerializer(serializers.Serializer):
project_id = serializers.UUIDField()
user_id = serializers.UUIDField()
hourly_rate = serializers.DecimalField(max_digits=10, decimal_places=2)
class ProjectUserRateCreateSerializer(serializers.Serializer):
project_id = serializers.UUIDField()
user_id = serializers.UUIDField()
hourly_rate = serializers.DecimalField(max_digits=10, decimal_places=2)
currency = serializers.CharField(max_length=3, default="USD")
class ProjectUserRateUpdateSerializer(serializers.Serializer):
hourly_rate = serializers.DecimalField(max_digits=10, decimal_places=2, required=False)
currency = serializers.CharField(max_length=3, required=False)
class ProjectUserRateUpdateSerializer(serializers.Serializer):
hourly_rate = serializers.DecimalField(max_digits=10, decimal_places=2, required=False)
currency = serializers.CharField(max_length=3, required=False)