3 changed files with 205 additions and 100 deletions
@ -1,96 +1,147 @@ |
|||
|
|||
from rest_framework import serializers |
|||
|
|||
from apps.transaction.models import TransactionParticipant, ParticipantInfo, TransactionReceipt |
|||
from apps.course.serializers import CourseDetailSerializer |
|||
from utils import FileFieldSerializer |
|||
|
|||
|
|||
|
|||
|
|||
class ParticipantInfoSerializer(serializers.ModelSerializer): |
|||
phone_number = serializers.CharField(max_length=30) |
|||
|
|||
class Meta: |
|||
model = ParticipantInfo |
|||
fields = ['fullname', 'email', 'phone_number', 'gender', 'birthdate'] |
|||
|
|||
def validate_phone_number(self, value): |
|||
return value |
|||
|
|||
|
|||
class TransactionParticipantSerializer(serializers.ModelSerializer): |
|||
participant_infos = ParticipantInfoSerializer(many=True) |
|||
|
|||
class Meta: |
|||
model = TransactionParticipant |
|||
fields = ['participant_infos'] |
|||
|
|||
|
|||
def create(self, validated_data): |
|||
participant_infos_data = validated_data.pop('participant_infos', []) |
|||
transaction_participant = TransactionParticipant.objects.create(**validated_data) |
|||
|
|||
for participant_info_data in participant_infos_data: |
|||
ParticipantInfo.objects.create(transaction_participant=transaction_participant, **participant_info_data) |
|||
|
|||
return transaction_participant |
|||
|
|||
|
|||
|
|||
class TransactionListSerializer(serializers.ModelSerializer): |
|||
course = serializers.SerializerMethodField() |
|||
receipts = serializers.SerializerMethodField() |
|||
|
|||
class Meta: |
|||
model = TransactionParticipant |
|||
fields = ['id', 'course', 'status', 'price', 'receipts', 'created_at', 'updated_at'] |
|||
|
|||
def get_course(self, obj): |
|||
return CourseDetailSerializer(obj.course, context=self.context).data |
|||
|
|||
def get_receipts(self, obj): |
|||
receipts = obj.receipts.all() |
|||
return TransactionReceiptSerializer(receipts, many=True, context=self.context).data |
|||
|
|||
|
|||
class TransactionReceiptSerializer(serializers.ModelSerializer): |
|||
""" |
|||
Serializer for uploading payment receipts |
|||
Uses FileFieldSerializer to handle pre-uploaded files from /upload-tmp-media/ |
|||
""" |
|||
file = FileFieldSerializer() |
|||
|
|||
class Meta: |
|||
model = TransactionReceipt |
|||
fields = ['id', 'file', 'description', 'uploaded_at'] |
|||
read_only_fields = ['id', 'uploaded_at'] |
|||
|
|||
|
|||
class UploadReceiptsSerializer(serializers.Serializer): |
|||
""" |
|||
Serializer for uploading multiple receipt files for a transaction. |
|||
Files should be pre-uploaded using /upload-tmp-media/ endpoint, |
|||
then their URLs should be sent here. |
|||
""" |
|||
files = serializers.ListField( |
|||
child=FileFieldSerializer(), |
|||
allow_empty=False, |
|||
max_length=10, |
|||
help_text="List of file URLs (max 10 files) - files should be pre-uploaded via /upload-tmp-media/" |
|||
) |
|||
description = serializers.CharField( |
|||
required=False, |
|||
allow_blank=True, |
|||
max_length=1000, |
|||
help_text="Optional description for the receipts" |
|||
) |
|||
|
|||
def validate_files(self, files): |
|||
""" |
|||
Validate uploaded file URLs |
|||
""" |
|||
if len(files) > 10: |
|||
raise serializers.ValidationError("You can upload a maximum of 10 files.") |
|||
|
|||
return files |
|||
|
|||
from rest_framework import serializers |
|||
|
|||
from apps.transaction.models import TransactionParticipant, ParticipantInfo, TransactionReceipt |
|||
from apps.course.serializers import CourseDetailSerializer |
|||
from utils import FileFieldSerializer |
|||
|
|||
|
|||
|
|||
|
|||
class ParticipantInfoSerializer(serializers.ModelSerializer): |
|||
phone_number = serializers.CharField(max_length=30) |
|||
|
|||
class Meta: |
|||
model = ParticipantInfo |
|||
fields = ['fullname', 'email', 'phone_number', 'gender', 'birthdate'] |
|||
|
|||
def validate_phone_number(self, value): |
|||
return value |
|||
|
|||
|
|||
class TransactionParticipantSerializer(serializers.ModelSerializer): |
|||
participant_infos = ParticipantInfoSerializer(many=True) |
|||
|
|||
class Meta: |
|||
model = TransactionParticipant |
|||
fields = ['participant_infos'] |
|||
|
|||
|
|||
def create(self, validated_data): |
|||
participant_infos_data = validated_data.pop('participant_infos', []) |
|||
transaction_participant = TransactionParticipant.objects.create(**validated_data) |
|||
|
|||
for participant_info_data in participant_infos_data: |
|||
ParticipantInfo.objects.create(transaction_participant=transaction_participant, **participant_info_data) |
|||
|
|||
return transaction_participant |
|||
|
|||
|
|||
|
|||
class TransactionListSerializer(serializers.ModelSerializer): |
|||
course = serializers.SerializerMethodField() |
|||
receipts = serializers.SerializerMethodField() |
|||
|
|||
class Meta: |
|||
model = TransactionParticipant |
|||
fields = ['id', 'course', 'status', 'price', 'receipts', 'created_at', 'updated_at'] |
|||
|
|||
def get_course(self, obj): |
|||
return CourseDetailSerializer(obj.course, context=self.context).data |
|||
|
|||
def get_receipts(self, obj): |
|||
receipts = obj.receipts.all() |
|||
return TransactionReceiptSerializer(receipts, many=True, context=self.context).data |
|||
|
|||
|
|||
class TransactionReceiptSerializer(serializers.ModelSerializer): |
|||
""" |
|||
Serializer for uploading payment receipts |
|||
Uses FileFieldSerializer to handle pre-uploaded files from /upload-tmp-media/ |
|||
""" |
|||
file = FileFieldSerializer() |
|||
|
|||
class Meta: |
|||
model = TransactionReceipt |
|||
fields = ['id', 'file', 'description', 'uploaded_at'] |
|||
read_only_fields = ['id', 'uploaded_at'] |
|||
|
|||
|
|||
class UploadReceiptsSerializer(serializers.Serializer): |
|||
""" |
|||
Serializer for uploading multiple receipt files for a transaction. |
|||
Files should be pre-uploaded using /upload-tmp-media/ endpoint, |
|||
then their URLs should be sent here. |
|||
""" |
|||
files = serializers.ListField( |
|||
child=FileFieldSerializer(), |
|||
allow_empty=False, |
|||
max_length=10, |
|||
help_text="List of file URLs (max 10 files) - files should be pre-uploaded via /upload-tmp-media/" |
|||
) |
|||
description = serializers.CharField( |
|||
required=False, |
|||
allow_blank=True, |
|||
max_length=1000, |
|||
help_text="Optional description for the receipts" |
|||
) |
|||
|
|||
def validate_files(self, files): |
|||
""" |
|||
Validate uploaded file URLs |
|||
""" |
|||
if len(files) > 10: |
|||
raise serializers.ValidationError("You can upload a maximum of 10 files.") |
|||
|
|||
return files |
|||
|
|||
|
|||
# ============================================================================= |
|||
# Admin Panel Serializers |
|||
# ============================================================================= |
|||
|
|||
class AdminTransactionReceiptSerializer(serializers.ModelSerializer): |
|||
file = serializers.SerializerMethodField() |
|||
|
|||
class Meta: |
|||
model = TransactionReceipt |
|||
fields = ['id', 'file', 'description', 'uploaded_at'] |
|||
|
|||
def get_file(self, obj): |
|||
request = self.context.get('request') |
|||
if obj.file: |
|||
if request: |
|||
return request.build_absolute_uri(obj.file.url) |
|||
return obj.file.url |
|||
return None |
|||
|
|||
|
|||
class AdminParticipantInfoSerializer(serializers.ModelSerializer): |
|||
phone_number = serializers.CharField(read_only=True) |
|||
|
|||
class Meta: |
|||
model = ParticipantInfo |
|||
fields = ['id', 'fullname', 'email', 'phone_number', 'gender', 'birthdate'] |
|||
|
|||
|
|||
class AdminTransactionSerializer(serializers.ModelSerializer): |
|||
user_name = serializers.CharField(source='user.fullname', read_only=True) |
|||
user_email = serializers.CharField(source='user.email', read_only=True) |
|||
course_title = serializers.CharField(source='course.title', read_only=True) |
|||
receipts = AdminTransactionReceiptSerializer(many=True, read_only=True) |
|||
participant_infos = AdminParticipantInfoSerializer(many=True, read_only=True) |
|||
|
|||
class Meta: |
|||
model = TransactionParticipant |
|||
fields = [ |
|||
'id', 'user', 'user_name', 'user_email', |
|||
'course', 'course_title', |
|||
'price', 'status', 'payment_method', |
|||
'participant_infos', 'receipts', |
|||
'created_at', 'updated_at' |
|||
] |
|||
read_only_fields = [ |
|||
'id', 'user', 'user_name', 'user_email', |
|||
'course', 'course_title', 'price', 'payment_method', |
|||
'participant_infos', 'receipts', 'created_at', 'updated_at' |
|||
] |
|||
@ -1,16 +1,19 @@ |
|||
|
|||
from django.urls import path, re_path |
|||
from django.urls import path, re_path, include |
|||
from rest_framework.routers import DefaultRouter |
|||
|
|||
from . import views |
|||
from .views import AdminTransactionViewSet |
|||
|
|||
|
|||
router = DefaultRouter() |
|||
router.register(r'admin/transactions', AdminTransactionViewSet, basename='admin-transactions') |
|||
|
|||
urlpatterns = [ |
|||
path('', include(router.urls)), |
|||
re_path(r'(?P<slug>[\w-]+)/join/$', views.TransactionParticipantCreateView.as_view(), name='transaction-participant-create'), |
|||
path('list/', views.TransactiontListView.as_view(), name='transaction-list'), |
|||
path('<int:pk>/delete/', views.SoftDeleteTransactionParticipantView.as_view(), name='soft-delete-transaction-participant'), |
|||
path('<int:transaction_id>/receipts/upload/', views.UploadTransactionReceiptsView.as_view(), name='upload-transaction-receipts'), |
|||
path('<int:transaction_id>/receipts/', views.TransactionReceiptsListView.as_view(), name='transaction-receipts-list'), |
|||
|
|||
] |
|||
|
|||
] |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue