From 88dc6932ec0a3d903661a353d53d88a1f70a035a Mon Sep 17 00:00:00 2001 From: Timofey Date: Sat, 24 May 2025 15:12:37 +0300 Subject: [PATCH] internal fixes --- backend/api/account/client/serializers.py | 43 ++++++++++-- backend/api/account/client/views.py | 65 ++++++++++++------- backend/routes/admin.py | 1 + backend/routes/migrations/0004_leads.py | 8 ++- .../0005_alter_leads_moving_price.py | 18 +++++ backend/routes/models.py | 15 ++--- frontend/app/types/index.ts | 1 + frontend/lib/main/sendLead.ts | 10 ++- 8 files changed, 120 insertions(+), 41 deletions(-) create mode 100644 backend/routes/migrations/0005_alter_leads_moving_price.py diff --git a/backend/api/account/client/serializers.py b/backend/api/account/client/serializers.py index 087bd27..d8ae3b0 100644 --- a/backend/api/account/client/serializers.py +++ b/backend/api/account/client/serializers.py @@ -271,17 +271,48 @@ class PlanChangeSerializer(serializers.Serializer): class LeadSerializer(serializers.ModelSerializer): route = serializers.PrimaryKeyRelatedField(queryset=Route.objects.all()) moving_user = serializers.PrimaryKeyRelatedField(queryset=User.objects.all()) + moving_price = serializers.DecimalField(max_digits=10, decimal_places=2) class Meta: model = Leads fields = ['route', 'moving_user', 'moving_price', 'moving_date', 'comment'] def validate_moving_date(self, value): - if value < timezone.now().date(): - raise serializers.ValidationError("Дата перевозки не может быть в прошлом") - return value + try: + if value < timezone.now().date(): + raise serializers.ValidationError("Дата перевозки не может быть в прошлом") + return value + except Exception as e: + raise def validate_moving_price(self, value): - if value <= 0: - raise serializers.ValidationError("Цена должна быть больше нуля") - return value \ No newline at end of file + try: + if isinstance(value, str): + value = value.replace(',', '.') + price = float(value) + if price <= 0: + raise serializers.ValidationError("Цена должна быть больше нуля") + return price + except (TypeError, ValueError) as e: + raise serializers.ValidationError("Некорректный формат цены") + + def validate(self, data): + try: + # проверяем что все обязательные поля присутствуют + required_fields = ['route', 'moving_user', 'moving_price', 'moving_date'] + missing_fields = [field for field in required_fields if field not in data] + if missing_fields: + raise serializers.ValidationError(f"Отсутствуют обязательные поля: {', '.join(missing_fields)}") + + # проверяем что пользователь не пытается откликнуться на свою заявку + if data['route'].owner.id == data['moving_user'].id: + raise serializers.ValidationError("Вы не можете откликнуться на собственную заявку") + return data + except Exception as e: + raise + + def to_internal_value(self, data): + try: + return super().to_internal_value(data) + except Exception as e: + raise \ No newline at end of file diff --git a/backend/api/account/client/views.py b/backend/api/account/client/views.py index 192aa74..b38a206 100644 --- a/backend/api/account/client/views.py +++ b/backend/api/account/client/views.py @@ -198,10 +198,11 @@ class LeadViewSet(ViewSet): @action(detail=False, methods=['post']) @handle_exceptions - def send_lead(self, request, id): + def send_lead(self, request): """ Создание новой заявки на перевозку """ + # добавляем текущего пользователя в данные data = request.data.copy() data['moving_user'] = request.user.id @@ -209,40 +210,54 @@ class LeadViewSet(ViewSet): # проверяем существование и доступность маршрута try: route = Route.objects.get(id=data.get('route')) - if route.owner == request.user: + 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) - 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, + is_valid = serializer.is_valid() + + if is_valid: + try: + 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 - ) + 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 + ) \ No newline at end of file diff --git a/backend/routes/admin.py b/backend/routes/admin.py index bf26264..d988e9c 100644 --- a/backend/routes/admin.py +++ b/backend/routes/admin.py @@ -4,3 +4,4 @@ from .models import * admin.site.register(Country) admin.site.register(City) admin.site.register(Route) +admin.site.register(Leads) diff --git a/backend/routes/migrations/0004_leads.py b/backend/routes/migrations/0004_leads.py index ff54919..214debc 100644 --- a/backend/routes/migrations/0004_leads.py +++ b/backend/routes/migrations/0004_leads.py @@ -1,5 +1,7 @@ -# Generated by Django 5.2.1 on 2025-05-24 11:12 +# Generated by Django 5.2.1 on 2025-05-24 12:05 +import django.db.models.deletion +from django.conf import settings from django.db import migrations, models @@ -7,6 +9,7 @@ class Migration(migrations.Migration): dependencies = [ ('routes', '0003_alter_route_owner_type'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ @@ -14,9 +17,12 @@ class Migration(migrations.Migration): name='Leads', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('moving_price', models.DecimalField(decimal_places=2, default=0, max_digits=10)), ('moving_date', models.DateField()), ('comment', models.CharField(blank=True, max_length=500, null=True)), ('created_at', models.DateTimeField(auto_now_add=True)), + ('moving_user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Перевозчик')), + ('route', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='routes.route', verbose_name='Маршрут')), ], options={ 'verbose_name': 'Заявка', diff --git a/backend/routes/migrations/0005_alter_leads_moving_price.py b/backend/routes/migrations/0005_alter_leads_moving_price.py new file mode 100644 index 0000000..043bbd7 --- /dev/null +++ b/backend/routes/migrations/0005_alter_leads_moving_price.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.1 on 2025-05-24 12:09 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('routes', '0004_leads'), + ] + + operations = [ + migrations.AlterField( + model_name='leads', + name='moving_price', + field=models.DecimalField(decimal_places=2, max_digits=10), + ), + ] diff --git a/backend/routes/models.py b/backend/routes/models.py index 8def69e..0a764c7 100644 --- a/backend/routes/models.py +++ b/backend/routes/models.py @@ -103,10 +103,9 @@ class Route(models.Model): ) def __str__(self): - if self.owner: - return f'{self.owner}' - else: - return str(self.owner_type) + from_city_name = self.from_city.name if self.from_city else 'Не указан' + to_city_name = self.to_city.name if self.to_city else 'Не указан' + return f"Маршрут #{self.id}: {from_city_name} → {to_city_name}" class Meta: verbose_name = (u'Маршрут') @@ -114,9 +113,9 @@ class Route(models.Model): ordering = ('id',) class Leads(models.Model): - route = models.ForeignKey(Route, verbose_name="Маршрут", on_delete=models.CASCADE), - moving_user = models.ForeignKey(User, verbose_name="Перевозчик", on_delete=models.CASCADE), - moving_price = models.DecimalField(max_digits=10, decimal_places=2), + route = models.ForeignKey(Route, verbose_name="Маршрут", on_delete=models.CASCADE) + moving_user = models.ForeignKey(User, verbose_name="Перевозчик", on_delete=models.CASCADE) + moving_price = models.DecimalField(max_digits=10, decimal_places=2) moving_date = models.DateField() comment = models.CharField(max_length=500, null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) @@ -127,4 +126,4 @@ class Leads(models.Model): ordering = ['-created_at'] def __str__(self): - return f"Заявка по маршруту ${self.route}" \ No newline at end of file + return f"Заявка по маршруту {self.route}" \ No newline at end of file diff --git a/frontend/app/types/index.ts b/frontend/app/types/index.ts index 303da08..07b399d 100644 --- a/frontend/app/types/index.ts +++ b/frontend/app/types/index.ts @@ -244,4 +244,5 @@ export interface Lead { moving_price: string moving_date: string comment: string + route?: number } diff --git a/frontend/lib/main/sendLead.ts b/frontend/lib/main/sendLead.ts index e446aed..eee0dc9 100644 --- a/frontend/lib/main/sendLead.ts +++ b/frontend/lib/main/sendLead.ts @@ -1,11 +1,18 @@ import { Lead } from '@/app/types' +import { getSession } from 'next-auth/react' -export const sendLead = async (data: Lead) => { +export const sendLead = async (data: Lead & { id?: number }) => { const API_URL = process.env.NEXT_PUBLIC_API_URL + const session = await getSession() + + if (!session?.accessToken) { + throw new Error('No access token found') + } const headers: Record = { 'Content-Type': 'application/json', Accept: 'application/json', + Authorization: `Bearer ${session.accessToken}`, } try { @@ -16,6 +23,7 @@ export const sendLead = async (data: Lead) => { moving_price: data.moving_price, moving_date: data.moving_date, comment: data.comment, + route: data.id, }) const response = await fetch(`${API_URL}/account/send_lead/`, {