From 8d7738483ca4d479f3ba9ae754b31f9268537690 Mon Sep 17 00:00:00 2001 From: behruz-dev Date: Tue, 25 Nov 2025 15:22:47 +0500 Subject: [PATCH] order apilar qoshildi --- config/settings/base.py | 1 + config/urls.py | 1 + core/apps/orders/admin/__init__.py | 2 + core/apps/orders/admin/order.py | 15 +++++ core/apps/orders/admin/product.py | 7 +++ core/apps/orders/apps.py | 6 ++ core/apps/orders/migrations/0001_initial.py | 63 +++++++++++++++++++++ core/apps/orders/migrations/__init__.py | 0 core/apps/orders/models/__init__.py | 3 + core/apps/orders/models/order.py | 21 +++++++ core/apps/orders/models/order_item.py | 15 +++++ core/apps/orders/models/product.py | 12 ++++ core/apps/orders/serializers/order.py | 58 +++++++++++++++++++ core/apps/orders/serializers/order_item.py | 17 ++++++ core/apps/orders/serializers/product.py | 12 ++++ core/apps/orders/urls.py | 22 +++++++ core/apps/orders/views/order.py | 56 ++++++++++++++++++ core/apps/orders/views/product.py | 31 ++++++++++ 18 files changed, 342 insertions(+) create mode 100644 core/apps/orders/admin/__init__.py create mode 100644 core/apps/orders/admin/order.py create mode 100644 core/apps/orders/admin/product.py create mode 100644 core/apps/orders/apps.py create mode 100644 core/apps/orders/migrations/0001_initial.py create mode 100644 core/apps/orders/migrations/__init__.py create mode 100644 core/apps/orders/models/__init__.py create mode 100644 core/apps/orders/models/order.py create mode 100644 core/apps/orders/models/order_item.py create mode 100644 core/apps/orders/models/product.py create mode 100644 core/apps/orders/serializers/order.py create mode 100644 core/apps/orders/serializers/order_item.py create mode 100644 core/apps/orders/serializers/product.py create mode 100644 core/apps/orders/urls.py create mode 100644 core/apps/orders/views/order.py create mode 100644 core/apps/orders/views/product.py diff --git a/config/settings/base.py b/config/settings/base.py index c923297..1ca9dea 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -27,6 +27,7 @@ INSTALLED_APPS = [ 'core.apps.shared', 'core.apps.authentication', 'core.apps.accounts', + 'core.apps.orders', ] MIDDLEWARE = [ diff --git a/config/urls.py b/config/urls.py index e850a69..4f81364 100644 --- a/config/urls.py +++ b/config/urls.py @@ -39,6 +39,7 @@ urlpatterns += [ path('accounts/', include('core.apps.accounts.urls')), path('authentication/', include('core.apps.authentication.urls')), path('shared/', include('core.apps.shared.urls')), + path('orders/', include('core.apps.orders.urls')), ], )), ] \ No newline at end of file diff --git a/core/apps/orders/admin/__init__.py b/core/apps/orders/admin/__init__.py new file mode 100644 index 0000000..74cfadc --- /dev/null +++ b/core/apps/orders/admin/__init__.py @@ -0,0 +1,2 @@ +from .product import * +from .order import * \ No newline at end of file diff --git a/core/apps/orders/admin/order.py b/core/apps/orders/admin/order.py new file mode 100644 index 0000000..a7d2c50 --- /dev/null +++ b/core/apps/orders/admin/order.py @@ -0,0 +1,15 @@ +from django.contrib import admin + +# orders +from core.apps.orders.models import Order, OrderItem + + +class OrderItemInline(admin.TabularInline): + model = OrderItem + extra = 0 + + +@admin.register(Order) +class OrderAdmin(admin.ModelAdmin): + list_display = ['id', 'employee_name', 'advance', 'paid_price', 'total_price'] + inlines = [OrderItemInline] \ No newline at end of file diff --git a/core/apps/orders/admin/product.py b/core/apps/orders/admin/product.py new file mode 100644 index 0000000..cb4c92a --- /dev/null +++ b/core/apps/orders/admin/product.py @@ -0,0 +1,7 @@ +from django.contrib import admin + +# orders +from core.apps.orders.models import Product + + +admin.site.register(Product) \ No newline at end of file diff --git a/core/apps/orders/apps.py b/core/apps/orders/apps.py new file mode 100644 index 0000000..2d18476 --- /dev/null +++ b/core/apps/orders/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class OrdersConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'core.apps.orders' diff --git a/core/apps/orders/migrations/0001_initial.py b/core/apps/orders/migrations/0001_initial.py new file mode 100644 index 0000000..4bf4158 --- /dev/null +++ b/core/apps/orders/migrations/0001_initial.py @@ -0,0 +1,63 @@ +# Generated by Django 5.2 on 2025-11-24 11:26 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('shared', '0006_plan_is_done'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Product', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('name', models.CharField(max_length=200)), + ('price', models.DecimalField(decimal_places=2, max_digits=15)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Order', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('total_price', models.DecimalField(decimal_places=2, max_digits=15)), + ('paid_price', models.DecimalField(decimal_places=2, max_digits=15)), + ('advance', models.FloatField()), + ('employee_name', models.CharField(max_length=200)), + ('pharmacy', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='shared.pharmacy')), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='OrderItem', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('quantity', models.PositiveIntegerField(default=0)), + ('total_price', models.DecimalField(decimal_places=2, max_digits=15)), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='order_items', to='orders.order')), + ('product', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='order_items', to='orders.product')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/core/apps/orders/migrations/__init__.py b/core/apps/orders/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/apps/orders/models/__init__.py b/core/apps/orders/models/__init__.py new file mode 100644 index 0000000..03c8670 --- /dev/null +++ b/core/apps/orders/models/__init__.py @@ -0,0 +1,3 @@ +from .product import * +from .order_item import * +from .order import * \ No newline at end of file diff --git a/core/apps/orders/models/order.py b/core/apps/orders/models/order.py new file mode 100644 index 0000000..0d7bc2f --- /dev/null +++ b/core/apps/orders/models/order.py @@ -0,0 +1,21 @@ +from django.db import models + +# shared +from core.apps.shared.models import BaseModel, Pharmacy +# accounts +from core.apps.accounts.models import User + + + +class Order(BaseModel): + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='orders') + pharmacy = models.ForeignKey(Pharmacy, on_delete=models.CASCADE, related_name='orders') + + total_price = models.DecimalField(decimal_places=2, max_digits=15) + paid_price = models.DecimalField(decimal_places=2, max_digits=15) + advance = models.FloatField() + employee_name = models.CharField(max_length=200) + + def __str__(self): + return f'#{self.id} from {self.user.first_name}, total_price - {self.total_price}, paid - {self.paid_price}' + \ No newline at end of file diff --git a/core/apps/orders/models/order_item.py b/core/apps/orders/models/order_item.py new file mode 100644 index 0000000..175a857 --- /dev/null +++ b/core/apps/orders/models/order_item.py @@ -0,0 +1,15 @@ +from django.db import models + +# shared +from core.apps.shared.models import BaseModel + + +class OrderItem(BaseModel): + product = models.ForeignKey('orders.Product', on_delete=models.CASCADE, related_name='order_items') + order = models.ForeignKey('orders.Order', on_delete=models.CASCADE, related_name='order_items') + + quantity = models.PositiveIntegerField(default=0) + total_price = models.DecimalField(max_digits=15, decimal_places=2) + + def __str__(self): + return f'{self.product.name} - {self.quantity}x' diff --git a/core/apps/orders/models/product.py b/core/apps/orders/models/product.py new file mode 100644 index 0000000..244bdb9 --- /dev/null +++ b/core/apps/orders/models/product.py @@ -0,0 +1,12 @@ +from django.db import models + +# shared +from core.apps.shared.models import BaseModel + + +class Product(BaseModel): + name = models.CharField(max_length=200) + price = models.DecimalField(decimal_places=2, max_digits=15) + + def __str__(self): + return self.name \ No newline at end of file diff --git a/core/apps/orders/serializers/order.py b/core/apps/orders/serializers/order.py new file mode 100644 index 0000000..78e6055 --- /dev/null +++ b/core/apps/orders/serializers/order.py @@ -0,0 +1,58 @@ +# django +from django.db import transaction + +# rest framework +from rest_framework import serializers + +# orders +from core.apps.orders.models import Order, OrderItem +from core.apps.orders.serializers.order_item import OrderItemSerializer +# shared +from core.apps.shared.models import Pharmacy + + +class OrderCreateSerializer(serializers.Serializer): + pharmacy_id = serializers.IntegerField() + paid_price = serializers.DecimalField(max_digits=15, decimal_places=2) + total_price = serializers.DecimalField(max_digits=15, decimal_places=2) + advance = serializers.FloatField() + employee_name = serializers.CharField() + items = OrderItemSerializer(many=True) + + def validate(self, data): + pharmacy = Pharmacy.objects.filter(id=data['pharmacy_id']).first() + if not pharmacy: + raise serializers.ValidationError({"pharmancy_id": "Pharmancy not found"}) + data['pharmacy'] = pharmacy + return data + + def create(self, validated_data): + with transaction.atomic(): + order = Order.objects.create( + pharmacy=validated_data.get('pharmacy'), + paid_price=validated_data.get('paid_price'), + advance=validated_data.get('advance'), + employee_name=validated_data.get('employee_name'), + total_price=validated_data.get('total_price'), + ) + order_items = [] + for order_item in validated_data.get('items'): + order_items(OrderItem( + product=order_item.get('product'), + order=order, + quantity=order_item.get('quantity'), + total_price=order_item.get('total_price'), + )) + OrderItem.objects.bulk_create(order_items) + return order + + +class OrderListSerializer(serializers.ModelSerializer): + order_items = OrderItemSerializer(many=True) + + class Meta: + model = Order + fields = [ + 'id', 'pharmacy', 'total_price', 'paid_price', 'advance', 'employee_name', + 'order_items' + ] \ No newline at end of file diff --git a/core/apps/orders/serializers/order_item.py b/core/apps/orders/serializers/order_item.py new file mode 100644 index 0000000..b78e671 --- /dev/null +++ b/core/apps/orders/serializers/order_item.py @@ -0,0 +1,17 @@ +# django +from django.db import transaction + +# rest framework +from rest_framework import serializers + +# orders +from core.apps.orders.models import OrderItem + + + +class OrderItemSerializer(serializers.ModelSerializer): + class Meta: + model = OrderItem + fields = [ + 'id', 'product', 'quantity', 'total_price' + ] \ No newline at end of file diff --git a/core/apps/orders/serializers/product.py b/core/apps/orders/serializers/product.py new file mode 100644 index 0000000..405c19a --- /dev/null +++ b/core/apps/orders/serializers/product.py @@ -0,0 +1,12 @@ +from rest_framework import serializers + +# orders +from core.apps.orders.models import Product + + +class ProductSerializer(serializers.ModelSerializer): + class Meta: + model = Product + fields = [ + 'id', 'name', 'price', 'created_at' + ] \ No newline at end of file diff --git a/core/apps/orders/urls.py b/core/apps/orders/urls.py new file mode 100644 index 0000000..c046f38 --- /dev/null +++ b/core/apps/orders/urls.py @@ -0,0 +1,22 @@ +from django.urls import path, include + +# orders product views +from core.apps.orders.views import product as product_view +# orders order views +from core.apps.orders.views import order as order_view + + +urlpatterns = [ + path('product/', include( + [ + path('list/', product_view.ProductApiView.as_view(), name='product-list-api'), + ] + )), + path('order/', include( + [ + path('list/', order_view.OrderListApiView.as_view(), name='order-list-api'), + path('create/', order_view.OrderCreateApiView.as_view(), name='order-create-api'), + ] + )), + +] diff --git a/core/apps/orders/views/order.py b/core/apps/orders/views/order.py new file mode 100644 index 0000000..c1eff8e --- /dev/null +++ b/core/apps/orders/views/order.py @@ -0,0 +1,56 @@ +# rest framework +from rest_framework import generics, permissions + +# drf yasg +from drf_yasg.utils import swagger_auto_schema + +# orders +from core.apps.orders.models import Order, OrderItem +from core.apps.orders.serializers.order import OrderCreateSerializer, OrderListSerializer +# shared +from core.apps.shared.utils.response_mixin import ResponseMixin +from core.apps.shared.serializers.base import BaseResponseSerializer, SuccessResponseSerializer + + +class OrderCreateApiView(generics.GenericAPIView, ResponseMixin): + serializer_class = OrderCreateSerializer + queryset = Order.objects.all() + permission_classes = [permissions.IsAuthenticated] + + @swagger_auto_schema( + responses={ + 201: SuccessResponseSerializer(), + 400: BaseResponseSerializer(), + 500: BaseResponseSerializer(), + } + ) + def post(self, request): + try: + serializer = self.serializer_class(data=request.data, context={'user': request.user}) + if serializer.is_valid(): + serializer.save() + return self.success_response(message='malumot qoshildi', status_code=201) + return self.failure_response(data=serializer.errors, message='malumot qoshilmadi') + except Exception as e: + return self.error_response(data=str(e), message='xatolik') + + +class OrderListApiView(generics.GenericAPIView, ResponseMixin): + serializer_class = OrderListSerializer + queryset = Order.objects.all() + permission_classes = [permissions.IsAuthenticated] + + @swagger_auto_schema( + responses={ + 200: SuccessResponseSerializer(), + 400: BaseResponseSerializer(), + 500: BaseResponseSerializer(), + } + ) + def get(self, request): + try: + queryset = self.queryset.filter(user=request.user) + serializer = self.serializer_class(queryset, many=True) + return self.success_response(data=serializer.data, message='malumotlar fetch qilindi') + except Exception as e: + return self.error_response(data=str(e), message='xatolik') diff --git a/core/apps/orders/views/product.py b/core/apps/orders/views/product.py new file mode 100644 index 0000000..3b411cf --- /dev/null +++ b/core/apps/orders/views/product.py @@ -0,0 +1,31 @@ +from rest_framework import permissions, generics + +# drf yasg +from drf_yasg.utils import swagger_auto_schema + +# orders +from core.apps.orders.models import Product +from core.apps.orders.serializers.product import ProductSerializer +# shared +from core.apps.shared.serializers.base import BaseResponseSerializer, SuccessResponseSerializer +from core.apps.shared.utils.response_mixin import ResponseMixin + + +class ProductApiView(generics.GenericAPIView, ResponseMixin): + serializer_class = ProductSerializer + queryset = Product.objects.all() + permission_classes = [permissions.IsAuthenticated] + + @swagger_auto_schema( + responses={ + 200: SuccessResponseSerializer(), + 400: BaseResponseSerializer(), + 500: BaseResponseSerializer(), + } + ) + def get(self, request): + try: + serializer = self.serializer_class(self.get_queryset(), many=True) + return self.success_response(data=serializer.data, message='malumotlar fetch qilindi') + except Exception as e: + return self.error_response(data=str(e), message='xatolik')