diff --git a/backend/api/account/client/serializers.py b/backend/api/account/client/serializers.py index f01679d..3c50ccc 100644 --- a/backend/api/account/client/serializers.py +++ b/backend/api/account/client/serializers.py @@ -1,7 +1,9 @@ from rest_framework import serializers -from routes.models import Route, City +from routes.models import Route, City, Country from django.conf import settings -from routes.constants.routeChoices import cargo_type_choices, type_transport_choices +from routes.constants.routeChoices import cargo_type_choices, type_transport_choices, owner_type_choices +from api.models import UserProfile +from django.shortcuts import get_object_or_404 import pytz class RouteSerializer(serializers.ModelSerializer): @@ -72,3 +74,111 @@ class RouteSerializer(serializers.ModelSerializer): def get_formatted_transport(self, obj): transport_types = dict(type_transport_choices) return transport_types.get(obj.type_transport, obj.type_transport) + + +class CreateRouteSerializer(serializers.ModelSerializer): + country_from = serializers.CharField(write_only=True) + city_from = serializers.CharField(write_only=True) + country_to = serializers.CharField(write_only=True) + city_to = serializers.CharField(write_only=True) + departure = serializers.DateTimeField(source='departure_DT') + arrival = serializers.DateTimeField(source='arrival_DT') + transport = serializers.ChoiceField(choices=type_transport_choices, source='type_transport') + email_notification = serializers.BooleanField(source='receive_msg_by_email') + contact_number = serializers.CharField(write_only=True) + owner_type = serializers.ChoiceField(choices=[('sender', 'Отправитель'), ('deliverer', 'Перевозчик')]) + + class Meta: + model = Route + fields = [ + 'transport', + 'country_from', + 'city_from', + 'country_to', + 'city_to', + 'cargo_type', + 'departure', + 'arrival', + 'phone_number', + 'comment', + 'email_notification', + 'owner_type', + ] + + def validate_phone_number(self, value): + if len(value) < 13: # +375XXXXXXXXX + raise serializers.ValidationError("Номер телефона слишком короткий") + if len(value) > 18: + raise serializers.ValidationError("Номер телефона слишком длинный") + return value + + def validate(self, data): + # проверяем что дата прибытия позже даты отправления + if data['departure_DT'] >= data['arrival_DT']: + raise serializers.ValidationError( + "Дата прибытия должна быть позже даты отправления" + ) + + # проверяем существование стран + try: + country_from = Country.objects.get(international_name=data['country_from']) + except Country.DoesNotExist: + raise serializers.ValidationError({ + "country_from": f"Страна '{data['country_from']}' не найдена в базе данных" + }) + + try: + country_to = Country.objects.get(international_name=data['country_to']) + except Country.DoesNotExist: + raise serializers.ValidationError({ + "country_to": f"Страна '{data['country_to']}' не найдена в базе данных" + }) + + # проверяем существование городов в указанных странах + try: + City.objects.get(name=data['city_from'], country=country_from) + except City.DoesNotExist: + raise serializers.ValidationError({ + "city_from": f"Город '{data['city_from']}' не найден в стране {country_from}" + }) + + try: + City.objects.get(name=data['city_to'], country=country_to) + except City.DoesNotExist: + raise serializers.ValidationError({ + "city_to": f"Город '{data['city_to']}' не найден в стране {country_to}" + }) + + return data + + def create(self, validated_data): + # получаем города и страны + country_from = Country.objects.get(international_name=validated_data.pop('country_from')) + country_to = Country.objects.get(international_name=validated_data.pop('country_to')) + + from_city = City.objects.get(name=validated_data.pop('city_from'), country=country_from) + to_city = City.objects.get(name=validated_data.pop('city_to'), country=country_to) + + # обновляем номер телефона в профиле пользователя + contact_number = validated_data.pop('contact_number') + user_profile = get_object_or_404(UserProfile, user=self.context['request'].user) + + # проверяем, не используется ли этот номер другим пользователем + if UserProfile.objects.filter(phone_number=contact_number).exclude(user=self.context['request'].user).exists(): + raise serializers.ValidationError({ + "contact_number": "Этот номер телефона уже используется другим пользователем" + }) + + user_profile.phone_number = contact_number + user_profile.save() + + # создаем маршрут + route = Route.objects.create( + from_city=from_city, + to_city=to_city, + owner=self.context['request'].user, + **validated_data # owner_type приходит с фронта + ) + + return route + \ No newline at end of file diff --git a/backend/api/account/client/views.py b/backend/api/account/client/views.py index 1aa59af..7daedf1 100644 --- a/backend/api/account/client/views.py +++ b/backend/api/account/client/views.py @@ -12,7 +12,7 @@ from api.auth.serializers import UserResponseSerializer from api.models import UserProfile from api.utils.decorators import handle_exceptions from routes.models import Route -from .serializers import RouteSerializer +from .serializers import RouteSerializer, CreateRouteSerializer class UserDataView(ViewSet): permission_classes = [IsAuthenticated] @@ -92,3 +92,11 @@ class AccountActionsView(ViewSet): 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_sender_route(self, request): + serializer = CreateRouteSerializer(data=request.data) + serializer.is_valid(raise_exception=True) + serializer.save() + return Response(serializer.data, status=status.HTTP_201_CREATED) diff --git a/backend/api/urls.py b/backend/api/urls.py index 298dd6e..c8877b3 100644 --- a/backend/api/urls.py +++ b/backend/api/urls.py @@ -23,5 +23,6 @@ urlpatterns = [ path ("v1/user/", UserDataView.as_view({'get': 'user_data'}), name="user"), path("v1/account/change_main_data/", AccountActionsView.as_view({'patch':'change_data_main_tab'}), name='change_data_main_tab'), - path("v1/account/routes/", AccountActionsView.as_view({'get':'user_routes'}), name='user_routes') + path("v1/account/routes/", AccountActionsView.as_view({'get':'user_routes'}), name='user_routes'), + path("v1/account/create_sender/", AccountActionsView.as_view({'post':'create_sender_route'}), name='create_sender_route') ] \ No newline at end of file diff --git a/frontend/app/(urls)/account/create-as-sender/page.tsx b/frontend/app/(urls)/account/create-as-sender/page.tsx index 28abb29..13102e5 100644 --- a/frontend/app/(urls)/account/create-as-sender/page.tsx +++ b/frontend/app/(urls)/account/create-as-sender/page.tsx @@ -8,6 +8,7 @@ import Button from '@/components/ui/Button' import TextAreaInput from '@/components/ui/TextAreaInput' import CheckboxInput from '@/components/ui/CheckboxInput' import { useForm } from '@/app/hooks/useForm' +import useUserStore from '@/app/store/userStore' import showToast from '@/components/ui/Toast' import { SenderPageProps, SelectOption } from '@/app/types' import { @@ -41,7 +42,7 @@ const validationRules = { required: true, pattern: /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/, }, - contact_number: { + phone_number: { required: true, minLength: 11, pattern: /^\+?[0-9]{11,}$/, @@ -51,6 +52,7 @@ const validationRules = { } const SenderPage = () => { + const { user, setUser } = useUserStore() const today = formatDateToHTML(new Date()) const initialValues: SenderPageProps = { @@ -62,7 +64,7 @@ const SenderPage = () => { cargo_type: '', departure: '', arrival: '', - contact_number: '', + phone_number: user?.phone_number || '', comment: '', email_notification: false, } @@ -240,7 +242,7 @@ const SenderPage = () => {