From fd6b9ab0c2f406bf1cef326897091fe20930219f Mon Sep 17 00:00:00 2001 From: behruz-dev Date: Mon, 27 Oct 2025 17:23:19 +0500 Subject: [PATCH 1/2] fix 500 error --- .../wherehouse/serializers/stock_movmend.py | 238 +++++++++++------- 1 file changed, 143 insertions(+), 95 deletions(-) diff --git a/core/apps/wherehouse/serializers/stock_movmend.py b/core/apps/wherehouse/serializers/stock_movmend.py index 6e8a756..86e2a62 100644 --- a/core/apps/wherehouse/serializers/stock_movmend.py +++ b/core/apps/wherehouse/serializers/stock_movmend.py @@ -1,10 +1,14 @@ from django.db import transaction - from rest_framework import serializers -from core.apps.wherehouse.models import StockMovemend, StockMovmendProduct, Inventory, WhereHouse -from core.apps.products.models import Unity, Product -from core.apps.projects.models import ProjectFolder, Project +from core.apps.products.models import Product, Unity +from core.apps.projects.models import Project, ProjectFolder +from core.apps.wherehouse.models import ( + Inventory, + StockMovemend, + StockMovmendProduct, + WhereHouse, +) class StockMovmendProductSerializer(serializers.Serializer): @@ -12,10 +16,10 @@ class StockMovmendProductSerializer(serializers.Serializer): quantity = serializers.IntegerField() def validate(self, data): - inventory = Inventory.objects.filter(id=data['inventory_id']).first() + inventory = Inventory.objects.filter(id=data["inventory_id"]).first() if not inventory: raise serializers.ValidationError("Inventory not found") - data['inventory'] = inventory + data["inventory"] = inventory return data @@ -29,117 +33,150 @@ class StockMovmendCreateSerializer(serializers.Serializer): comment = serializers.CharField(required=False) def validate(self, data): - if data.get('project_folder_id'): - project_folder = ProjectFolder.objects.filter(id=data['project_folder_id']).first() + if data.get("project_folder_id"): + project_folder = ProjectFolder.objects.filter( + id=data["project_folder_id"] + ).first() if not project_folder: raise serializers.ValidationError("Project Folder not found") - data['project_folder'] = project_folder - if data.get('project_id'): - project = Project.objects.filter(id=data['project_id']).first() + data["project_folder"] = project_folder + if data.get("project_id"): + project = Project.objects.filter(id=data["project_id"]).first() if not project: raise serializers.ValidationError("Project not found") - data['project'] = project - wherehouse_to = WhereHouse.objects.filter(id=data['wherehouse_to_id']).first() + data["project"] = project + wherehouse_to = WhereHouse.objects.filter(id=data["wherehouse_to_id"]).first() if not wherehouse_to: raise serializers.ValidationError("WhereHouse to not found") - wherehouse_from = WhereHouse.objects.filter(id=data['wherehouse_from_id']).first() + wherehouse_from = WhereHouse.objects.filter( + id=data["wherehouse_from_id"] + ).first() if not wherehouse_from: raise serializers.ValidationError("WhereHouse from not found") - data['wherehouse_to'] = wherehouse_to - data['wherehouse_from'] = wherehouse_from + data["wherehouse_to"] = wherehouse_to + data["wherehouse_from"] = wherehouse_from return data - + def create(self, validated_data): with transaction.atomic(): - products = validated_data.pop('products') + products = validated_data.pop("products") stock_movemend = StockMovemend.objects.create( - project_folder=validated_data.get('project_folder'), - project=validated_data.get('project'), - date=validated_data.get('date'), - comment=validated_data.get('comment'), - wherehouse_to=validated_data.get('wherehouse_to'), - wherehouse_from=validated_data.get('wherehouse_from'), - recipient=self.context.get('user'), + project_folder=validated_data.get("project_folder"), + project=validated_data.get("project"), + date=validated_data.get("date"), + comment=validated_data.get("comment"), + wherehouse_to=validated_data.get("wherehouse_to"), + wherehouse_from=validated_data.get("wherehouse_from"), + recipient=self.context.get("user"), ) movmend_products = [] for product in products: - movmend_products.append(StockMovmendProduct( - inventory=product.get('inventory'), - quantity=product.get('quantity'), - stock_movemend=stock_movemend, - )) + movmend_products.append( + StockMovmendProduct( + inventory=product.get("inventory"), + quantity=product.get("quantity"), + stock_movemend=stock_movemend, + ) + ) StockMovmendProduct.objects.bulk_create(movmend_products) - return stock_movemend - + return stock_movemend + class StockMovemendProductListSerializer(serializers.ModelSerializer): - product = serializers.SerializerMethodField(method_name='get_product') - unity = serializers.SerializerMethodField(method_name='get_unity') + product = serializers.SerializerMethodField(method_name="get_product") + unity = serializers.SerializerMethodField(method_name="get_unity") class Meta: model = StockMovmendProduct - fields = [ - 'id', 'product', 'unity', 'quantity' - ] + fields = ["id", "product", "unity", "quantity"] def get_product(self, obj): - return { - 'id': obj.inventory.product.id, - 'type': obj.inventory.product.type, - 'name': obj.inventory.product.name, - } + return ( + { + "id": obj.inventory.product.id, + "type": obj.inventory.product.type, + "name": obj.inventory.product.name, + } + if obj.inventory.product + else None + ) def get_unity(self, obj): - return { - 'id': obj.inventory.unity.id, - 'value': obj.inventory.unity.value, - } + return ( + { + "id": obj.inventory.unity.id, + "value": obj.inventory.unity.value, + } + if obj.inventory.unity + else None + ) class StockMovemendListSerializer(serializers.ModelSerializer): movemend_products = StockMovemendProductListSerializer(many=True) - wherehouse_to = serializers.SerializerMethodField(method_name='get_wherehouse_to') - wherehouse_from = serializers.SerializerMethodField(method_name='get_wherehouse_from') - recipient = serializers.SerializerMethodField(method_name='get_recipient') - project_folder = serializers.SerializerMethodField(method_name='get_project_folder') - project = serializers.SerializerMethodField(method_name='get_project') - + wherehouse_to = serializers.SerializerMethodField(method_name="get_wherehouse_to") + wherehouse_from = serializers.SerializerMethodField( + method_name="get_wherehouse_from" + ) + recipient = serializers.SerializerMethodField(method_name="get_recipient") + project_folder = serializers.SerializerMethodField(method_name="get_project_folder") + project = serializers.SerializerMethodField(method_name="get_project") + class Meta: model = StockMovemend fields = [ - 'id', 'number', 'wherehouse_to', 'wherehouse_from', 'recipient', 'project_folder', - 'project', 'movemend_type', 'date', 'comment', 'movemend_products' + "id", + "number", + "wherehouse_to", + "wherehouse_from", + "recipient", + "project_folder", + "project", + "movemend_type", + "date", + "comment", + "movemend_products", ] - + def get_wherehouse_to(self, obj): - return { - 'id': obj.wherehouse_to.id, - 'name': obj.wherehouse_to.name, - } + return ( + { + "id": obj.wherehouse_to.id, + "name": obj.wherehouse_to.name, + } + if obj.wherehouse_to + else None + ) def get_wherehouse_from(self, obj): - return { - 'id': obj.wherehouse_from.id, - 'name': obj.wherehouse_from.name, - } - + return ( + { + "id": obj.wherehouse_from.id, + "name": obj.wherehouse_from.name, + } + if obj.wherehouse_from + else None + ) + def get_recipient(self, obj): - return { - 'id': obj.recipient.id, - 'full_name': obj.recipient.full_name, - } if obj.recipient else None - + return ( + { + "id": obj.recipient.id, + "full_name": obj.recipient.full_name, + } + if obj.recipient + else None + ) + def get_project_folder(self, obj): - return { - 'id': obj.project_folder.id, - 'name': obj.project_folder.name - } + return ( + {"id": obj.project_folder.id, "name": obj.project_folder.name} + if obj.project_folder + else None + ) def get_project(self, obj): - return { - 'id': obj.project.id, - 'name': obj.project.name - } if obj.project else None + return {"id": obj.project.id, "name": obj.project.name} if obj.project else None class StockMovemendProductUpdateSerializer(serializers.Serializer): @@ -147,12 +184,16 @@ class StockMovemendProductUpdateSerializer(serializers.Serializer): quantity = serializers.IntegerField() def validate(self, data): - movemend_product = StockMovmendProduct.objects.filter(id=data['movemend_product_id']).first() + movemend_product = StockMovmendProduct.objects.filter( + id=data["movemend_product_id"] + ).first() if not movemend_product: raise serializers.ValidationError("Stock Movemend Product not found") - if movemend_product.inventory.quantity < data['quantity']: - raise serializers.ValidationError("invalid quantity, quantity must les than product quantity") - data['movemend_product'] = movemend_product + if movemend_product.inventory.quantity < data["quantity"]: + raise serializers.ValidationError( + "invalid quantity, quantity must les than product quantity" + ) + data["movemend_product"] = movemend_product return data @@ -162,31 +203,38 @@ class StockMovemendUpdateSerializer(serializers.ModelSerializer): class Meta: model = StockMovemend fields = [ - 'wherehouse_to', 'project_folder', 'project', 'date', - 'comment', 'movemend_products' + "wherehouse_to", + "project_folder", + "project", + "date", + "comment", + "movemend_products", ] - extra_kwargs = {'wherehouse_to': {'required': False}} + extra_kwargs = {"wherehouse_to": {"required": False}} def update(self, instance, validated_data): with transaction.atomic(): - instance.wherehouse_to = validated_data.get('wherehouse_to', instance.wherehouse_to) - instance.project_folder = validated_data.get('project_folder', instance.project_folder) - instance.project = validated_data.get('project', instance.project) - instance.date = validated_data.get('date', instance.date) - instance.comment = validated_data.get('comment', instance.comment) + instance.wherehouse_to = validated_data.get( + "wherehouse_to", instance.wherehouse_to + ) + instance.project_folder = validated_data.get( + "project_folder", instance.project_folder + ) + instance.project = validated_data.get("project", instance.project) + instance.date = validated_data.get("date", instance.date) + instance.comment = validated_data.get("comment", instance.comment) instance.save() - movemend_products = validated_data.pop('movemend_products', []) + movemend_products = validated_data.pop("movemend_products", []) if movemend_products: updated_products = [] for product_data in movemend_products: - product = product_data['movemend_product'] - product.quantity = product_data['quantity'] + product = product_data["movemend_product"] + product.quantity = product_data["quantity"] updated_products.append(product) StockMovmendProduct.objects.bulk_update( - updated_products, - fields=['quantity'] + updated_products, fields=["quantity"] ) - return instance \ No newline at end of file + return instance From 544b04a21e2b253e02431d193f6f8ddec01071d0 Mon Sep 17 00:00:00 2001 From: behruz-dev Date: Tue, 28 Oct 2025 16:03:54 +0500 Subject: [PATCH 2/2] add notification and send notification --- config/settings/base.py | 1 + config/urls.py | 1 + core/apps/notifications/__init__.py | 0 core/apps/notifications/apps.py | 6 ++++ .../notifications/migrations/0001_initial.py | 31 +++++++++++++++++++ .../apps/notifications/migrations/__init__.py | 0 core/apps/notifications/models/__init__.py | 1 + .../apps/notifications/models/notification.py | 9 ++++++ .../notifications/serializers/notification.py | 11 +++++++ core/apps/notifications/urls.py | 7 +++++ core/apps/notifications/utils/notify_user.py | 8 +++++ .../notifications/utils/send_notification.py | 16 ++++++++++ core/apps/notifications/views/notification.py | 20 ++++++++++++ core/apps/orders/serializers/order.py | 14 +++++++-- 14 files changed, 123 insertions(+), 2 deletions(-) create mode 100644 core/apps/notifications/__init__.py create mode 100644 core/apps/notifications/apps.py create mode 100644 core/apps/notifications/migrations/0001_initial.py create mode 100644 core/apps/notifications/migrations/__init__.py create mode 100644 core/apps/notifications/models/__init__.py create mode 100644 core/apps/notifications/models/notification.py create mode 100644 core/apps/notifications/serializers/notification.py create mode 100644 core/apps/notifications/urls.py create mode 100644 core/apps/notifications/utils/notify_user.py create mode 100644 core/apps/notifications/utils/send_notification.py create mode 100644 core/apps/notifications/views/notification.py diff --git a/config/settings/base.py b/config/settings/base.py index df44e85..2c9d2c2 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -30,6 +30,7 @@ APPS = [ 'core.apps.orders', 'core.apps.finance', 'core.apps.counterparty', + 'core.apps.notifications', ] PACKAGES = [ diff --git a/config/urls.py b/config/urls.py index ac26667..aff70b0 100644 --- a/config/urls.py +++ b/config/urls.py @@ -35,6 +35,7 @@ urlpatterns = [ path('orders/', include('core.apps.orders.urls')), path('finance/', include('core.apps.finance.urls')), path('counterparties/', include('core.apps.counterparty.urls')), + path('notifications/', include('core.apps.notifications.urls')), ] )), diff --git a/core/apps/notifications/__init__.py b/core/apps/notifications/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/apps/notifications/apps.py b/core/apps/notifications/apps.py new file mode 100644 index 0000000..eb827b0 --- /dev/null +++ b/core/apps/notifications/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class NotificationsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'core.apps.notifications' diff --git a/core/apps/notifications/migrations/0001_initial.py b/core/apps/notifications/migrations/0001_initial.py new file mode 100644 index 0000000..689f2ab --- /dev/null +++ b/core/apps/notifications/migrations/0001_initial.py @@ -0,0 +1,31 @@ +# Generated by Django 5.2.4 on 2025-10-28 15:45 + +import django.db.models.deletion +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Notification', + 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)), + ('token', models.CharField(max_length=255, unique=True)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='notifications', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/core/apps/notifications/migrations/__init__.py b/core/apps/notifications/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/core/apps/notifications/models/__init__.py b/core/apps/notifications/models/__init__.py new file mode 100644 index 0000000..b70bf7e --- /dev/null +++ b/core/apps/notifications/models/__init__.py @@ -0,0 +1 @@ +from .notification import * \ No newline at end of file diff --git a/core/apps/notifications/models/notification.py b/core/apps/notifications/models/notification.py new file mode 100644 index 0000000..b03cf56 --- /dev/null +++ b/core/apps/notifications/models/notification.py @@ -0,0 +1,9 @@ +from django.db import models + +from core.apps.shared.models import BaseModel +from core.apps.accounts.models import User + +class Notification(BaseModel): + user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='notifications') + token = models.CharField(max_length=255, unique=True) + \ No newline at end of file diff --git a/core/apps/notifications/serializers/notification.py b/core/apps/notifications/serializers/notification.py new file mode 100644 index 0000000..7188384 --- /dev/null +++ b/core/apps/notifications/serializers/notification.py @@ -0,0 +1,11 @@ +from rest_framework import serializers + +from core.apps.notifications.models import Notification + + +class NotificationSerializer(serializers.ModelSerializer): + class Meta: + model = Notification + fields = [ + 'token' + ] \ No newline at end of file diff --git a/core/apps/notifications/urls.py b/core/apps/notifications/urls.py new file mode 100644 index 0000000..2bdceed --- /dev/null +++ b/core/apps/notifications/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from core.apps.notifications.views import notification + +urlpatterns = [ + path('device/register/', notification.RegisterExpoPushToken.as_view()), +] \ No newline at end of file diff --git a/core/apps/notifications/utils/notify_user.py b/core/apps/notifications/utils/notify_user.py new file mode 100644 index 0000000..37f4381 --- /dev/null +++ b/core/apps/notifications/utils/notify_user.py @@ -0,0 +1,8 @@ +from core.apps.notifications.models import Notification +from core.apps.notifications.utils.send_notification import send_notification + + +def notify_user(user, title, body): + tokens = Notification.objects.filter(user=user) + for token in tokens: + send_notification(token.token, title, body) \ No newline at end of file diff --git a/core/apps/notifications/utils/send_notification.py b/core/apps/notifications/utils/send_notification.py new file mode 100644 index 0000000..f0dc668 --- /dev/null +++ b/core/apps/notifications/utils/send_notification.py @@ -0,0 +1,16 @@ +import requests + +def send_notification(token, title, body, data=None): + message = { + "to": token, + "sound": "default", + "title": title, + "body": body, + "data": data or {}, + } + response = requests.post( + "https://exp.host/--/api/v2/push/send", + json=message, + headers={"Content-Type": "application/json"} + ) + return response.json() \ No newline at end of file diff --git a/core/apps/notifications/views/notification.py b/core/apps/notifications/views/notification.py new file mode 100644 index 0000000..2f00ec9 --- /dev/null +++ b/core/apps/notifications/views/notification.py @@ -0,0 +1,20 @@ +from rest_framework import generics, status +from rest_framework.response import Response + +from core.apps.notifications.serializers import notification as serializers +from core.apps.notifications.models import Notification + + +class RegisterExpoPushToken(generics.GenericAPIView): + serializer_class = serializers.NotificationSerializer + queryset = Notification.objects.all() + + def post(self, request): + serializer = self.serializer_class(data=request.data) + if serializer.is_valid(): + Notification.objects.update_or_create( + user=request.user, + token=serializer.validated_data['token'] + ) + return Response({"message": "Token saqlandi"}, status=status.HTTP_201_CREATED) + return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) \ No newline at end of file diff --git a/core/apps/orders/serializers/order.py b/core/apps/orders/serializers/order.py index 91a25bd..eb63664 100644 --- a/core/apps/orders/serializers/order.py +++ b/core/apps/orders/serializers/order.py @@ -16,6 +16,8 @@ from core.apps.projects.models import Project, ProjectFolder from core.apps.projects.serializers.project import ProjectListSerializer, ProjectFolderListSerializer # counterparty from core.apps.counterparty.models import Counterparty +# notifications +from core.apps.notifications.utils.notify_user import notify_user class OrderCreateSerializer(serializers.Serializer): @@ -71,7 +73,7 @@ class MultipleOrderCreateSerializer(serializers.Serializer): orders = [] for resource in resources: - orders.append(Order( + order = Order( product=resource['product'], unity=resource['unity'], wherehouse=resource['wherehouse'], @@ -80,8 +82,16 @@ class MultipleOrderCreateSerializer(serializers.Serializer): quantity=resource['quantity'], date=common_date, employee=self.context.get('user'), - )) + ) + orders.append(order) + body = f""" + {user.full_name} {order.project_folder.name} uchun {order.wherehouse.name} ombor ga {order.quantity} {order.unity.value} {order.product.name} ga buyurtma berdi. Buyurtma + yetkazish sanasi {common_date} + """ + notify_user(user=self.context.get("user"), title="Ta'minot",body=body) + created_orders = Order.objects.bulk_create(orders) + user = self.context.get('user') return created_orders