Browse Source

transactions views and serializers added for admin panel

master
Mohsen Taba 2 months ago
parent
commit
be2c84af77
  1. 243
      apps/transaction/serializers.py
  2. 11
      apps/transaction/urls.py
  3. 51
      apps/transaction/views.py

243
apps/transaction/serializers.py

@ -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'
]

11
apps/transaction/urls.py

@ -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'),
]
]

51
apps/transaction/views.py

@ -410,3 +410,54 @@ class TransactionReceiptsListView(generics.ListAPIView):
) )
return TransactionReceipt.objects.filter(transaction=transaction) return TransactionReceipt.objects.filter(transaction=transaction)
# =============================================================================
# Admin Panel ViewSet
# =============================================================================
from rest_framework.viewsets import ModelViewSet
from rest_framework.permissions import IsAdminUser
from apps.transaction.serializers import AdminTransactionSerializer
from django.db.models import Q
class AdminTransactionViewSet(ModelViewSet):
"""
Admin-only viewset for viewing and updating transactions.
Only GET (list/retrieve) and PATCH (update status) are allowed.
"""
queryset = TransactionParticipant.objects.all()
serializer_class = AdminTransactionSerializer
permission_classes = [IsAuthenticated, IsAdminUser]
authentication_classes = [TokenAuthentication]
http_method_names = ['get', 'patch', 'head', 'options']
def get_queryset(self):
queryset = TransactionParticipant.objects.all().select_related('user', 'course').prefetch_related('receipts', 'participant_infos')
# Search
search_query = self.request.query_params.get('search', None)
if search_query:
queryset = queryset.filter(
Q(user__fullname__icontains=search_query) |
Q(user__email__icontains=search_query) |
Q(course__title__icontains=search_query)
)
# Status filter
status_param = self.request.query_params.get('status', None)
if status_param:
queryset = queryset.filter(status=status_param)
# Course filter
course_id = self.request.query_params.get('course', None)
if course_id:
queryset = queryset.filter(course_id=course_id)
# Payment method filter
payment_method = self.request.query_params.get('payment_method', None)
if payment_method:
queryset = queryset.filter(payment_method=payment_method)
return queryset.order_by('-created_at')
Loading…
Cancel
Save