From 3887fc7a878d11135158bcb1a095832ffa4bb777 Mon Sep 17 00:00:00 2001 From: behruz-dev Date: Tue, 19 Aug 2025 15:55:24 +0500 Subject: [PATCH] add new api --- core/apps/orders/admin/__init__.py | 3 +- core/apps/orders/admin/offer.py | 10 +++ core/apps/orders/migrations/0005_offer.py | 34 ++++++++ .../migrations/0006_offer_price_type.py | 18 ++++ core/apps/orders/models/__init__.py | 3 +- core/apps/orders/models/order_offer.py | 37 ++++++++ core/apps/orders/serializers/offer.py | 70 +++++++++++++++ core/apps/orders/serializers/order.py | 1 + core/apps/orders/urls.py | 12 ++- core/apps/orders/views/offer.py | 85 +++++++++++++++++++ 10 files changed, 269 insertions(+), 4 deletions(-) create mode 100644 core/apps/orders/admin/offer.py create mode 100644 core/apps/orders/migrations/0005_offer.py create mode 100644 core/apps/orders/migrations/0006_offer_price_type.py create mode 100644 core/apps/orders/models/order_offer.py create mode 100644 core/apps/orders/serializers/offer.py create mode 100644 core/apps/orders/views/offer.py diff --git a/core/apps/orders/admin/__init__.py b/core/apps/orders/admin/__init__.py index 5c48881..93e5e7a 100644 --- a/core/apps/orders/admin/__init__.py +++ b/core/apps/orders/admin/__init__.py @@ -1 +1,2 @@ -from .order import * \ No newline at end of file +from .order import * +from .offer import * \ No newline at end of file diff --git a/core/apps/orders/admin/offer.py b/core/apps/orders/admin/offer.py new file mode 100644 index 0000000..a53f291 --- /dev/null +++ b/core/apps/orders/admin/offer.py @@ -0,0 +1,10 @@ +from django.contrib import admin + +from core.apps.orders.models import Offer + + +@admin.register(Offer) +class OfferAdmin(admin.ModelAdmin): + list_display = ['id', 'name', 'number', 'order'] + search_fields = ['name', 'phone', 'number', 'price'] + diff --git a/core/apps/orders/migrations/0005_offer.py b/core/apps/orders/migrations/0005_offer.py new file mode 100644 index 0000000..076f4ec --- /dev/null +++ b/core/apps/orders/migrations/0005_offer.py @@ -0,0 +1,34 @@ +# Generated by Django 5.2.4 on 2025-08-19 15:00 + +import django.db.models.deletion +import uuid +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orders', '0004_remove_order_project_department_order_project_folder_and_more'), + ] + + operations = [ + migrations.CreateModel( + name='Offer', + fields=[ + ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False, unique=True)), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('name', models.CharField(max_length=200)), + ('price', models.PositiveBigIntegerField()), + ('phone', models.CharField(blank=True, max_length=15, null=True)), + ('comment', models.TextField(blank=True, null=True)), + ('qqs', models.BooleanField(blank=True, default=False, null=True)), + ('number', models.PositiveIntegerField(default=1)), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='offers', to='orders.order')), + ], + options={ + 'verbose_name': 'Taklif', + 'verbose_name_plural': 'Takliflar', + }, + ), + ] diff --git a/core/apps/orders/migrations/0006_offer_price_type.py b/core/apps/orders/migrations/0006_offer_price_type.py new file mode 100644 index 0000000..aba6066 --- /dev/null +++ b/core/apps/orders/migrations/0006_offer_price_type.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.4 on 2025-08-19 15:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('orders', '0005_offer'), + ] + + operations = [ + migrations.AddField( + model_name='offer', + name='price_type', + field=models.CharField(choices=[('UZS', 'uzs'), ('USD', 'usd')], default='uzs'), + ), + ] diff --git a/core/apps/orders/models/__init__.py b/core/apps/orders/models/__init__.py index 5c48881..5b5e519 100644 --- a/core/apps/orders/models/__init__.py +++ b/core/apps/orders/models/__init__.py @@ -1 +1,2 @@ -from .order import * \ No newline at end of file +from .order import * +from .order_offer import * \ No newline at end of file diff --git a/core/apps/orders/models/order_offer.py b/core/apps/orders/models/order_offer.py new file mode 100644 index 0000000..ac82dd3 --- /dev/null +++ b/core/apps/orders/models/order_offer.py @@ -0,0 +1,37 @@ +from django.db import models + +from core.apps.shared.models import BaseModel +from core.apps.orders.models import Order + + +class Offer(BaseModel): + PRICE_TYPE = ( + ('UZS', 'uzs'), + ('USD', 'usd') + ) + + order = models.ForeignKey(Order, on_delete=models.CASCADE, related_name='offers') + name = models.CharField(max_length=200) + price = models.PositiveBigIntegerField() + price_type = models.CharField(choices=PRICE_TYPE, default='uzs') + phone = models.CharField(max_length=15, null=True, blank=True) + comment = models.TextField(null=True, blank=True) + qqs = models.BooleanField(default=False, null=True, blank=True) + number = models.PositiveIntegerField(default=1) + + def __str__(self): + return self.name + + def save(self, *args, **kwargs): + if not self.pk: + last_offer = Offer.objects.filter(order=self.order).order_by('-number').first() + if last_offer: + self.number = last_offer.number + 1 + else: + self.number = 1 + return super().save(*args, **kwargs) + + class Meta: + verbose_name = 'Taklif' + verbose_name_plural = 'Takliflar' + \ No newline at end of file diff --git a/core/apps/orders/serializers/offer.py b/core/apps/orders/serializers/offer.py new file mode 100644 index 0000000..21dfeea --- /dev/null +++ b/core/apps/orders/serializers/offer.py @@ -0,0 +1,70 @@ +from django.db import transaction + +from rest_framework import serializers + +from core.apps.orders.models import Offer, Order + + +class OfferCreateSerializer(serializers.Serializer): + name = serializers.CharField() + price = serializers.IntegerField() + phone = serializers.CharField(required=False) + comment = serializers.CharField(required=False) + qqs = serializers.CharField(required=False) + price_type = serializers.ChoiceField(Offer.PRICE_TYPE) + + +class MultipleOfferCreateSerializer(serializers.Serializer): + order_id = serializers.UUIDField() + offers = serializers.ListSerializer(child=OfferCreateSerializer()) + + def validate(self, data): + order = Order.objects.filter(id=data['order_id']).first() + if not order: + raise serializers.ValidationError("Order not found") + data['order'] = order + return data + + def create(self, validated_data): + with transaction.atomic(): + offers = [] + for offer in validated_data.pop('offers'): + offer.append( + Offer( + name=offer['name'], + price=offer['price'], + phone=offer.get('phone'), + comment=offer.get('comment'), + qqs=offer.get('qqs'), + price_type=offer.get('price_type') + ) + ) + + return Offer.objects.bulk_update(offers) + + +class OfferListSerializer(serializers.ModelSerializer): + class Meta: + model = Offer + fields = [ + 'id', 'name', 'price', 'number', 'phone', 'comment', 'qqs', 'price_type' + ] + + +class OfferUpdateSerializer(serializers.ModelSerializer): + class Meta: + model = Offer + fields = [ + 'name', 'price', 'number', 'phone', 'comment', 'qqs', 'price_type' + ] + + def update(self, instance, validated_data): + instance.name = validated_data.get('name', instance.name) + instance.price = validated_data.get('price', instance.price) + instance.number = validated_data.get('number', instance.number) + instance.phone = validated_data.get('phone', instance.phone) + instance.comment = validated_data.get('comment', instance.comment) + instance.qqs = validated_data.get('qqs', instance.qqs) + instance.price_type = validated_data.get('price_type', instance.price_type) + instance.save() + return instance \ No newline at end of file diff --git a/core/apps/orders/serializers/order.py b/core/apps/orders/serializers/order.py index 552ef9e..0de5e57 100644 --- a/core/apps/orders/serializers/order.py +++ b/core/apps/orders/serializers/order.py @@ -94,6 +94,7 @@ class MultipleOrderCreateSerializer(serializers.Serializer): created_orders = Order.objects.bulk_create(orders) return created_orders + class OrderListSerializer(serializers.ModelSerializer): product = ProductListSerializer() unity = UnityListSerializer() diff --git a/core/apps/orders/urls.py b/core/apps/orders/urls.py index eef0300..fc2720a 100644 --- a/core/apps/orders/urls.py +++ b/core/apps/orders/urls.py @@ -1,11 +1,11 @@ from django.urls import path, include from core.apps.orders.views import order as order_views +from core.apps.orders.views import offer as offer_views urlpatterns = [ - path('order/', include( - [ + path('order/', include( [ path('list/', order_views.OrderListApiView.as_view()), path('create/', order_views.OrderCreateApiView.as_view()), path('/update/', order_views.OrderUpdateApiView.as_view()), @@ -15,4 +15,12 @@ urlpatterns = [ path("accepted/list/", order_views.OrderAcceptApiView.as_view()), ] )), + path('offer/', include( + [ + path('create/', offer_views.OffersCreateApiView.as_view()), + path('list/', offer_views.OfferListApiView.as_view()), + path('/delete/', offer_views.OfferDeleteApiView.as_view()), + path('/update/', offer_views.OfferUpdateApiView.as_view()), + ] + )), ] \ No newline at end of file diff --git a/core/apps/orders/views/offer.py b/core/apps/orders/views/offer.py new file mode 100644 index 0000000..f03b4c7 --- /dev/null +++ b/core/apps/orders/views/offer.py @@ -0,0 +1,85 @@ +from django.shortcuts import get_object_or_404 + +from rest_framework import generics, views +from rest_framework.response import Response + +from core.apps.shared.paginations.custom import PageNumberPagination +from core.apps.accounts.permissions.permissions import HasRolePermission +from core.apps.orders.serializers import offer as serializers +from core.apps.orders.models import Offer + + +class OffersCreateApiView(generics.GenericAPIView): + permission_classes = [HasRolePermission] + required_permissions = ['offer'] + queryset = Offer.objects.all() + serializer_class = serializers.MultipleOfferCreateSerializer + + def post(self, request): + serializer = self.serializer_class(data=request.data) + if serializer.is_valid(raise_exception=True): + serializer.save() + return Response( + { + "success": True, + "message": "Offers created!" + }, + status=201 + ) + + +class OfferListApiView(generics.GenericAPIView): + permission_classes = [HasRolePermission] + queryset = Offer.objects.all() + required_permissions = ['offer'] + serializer_class = serializers.OfferListSerializer + + def get(self, request): + offers = Offer.objects.all() + serializer = self.serializer_class(offers, many=True) + self.paginate_queryset(serializer) + return self.get_paginated_response + + +class OfferUpdateApiView(generics.GenericAPIView): + serializer_class = serializers.OfferUpdateSerializer + queryset = Offer.objects.all() + lookup_field = 'id' + permission_classes = [HasRolePermission] + required_permissions = ['offer'] + + def put(self, request, id): + offer = get_object_or_404(Offer, id=id) + serializer = self.serializer_class(data=request.data, instance=offer) + if serializer.is_valid(raise_exception=True): + serializer.save() + return Response( + { + 'success': True, + "message": "Offer successfully updated!" + }, + status=200 + ) + + def patch(self, request, id): + offer = get_object_or_404(Offer, id=id) + serializer = self.serializer_class(data=request.data, instance=offer, partial=True) + if serializer.is_valid(raise_exception=True): + serializer.save() + return Response( + { + 'success': True, + "message": "Offer successfully updated!" + }, + status=200 + ) + + +class OfferDeleteApiView(views.APIView): + permission_classes = [HasRolePermission] + required_permissions = ['offer'] + + def delete(self, request, id): + offer = get_object_or_404(Offer, id=id) + offer.delete() + return Response({'success': True}, status=204) \ No newline at end of file