Merge remote-tracking branch 'origin/main'
This commit is contained in:
@@ -0,0 +1,26 @@
|
|||||||
|
# Generated by Django 5.1.2 on 2024-12-14 10:38
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('account', '0005_remove_user_groups_remove_user_user_permissions'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='UserAddressModel',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=30)),
|
||||||
|
('address', models.TextField()),
|
||||||
|
('postal_code', models.CharField(max_length=10)),
|
||||||
|
('phone', models.CharField(max_length=11)),
|
||||||
|
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -98,3 +98,12 @@ class User(AbstractBaseUser, PermissionsMixin):
|
|||||||
return self.email
|
return self.email
|
||||||
|
|
||||||
|
|
||||||
|
class UserAddressModel(models.Model):
|
||||||
|
user = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
name = models.CharField(max_length=30)
|
||||||
|
address = models.TextField()
|
||||||
|
postal_code = models.CharField(max_length=10)
|
||||||
|
phone = models.CharField(max_length=11)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.user.phone}, {self.name}"
|
||||||
@@ -9,3 +9,15 @@ class ProfileSerializer(serializers.ModelSerializer):
|
|||||||
model = User
|
model = User
|
||||||
fields = ['first_name', 'last_name', 'email', 'profile_photo', 'phone']
|
fields = ['first_name', 'last_name', 'email', 'profile_photo', 'phone']
|
||||||
read_only_fields = ("phone",)
|
read_only_fields = ("phone",)
|
||||||
|
|
||||||
|
|
||||||
|
class UserAddressSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = UserAddressModel
|
||||||
|
fields = ['id', 'name', 'address', 'postal_code', 'phone']
|
||||||
|
|
||||||
|
def validate(self, data):
|
||||||
|
user = self.context['request'].user
|
||||||
|
if not user.is_authenticated:
|
||||||
|
raise serializers.ValidationError("You must be logged in to perform this action.")
|
||||||
|
return data
|
||||||
@@ -3,4 +3,10 @@ from . import views
|
|||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('profile', views.ProfileView.as_view()),
|
path('profile', views.ProfileView.as_view()),
|
||||||
|
path('address/create', views.CreateAddressView.as_view(), name='create-address'),
|
||||||
|
path('address/edit/<int:pk>', views.EditAddressView.as_view(), name='edit-address'),
|
||||||
|
path('address/delete/<int:pk>', views.DeleteAddressView.as_view(), name='delete-address'),
|
||||||
|
path('address/list', views.GetUserAddressesView.as_view(), name='list-addresses'),
|
||||||
|
path('address/<int:pk>', views.GetIDUserAddressView.as_view(), name='get-ID-address'),
|
||||||
|
|
||||||
]
|
]
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
from django.shortcuts import render
|
from django.shortcuts import render
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
from .serializers import ProfileSerializer
|
from rest_framework import generics, permissions, status
|
||||||
from rest_framework.permissions import IsAuthenticated
|
|
||||||
from rest_framework import status
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from .serializers import ProfileSerializer, UserAddressSerializer
|
||||||
|
from .models import UserAddressModel
|
||||||
|
from rest_framework.permissions import IsAuthenticated
|
||||||
class ProfileView(APIView):
|
class ProfileView(APIView):
|
||||||
serializer_class = ProfileSerializer
|
serializer_class = ProfileSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
user_ser = self.serializer_class(instance=request.user)
|
user_ser = self.serializer_class(instance=request.user)
|
||||||
return Response(user_ser.data, status=status.HTTP_200_OK)
|
return Response(user_ser.data, status=status.HTTP_200_OK)
|
||||||
@@ -19,4 +20,41 @@ class ProfileView(APIView):
|
|||||||
if user_ser.is_valid():
|
if user_ser.is_valid():
|
||||||
user_ser.save()
|
user_ser.save()
|
||||||
return Response(user_ser.data)
|
return Response(user_ser.data)
|
||||||
return Response(user_ser.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(user_ser.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
class CreateAddressView(generics.CreateAPIView):
|
||||||
|
queryset = UserAddressModel.objects.all()
|
||||||
|
serializer_class = UserAddressSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def perform_create(self, serializer):
|
||||||
|
serializer.save(user=self.request.user)
|
||||||
|
|
||||||
|
class EditAddressView(generics.UpdateAPIView):
|
||||||
|
queryset = UserAddressModel.objects.all()
|
||||||
|
serializer_class = UserAddressSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return UserAddressModel.objects.filter(user=self.request.user)
|
||||||
|
|
||||||
|
class DeleteAddressView(generics.DestroyAPIView):
|
||||||
|
queryset = UserAddressModel.objects.all()
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return UserAddressModel.objects.filter(user=self.request.user)
|
||||||
|
|
||||||
|
class GetUserAddressesView(generics.ListAPIView):
|
||||||
|
serializer_class = UserAddressSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return UserAddressModel.objects.filter(user=self.request.user)
|
||||||
|
|
||||||
|
class GetIDUserAddressView(generics.RetrieveAPIView):
|
||||||
|
serializer_class = UserAddressSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return UserAddressModel.objects.filter(user=self.request.user)
|
||||||
@@ -97,6 +97,7 @@ INSTALLED_APPS = [
|
|||||||
'product',
|
'product',
|
||||||
'account',
|
'account',
|
||||||
'entertainment',
|
'entertainment',
|
||||||
|
'ticket',
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ urlpatterns = [
|
|||||||
# path('comment/<int:pk>', views.CommentView.as_view(), name='comment-list'),
|
# path('comment/<int:pk>', views.CommentView.as_view(), name='comment-list'),
|
||||||
path('products/', include('product.urls')),
|
path('products/', include('product.urls')),
|
||||||
path('accounts/', include('account.urls')),
|
path('accounts/', include('account.urls')),
|
||||||
|
path('tickets/', include('ticket.urls')),
|
||||||
path('', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
|
path('', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
|
# Register your models here.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
from django.apps import AppConfig
|
||||||
|
|
||||||
|
|
||||||
|
class TicketConfig(AppConfig):
|
||||||
|
default_auto_field = 'django.db.models.BigAutoField'
|
||||||
|
name = 'ticket'
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Generated by Django 5.1.2 on 2024-12-14 10:51
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
initial = True
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Ticket',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('subject', models.CharField(max_length=255)),
|
||||||
|
('status', models.CharField(choices=[('open', 'Open'), ('in_progress', 'In Progress'), ('resolved', 'Resolved'), ('closed', 'Closed')], default='open', max_length=20)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('admin', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assigned_tickets', to=settings.AUTH_USER_MODEL)),
|
||||||
|
('customer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tickets', to=settings.AUTH_USER_MODEL)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Message',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('content', models.TextField()),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('sender', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||||
|
('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='messages', to='ticket.ticket')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
from django.db import models
|
||||||
|
from account.models import User
|
||||||
|
|
||||||
|
class Ticket(models.Model):
|
||||||
|
STATUS_CHOICES = [
|
||||||
|
('open', 'Open'),
|
||||||
|
('in_progress', 'In Progress'),
|
||||||
|
('resolved', 'Resolved'),
|
||||||
|
('closed', 'Closed'),
|
||||||
|
]
|
||||||
|
|
||||||
|
subject = models.CharField(max_length=255)
|
||||||
|
customer = models.ForeignKey(User, on_delete=models.CASCADE, related_name="tickets")
|
||||||
|
admin = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name="assigned_tickets")
|
||||||
|
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='open')
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.subject
|
||||||
|
|
||||||
|
class Message(models.Model):
|
||||||
|
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, related_name="messages")
|
||||||
|
sender = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
|
content = models.TextField()
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"Message by {self.sender.username} on {self.ticket.subject}"
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
from .models import Ticket, Message
|
||||||
|
|
||||||
|
class MessageSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Message
|
||||||
|
fields = '__all__'
|
||||||
|
|
||||||
|
class TicketSerializer(serializers.ModelSerializer):
|
||||||
|
messages = MessageSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Ticket
|
||||||
|
fields = '__all__'
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
from django.test import TestCase
|
||||||
|
|
||||||
|
# Create your tests here.
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
from django.urls import path
|
||||||
|
from .views import (
|
||||||
|
TicketCreateView,
|
||||||
|
TicketListView,
|
||||||
|
TicketDetailView,
|
||||||
|
MessageCreateView,
|
||||||
|
UpdateTicketStatusView
|
||||||
|
)
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path('create', TicketCreateView.as_view(), name='ticket-create'),
|
||||||
|
path('', TicketListView.as_view(), name='ticket-list'),
|
||||||
|
path('<int:pk>', TicketDetailView.as_view(), name='ticket-detail'),
|
||||||
|
path('<int:pk>/messages', MessageCreateView.as_view(), name='message-create'),
|
||||||
|
path('<int:pk>/update-status', UpdateTicketStatusView.as_view(), name='update-ticket-status'),
|
||||||
|
]
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
from rest_framework import generics, permissions
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework.views import APIView
|
||||||
|
from .models import Ticket, Message
|
||||||
|
from .serializers import TicketSerializer, MessageSerializer
|
||||||
|
|
||||||
|
class TicketCreateView(generics.CreateAPIView):
|
||||||
|
queryset = Ticket.objects.all()
|
||||||
|
serializer_class = TicketSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def perform_create(self, serializer):
|
||||||
|
serializer.save(customer=self.request.user)
|
||||||
|
|
||||||
|
|
||||||
|
class TicketListView(generics.ListAPIView):
|
||||||
|
serializer_class = TicketSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
user = self.request.user
|
||||||
|
if user.is_staff:
|
||||||
|
return Ticket.objects.all()
|
||||||
|
return Ticket.objects.filter(customer=user)
|
||||||
|
|
||||||
|
|
||||||
|
class TicketDetailView(generics.RetrieveAPIView):
|
||||||
|
queryset = Ticket.objects.all()
|
||||||
|
serializer_class = TicketSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
user = self.request.user
|
||||||
|
if user.is_staff:
|
||||||
|
return Ticket.objects.all()
|
||||||
|
return Ticket.objects.filter(customer=user)
|
||||||
|
|
||||||
|
|
||||||
|
class MessageCreateView(generics.CreateAPIView):
|
||||||
|
queryset = Message.objects.all()
|
||||||
|
serializer_class = MessageSerializer
|
||||||
|
permission_classes = [permissions.IsAuthenticated]
|
||||||
|
|
||||||
|
def perform_create(self, serializer):
|
||||||
|
ticket = serializer.validated_data.get('ticket')
|
||||||
|
if self.request.user != ticket.customer and not self.request.user.is_staff:
|
||||||
|
raise permissions.PermissionDenied("You are not authorized to send a message to this ticket.")
|
||||||
|
serializer.save(sender=self.request.user)
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateTicketStatusView(APIView):
|
||||||
|
permission_classes = [permissions.IsAdminUser]
|
||||||
|
|
||||||
|
def post(self, request, pk):
|
||||||
|
try:
|
||||||
|
ticket = Ticket.objects.get(pk=pk)
|
||||||
|
except Ticket.DoesNotExist:
|
||||||
|
return Response({"error": "Ticket not found"}, status=404)
|
||||||
|
|
||||||
|
new_status = request.data.get('status')
|
||||||
|
if new_status not in ['open', 'in_progress', 'resolved', 'closed']:
|
||||||
|
return Response({"error": "Invalid status"}, status=400)
|
||||||
|
ticket.status = new_status
|
||||||
|
ticket.save()
|
||||||
|
return Response({"message": "Ticket status updated successfully"})
|
||||||
Reference in New Issue
Block a user