from rest_framework import status from rest_framework.viewsets import ViewSet from rest_framework.permissions import IsAuthenticated from rest_framework.decorators import action from rest_framework.response import Response from django.shortcuts import get_object_or_404 from django.core.validators import validate_email from django.core.exceptions import ValidationError from django.db import models from django.contrib.auth.models import User from api.auth.serializers import UserResponseSerializer from api.models import UserProfile from api.utils.decorators import handle_exceptions from routes.models import Route, City, Country, Leads from sitemanagement.models import Pricing from .serializers import RouteSerializer, CreateRouteSerializer, CitySerializer, CountrySerializer, PlanChangeSerializer, PricingSerializer, LeadSerializer class UserDataView(ViewSet): """Эндпоинт для наполнения стора фронта данными""" permission_classes = [IsAuthenticated] def initial(self, request, *args, **kwargs): try: super().initial(request, *args, **kwargs) except Exception as e: print(f"Authentication error: {e}") raise @action(detail=False, methods=['get']) @handle_exceptions def user_data(self, request): user = request.user try: user_data = UserResponseSerializer(user).data return Response(user_data, status=status.HTTP_200_OK) except UserProfile.DoesNotExist: return Response( {"error": "User profile not found"}, status=status.HTTP_404_NOT_FOUND ) class AccountActionsView(ViewSet): """Действия в аккаунте пользователя: - PATCH данных в account/main - POST новых заявок""" permission_classes = [IsAuthenticated] @action(detail=False, methods=['patch']) @handle_exceptions def change_data_main_tab(self, request): """Обновление данных на главной странице аккаунта""" user = request.user user_profile = get_object_or_404(UserProfile, user=user) # обновляем данные пользователя if 'firstName' in request.data: user.first_name = request.data['firstName'] if 'lastName' in request.data: user.last_name = request.data['lastName'] if 'email' in request.data: email = request.data['email'] validate_email(email) # handle_exceptions обработает ValidationError user.email = email user.username = email # обновляем номер телефона if 'phone_number' in request.data: phone = request.data['phone_number'] if phone: if len(phone) < 13: # +375XXXXXXXXX raise ValidationError("Номер телефона слишком короткий") if len(phone) > 18: raise ValidationError("Номер телефона слишком длинный") # проверка на уникальность if UserProfile.objects.filter(phone_number=phone).exclude(user=user).exists(): raise ValidationError("Этот номер телефона уже используется") user_profile.phone_number = phone # сохраняем изменения user.save() user_profile.save() return Response({ "message": "Данные успешно обновлены", "user": UserResponseSerializer(user).data }, status=status.HTTP_200_OK) @action(detail=False, methods=['get']) @handle_exceptions def user_routes(self, request): """Получаем список заявок юзера""" user = request.user routes = Route.objects.filter(owner=user) return Response(RouteSerializer(routes, many=True).data, status=status.HTTP_200_OK) @action(detail=False, methods=['post']) @handle_exceptions def create_route(self, request): """Создаем новую заявку""" serializer = CreateRouteSerializer(data=request.data, context={'request': request}) if not serializer.is_valid(): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) route = serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) class CityView(ViewSet): """Получаем список городов из базы для автокомплита""" @action(detail=False, methods=['get']) @handle_exceptions def get_cities(self, request): # получаем параметр country_id из query params country_id = request.query_params.get('country_id') # базовый QuerySet cities = City.objects.all() # фильтруем города по стране, если указан country_id if country_id: cities = cities.filter(country_id=country_id) # поиск по названию города search = request.query_params.get('search') if search: cities = cities.filter(name__icontains=search) # ограничиваем количество результатов и сортируем по имени cities = cities.order_by('name')[:100] return Response(CitySerializer(cities, many=True).data, status=status.HTTP_200_OK) class CountryView(ViewSet): """Получаем список стран из базы для автокомплита""" @action(detail=False, methods=['get']) @handle_exceptions def get_countries(self, request): # базовый QuerySet countries = Country.objects.all() # поиск по названию страны search = request.query_params.get('search') if search: countries = countries.filter( models.Q(international_name__icontains=search) | models.Q(official_name__icontains=search) ) # сортируем по международному названию countries = countries.order_by('international_name') return Response(CountrySerializer(countries, many=True).data, status=status.HTTP_200_OK) class ChangeUserMembership(ViewSet): """Меняем тарифный план пользователя""" @action(detail=False, methods=['post']) @handle_exceptions def change_plan(self, request): """Меняем пользователю тарифный план""" user = request.user user_profile = get_object_or_404(UserProfile, user=user) serializer = PlanChangeSerializer(user_profile, data=request.data) if serializer.is_valid(): serializer.save() return Response({"message": "Тариф успешно изменен"}, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) class GetMembershipData(ViewSet): """Получаем все тарифные планы""" @action(detail=False, methods=['get']) @handle_exceptions def get_pricing_data(self, request): """Получаем данные по тарифам""" pricing_data = Pricing.objects.all().order_by('price') serializer = PricingSerializer(pricing_data, many=True) return Response(serializer.data, status=status.HTTP_200_OK) class LeadViewSet(ViewSet): """ViewSet для работы с заявками на перевозку""" permission_classes = [IsAuthenticated] @action(detail=False, methods=['post']) @handle_exceptions def send_lead(self, request, id): """ Создание новой заявки на перевозку """ # добавляем текущего пользователя в данные data = request.data.copy() data['moving_user'] = request.user.id # проверяем существование и доступность маршрута try: route = Route.objects.get(id=data.get('route')) if route.owner == request.user: return Response( {"error": "Вы не можете откликнуться на собственную заявку"}, status=status.HTTP_400_BAD_REQUEST ) except Route.DoesNotExist: return Response( {"error": "Указанный маршрут не найден"}, status=status.HTTP_404_NOT_FOUND ) serializer = LeadSerializer(data=data) if serializer.is_valid(): lead = serializer.save() # собираем ответ с данными о заявке для фронта response_data = { "status": "success", "message": "Заявка успешно создана", "data": { "id": lead.id, "route_id": lead.route.id, "moving_price": lead.moving_price, "moving_date": lead.moving_date, } } return Response(response_data, status=status.HTTP_201_CREATED) return Response( { "status": "error", "message": "Ошибка валидации данных", "errors": serializer.errors }, status=status.HTTP_400_BAD_REQUEST )