From 544b04a21e2b253e02431d193f6f8ddec01071d0 Mon Sep 17 00:00:00 2001 From: behruz-dev Date: Tue, 28 Oct 2025 16:03:54 +0500 Subject: [PATCH] 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