Merge pull request #25 from xoliqberdiyev/test

Test
This commit is contained in:
xolikberdiyev
2025-10-30 15:32:48 +05:00
committed by GitHub
25 changed files with 5859 additions and 289 deletions

11
config/firebase.py Normal file
View File

@@ -0,0 +1,11 @@
import os
import firebase_admin
from firebase_admin import credentials
from django.conf import settings
def initialize_firebase():
if not firebase_admin._apps:
cred_path = os.path.join(settings.BASE_DIR, 'config/firebase/ibapp-firebase-key.json')
cred = credentials.Certificate(cred_path)
firebase_admin.initialize_app(cred)

View File

@@ -0,0 +1,13 @@
{
"type": "service_account",
"project_id": "ibapp-5458d",
"private_key_id": "0eba3cee419c4ce5c75ebb27e150dc8e8491de8f",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC/8FhTrfpCD1kO\nB1j6Xw/FpNIk06w28JP5EydBsUi7fNs7b4MsGu3+gNuWZDFAHvXrijdptMBOs0A+\nPG/hDIrPccVybPXkXFxQGnt7SH1SmbB5r99EYVMvbqMqBru9vByX7TGKxFHec0++\nD2RmuQjOLmP1TKDS/YavmXtrmTtdTlGSzAtKGV7ep3kDUk3ocx+XoO0zex5f61C9\nANJEe94bFX6ptGYTJ2yMf43cA7LnLz8omxrTzgTZUDVyGA1tyCeWiLfg+reCdm3G\nySVCojqVmmGImZpYv7cLsEIOH6RxqR9FNbQKA+sh5WasE7NJFPTkXgd2uMwuoBzx\nvMJZSuIBAgMBAAECggEAAySQkT40Q/Lem/1Oz3OYXWhZRd/EFz3DchIxGwhEVivN\n36Bh0M30hlhQB0YqfoGfuFhK0G8td9VRx8FTXW5y693DfTxjEJNMXP75H/mojlc/\nO9e/YpjPJsd0VA8sKnmd1j4gQcTD1txqSL3AOY9C9sKl7FxVEyZ5XlR+mRX+WUwU\nW2oueKrYZs0k8ivwkvnzC0IaZl1JToMiz6MewC3fJf6vjiFpwxLk8iF3c6XXElaW\nANtnYSU4SJ5I5UxnQ+kWniIYxHaRdnziImyupkyvqNOQ36F3elRfNDLwRb5L2VyH\nm1f8kirsuvdQi4NJtKLi6LXK/XBEoJrBQuZPDKdJ4QKBgQD57WMrMISQv7htJj8/\nFwgZ7y+fk01zRAET5dWUPqhEqpjeDvKhXHJxFDdAnz2BFJEbQ9UvLFJtfdjvBw5e\n+QMYGKqdwJoRaGFRA5q0S4LA/Q8a4QOhzigMWx+orsnXr2adUWrqhuBv1s+39HoE\n9UtPnoZlzI3Fvxwr+wOM0WgOhQKBgQDEmkEkjJ+hQAQnHc3bY45gf9N1fGqbGBk1\nqIiqLXAYYmzHG/vX5O/SdVJDAU2u8ai232a0p5rYmhkHHSt5r7BdjbUDnxQCdrg+\nfBuu9+rlVCFEuctNdr+XQbvMXgh/fpPqrutN9H6pcNG6+lSh4yccK9uFHW+23e4N\naHxqUHW0TQKBgQDcaCGAwTTngmxetdApD2KxnGJfVESFVn+s0I/eQLOceuZ9Tqli\n7GhwmOdxMf3HjB+778Jd67R1ovphMdPWpbu40GgG3wgAOhE4pPkV71HMaF+d0Lqn\nQ2vGGcZ9uEeA0sqoCllDotbjSom9LPk+ziQwfxj/Rbnxnmx1zNSAp1whEQKBgQCU\nLDHmivmQPUAj/wl0TL80qYJErKVoCKyPTVra723pYtza35Nabpf1BSkfiO70Z/uh\nABRXGW7RyqoMO4bVqn0AtVl0xkL9bF6F8WGn5/+oIVUFiAXadyIErK0OZNyAwnRW\nzshXM2r25ymagyqf7CRAzikfVUUqDG9quZHzSnq7jQKBgBWFCBly2BKAsoeiYZow\n6we7j167UasDsqypvFRAR5txMG1CSQgcy9bs8Zk6pPl0YZ/15tdabb05jUeoOVBB\nHHkpC+VN4uBUhcbvKusPawvdF/4SdjYMQRt5WhF4tQLEyc9rJea0XARwsfVDcNeo\n1qUYaaXno/dXsquLjwc9BdSb\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-fbsvc@ibapp-5458d.iam.gserviceaccount.com",
"client_id": "114063251328249657406",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-fbsvc%40ibapp-5458d.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}

View File

@@ -1,6 +1,14 @@
from datetime import timedelta from datetime import timedelta
from pathlib import Path from pathlib import Path
from config.conf.celery import *
from config.conf.cors_headers import *
from config.conf.drf_yasg import *
from config.conf.jazzmin import *
from config.conf.logs import *
from config.conf.redis import *
from config.conf.rest_framework import *
from config.conf.rest_framework_simplejwt import *
from config.env import env from config.env import env
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
@@ -11,44 +19,44 @@ BASE_DIR = Path(__file__).resolve().parent.parent.parent
# See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = env.str('SECRET_KEY') SECRET_KEY = env.str("SECRET_KEY")
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = env.bool('DEBUG') DEBUG = env.bool("DEBUG")
ALLOWED_HOSTS = env.list('ALLOWED_HOSTS') ALLOWED_HOSTS = env.list("ALLOWED_HOSTS")
# Application definition # Application definition
APPS = [ APPS = [
'core.apps.accounts', "core.apps.accounts",
'core.apps.shared', "core.apps.shared",
'core.apps.company', "core.apps.company",
'core.apps.wherehouse', "core.apps.wherehouse",
'core.apps.products', "core.apps.products",
'core.apps.projects', "core.apps.projects",
'core.apps.orders', "core.apps.orders",
'core.apps.finance', "core.apps.finance",
'core.apps.counterparty', "core.apps.counterparty",
'core.apps.notifications', "core.apps.notifications",
] ]
PACKAGES = [ PACKAGES = [
'drf_yasg', "drf_yasg",
'rest_framework', "rest_framework",
'rest_framework_simplejwt', "rest_framework_simplejwt",
'corsheaders', "corsheaders",
'cacheops', "cacheops",
] ]
DJANGO_APPS = [ DJANGO_APPS = [
'jazzmin', "jazzmin",
'django.contrib.admin', "django.contrib.admin",
'django.contrib.auth', "django.contrib.auth",
'django.contrib.contenttypes', "django.contrib.contenttypes",
'django.contrib.sessions', "django.contrib.sessions",
'django.contrib.messages', "django.contrib.messages",
'django.contrib.staticfiles', "django.contrib.staticfiles",
] ]
INSTALLED_APPS = [] INSTALLED_APPS = []
@@ -58,48 +66,48 @@ INSTALLED_APPS += APPS
MIDDLEWARE = [ MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware', "django.middleware.security.SecurityMiddleware",
'django.contrib.sessions.middleware.SessionMiddleware', "django.contrib.sessions.middleware.SessionMiddleware",
'corsheaders.middleware.CorsMiddleware', "corsheaders.middleware.CorsMiddleware",
'django.middleware.common.CommonMiddleware', "django.middleware.common.CommonMiddleware",
'django.middleware.csrf.CsrfViewMiddleware', "django.middleware.csrf.CsrfViewMiddleware",
'django.contrib.auth.middleware.AuthenticationMiddleware', "django.contrib.auth.middleware.AuthenticationMiddleware",
'django.contrib.messages.middleware.MessageMiddleware', "django.contrib.messages.middleware.MessageMiddleware",
'django.middleware.clickjacking.XFrameOptionsMiddleware', "django.middleware.clickjacking.XFrameOptionsMiddleware",
# 'silk.middleware.SilkyMiddleware', # 'silk.middleware.SilkyMiddleware',
] ]
ROOT_URLCONF = 'config.urls' ROOT_URLCONF = "config.urls"
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', "BACKEND": "django.template.backends.django.DjangoTemplates",
'DIRS': [], "DIRS": [],
'APP_DIRS': True, "APP_DIRS": True,
'OPTIONS': { "OPTIONS": {
'context_processors': [ "context_processors": [
'django.template.context_processors.request', "django.template.context_processors.request",
'django.contrib.auth.context_processors.auth', "django.contrib.auth.context_processors.auth",
'django.contrib.messages.context_processors.messages', "django.contrib.messages.context_processors.messages",
], ],
}, },
}, },
] ]
WSGI_APPLICATION = 'config.wsgi.application' WSGI_APPLICATION = "config.wsgi.application"
# Database # Database
# https://docs.djangoproject.com/en/5.2/ref/settings/#databases # https://docs.djangoproject.com/en/5.2/ref/settings/#databases
DATABASES = { DATABASES = {
'default': { "default": {
'ENGINE': 'django.db.backends.postgresql', "ENGINE": "django.db.backends.postgresql",
'NAME': env.str('POSTGRES_DB'), "NAME": env.str("POSTGRES_DB"),
'USER': env.str('POSTGRES_USER'), "USER": env.str("POSTGRES_USER"),
'PASSWORD': env.str('POSTGRES_PASSWORD'), "PASSWORD": env.str("POSTGRES_PASSWORD"),
'HOST': env.str('POSTGRES_HOST'), "HOST": env.str("POSTGRES_HOST"),
'PORT': env.str('POSTGRES_PORT'), "PORT": env.str("POSTGRES_PORT"),
} }
} }
@@ -109,16 +117,16 @@ DATABASES = {
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{ {
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
}, },
{ {
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
}, },
{ {
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
}, },
{ {
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
}, },
] ]
@@ -126,9 +134,9 @@ AUTH_PASSWORD_VALIDATORS = [
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/5.2/topics/i18n/ # https://docs.djangoproject.com/en/5.2/topics/i18n/
LANGUAGE_CODE = 'uz' LANGUAGE_CODE = "uz"
TIME_ZONE = 'Asia/Tashkent' TIME_ZONE = "Asia/Tashkent"
USE_I18N = True USE_I18N = True
@@ -138,25 +146,19 @@ USE_TZ = False
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.2/howto/static-files/ # https://docs.djangoproject.com/en/5.2/howto/static-files/
STATIC_URL = 'static/' STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / 'resources/static' STATIC_ROOT = BASE_DIR / "resources/static"
MEDIA_URL = 'media/' MEDIA_URL = "media/"
MEDIA_ROOT = BASE_DIR / 'resources/media' MEDIA_ROOT = BASE_DIR / "resources/media"
# Default primary key field type # Default primary key field type
# https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
AUTH_USER_MODEL = 'accounts.User' AUTH_USER_MODEL = "accounts.User"
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', env.str("SWAGGER_PROTOCOL", 'https')) SECURE_PROXY_SSL_HEADER = (
"HTTP_X_FORWARDED_PROTO",
from config.conf.rest_framework import * env.str("SWAGGER_PROTOCOL", "https"),
from config.conf.rest_framework_simplejwt import * )
from config.conf.logs import *
from config.conf.cors_headers import *
from config.conf.drf_yasg import *
from config.conf.jazzmin import *
from config.conf.celery import *
from config.conf.redis import *

View File

@@ -1,4 +1,5 @@
import json import json
from django.core.management import BaseCommand from django.core.management import BaseCommand
from core.apps.counterparty.models import Counterparty from core.apps.counterparty.models import Counterparty
@@ -9,24 +10,27 @@ class Command(BaseCommand):
parser.add_argument("file_path", type=str) parser.add_argument("file_path", type=str)
def handle(self, *args, **options): def handle(self, *args, **options):
file_path = options['file_path'] file_path = options["file_path"]
with open(file_path, 'r') as f: with open(file_path, "r") as f:
data = json.load(f) data = json.load(f)
for item in data['data']['data']: for item in data["data"]["data"]:
Counterparty.objects.get_or_create( Counterparty.objects.get_or_create(
name=item['name'], name=item["name"],
defaults={ defaults={
'phone': item['person']['phone'], "phone": item["person"]["phone"],
'inn': item['person']['tin'], "inn": item["person"]["tin"],
'balance_currency': item['balances'][0]['currency']['symbol'].lower() if item['balances'] else 'uzs', "balance_currency": (
'is_archived': item['is_archived'], item["balances"][0]["currency"]["symbol"].lower()
'balance': item['total_amount'], if item["balances"]
'total_debit': item['debt_amount'], else "uzs"
'total_kredit': item['credit_amount'], ),
"is_archived": item["is_archived"],
"balance": item["total_amount"],
"total_debit": item["debt_amount"],
"total_kredit": item["credit_amount"],
}, },
) )
self.stdout.write("Counterparties added") self.stdout.write("Counterparties added")

View File

@@ -3,6 +3,7 @@ from django.db import transaction
from rest_framework import serializers from rest_framework import serializers
from core.apps.finance.models import Expence from core.apps.finance.models import Expence
from core.apps.notifications.utils.notify_user import notify_user
class ExpenceCreateSerializer(serializers.ModelSerializer): class ExpenceCreateSerializer(serializers.ModelSerializer):
@@ -33,7 +34,7 @@ class ExpenceCreateSerializer(serializers.ModelSerializer):
) )
cash_transaction = expence.cash_transaction cash_transaction = expence.cash_transaction
payment_type = expence.payment_type payment_type = expence.payment_type
user = self.context.get('user')
if validated_data.get('currency') == 'uzs': if validated_data.get('currency') == 'uzs':
cash_transaction.expence_balance_uzs += expence.price cash_transaction.expence_balance_uzs += expence.price
cash_transaction.total_balance_uzs = cash_transaction.income_balance_uzs - cash_transaction.expence_balance_uzs cash_transaction.total_balance_uzs = cash_transaction.income_balance_uzs - cash_transaction.expence_balance_uzs
@@ -69,7 +70,11 @@ class ExpenceCreateSerializer(serializers.ModelSerializer):
expence.counterparty.debit_usd += validated_data.get('price') expence.counterparty.debit_usd += validated_data.get('price')
expence.counterparty.total_debit += expence.price expence.counterparty.total_debit += expence.price
expence.counterparty.save() expence.counterparty.save()
body = f"""{user.full_name} {expence.price} {expence.currency.upper()}... \n screen: /monitoring"""
data = {
"screen": "/monitoring"
}
notify_user(user=user, title="Tasdiqlang yoki rad eting", body=body, data=data)
cash_transaction.save() cash_transaction.save()
payment_type.save() payment_type.save()
return expence return expence
@@ -89,7 +94,7 @@ class ExpenceListSerializer(serializers.ModelSerializer):
fields = [ fields = [
'id', 'cash_transaction', 'payment_type', 'project_folder', 'project', 'expence_type', 'id', 'cash_transaction', 'payment_type', 'project_folder', 'project', 'expence_type',
'counterparty', 'price', 'exchange_rate', 'currency', 'date', 'comment', 'audit', 'file', 'counterparty', 'price', 'exchange_rate', 'currency', 'date', 'comment', 'audit', 'file',
'user', 'expence_chats', 'user', 'expence_chats', 'created_at'
] ]
def get_user(self, obj): def get_user(self, obj):

View File

@@ -1,103 +1,133 @@
from django.db import transaction from django.db import transaction
from rest_framework import serializers from rest_framework import serializers
from core.apps.finance.models import Income from core.apps.finance.models import Income
class IncomeListSerializer(serializers.ModelSerializer): class IncomeListSerializer(serializers.ModelSerializer):
cash_transaction = serializers.SerializerMethodField(method_name='get_cash_transaction') cash_transaction = serializers.SerializerMethodField(
payment_type = serializers.SerializerMethodField(method_name='get_payment_type') method_name="get_cash_transaction"
project_folder = serializers.SerializerMethodField(method_name='get_project_folder') )
project = serializers.SerializerMethodField(method_name='get_project') payment_type = serializers.SerializerMethodField(method_name="get_payment_type")
counterparty = serializers.SerializerMethodField(method_name='get_counterparty') project_folder = serializers.SerializerMethodField(method_name="get_project_folder")
type_income = serializers.SerializerMethodField(method_name='get_type_income') project = serializers.SerializerMethodField(method_name="get_project")
user = serializers.SerializerMethodField(method_name='get_user') counterparty = serializers.SerializerMethodField(method_name="get_counterparty")
type_income = serializers.SerializerMethodField(method_name="get_type_income")
user = serializers.SerializerMethodField(method_name="get_user")
class Meta: class Meta:
model = Income model = Income
fields = [ fields = [
'id', 'cash_transaction', 'payment_type', 'project_folder', 'project', "id",
'counterparty', 'type_income', 'currency', 'price', 'exchange_rate', 'date', "cash_transaction",
'comment', 'file', 'audit', 'user', 'income_chat' "payment_type",
"project_folder",
"project",
"counterparty",
"type_income",
"currency",
"price",
"exchange_rate",
"date",
"comment",
"file",
"audit",
"user",
"income_chat",
"created_at",
] ]
def get_cash_transaction(self, obj): def get_cash_transaction(self, obj):
return { return {"id": obj.cash_transaction.id, "name": obj.cash_transaction.name}
'id': obj.cash_transaction.id,
'name': obj.cash_transaction.name
}
def get_payment_type(self, obj): def get_payment_type(self, obj):
return { return {"id": obj.payment_type.id, "name": obj.payment_type.name}
'id': obj.payment_type.id,
'name': obj.payment_type.name
}
def get_project_folder(self, obj): def get_project_folder(self, obj):
return { return (
'id': obj.project_folder.id, {"id": obj.project_folder.id, "name": obj.project_folder.name}
'name': obj.project_folder.name if obj.project_folder
} if obj.project_folder else None else None
)
def get_project(self, obj): def get_project(self, obj):
return { return {"id": obj.project.id, "name": obj.project.name} if obj.project else None
'id': obj.project.id,
'name': obj.project.name
} if obj.project else None
def get_counterparty(self, obj): def get_counterparty(self, obj):
return { return (
'id': obj.counterparty.id, {"id": obj.counterparty.id, "name": obj.counterparty.name}
'name': obj.counterparty.name if obj.counterparty
} if obj.counterparty else None else None
)
def get_type_income(self, obj): def get_type_income(self, obj):
return { return (
'id': obj.type_income.id, {"id": obj.type_income.id, "name": obj.type_income.name}
'name': obj.type_income.name if obj.type_income
} if obj.type_income else None else None
)
def get_user(self, obj): def get_user(self, obj):
return { return (
'id': obj.user.id, {
'full_name': obj.user.full_name, "id": obj.user.id,
} if obj.user else None "full_name": obj.user.full_name,
}
if obj.user
else None
)
class IncomeCreateSerializer(serializers.ModelSerializer): class IncomeCreateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Income model = Income
fields = [ fields = [
'cash_transaction', 'payment_type', 'project_folder', 'project', "cash_transaction",
'counterparty', 'type_income', 'currency', 'price', 'exchange_rate', 'date', "payment_type",
'comment', 'file', 'audit' "project_folder",
"project",
"counterparty",
"type_income",
"currency",
"price",
"exchange_rate",
"date",
"comment",
"file",
"audit",
] ]
def create(self, validated_data): def create(self, validated_data):
with transaction.atomic(): with transaction.atomic():
income = Income.objects.create( income = Income.objects.create(
user=self.context.get('user'), user=self.context.get("user"),
cash_transaction=validated_data['cash_transaction'], cash_transaction=validated_data["cash_transaction"],
payment_type=validated_data['payment_type'], payment_type=validated_data["payment_type"],
project_folder=validated_data.get('project_folder'), project_folder=validated_data.get("project_folder"),
project=validated_data.get('project'), project=validated_data.get("project"),
counterparty=validated_data.get('counterparty'), counterparty=validated_data.get("counterparty"),
type_income=validated_data.get('type_income'), type_income=validated_data.get("type_income"),
currency=validated_data.get('currency'), currency=validated_data.get("currency"),
price=validated_data.get('price') * validated_data.get('exchange_rate') if validated_data.get('exchange_rate') else validated_data.get('price'), price=(
exchange_rate=validated_data.get('exchange_rate'), validated_data.get("price") * validated_data.get("exchange_rate")
date=validated_data.get('date'), if validated_data.get("exchange_rate")
comment=validated_data.get('comment'), else validated_data.get("price")
file=validated_data.get('file'), ),
audit=validated_data.get('audit') exchange_rate=validated_data.get("exchange_rate"),
date=validated_data.get("date"),
comment=validated_data.get("comment"),
file=validated_data.get("file"),
audit=validated_data.get("audit"),
) )
cash_transaction = income.cash_transaction cash_transaction = income.cash_transaction
payment_type = income.payment_type payment_type = income.payment_type
if validated_data.get('currency') == 'uzs': if validated_data.get("currency") == "uzs":
cash_transaction.income_balance_uzs += income.price cash_transaction.income_balance_uzs += income.price
cash_transaction.total_balance_uzs = cash_transaction.income_balance_uzs - cash_transaction.expence_balance_uzs cash_transaction.total_balance_uzs = (
cash_transaction.income_balance_uzs
- cash_transaction.expence_balance_uzs
)
payment_type.total_uzs += income.price payment_type.total_uzs += income.price
if income.counterparty: if income.counterparty:
@@ -105,26 +135,33 @@ class IncomeCreateSerializer(serializers.ModelSerializer):
income.counterparty.debit_uzs -= income.price income.counterparty.debit_uzs -= income.price
income.counterparty.total_debit -= income.price income.counterparty.total_debit -= income.price
income.counterparty.kredit_uzs += income.counterparty.debit_uzs - income.price income.counterparty.kredit_uzs += (
income.counterparty.debit_uzs - income.price
)
income.counterparty.total_kredit += income.price income.counterparty.total_kredit += income.price
else: else:
income.counterparty.kredit_uzs += income.price income.counterparty.kredit_uzs += income.price
income.counterparty.total_kredit += income.price income.counterparty.total_kredit += income.price
income.counterparty.save() income.counterparty.save()
elif validated_data.get('currency') == 'usd': elif validated_data.get("currency") == "usd":
cash_transaction.income_balance_usd += income.price cash_transaction.income_balance_usd += income.price
cash_transaction.total_balance_usd = cash_transaction.income_balance_usd - cash_transaction.expence_balance_usd cash_transaction.total_balance_usd = (
cash_transaction.income_balance_usd
- cash_transaction.expence_balance_usd
)
payment_type.total_usd += income.price payment_type.total_usd += income.price
if income.counterparty: if income.counterparty:
if income.counterparty.debit_usd != 0: if income.counterparty.debit_usd != 0:
income.counterparty.debit_usd -= validated_data.get('price') income.counterparty.debit_usd -= validated_data.get("price")
income.counterparty.total_debit -= income.price income.counterparty.total_debit -= income.price
income.counterparty.kredit_usd += income.counterparty.debit_usd - validated_data.get('price') income.counterparty.kredit_usd += (
income.counterparty.debit_usd - validated_data.get("price")
)
income.counterparty.total_kredit += income.price income.counterparty.total_kredit += income.price
else: else:
income.counterparty.kredit_usd += validated_data.get('price') income.counterparty.kredit_usd += validated_data.get("price")
income.counterparty.total_kredit += income.price income.counterparty.total_kredit += income.price
income.counterparty.save() income.counterparty.save()
@@ -141,46 +178,83 @@ class IncomeUpdateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Income model = Income
fields = [ fields = [
'project_folder', 'project', 'price', 'type_income', 'counterparty', "project_folder",
'date', 'comment', 'audit', 'file', "project",
"price",
"type_income",
"counterparty",
"date",
"comment",
"audit",
"file",
] ]
extra_kwargs = { extra_kwargs = {
'price': {'required': False}, "price": {"required": False},
} }
def update(self, instance, validated_data): def update(self, instance, validated_data):
old_price = instance.price old_price = instance.price
instance.project_folder = validated_data.get('project_folder', instance.project_folder) instance.project_folder = validated_data.get(
instance.project = validated_data.get('project', instance.project) "project_folder", instance.project_folder
instance.price = validated_data.get('price', instance.price) )
instance.type_income = validated_data.get('type_income', instance.type_income) instance.project = validated_data.get("project", instance.project)
instance.counterparty = validated_data.get('counterparty', instance.counterparty) instance.price = validated_data.get("price", instance.price)
instance.date = validated_data.get('date', instance.date) instance.type_income = validated_data.get("type_income", instance.type_income)
instance.comment = validated_data.get('comment', instance.comment) instance.counterparty = validated_data.get(
instance.audit = validated_data.get('audit', instance.audit) "counterparty", instance.counterparty
instance.file = validated_data.get('file', instance.file) )
if validated_data.get('price'): instance.date = validated_data.get("date", instance.date)
instance.comment = validated_data.get("comment", instance.comment)
instance.audit = validated_data.get("audit", instance.audit)
instance.file = validated_data.get("file", instance.file)
if validated_data.get("price"):
cash_transaction = instance.cash_transaction cash_transaction = instance.cash_transaction
payment_type = instance.payment_type payment_type = instance.payment_type
if old_price > validated_data.get('price'): if old_price > validated_data.get("price"):
if instance.currency == 'uzs': if instance.currency == "uzs":
cash_transaction.income_balance_uzs -= (old_price - validated_data.get('price')) cash_transaction.income_balance_uzs -= (
cash_transaction.total_balance_uzs -= (old_price - validated_data.get('price')) old_price - validated_data.get("price")
payment_type.total_uzs -= (old_price - validated_data.get('price')) if payment_type.total_uzs > (old_price - validated_data.get('price')) else 0 )
cash_transaction.total_balance_uzs -= (
old_price - validated_data.get("price")
)
payment_type.total_uzs -= (
(old_price - validated_data.get("price"))
if payment_type.total_uzs
> (old_price - validated_data.get("price"))
else 0
)
else: else:
cash_transaction.income_balance_usd -= (old_price - validated_data.get('price')) cash_transaction.income_balance_usd -= (
cash_transaction.total_balance_usd -= (old_price - validated_data.get('price')) old_price - validated_data.get("price")
payment_type.total_usd -= (old_price - validated_data.get('price')) if payment_type.total_usd > (old_price - validated_data.get('price')) else 0 )
cash_transaction.total_balance_usd -= (
old_price - validated_data.get("price")
)
payment_type.total_usd -= (
(old_price - validated_data.get("price"))
if payment_type.total_usd
> (old_price - validated_data.get("price"))
else 0
)
elif old_price < validated_data.get('price'): elif old_price < validated_data.get("price"):
if instance.currency == 'uzs': if instance.currency == "uzs":
cash_transaction.income_balance_uzs += (old_price - validated_data.get('price')) cash_transaction.income_balance_uzs += (
cash_transaction.total_balance_uzs += (old_price - validated_data.get('price')) old_price - validated_data.get("price")
payment_type.total_uzs += (old_price - validated_data.get('price')) )
cash_transaction.total_balance_uzs += (
old_price - validated_data.get("price")
)
payment_type.total_uzs += old_price - validated_data.get("price")
else: else:
cash_transaction.income_balance_usd += (old_price - validated_data.get('price')) cash_transaction.income_balance_usd += (
cash_transaction.total_balance_usd += (old_price - validated_data.get('price')) old_price - validated_data.get("price")
payment_type.total_usd += (old_price - validated_data.get('price')) )
cash_transaction.total_balance_usd += (
old_price - validated_data.get("price")
)
payment_type.total_usd += old_price - validated_data.get("price")
cash_transaction.save() cash_transaction.save()
payment_type.save() payment_type.save()
instance.save() instance.save()

View File

@@ -0,0 +1 @@
from .notification import *

View File

@@ -0,0 +1,5 @@
from django.contrib import admin
from core.apps.notifications.models import Notification
admin.site.register(Notification)

View File

@@ -4,3 +4,8 @@ from django.apps import AppConfig
class NotificationsConfig(AppConfig): class NotificationsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'core.apps.notifications' name = 'core.apps.notifications'
def ready(self):
import core.apps.notifications.admin
from config.firebase import initialize_firebase
initialize_firebase()

View File

@@ -0,0 +1,24 @@
# Generated by Django 5.2.4 on 2025-10-30 14:56
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('notifications', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AddField(
model_name='notification',
name='type',
field=models.CharField(choices=[('web', 'web'), ('mobile', 'mobile')], default='mobile', max_length=6),
),
migrations.AlterUniqueTogether(
name='notification',
unique_together={('type', 'user', 'token')},
),
]

View File

@@ -3,7 +3,15 @@ from django.db import models
from core.apps.shared.models import BaseModel from core.apps.shared.models import BaseModel
from core.apps.accounts.models import User from core.apps.accounts.models import User
class Notification(BaseModel): class Notification(BaseModel):
type = models.CharField(
choices=[('web', 'web'), ('mobile', 'mobile')],
max_length=6,
default='mobile'
)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='notifications') user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='notifications')
token = models.CharField(max_length=255, unique=True) token = models.CharField(max_length=255, unique=True)
class Meta:
unique_together = ('type', 'user', 'token')

View File

@@ -7,5 +7,5 @@ class NotificationSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Notification model = Notification
fields = [ fields = [
'token' 'type', 'token'
] ]

View File

@@ -1,8 +1,11 @@
from core.apps.notifications.models import Notification from core.apps.notifications.models import Notification
from core.apps.notifications.utils.send_notification import send_notification from core.apps.notifications.utils.send_notification import send_notification, send_web_notification
def notify_user(user, title, body): def notify_user(user, title, body, data):
tokens = Notification.objects.filter(user=user) tokens = Notification.objects.filter(user=user)
for token in tokens: for token in tokens:
send_notification(token.token, title, body) if token.type == 'mobile':
send_notification(token.token, title, body, data)
if token.type == 'web':
send_web_notification(token.token, title, body, data)

View File

@@ -1,16 +1,53 @@
import requests import requests
from firebase_admin import messaging
from django.conf import settings
from core.apps.notifications.models import Notification
EXPO_API_URL = "https://exp.host/--/api/v2/push/send"
def send_notification(token, title, body, data=None): def send_notification(token, title, body, data=None):
message = { tokens = list(Notification.objects.exclude(token=token).values_list("token", flat=True))
if not tokens:
return {"error": "No tokens found"}
messages = [
{
"to": token, "to": token,
"sound": "default", "sound": "default",
"title": title, "title": title,
"body": body, "body": body,
"data": data or {}, "data": data or {},
} }
response = requests.post( for token in tokens
"https://exp.host/--/api/v2/push/send", ]
json=message,
headers={"Content-Type": "application/json"} chunk_size = 100
results = []
for i in range(0, len(messages), chunk_size):
chunk = messages[i:i + chunk_size]
response = requests.post(EXPO_API_URL, json=chunk, headers={"Content-Type": "application/json"})
try:
results.append(response.json())
except Exception:
results.append({"error": "Invalid JSON response"})
return results
def send_web_notification(token, title, body, data=None):
tokens = list(Notification.objects.exclude(token=token).values_list('token', flat=True))
if not tokens:
return
message = messaging.MulticastMessage(
notification=messaging.Notification(
title=title,
body=body
),
data=data or {},
tokens=tokens,
) )
return response.json()
response = messaging.send_each_for_multicast(message)

View File

@@ -3,18 +3,23 @@ from rest_framework.response import Response
from core.apps.notifications.serializers import notification as serializers from core.apps.notifications.serializers import notification as serializers
from core.apps.notifications.models import Notification from core.apps.notifications.models import Notification
from core.apps.accounts.permissions.permissions import HasRolePermission
class RegisterExpoPushToken(generics.GenericAPIView): class RegisterExpoPushToken(generics.GenericAPIView):
serializer_class = serializers.NotificationSerializer serializer_class = serializers.NotificationSerializer
queryset = Notification.objects.all() queryset = Notification.objects.all()
permission_classes = [HasRolePermission]
def post(self, request): def post(self, request):
serializer = self.serializer_class(data=request.data) serializer = self.serializer_class(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
Notification.objects.update_or_create( Notification.objects.get_or_create(
user=request.user, user=request.user,
token=serializer.validated_data['token'] token=serializer.validated_data['token'],
type=serializer.validated_data.get('type') \
if serializer.validated_data.get('type') \
else 'mobile',
) )
return Response({"message": "Token saqlandi"}, status=status.HTTP_201_CREATED) return Response({"message": "Token saqlandi"}, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

View File

@@ -5,4 +5,14 @@ from core.apps.orders.models import Order
@admin.register(Order) @admin.register(Order)
class OrderAdmin(admin.ModelAdmin): class OrderAdmin(admin.ModelAdmin):
list_display = ['id', 'product', 'wherehouse', 'currency'] list_display = ['id', 'product', 'wherehouse', 'currency', 'counterparty']
# # Statuslar: {'ORDERED', 'PARTIALLY_RECIEVED', 'pending', "open", "buying", "rejected", "recieved", "passive"}
# open -> yangi == NEW
# ordered -> partiya qilingan == PARTY_IS_MADE
# pending -> kutilmoqda == EXPECTED
# passive -> qoralama == DRAFT
# recieved -> yopilgan == RECIEVED
# rejected -> bekor qilingan == CANCELLED
# buying -> sotib olinmoqda == PURCHASED
# partially_recieved -> jarayonda == PROCESS

View File

@@ -1,6 +1,6 @@
from django.contrib import admin from django.contrib import admin
from core.apps.orders.models import Party, PartyAmount, DeletedParty from core.apps.orders.models import DeletedParty, Party, PartyAmount
class PartyAmountInline(admin.StackedInline): class PartyAmountInline(admin.StackedInline):
@@ -11,17 +11,28 @@ class PartyAmountInline(admin.StackedInline):
@admin.register(Party) @admin.register(Party)
class PartyAdmin(admin.ModelAdmin): class PartyAdmin(admin.ModelAdmin):
list_display = ['id','number','mediator', 'delivery_date', 'payment_date', 'is_deleted'] list_display = [
"id",
"number",
"mediator",
"delivery_date",
"payment_date",
"is_deleted",
]
inlines = [PartyAmountInline] inlines = [PartyAmountInline]
search_fields = [
"number",
]
@admin.register(PartyAmount) @admin.register(PartyAmount)
class PartyAmountAdmin(admin.ModelAdmin): class PartyAmountAdmin(admin.ModelAdmin):
list_display = ['id', 'total_price', 'cost_amount'] list_display = ["id", "total_price", "cost_amount"]
def has_module_permission(self, request): def has_module_permission(self, request):
return False return False
@admin.register(DeletedParty) @admin.register(DeletedParty)
class DeletedPartyAdmin(admin.ModelAdmin): class DeletedPartyAdmin(admin.ModelAdmin):
list_display = ['id', 'deleted_date', 'party'] list_display = ["id", "deleted_date", "party"]

View File

@@ -20,6 +20,7 @@ class Command(BaseCommand):
with open(file_path, 'r', encoding='utf-8') as f: with open(file_path, 'r', encoding='utf-8') as f:
data = json.load(f) data = json.load(f)
for item in data['data']['data']: for item in data['data']['data']:
product_name = item['product']['name']['uz'] product_name = item['product']['name']['uz']
unit_name = item['unit']['name']['uz'] unit_name = item['unit']['name']['uz']
@@ -53,6 +54,6 @@ class Command(BaseCommand):
created_at=created_at, created_at=created_at,
date=delivery_date date=delivery_date
) )
break # break
self.stdout.write(self.style.SUCCESS("Orders imported successfully ✅")) self.stdout.write(self.style.SUCCESS("Orders imported successfully ✅"))

View File

@@ -1,86 +1,109 @@
import json, requests import json
import requests
from django.core.management import BaseCommand from django.core.management import BaseCommand
from core.apps.orders.models import Order, Party, PartyAmount
from core.apps.accounts.models import User from core.apps.accounts.models import User
from core.apps.products.models import Product, Unity
from core.apps.counterparty.models import Counterparty from core.apps.counterparty.models import Counterparty
from core.apps.wherehouse.models import WhereHouse from core.apps.orders.models import Order, Party, PartyAmount
from core.apps.projects.models import Project, ProjectFolder
from core.apps.orders.utils.parse_date import parse_date from core.apps.orders.utils.parse_date import parse_date
from core.apps.products.models import Product, Unity
from core.apps.projects.models import Project, ProjectFolder
from core.apps.wherehouse.models import WhereHouse
token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2JhY2tlbmQuYXBwLnV5cXVyLnV6L21haW4vYXV0aC9sb2dpbiIsImlhdCI6MTc2MTczMTI4MSwiZXhwIjoxNzYxODE3NjgxLCJuYmYiOjE3NjE3MzEyODEsImp0aSI6ImszNVRQUDBiblpSU3ZFS24iLCJzdWIiOiIxMDQiLCJwcnYiOiIyM2JkNWM4OTQ5ZjYwMGFkYjM5ZTcwMWM0MDA4NzJkYjdhNTk3NmY3In0.G5rjiIV-roIgEZzH9s8XgLYnS9tunnMgnCa2U6S3vd0"
token = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2JhY2tlbmQuYXBwLnV5cXVyLnV6L21haW4vYXV0aC9sb2dpbiIsImlhdCI6MTc2MTI4NjcwMywiZXhwIjoxNzYxNjQ2NzAzLCJuYmYiOjE3NjEyODY3MDMsImp0aSI6IjRBNkh4aHI5WkRqOGxVMzUiLCJzdWIiOiIxMDQiLCJwcnYiOiIyM2JkNWM4OTQ5ZjYwMGFkYjM5ZTcwMWM0MDA4NzJkYjdhNTk3NmY3In0.O4NGZL_a3WIrjko5W2sEOBAbM5lv0miVgVa9tfYuyhM'
class Command(BaseCommand): class Command(BaseCommand):
def add_arguments(self, parser): def add_arguments(self, parser):
parser.add_argument('file_path', type=str) parser.add_argument("file_path", type=str)
def handle(self, *args, **options): def handle(self, *args, **options):
file_path = options['file_path'] file_path = options["file_path"]
headers = { headers = {"Authorization": f"Bearer {token}"}
"Authorization": f"Bearer {token}"
}
with open(file_path, 'r') as f: with open(file_path, "r") as f:
data = json.load(f) data = json.load(f)
for item in data['data']['data']: statuses = {
url = f'https://backend.app.uyqur.uz/main/supply/order-view?id={item['id']}' "open": "NEW",
"ordered": "PARTY_IS_MADE",
"pending": "EXPECTED",
"passive": "DRAFT",
"recieved": "RECIEVED",
"rejected": "CANCELLED",
"buying": "PURCHASED",
"partially_recieved": "PROCESS"
}
for item in data["data"]["data"]:
url = f"https://backend.app.uyqur.uz/main/supply/order-view?id={item['id']}"
res = requests.get(url, headers=headers) res = requests.get(url, headers=headers)
data = res.json()['data'] data = res.json()["data"]
user = None user = None
if data.get('agent'): if data.get("agent"):
user = User.objects.filter(full_name=data['agent']['full_name']).first() user = User.objects.filter(full_name=data["agent"]["full_name"]).first()
if not user: if not user:
continue continue
party, created = Party.objects.get_or_create(
number=data['id'], party, created = Party.objects.update_or_create(
number=data["id"],
defaults={ defaults={
'mediator': user, "mediator": user,
'delivery_date': parse_date(data['delivery_date']), "delivery_date": parse_date(data["delivery_date"]),
'closed_date': parse_date(data['recieved_date']), "closed_date": parse_date(data["recieved_date"]),
'order_date': parse_date(data['ordered_date']), "order_date": parse_date(data["ordered_date"]),
'payment_date': parse_date(data['payment_date']) if parse_date(data['payment_date']) else parse_date(data['ordered_date']), "payment_date": (
'status': data['status'].upper(), parse_date(data["payment_date"])
'payment_percentage': data['payment_percent'], if parse_date(data["payment_date"])
'process': data['percent'], else parse_date(data["ordered_date"])
} ),
"status": statuses.get(data["status"].lower()),
"payment_percentage": data["payment_percent"],
"process": data["percent"],
},
) )
orders = [] orders = []
total_price = 0 total_price = 0
paid_amount = 0 paid_amount = 0
calculated_amount = 0 calculated_amount = 0
must_pay_amount = 0 must_pay_amount = 0
for i in data['warehouse_products']: for i in data["warehouse_products"]:
product = Product.objects.filter(name__icontains=i['product']['name']['uz']).first() product = Product.objects.filter(
name__icontains=i["product"]["name"]["uz"]
).first()
if not product: if not product:
continue continue
unit = Unity.objects.filter(value=i['unit']['name']['uz']).first() unit = Unity.objects.filter(value=i["unit"]["name"]["uz"]).first()
counterparty = Counterparty.objects.filter(name=i['company_person']['name']).first() counterparty = Counterparty.objects.filter(
name=i["company_person"]["name"]
).first()
wherehouse = None wherehouse = None
if i.get('warehouse'): if i.get("warehouse"):
wherehouse = WhereHouse.objects.filter(name=i.get('warehouse').get('name')).first() wherehouse = WhereHouse.objects.filter(
name=i.get("warehouse").get("name")
).first()
project_folder = None project_folder = None
if i.get('project'): if i.get("project"):
project_folder = ProjectFolder.objects.filter(name=i['project']['name']).first() project_folder = ProjectFolder.objects.filter(
name=i["project"]["name"]
).first()
order, created = Order.objects.get_or_create( order = Order.objects.create(
product=product, product=product,
amount=i["amount"],
total_price=i["total_amount"],
quantity=i["quantity"],
unity=unit, unity=unit,
counterparty=counterparty, currency=i["currency"]["symbol"].lower(),
currency=i['currency']['symbol'].lower(),
wherehouse=wherehouse,
project_folder=project_folder, project_folder=project_folder,
amount=i['amount'], wherehouse=wherehouse,
total_price=i['total_amount'], counterparty=counterparty,
quantity=i['quantity'],
) )
total_price += i['total_amount'] total_price += i["total_amount"]
paid_amount += i['paid_amount'] paid_amount += i["paid_amount"]
calculated_amount += i['calculated_amount'] calculated_amount += i["calculated_amount"]
must_pay_amount += i['must_pay_amount'] must_pay_amount += i["must_pay_amount"]
orders.append(order) orders.append(order)
party.orders.set(orders) party.orders.set(orders)
PartyAmount.objects.get_or_create( PartyAmount.objects.get_or_create(
@@ -90,6 +113,6 @@ class Command(BaseCommand):
"calculated_amount": calculated_amount, "calculated_amount": calculated_amount,
"paid_amount": paid_amount, "paid_amount": paid_amount,
"payment_amount": must_pay_amount, "payment_amount": must_pay_amount,
} },
) )
self.stdout.write("Parties added") self.stdout.write("Parties added")

View File

@@ -71,6 +71,7 @@ class MultipleOrderCreateSerializer(serializers.Serializer):
resources = validated_data.pop('resources') resources = validated_data.pop('resources')
common_date = validated_data.get('date') common_date = validated_data.get('date')
orders = [] orders = []
user = self.context.get('user')
for resource in resources: for resource in resources:
order = Order( order = Order(
@@ -84,14 +85,14 @@ class MultipleOrderCreateSerializer(serializers.Serializer):
employee=self.context.get('user'), employee=self.context.get('user'),
) )
orders.append(order) orders.append(order)
body = f""" 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}
{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) data = {
"screen": "/supply"
}
notify_user(user=user, title="Ta'minot",body=body, data=data)
created_orders = Order.objects.bulk_create(orders) created_orders = Order.objects.bulk_create(orders)
user = self.context.get('user')
return created_orders return created_orders

View File

@@ -1,11 +1,10 @@
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from rest_framework import generics from rest_framework import generics
from rest_framework.response import Response from rest_framework.response import Response
from core.apps.accounts.permissions.permissions import HasRolePermission
from core.apps.shared.models import UsdCourse from core.apps.shared.models import UsdCourse
from core.apps.shared.serializers.usd_course import UsdCourseSerializer from core.apps.shared.serializers.usd_course import UsdCourseSerializer
from core.apps.accounts.permissions.permissions import HasRolePermission
class UsdCourseApiView(generics.GenericAPIView): class UsdCourseApiView(generics.GenericAPIView):
@@ -20,10 +19,15 @@ class UsdCourseApiView(generics.GenericAPIView):
def patch(self, request): def patch(self, request):
usd_cource = UsdCourse.objects.first() usd_cource = UsdCourse.objects.first()
if not usd_cource:
usd_cource = UsdCourse.objects.create(value=0)
old_value = usd_cource.value
serializer = self.serializer_class(data=request.data) serializer = self.serializer_class(data=request.data)
if not serializer.is_valid(): if not serializer.is_valid():
return Response(serializer.errors, status=400) return Response(serializer.errors, status=400)
value = serializer.validated_data.get('value') value = serializer.validated_data.get("value")
usd_cource.value = value usd_cource.value = value
usd_cource.save() usd_cource.save()
return Response({'new_value': usd_cource.value, 'old_value': value}, status=200) return Response(
{"new_value": usd_cource.value, "old_value": old_value}, status=200
)

5279
data/counterparty/cp1.json Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,22 @@
aiohappyeyeballs==2.6.1
aiohttp==3.13.2
aiosignal==1.4.0
amqp==5.3.1 amqp==5.3.1
anyio==4.11.0
asgiref==3.9.1 asgiref==3.9.1
attrs==25.4.0
billiard==4.2.1 billiard==4.2.1
CacheControl==0.14.3
cachetools==6.2.1
celery==5.5.3 celery==5.5.3
certifi==2025.10.5
cffi==2.0.0
charset-normalizer==3.4.4
click==8.2.1 click==8.2.1
click-didyoumean==0.3.1 click-didyoumean==0.3.1
click-plugins==1.1.1.2 click-plugins==1.1.1.2
click-repl==0.3.0 click-repl==0.3.0
cryptography==46.0.3
Django==5.2.4 Django==5.2.4
django-cacheops==7.2 django-cacheops==7.2
django-cors-headers==4.7.0 django-cors-headers==4.7.0
@@ -18,26 +29,59 @@ django-silk==5.4.0
djangorestframework==3.16.0 djangorestframework==3.16.0
djangorestframework_simplejwt==5.5.1 djangorestframework_simplejwt==5.5.1
drf-yasg==1.21.10 drf-yasg==1.21.10
exponent_server_sdk==2.2.0
firebase_admin==7.1.0
frozenlist==1.8.0
funcy==2.0 funcy==2.0
google-api-core==2.28.1
google-auth==2.42.0
google-cloud-core==2.5.0
google-cloud-firestore==2.21.0
google-cloud-storage==3.4.1
google-crc32c==1.7.1
google-resumable-media==2.7.2
googleapis-common-protos==1.71.0
gprof2dot==2025.4.14 gprof2dot==2025.4.14
grpcio==1.76.0
grpcio-status==1.76.0
gunicorn==23.0.0 gunicorn==23.0.0
h11==0.16.0 h11==0.16.0
h2==4.3.0
hpack==4.1.0
httpcore==1.0.9
httpx==0.28.1
hyperframe==6.1.0
idna==3.11
inflection==0.5.1 inflection==0.5.1
kombu==5.5.4 kombu==5.5.4
msgpack==1.1.2
multidict==6.7.0
packaging==25.0 packaging==25.0
pillow==11.3.0 pillow==11.3.0
prompt_toolkit==3.0.51 prompt_toolkit==3.0.51
propcache==0.4.1
proto-plus==1.26.1
protobuf==6.33.0
psycopg2-binary==2.9.10 psycopg2-binary==2.9.10
pyasn1==0.6.1
pyasn1_modules==0.4.2
pycparser==2.23
pyfcm==2.1.0
PyJWT==2.10.1 PyJWT==2.10.1
python-dateutil==2.9.0.post0 python-dateutil==2.9.0.post0
pytz==2025.2 pytz==2025.2
PyYAML==6.0.2 PyYAML==6.0.2
redis==6.2.0 redis==6.2.0
requests==2.32.5
rsa==4.9.1
six==1.17.0 six==1.17.0
sniffio==1.3.1
sqlparse==0.5.3 sqlparse==0.5.3
typing_extensions==4.15.0
tzdata==2025.2 tzdata==2025.2
uritemplate==4.2.0 uritemplate==4.2.0
urllib3==2.5.0
uvicorn==0.35.0 uvicorn==0.35.0
vine==5.1.0 vine==5.1.0
wcwidth==0.2.13 wcwidth==0.2.13
requests yarl==1.22.0