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.db.models import Q from .serializers import RouteSerializer, CreateRouteSerializer, CitySerializer, CountrySerializer, PlanChangeSerializer, PricingSerializer, LeadSerializer, LeadResponseSerializer from api.auth.serializers import UserResponseSerializer from api.models import UserProfile from routes.models import Route, City, Country, Leads from sitemanagement.models import Pricing from api.utils.decorators import handle_exceptions from api.utils.emailSender import send_email 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( Q(name__icontains=search) | # поиск по английскому названию Q(russian_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): """ Создание новой заявки на перевозку """ # добавляем текущего пользователя в данные data = request.data.copy() data['moving_user'] = request.user.id # проверяем существование и доступность маршрута try: route = Route.objects.get(id=data.get('route')) if route.owner.id == request.user.id: print("Error: User trying to respond to their own route") return Response( {"error": "Вы не можете откликнуться на собственную заявку"}, status=status.HTTP_400_BAD_REQUEST ) except Route.DoesNotExist: print("Route not found for ID:", data.get('route')) return Response( {"error": "Указанный маршрут не найден"}, status=status.HTTP_404_NOT_FOUND ) serializer = LeadSerializer(data=data) is_valid = serializer.is_valid() if is_valid: try: lead: Leads = 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, } } # отправляем емаил владельцу маршрута try: route_owner_email = route.owner.email email_subject = f"Новая заявка на перевозку #{lead.id}" email_content = f"""

Получена новая заявка на перевозку

Детали заявки:

""" email_result = send_email( to=[route_owner_email], subject=email_subject, html_content=email_content ) if "Ошибка" in email_result: print(f"Warning: Failed to send email notification: {email_result}") except Exception as email_error: print(f"Warning: Error while sending email notification: {str(email_error)}") return Response(response_data, status=status.HTTP_201_CREATED) except Exception as e: return Response( { "status": "error", "message": "Ошибка при сохранении заявки", "error": str(e) }, status=status.HTTP_500_INTERNAL_SERVER_ERROR ) else: return Response( { "status": "error", "message": "Ошибка валидации данных", "errors": serializer.errors }, status=status.HTTP_400_BAD_REQUEST ) @action(detail=False, methods=['get']) @handle_exceptions def get_leads(self, request): """Получаем список заявок на перевозку""" leads = Leads.objects.select_related( 'route', 'route__from_city', 'route__to_city', 'route__from_city__country', 'route__to_city__country', 'route__owner' ).filter(moving_user=request.user).order_by('-created_at') return Response( LeadResponseSerializer(leads, many=True).data, status=status.HTTP_200_OK )