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 . import views |
||||
|
from .views import AdminTransactionViewSet |
||||
|
|
||||
|
|
||||
|
router = DefaultRouter() |
||||
|
router.register(r'admin/transactions', AdminTransactionViewSet, basename='admin-transactions') |
||||
|
|
||||
urlpatterns = [ |
urlpatterns = [ |
||||
|
path('', include(router.urls)), |
||||
re_path(r'(?P<slug>[\w-]+)/join/$', views.TransactionParticipantCreateView.as_view(), name='transaction-participant-create'), |
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('list/', views.TransactiontListView.as_view(), name='transaction-list'), |
||||
path('<int:pk>/delete/', views.SoftDeleteTransactionParticipantView.as_view(), name='soft-delete-transaction-participant'), |
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/upload/', views.UploadTransactionReceiptsView.as_view(), name='upload-transaction-receipts'), |
||||
path('<int:transaction_id>/receipts/', views.TransactionReceiptsListView.as_view(), name='transaction-receipts-list'), |
path('<int:transaction_id>/receipts/', views.TransactionReceiptsListView.as_view(), name='transaction-receipts-list'), |
||||
|
|
||||
] |
|
||||
|
|
||||
|
] |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue