commit b42c80cf1c840e902b557c54945e8470a626254d
Author: 7nimor <7nimor@gmail.com>
Date: Sun Jan 18 11:29:19 2026 +0330
first commit
diff --git a/.env.local b/.env.local
new file mode 100644
index 0000000..d4245d4
--- /dev/null
+++ b/.env.local
@@ -0,0 +1,25 @@
+SECRET_KEY=django-insecure-rd)c)2#!cf&a^+(-*nwh_lm5yoo!5bxnfw=k&ll_jilx+uf4#a
+DEBUG=True
+ALLOWED_HOSTS=rsibackend.rasadyaar.ir,127.0.0.1,rasadyaar.ir,rasadyar.net,rsibackend.rasadyar.com,rasadyar.com
+DB_NAME=rsi
+DB_USER=postgres
+DB_PASSWORD=hbZnuB4pipCPooBp4BApeR2njtbwa3PDJlKbEdbR5D86Vo6jvoxl3MDpPm2ZfQAu
+DB_HOST=31.7.78.133
+DB_PORT=14339
+CELERY_BROKER_URL=redis://redis://localhost:6379
+CELERY_RESULT_BACKEND=redis://redis://localhost:6379
+CELERY_ACCEPT_CONTENT=application/json
+CELERY_TASK_SERIALIZER=json
+CELERY_RESULT_SERIALIZER=json
+CELERY_TIMEZONE=Asia/Tehran
+CORS_ORIGIN_ALLOW_ALL=True
+CORS_ORIGIN_WHITELIST=http://localhost:8080,http://127.0.0.1:8080,http://127.0.0.1:3000,http://localhost:3000,https://rsibackend.rasadyaar.ir,https://rasadyaar.ir,https://rasadyar.net,https://rsibackend.rasadyar.com,https://rasadyar.com
+CORS_ALLOWED_ORIGINS=http://localhost:8080,http://127.0.0.1:8080,http://127.0.0.1:3000,http://localhost:3000,https://rsibackend.rasadyaar.ir,https://rasadyaar.ir,https://rasadyar.net,https://rsibackend.rasadyar.com,https://rasadyar.com
+SECURE_PROXY_SSL_HEADER=HTTP_X_FORWARDED_PROTO,https
+SECURE_SSL_REDIRECT=False
+SESSION_COOKIE_SECURE=True
+CSRF_COOKIE_SECURE=True
+REDIS_URL=redis://:ydnW4hwzuDRYcTX3FWCHgQ1f@apo.liara.cloud:33740/0
+
+
+ENV RUNNING_IN_DOCKER=0
\ No newline at end of file
diff --git a/.env.prod b/.env.prod
new file mode 100644
index 0000000..c71bee7
--- /dev/null
+++ b/.env.prod
@@ -0,0 +1,22 @@
+SECRET_KEY=django-insecure-rd)c)2#!cf&a^+(-*nwh_lm5yoo!5bxnfw=k&ll_jilx+uf4#a
+DEBUG=True
+ALLOWED_HOSTS=rsibackend.rasadyaar.ir,127.0.0.1,rasadyaar.ir,rasadyar.net,rsibackend.rasadyar.com,rasadyar.com
+DB_NAME=rsi
+DB_USER=postgres
+DB_PASSWORD=hbZnuB4pipCPooBp4BApeR2njtbwa3PDJlKbEdbR5D86Vo6jvoxl3MDpPm2ZfQAu
+DB_HOST=31.7.78.133
+DB_PORT=14339
+CELERY_BROKER_URL=redis://redis://localhost:6379
+CELERY_RESULT_BACKEND=redis://redis://localhost:6379
+CELERY_ACCEPT_CONTENT=application/json
+CELERY_TASK_SERIALIZER=json
+CELERY_RESULT_SERIALIZER=json
+CELERY_TIMEZONE=Asia/Tehran
+CORS_ORIGIN_ALLOW_ALL=True
+CORS_ORIGIN_WHITELIST=http://localhost:8080,http://127.0.0.1:8080,http://127.0.0.1:3000,http://localhost:3000,https://rsibackend.rasadyaar.ir,https://rasadyaar.ir,https://rasadyar.net,https://rsibackend.rasadyar.com,https://rasadyar.com
+CORS_ALLOWED_ORIGINS=http://localhost:8080,http://127.0.0.1:8080,http://127.0.0.1:3000,http://localhost:3000,https://rsibackend.rasadyaar.ir,https://rasadyaar.ir,https://rasadyar.net,https://rsibackend.rasadyar.com,https://rasadyar.com
+SECURE_PROXY_SSL_HEADER=HTTP_X_FORWARDED_PROTO,https
+SECURE_SSL_REDIRECT=False
+SESSION_COOKIE_SECURE=True
+CSRF_COOKIE_SECURE=True
+REDIS_URL=redis://:ydnW4hwzuDRYcTX3FWCHgQ1f@apo.liara.cloud:33740/0
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..13566b8
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml
diff --git a/.idea/RSI.iml b/.idea/RSI.iml
new file mode 100644
index 0000000..cfcee21
--- /dev/null
+++ b/.idea/RSI.iml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml
new file mode 100644
index 0000000..bbadf7d
--- /dev/null
+++ b/.idea/dataSources.xml
@@ -0,0 +1,12 @@
+
+
+
+
+ postgresql
+ true
+ org.postgresql.Driver
+ jdbc:postgresql://31.7.78.133:14339/rsi
+ $ProjectFileDir$
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..3da04d2
--- /dev/null
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..105ce2d
--- /dev/null
+++ b/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..35f7c8b
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..48c3c01
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..62bd7a0
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..a90bb1e
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,19 @@
+# Dockerfile
+FROM ghcr.io/seniorkian/python39-rsi:1.0.0
+ENV TZ="Asia/Tehran"
+
+# Set working directory
+WORKDIR /app
+
+# فقط کپی requirements و نصب پکیجهای پایتون
+COPY ./requirements.txt .
+RUN pip install --no-cache-dir -r requirements.txt
+
+# Copy project files
+COPY . .
+
+# Expose Django port
+EXPOSE 8000
+
+# Run Django development server
+CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
diff --git a/RSI/__init__.py b/RSI/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/RSI/__pycache__/__init__.cpython-310.pyc b/RSI/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..c8027ad
Binary files /dev/null and b/RSI/__pycache__/__init__.cpython-310.pyc differ
diff --git a/RSI/__pycache__/__init__.cpython-311.pyc b/RSI/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000..969a982
Binary files /dev/null and b/RSI/__pycache__/__init__.cpython-311.pyc differ
diff --git a/RSI/__pycache__/__init__.cpython-312.pyc b/RSI/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..de388e4
Binary files /dev/null and b/RSI/__pycache__/__init__.cpython-312.pyc differ
diff --git a/RSI/__pycache__/__init__.cpython-39.pyc b/RSI/__pycache__/__init__.cpython-39.pyc
new file mode 100644
index 0000000..b331875
Binary files /dev/null and b/RSI/__pycache__/__init__.cpython-39.pyc differ
diff --git a/RSI/__pycache__/settings.cpython-310.pyc b/RSI/__pycache__/settings.cpython-310.pyc
new file mode 100644
index 0000000..abe6012
Binary files /dev/null and b/RSI/__pycache__/settings.cpython-310.pyc differ
diff --git a/RSI/__pycache__/settings.cpython-311.pyc b/RSI/__pycache__/settings.cpython-311.pyc
new file mode 100644
index 0000000..b76f1bd
Binary files /dev/null and b/RSI/__pycache__/settings.cpython-311.pyc differ
diff --git a/RSI/__pycache__/settings.cpython-312.pyc b/RSI/__pycache__/settings.cpython-312.pyc
new file mode 100644
index 0000000..4e720b2
Binary files /dev/null and b/RSI/__pycache__/settings.cpython-312.pyc differ
diff --git a/RSI/__pycache__/settings.cpython-39.pyc b/RSI/__pycache__/settings.cpython-39.pyc
new file mode 100644
index 0000000..5992137
Binary files /dev/null and b/RSI/__pycache__/settings.cpython-39.pyc differ
diff --git a/RSI/__pycache__/urls.cpython-310.pyc b/RSI/__pycache__/urls.cpython-310.pyc
new file mode 100644
index 0000000..5da7e31
Binary files /dev/null and b/RSI/__pycache__/urls.cpython-310.pyc differ
diff --git a/RSI/__pycache__/urls.cpython-311.pyc b/RSI/__pycache__/urls.cpython-311.pyc
new file mode 100644
index 0000000..ef1d773
Binary files /dev/null and b/RSI/__pycache__/urls.cpython-311.pyc differ
diff --git a/RSI/__pycache__/urls.cpython-312.pyc b/RSI/__pycache__/urls.cpython-312.pyc
new file mode 100644
index 0000000..a8bddf0
Binary files /dev/null and b/RSI/__pycache__/urls.cpython-312.pyc differ
diff --git a/RSI/__pycache__/urls.cpython-39.pyc b/RSI/__pycache__/urls.cpython-39.pyc
new file mode 100644
index 0000000..aeebbb9
Binary files /dev/null and b/RSI/__pycache__/urls.cpython-39.pyc differ
diff --git a/RSI/__pycache__/wsgi.cpython-310.pyc b/RSI/__pycache__/wsgi.cpython-310.pyc
new file mode 100644
index 0000000..ca48637
Binary files /dev/null and b/RSI/__pycache__/wsgi.cpython-310.pyc differ
diff --git a/RSI/__pycache__/wsgi.cpython-311.pyc b/RSI/__pycache__/wsgi.cpython-311.pyc
new file mode 100644
index 0000000..3242fac
Binary files /dev/null and b/RSI/__pycache__/wsgi.cpython-311.pyc differ
diff --git a/RSI/__pycache__/wsgi.cpython-312.pyc b/RSI/__pycache__/wsgi.cpython-312.pyc
new file mode 100644
index 0000000..e72caa2
Binary files /dev/null and b/RSI/__pycache__/wsgi.cpython-312.pyc differ
diff --git a/RSI/__pycache__/wsgi.cpython-39.pyc b/RSI/__pycache__/wsgi.cpython-39.pyc
new file mode 100644
index 0000000..ec42f43
Binary files /dev/null and b/RSI/__pycache__/wsgi.cpython-39.pyc differ
diff --git a/RSI/asgi.py b/RSI/asgi.py
new file mode 100644
index 0000000..c8f3879
--- /dev/null
+++ b/RSI/asgi.py
@@ -0,0 +1,16 @@
+"""
+ASGI config for RSI project.
+
+It exposes the ASGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
+"""
+
+import os
+
+from django.core.asgi import get_asgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'RSI.settings')
+
+application = get_asgi_application()
diff --git a/RSI/settings.py b/RSI/settings.py
new file mode 100644
index 0000000..bce8cf1
--- /dev/null
+++ b/RSI/settings.py
@@ -0,0 +1,155 @@
+"""
+Django settings for RSI project.
+
+Generated by 'django-admin startproject' using Django 4.2.19.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/4.2/topics/settings/
+
+For the full list of settings and their values, see
+https://docs.djangoproject.com/en/4.2/ref/settings/
+"""
+
+import socket
+from pathlib import Path
+import os
+from dotenv import load_dotenv
+
+BASE_DIR = Path(__file__).resolve().parent.parent
+
+loc_ip = socket.gethostbyname(socket.gethostname())
+
+SECRET_KEY = os.environ.get("SECRET_KEY")
+
+if not os.getenv("RUNNING_IN_DOCKER"):
+ dotenv_path = BASE_DIR / ".env.local"
+ load_dotenv(dotenv_path)
+
+DEBUG = os.environ.get("DEBUG")
+
+ALLOWED_HOSTS = os.environ.get("ALLOWED_HOSTS").split(',')
+
+
+# Application definition
+
+INSTALLED_APPS = [
+ 'django.contrib.admin',
+ 'django.contrib.auth',
+ 'django.contrib.contenttypes',
+ 'django.contrib.sessions',
+ 'django.contrib.messages',
+ 'django.contrib.staticfiles',
+ 'corsheaders',
+ 'rest_framework',
+ 'app',
+ 'authentication',
+ 'django_filters',
+
+]
+
+MIDDLEWARE = [
+ "corsheaders.middleware.CorsMiddleware",
+ 'django.middleware.security.SecurityMiddleware',
+ 'django.contrib.sessions.middleware.SessionMiddleware',
+ 'django.middleware.common.CommonMiddleware',
+ 'django.middleware.csrf.CsrfViewMiddleware',
+ 'django.contrib.auth.middleware.AuthenticationMiddleware',
+ 'django.contrib.messages.middleware.MessageMiddleware',
+ 'django.middleware.clickjacking.XFrameOptionsMiddleware',
+]
+
+ROOT_URLCONF = 'RSI.urls'
+
+TEMPLATES = [
+ {
+ 'BACKEND': 'django.template.backends.django.DjangoTemplates',
+ 'DIRS': [],
+ 'APP_DIRS': True,
+ 'OPTIONS': {
+ 'context_processors': [
+ 'django.template.context_processors.debug',
+ 'django.template.context_processors.request',
+ 'django.contrib.auth.context_processors.auth',
+ 'django.contrib.messages.context_processors.messages',
+ ],
+ },
+ },
+]
+
+WSGI_APPLICATION = 'RSI.wsgi.application'
+
+
+# Database
+# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
+
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.postgresql_psycopg2',
+ 'NAME': os.environ.get("DB_NAME"),
+ 'USER': os.environ.get("DB_USER"),
+ 'PASSWORD': os.environ.get("DB_PASSWORD"),
+ 'HOST': os.environ.get("DB_HOST"),
+ 'PORT': os.environ.get("DB_PORT"),
+ }
+
+}
+
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
+DATA_UPLOAD_MAX_MEMORY_SIZE = 154857600
+
+# Password validation
+# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
+
+AUTH_PASSWORD_VALIDATORS = [
+ {
+ 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
+ },
+ {
+ 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
+ },
+]
+
+
+
+REST_FRAMEWORK = {
+ 'DEFAULT_PERMISSION_CLASSES': (
+ 'rest_framework.permissions.IsAuthenticated',
+ ),
+ 'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
+
+}
+
+
+# Internationalization
+# https://docs.djangoproject.com/en/4.2/topics/i18n/
+
+LANGUAGE_CODE = 'en-us'
+
+TIME_ZONE = 'UTC'
+
+USE_I18N = True
+
+USE_TZ = True
+
+
+CORS_ORIGIN_ALLOW_CREDENTIALS = True
+CORS_ORIGIN_ALLOW_ALL = os.environ.get("CORS_ORIGIN_ALLOW_ALL", "False").lower() == "true"
+CORS_ORIGIN_WHITELIST = os.environ.get("CORS_ORIGIN_WHITELIST").split(',')
+
+CORS_ALLOWED_ORIGINS = os.environ.get("CORS_ORIGIN_WHITELIST").split(',')
+
+# Static files (CSS, JavaScript, Images)
+# https://docs.djangoproject.com/en/4.2/howto/static-files/
+
+STATIC_URL = 'static/'
+
+# Default primary key field type
+# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
diff --git a/RSI/urls.py b/RSI/urls.py
new file mode 100644
index 0000000..44d04fe
--- /dev/null
+++ b/RSI/urls.py
@@ -0,0 +1,24 @@
+"""
+URL configuration for RSI project.
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+ https://docs.djangoproject.com/en/4.2/topics/http/urls/
+Examples:
+Function views
+ 1. Add an import: from my_app import views
+ 2. Add a URL to urlpatterns: path('', views.home, name='home')
+Class-based views
+ 1. Add an import: from other_app.views import Home
+ 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
+Including another URLconf
+ 1. Import the include() function: from django.urls import include, path
+ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.urls import path , include
+
+urlpatterns = [
+ path('admin/', admin.site.urls),
+ path('authentication/', include('authentication.urls')),
+ path('app/', include('app.urls'))
+]
\ No newline at end of file
diff --git a/RSI/wsgi.py b/RSI/wsgi.py
new file mode 100644
index 0000000..043abda
--- /dev/null
+++ b/RSI/wsgi.py
@@ -0,0 +1,16 @@
+"""
+WSGI config for RSI project.
+
+It exposes the WSGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
+"""
+
+import os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'RSI.settings')
+
+application = get_wsgi_application()
diff --git a/__pycache__/helpers.cpython-310.pyc b/__pycache__/helpers.cpython-310.pyc
new file mode 100644
index 0000000..5bbcbf5
Binary files /dev/null and b/__pycache__/helpers.cpython-310.pyc differ
diff --git a/__pycache__/helpers.cpython-311.pyc b/__pycache__/helpers.cpython-311.pyc
new file mode 100644
index 0000000..a940fd3
Binary files /dev/null and b/__pycache__/helpers.cpython-311.pyc differ
diff --git a/__pycache__/helpers.cpython-312.pyc b/__pycache__/helpers.cpython-312.pyc
new file mode 100644
index 0000000..4fbe9c7
Binary files /dev/null and b/__pycache__/helpers.cpython-312.pyc differ
diff --git a/__pycache__/helpers.cpython-39.pyc b/__pycache__/helpers.cpython-39.pyc
new file mode 100644
index 0000000..aa626db
Binary files /dev/null and b/__pycache__/helpers.cpython-39.pyc differ
diff --git a/__pycache__/manage.cpython-312.pyc b/__pycache__/manage.cpython-312.pyc
new file mode 100644
index 0000000..a84f7cc
Binary files /dev/null and b/__pycache__/manage.cpython-312.pyc differ
diff --git a/__pycache__/manage.cpython-39.pyc b/__pycache__/manage.cpython-39.pyc
new file mode 100644
index 0000000..2927cf7
Binary files /dev/null and b/__pycache__/manage.cpython-39.pyc differ
diff --git a/app/__init__.py b/app/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/app/__pycache__/__init__.cpython-310.pyc b/app/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..95478d4
Binary files /dev/null and b/app/__pycache__/__init__.cpython-310.pyc differ
diff --git a/app/__pycache__/__init__.cpython-311.pyc b/app/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000..d019d5b
Binary files /dev/null and b/app/__pycache__/__init__.cpython-311.pyc differ
diff --git a/app/__pycache__/__init__.cpython-312.pyc b/app/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..e585a3f
Binary files /dev/null and b/app/__pycache__/__init__.cpython-312.pyc differ
diff --git a/app/__pycache__/__init__.cpython-39.pyc b/app/__pycache__/__init__.cpython-39.pyc
new file mode 100644
index 0000000..4e3a271
Binary files /dev/null and b/app/__pycache__/__init__.cpython-39.pyc differ
diff --git a/app/__pycache__/admin.cpython-310.pyc b/app/__pycache__/admin.cpython-310.pyc
new file mode 100644
index 0000000..cd8ac1b
Binary files /dev/null and b/app/__pycache__/admin.cpython-310.pyc differ
diff --git a/app/__pycache__/admin.cpython-311.pyc b/app/__pycache__/admin.cpython-311.pyc
new file mode 100644
index 0000000..ecbe9c5
Binary files /dev/null and b/app/__pycache__/admin.cpython-311.pyc differ
diff --git a/app/__pycache__/admin.cpython-312.pyc b/app/__pycache__/admin.cpython-312.pyc
new file mode 100644
index 0000000..6ba60a1
Binary files /dev/null and b/app/__pycache__/admin.cpython-312.pyc differ
diff --git a/app/__pycache__/admin.cpython-39.pyc b/app/__pycache__/admin.cpython-39.pyc
new file mode 100644
index 0000000..a101e30
Binary files /dev/null and b/app/__pycache__/admin.cpython-39.pyc differ
diff --git a/app/__pycache__/apps.cpython-310.pyc b/app/__pycache__/apps.cpython-310.pyc
new file mode 100644
index 0000000..8e3c716
Binary files /dev/null and b/app/__pycache__/apps.cpython-310.pyc differ
diff --git a/app/__pycache__/apps.cpython-311.pyc b/app/__pycache__/apps.cpython-311.pyc
new file mode 100644
index 0000000..ac7ffe8
Binary files /dev/null and b/app/__pycache__/apps.cpython-311.pyc differ
diff --git a/app/__pycache__/apps.cpython-312.pyc b/app/__pycache__/apps.cpython-312.pyc
new file mode 100644
index 0000000..71dcad5
Binary files /dev/null and b/app/__pycache__/apps.cpython-312.pyc differ
diff --git a/app/__pycache__/apps.cpython-39.pyc b/app/__pycache__/apps.cpython-39.pyc
new file mode 100644
index 0000000..dae28ef
Binary files /dev/null and b/app/__pycache__/apps.cpython-39.pyc differ
diff --git a/app/__pycache__/cityandprovince.cpython-311.pyc b/app/__pycache__/cityandprovince.cpython-311.pyc
new file mode 100644
index 0000000..682c8f4
Binary files /dev/null and b/app/__pycache__/cityandprovince.cpython-311.pyc differ
diff --git a/app/__pycache__/cityandprovince.cpython-312.pyc b/app/__pycache__/cityandprovince.cpython-312.pyc
new file mode 100644
index 0000000..34938e6
Binary files /dev/null and b/app/__pycache__/cityandprovince.cpython-312.pyc differ
diff --git a/app/__pycache__/cityandprovince.cpython-39.pyc b/app/__pycache__/cityandprovince.cpython-39.pyc
new file mode 100644
index 0000000..2352e09
Binary files /dev/null and b/app/__pycache__/cityandprovince.cpython-39.pyc differ
diff --git a/app/__pycache__/excel_processing.cpython-311.pyc b/app/__pycache__/excel_processing.cpython-311.pyc
new file mode 100644
index 0000000..c92547f
Binary files /dev/null and b/app/__pycache__/excel_processing.cpython-311.pyc differ
diff --git a/app/__pycache__/excel_processing.cpython-312.pyc b/app/__pycache__/excel_processing.cpython-312.pyc
new file mode 100644
index 0000000..b8db971
Binary files /dev/null and b/app/__pycache__/excel_processing.cpython-312.pyc differ
diff --git a/app/__pycache__/excel_processing.cpython-39.pyc b/app/__pycache__/excel_processing.cpython-39.pyc
new file mode 100644
index 0000000..0e9ade1
Binary files /dev/null and b/app/__pycache__/excel_processing.cpython-39.pyc differ
diff --git a/app/__pycache__/filtersets.cpython-310.pyc b/app/__pycache__/filtersets.cpython-310.pyc
new file mode 100644
index 0000000..102a7ea
Binary files /dev/null and b/app/__pycache__/filtersets.cpython-310.pyc differ
diff --git a/app/__pycache__/filtersets.cpython-311.pyc b/app/__pycache__/filtersets.cpython-311.pyc
new file mode 100644
index 0000000..7018363
Binary files /dev/null and b/app/__pycache__/filtersets.cpython-311.pyc differ
diff --git a/app/__pycache__/filtersets.cpython-312.pyc b/app/__pycache__/filtersets.cpython-312.pyc
new file mode 100644
index 0000000..dfe4609
Binary files /dev/null and b/app/__pycache__/filtersets.cpython-312.pyc differ
diff --git a/app/__pycache__/filtersets.cpython-39.pyc b/app/__pycache__/filtersets.cpython-39.pyc
new file mode 100644
index 0000000..4acabfd
Binary files /dev/null and b/app/__pycache__/filtersets.cpython-39.pyc differ
diff --git a/app/__pycache__/helper.cpython-311.pyc b/app/__pycache__/helper.cpython-311.pyc
new file mode 100644
index 0000000..65008ca
Binary files /dev/null and b/app/__pycache__/helper.cpython-311.pyc differ
diff --git a/app/__pycache__/helper.cpython-312.pyc b/app/__pycache__/helper.cpython-312.pyc
new file mode 100644
index 0000000..c93382b
Binary files /dev/null and b/app/__pycache__/helper.cpython-312.pyc differ
diff --git a/app/__pycache__/helper.cpython-39.pyc b/app/__pycache__/helper.cpython-39.pyc
new file mode 100644
index 0000000..65d0266
Binary files /dev/null and b/app/__pycache__/helper.cpython-39.pyc differ
diff --git a/app/__pycache__/helper_excel.cpython-311.pyc b/app/__pycache__/helper_excel.cpython-311.pyc
new file mode 100644
index 0000000..726e4f8
Binary files /dev/null and b/app/__pycache__/helper_excel.cpython-311.pyc differ
diff --git a/app/__pycache__/helper_excel.cpython-312.pyc b/app/__pycache__/helper_excel.cpython-312.pyc
new file mode 100644
index 0000000..b83ed7b
Binary files /dev/null and b/app/__pycache__/helper_excel.cpython-312.pyc differ
diff --git a/app/__pycache__/helper_excel.cpython-39.pyc b/app/__pycache__/helper_excel.cpython-39.pyc
new file mode 100644
index 0000000..ff0e54b
Binary files /dev/null and b/app/__pycache__/helper_excel.cpython-39.pyc differ
diff --git a/app/__pycache__/models.cpython-310.pyc b/app/__pycache__/models.cpython-310.pyc
new file mode 100644
index 0000000..98ac200
Binary files /dev/null and b/app/__pycache__/models.cpython-310.pyc differ
diff --git a/app/__pycache__/models.cpython-311.pyc b/app/__pycache__/models.cpython-311.pyc
new file mode 100644
index 0000000..e2c39ec
Binary files /dev/null and b/app/__pycache__/models.cpython-311.pyc differ
diff --git a/app/__pycache__/models.cpython-312.pyc b/app/__pycache__/models.cpython-312.pyc
new file mode 100644
index 0000000..b996c78
Binary files /dev/null and b/app/__pycache__/models.cpython-312.pyc differ
diff --git a/app/__pycache__/models.cpython-39.pyc b/app/__pycache__/models.cpython-39.pyc
new file mode 100644
index 0000000..aa56fc7
Binary files /dev/null and b/app/__pycache__/models.cpython-39.pyc differ
diff --git a/app/__pycache__/scripts.cpython-310.pyc b/app/__pycache__/scripts.cpython-310.pyc
new file mode 100644
index 0000000..fcfab77
Binary files /dev/null and b/app/__pycache__/scripts.cpython-310.pyc differ
diff --git a/app/__pycache__/scripts.cpython-311.pyc b/app/__pycache__/scripts.cpython-311.pyc
new file mode 100644
index 0000000..1c3e029
Binary files /dev/null and b/app/__pycache__/scripts.cpython-311.pyc differ
diff --git a/app/__pycache__/scripts.cpython-312.pyc b/app/__pycache__/scripts.cpython-312.pyc
new file mode 100644
index 0000000..8ddf365
Binary files /dev/null and b/app/__pycache__/scripts.cpython-312.pyc differ
diff --git a/app/__pycache__/scripts.cpython-39.pyc b/app/__pycache__/scripts.cpython-39.pyc
new file mode 100644
index 0000000..b55f85d
Binary files /dev/null and b/app/__pycache__/scripts.cpython-39.pyc differ
diff --git a/app/__pycache__/serializers.cpython-310.pyc b/app/__pycache__/serializers.cpython-310.pyc
new file mode 100644
index 0000000..0ac6336
Binary files /dev/null and b/app/__pycache__/serializers.cpython-310.pyc differ
diff --git a/app/__pycache__/serializers.cpython-311.pyc b/app/__pycache__/serializers.cpython-311.pyc
new file mode 100644
index 0000000..3d2373e
Binary files /dev/null and b/app/__pycache__/serializers.cpython-311.pyc differ
diff --git a/app/__pycache__/serializers.cpython-312.pyc b/app/__pycache__/serializers.cpython-312.pyc
new file mode 100644
index 0000000..dcd9487
Binary files /dev/null and b/app/__pycache__/serializers.cpython-312.pyc differ
diff --git a/app/__pycache__/serializers.cpython-39.pyc b/app/__pycache__/serializers.cpython-39.pyc
new file mode 100644
index 0000000..342c9ac
Binary files /dev/null and b/app/__pycache__/serializers.cpython-39.pyc differ
diff --git a/app/__pycache__/urls.cpython-310.pyc b/app/__pycache__/urls.cpython-310.pyc
new file mode 100644
index 0000000..90b8f25
Binary files /dev/null and b/app/__pycache__/urls.cpython-310.pyc differ
diff --git a/app/__pycache__/urls.cpython-311.pyc b/app/__pycache__/urls.cpython-311.pyc
new file mode 100644
index 0000000..e041891
Binary files /dev/null and b/app/__pycache__/urls.cpython-311.pyc differ
diff --git a/app/__pycache__/urls.cpython-312.pyc b/app/__pycache__/urls.cpython-312.pyc
new file mode 100644
index 0000000..928ccf6
Binary files /dev/null and b/app/__pycache__/urls.cpython-312.pyc differ
diff --git a/app/__pycache__/urls.cpython-39.pyc b/app/__pycache__/urls.cpython-39.pyc
new file mode 100644
index 0000000..b8a2656
Binary files /dev/null and b/app/__pycache__/urls.cpython-39.pyc differ
diff --git a/app/__pycache__/views.cpython-310.pyc b/app/__pycache__/views.cpython-310.pyc
new file mode 100644
index 0000000..ee5fd88
Binary files /dev/null and b/app/__pycache__/views.cpython-310.pyc differ
diff --git a/app/__pycache__/views.cpython-311.pyc b/app/__pycache__/views.cpython-311.pyc
new file mode 100644
index 0000000..0681f87
Binary files /dev/null and b/app/__pycache__/views.cpython-311.pyc differ
diff --git a/app/__pycache__/views.cpython-312.pyc b/app/__pycache__/views.cpython-312.pyc
new file mode 100644
index 0000000..95968d5
Binary files /dev/null and b/app/__pycache__/views.cpython-312.pyc differ
diff --git a/app/__pycache__/views.cpython-39.pyc b/app/__pycache__/views.cpython-39.pyc
new file mode 100644
index 0000000..5189962
Binary files /dev/null and b/app/__pycache__/views.cpython-39.pyc differ
diff --git a/app/admin.py b/app/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/app/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/app/apps.py b/app/apps.py
new file mode 100644
index 0000000..ed327d2
--- /dev/null
+++ b/app/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class AppConfig(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'app'
diff --git a/app/cityandprovince.py b/app/cityandprovince.py
new file mode 100644
index 0000000..9cbf0cf
--- /dev/null
+++ b/app/cityandprovince.py
@@ -0,0 +1,663 @@
+search_city_list = ["آغاجری"
+ "سلسله", "معمولان", "کوهدشت", "آمل", "بابل", "بابلسر", "بهشهر",
+ "تنکابن", "جویبار", "چالوس", "رامسر", "ساری", "سواد کوه",
+ "سوادکوه شمالی", "سیمرغ", "عباس آباد", "فریدونکنار", "قائم شهر",
+ "گلوگاه", "محمود آباد", "میاندورود", "نکا", "نور", "نوشهر",
+ "کلاردشت", "آشتیان", "اراک", "تفرش", "خمین", "خنداب", "دلیجان",
+ "زرندیه", "ساوه", "شازند", "فراهان", "کمیجان", "محلات", "ابوموسی",
+ "بستک", "بشاگرد", "بندرعباس", "بندرلنگه", "پارسیان", "جاسک",
+ "حاجی آباد", "خمیر", "رودان", "سیریک", "قشم", "میناب", "اسدآباد",
+ "بهار", "تویسرکان", "درگزین", "رزن", "فامنین", "کبودرآهنگ", "ملایر",
+ "نهاوند", "همدان", "ابرکوه", "اردکان", "بافق", "بهاباد", "تفت",
+ "خاتم", "صدوق", "مروست", "زارچ", "مهریز", "میبد", "یزد",
+ "ثلاث باباجانی", "جوانرود", "دالاهو", "روانسر", "سر پل ذهاب",
+ "سنقر", "صحنه", "قصر شیرین", "گیلانغرب", "ماهیدشت", "هرسین",
+ "کرمانشاه", "کنگاور", "باشت", "بهمئی", "بویراحمد", "چرام", "دنا",
+ "گچساران", "لنده", "مارگون", "کهگیلویه", "آزاد شهر", "آق قلا",
+ "بندر گز", "ترکمن", "رامیان", "علی آباد", "گالیکش", "گرگان",
+ "گمیشان", "گنبد کاووس", "مراوه تپه", "مینودشت", "کردکوی",
+ "کلاله", "آستارا", "آستانه اشرفیه", "املش", "بندر انزلی",
+ "تالش", "خمام", "رشت", "رضوانشهر", "رودبار", "رودسر", "سیاهکل",
+ "شفت", "صومعه سرا", "طوالش", "فومن", "لاهیجان", "لنگرود", "ماسال",
+ "ازنا", "الیگودرز", "بروجرد", "پلدختر", "چگنی", "خرم آباد",
+ "دلفان", "دورود", "رومشکان", "زاهدان", "زرآباد", "زهک", "سراوان",
+ "سرباز", "سیب سوران", "فنوج", "قصرقند", "گلشن", "لاشار",
+ "مهرستان", "میرجاوه", "نیمروز", "نیک شهر", "هامون", "هیرمند",
+ "کنارک", "آباده", "ارسنجان", "استهبان", "اقلید", "اوز", "بختگان",
+ "بوانات", "بیضا", "پاسارگاد", "جهرم", "جویم", "خرامه", "خرم بید",
+ "خفر", "خنج", "داراب", "رستم", "زرقان", "زرین دشت", "سپیدان",
+ "سرچهان", "سروستان", "شیراز", "فراشبند", "فسا", "فیروز آباد",
+ "قیر و کارزین", "گراش", "لارستان", "لامرد", "مرودشت", "ممسنی",
+ "مهر", "نی ریز", "کازرون", "کوار", "کوه چنار", "آبیک", "آوج",
+ "البرز", "بوئین زهرا", "تاکستان", "قزوین", "جعفریه", "خلجستان",
+ "سلفچگان", "قم", "کهک", "بانه", "بیجار", "دهگلان", "دیواندره"
+ "سرو آباد", "سقز", "سنندج", "قروه", "مریوان",
+ "کامیاران",
+ "ارزوئیه", "انار", "بافت", "بردسیر", "بم", "رابر", "راور",
+ "رفسنجان", "ریگان", "زرند", "سیرجان", "شهربابک", "فهرج",
+ "گنبکی", "نرماشیر", "کرمان", "کوهبنان", "اسلام آباد غرب",
+ "پاوه", "رشتخوار", "زاوه", "زبرخان", "سبزوار", "سرخس",
+ "ششتمد", "صالح آباد", "طرقبه", "فریمان", "فیروزه", "قوچان",
+ "گلبهار", "گناباد", "مشهد", "مه ولات", "نیشابور", "کاشمر",
+ "کلات", "کوهسرخ", "اسفراین", "بام و صفی آباد", "بجنورد",
+ "جاجرم", "راز و جرگلان", "سملقان", "شیروان", "فاروج", "گرمه",
+ "مانه", "آبادان"
+ , "امیدیه", "اندیمشک", "اندیکا", "اهواز", "ایذه", "باغ ملک", "باوی", "بندر ماهشهر",
+ "بهبهان", "حمیدیه", "خرمشهر", "دزپارت", "دزفول", "دشت آزادگان", "رامشیر", "رامهرمز", "شادگان",
+ "شوش", "شوشتر", "صیدون", "گتوند", "لالی", "مسجد سلیمان", "هفتگل", "هندیجان", "هویزه", "کارون",
+ "کرخه", "ابهر", "ایجرود", "خدابنده", "خرمدره", "زنجان", "سلطانیه", "طارم", "ماهنشان", "آرادان",
+ "دامغان", "سرخه", "سمنان", "شاهرود", "گرمسار", "مهدی شهر", "میامی", "ایرانشهر", "بمپور", "تفتان",
+ "چاه بهار", "خاش", "دشتیاری", "دلگان", "راسک", "زابل", "آذرشهر", "اسکو", "اهر", "بستان آباد",
+ "بناب", "تبریز", "جلفا", "چاراویماق", "خدا آفرین", "سراب", "شبستر", "عجب شیر", "مراغه", "مرند",
+ "ملکان", "میانه", "هریس", "هشترود", "هوراند", "ورزقان", "کلیبر", "ارومیه", "اشنویه", "بوکان",
+ "پلدشت", "پیرانشهر", "تکاب", "چاربرج", "چالدران", "چایپاره", "خوی", "سر دشت", "سلماس", "شاهین دژ",
+ "شوط", "ماکو", "مهاباد", "میاندوآب", "نقده", "اردبیل", "اصلاندوز", "انگوت", "بیله سوار",
+ "پارس آباد", "خلخال", "سرعین", "گرمی", "مشگین شهر", "نمین", "نیر", "کوثر", "آران و بیدگل",
+ "اردستان", "اصفهان", "برخوار", "بوئین و میاندشت", "تیران و کرون", "جرقویه", "چادگان", "خمینی شهر",
+ "خوانسار", "خور و بیابانک", "دهاقان", "زرین شهر", "سمیرم", "شاهین شهر", "شهرضا", "فریدن",
+ "فریدونشهر", "فلاورجان", "گلپایگان", "مبارکه", "میمه", "نائین", "نجف آباد", "نطنز", "هرند", "ورزنه",
+ "کاشان", "کوهپایه", "اشتهارد", "چهار باغ", "ساوجبلاغ", "طالقان", "فردیس", "نظر آباد", "کرج",
+ "آبدانان", "ایلام", "ایوان", "بدره", "چرداول", "چوار", "درهشهر", "دهلران", "سیروان", "ملکشاهی",
+ "مهران", "هلیلان", "بوشهر", "تنگستان", "جم", "دشتستان", "دشتی", "دیر", "دیلم", "عسلویه", "گناوه",
+ "کنگان", "اسلامشهر", "بهارستان", "پاکدشت", "پردیس", "پیشوا", "تهران", "دماوند", "رباط کریم", "ری",
+ "شمیرانات", "شهریار", "فیروز کوه", "قدس", "قرچک", "ملارد", "ورامین", "اردل", "بروجن", "بن",
+ "خانمیرزا", "سامان", "شهر کرد", "فارسان", "فرخ شهر", "فلارد", "لردگان", "کوهرنگ", "کیار", "بشرویه",
+ "بیرجند", "خوسف", "درمیان", "زیرکوه", "سرایان", "سربیشه", "طبس", "فردوس", "قائنات", "نهبندان",
+ "باخرز", "بجستان", "بردسکن", "تایباد", "تربت جام", "تربت حیدریه", "جغتای", "جوین", "چناران",
+ "خلیل آباد", "خواف", "خوشاب", "داورزن", "درگز"]
+search_province_list = [
+ "همدان", "مرکزی", "بوشهر", "آذربایجان شرقی", "آذربایجان غربی", "اردبیل",
+ "اصفهان", "البرز", "ایلام", "تهران", "چهار محال و بختیاری", "خراسان جنوبی",
+ "خراسان رضوی", "خراسان شمالی", "خوزستان", "زنجان", "سمنان", "سیستان و بلوچستان",
+ "فارس", "قزوین", "قم", "کردستان", "کرمان", "کرمانشاه", "کهکیلویه و بویراحمد",
+ "گلستان", "گیلان", "لرستان", "مازندران", "هرمزگان", "یزد"
+]
+
+irancity = [
+ {"name": "سلسله", "id": "65885", "province_id": "65546"},
+ {"name": "معمولان", "id": "66903", "province_id": "65546"},
+ {"name": "کوهدشت", "id": "65886", "province_id": "65546"},
+ {"name": "آمل", "id": "65887", "province_id": "65547"},
+ {"name": "بابل", "id": "65888", "province_id": "65547"},
+ {"name": "بابلسر", "id": "65889", "province_id": "65547"},
+ {"name": "بهشهر", "id": "65890", "province_id": "65547"},
+ {"name": "تنکابن", "id": "65891", "province_id": "65547"},
+ {"name": "جویبار", "id": "65892", "province_id": "65547"},
+ {"name": "چالوس", "id": "65893", "province_id": "65547"},
+ {"name": "رامسر", "id": "65894", "province_id": "65547"},
+ {"name": "ساری", "id": "65895", "province_id": "65547"},
+ {"name": "سواد کوه", "id": "65896", "province_id": "65547"},
+ {"name": "سوادکوه شمالی", "id": "66870", "province_id": "65547"},
+ {"name": "سیمرغ", "id": "66871", "province_id": "65547"},
+ {"name": "عباس آباد", "id": "65897", "province_id": "65547"},
+ {"name": "فریدونکنار", "id": "65898", "province_id": "65547"},
+ {"name": "قائم شهر", "id": "65899", "province_id": "65547"},
+ {"name": "گلوگاه", "id": "65900", "province_id": "65547"},
+ {"name": "محمود آباد", "id": "65901", "province_id": "65547"},
+ {"name": "میاندورود", "id": "65902", "province_id": "65547"},
+ {"name": "نکا", "id": "65903", "province_id": "65547"},
+ {"name": "نور", "id": "65904", "province_id": "65547"},
+ {"name": "نوشهر", "id": "65905", "province_id": "65547"},
+ {"name": "کلاردشت", "id": "66869", "province_id": "65547"},
+ {"name": "آشتیان", "id": "65906", "province_id": "65548"},
+ {"name": "اراک", "id": "65907", "province_id": "65548"},
+ {"name": "تفرش", "id": "65908", "province_id": "65548"},
+ {"name": "خمین", "id": "65909", "province_id": "65548"},
+ {"name": "خنداب", "id": "65910", "province_id": "65548"},
+ {"name": "دلیجان", "id": "65911", "province_id": "65548"},
+ {"name": "زرندیه", "id": "65912", "province_id": "65548"},
+ {"name": "ساوه", "id": "65913", "province_id": "65548"},
+ {"name": "شازند", "id": "65914", "province_id": "65548"},
+ {"name": "فراهان", "id": "65915", "province_id": "65548"},
+ {"name": "کمیجان", "id": "65916", "province_id": "65548"},
+ {"name": "محلات", "id": "65917", "province_id": "65548"},
+ {"name": "ابوموسی", "id": "65918", "province_id": "65549"},
+ {"name": "بستک", "id": "65919", "province_id": "65549"},
+ {"name": "بشاگرد", "id": "65920", "province_id": "65549"},
+ {"name": "بندرعباس", "id": "65921", "province_id": "65549"},
+ {"name": "بندرلنگه", "id": "65922", "province_id": "65549"},
+ {"name": "پارسیان", "id": "65923", "province_id": "65549"},
+ {"name": "جاسک", "id": "65924", "province_id": "65549"},
+ {"name": "حاجی آباد", "id": "65925", "province_id": "65549"},
+ {"name": "خمیر", "id": "65926", "province_id": "65549"},
+ {"name": "رودان", "id": "65927", "province_id": "65549"},
+ {"name": "سیریک", "id": "65928", "province_id": "65549"},
+ {"name": "قشم", "id": "65929", "province_id": "65549"},
+ {"name": "میناب", "id": "65930", "province_id": "65549"},
+ {"name": "اسدآباد", "id": "65931", "province_id": "65550"},
+ {"name": "بهار", "id": "65932", "province_id": "65550"},
+ {"name": "تویسرکان", "id": "65933", "province_id": "65550"},
+ {"name": "درگزین", "id": "66884", "province_id": "65550"},
+ {"name": "رزن", "id": "65934", "province_id": "65550"},
+ {"name": "فامنین", "id": "66853", "province_id": "65550"},
+ {"name": "کبودرآهنگ", "id": "65936", "province_id": "65550"},
+ {"name": "ملایر", "id": "65937", "province_id": "65550"},
+ {"name": "نهاوند", "id": "65938", "province_id": "65550"},
+ {"name": "همدان", "id": "65939", "province_id": "65550"},
+ {"name": "ابرکوه", "id": "65940", "province_id": "65551"},
+ {"name": "اردکان", "id": "65941", "province_id": "65551"},
+ {"name": "بافق", "id": "65942", "province_id": "65551"},
+ {"name": "بهاباد", "id": "65943", "province_id": "65551"},
+ {"name": "تفت", "id": "65944", "province_id": "65551"},
+ {"name": "خاتم", "id": "65945", "province_id": "65551"},
+ {"name": "صدوق", "id": "65946", "province_id": "65551"},
+ {"name": "مروست", "id": "66812", "province_id": "65551"},
+ {"name": "زارچ", "id": "66899", "province_id": "65551"},
+ {"name": "مهریز", "id": "65948", "province_id": "65551"},
+ {"name": "میبد", "id": "65949", "province_id": "65551"},
+ {"name": "یزد", "id": "65950", "province_id": "65551"},
+ {"name": "ثلاث باباجانی", "id": "65828", "province_id": "65542"},
+ {"name": "جوانرود", "id": "65829", "province_id": "65542"},
+ {"name": "دالاهو", "id": "65830", "province_id": "65542"},
+ {"name": "روانسر", "id": "65831", "province_id": "65542"},
+ {"name": "سر پل ذهاب", "id": "65832", "province_id": "65542"},
+ {"name": "سنقر", "id": "65833", "province_id": "65542"},
+ {"name": "صحنه", "id": "65834", "province_id": "65542"},
+ {"name": "قصر شیرین", "id": "65835", "province_id": "65542"},
+ {"name": "گیلانغرب", "id": "65838", "province_id": "65542"},
+ {"name": "ماهیدشت", "id": "66823", "province_id": "65542"},
+ {"name": "هرسین", "id": "65839", "province_id": "65542"},
+ {"name": "کرمانشاه", "id": "65836", "province_id": "65542"},
+ {"name": "کنگاور", "id": "65837", "province_id": "65542"},
+ {"name": "باشت", "id": "65840", "province_id": "65543"},
+ {"name": "بهمئی", "id": "65841", "province_id": "65543"},
+ {"name": "بویراحمد", "id": "65842", "province_id": "65543"},
+ {"name": "چرام", "id": "65843", "province_id": "65543"},
+ {"name": "دنا", "id": "65844", "province_id": "65543"},
+ {"name": "گچساران", "id": "65846", "province_id": "65543"},
+ {"name": "لنده", "id": "66594", "province_id": "65543"},
+ {"name": "مارگون", "id": "66587", "province_id": "65543"},
+ {"name": "کهگیلویه", "id": "65845", "province_id": "65543"},
+ {"name": "آزاد شهر", "id": "65847", "province_id": "65544"},
+ {"name": "آق قلا", "id": "65848", "province_id": "65544"},
+ {"name": "بندر گز", "id": "65849", "province_id": "65544"},
+ {"name": "ترکمن", "id": "65850", "province_id": "65544"},
+ {"name": "رامیان", "id": "65851", "province_id": "65544"},
+ {"name": "علی آباد", "id": "65852", "province_id": "65544"},
+ {"name": "گالیکش", "id": "65855", "province_id": "65544"},
+ {"name": "گرگان", "id": "65856", "province_id": "65544"},
+ {"name": "گمیشان", "id": "65857", "province_id": "65544"},
+ {"name": "گنبد کاووس", "id": "65858", "province_id": "65544"},
+ {"name": "مراوه تپه", "id": "65859", "province_id": "65544"},
+ {"name": "مینودشت", "id": "65860", "province_id": "65544"},
+ {"name": "کردکوی", "id": "65853", "province_id": "65544"},
+ {"name": "کلاله", "id": "65854", "province_id": "65544"},
+ {"name": "آستارا", "id": "65861", "province_id": "65545"},
+ {"name": "آستانه اشرفیه", "id": "65862", "province_id": "65545"},
+ {"name": "املش", "id": "65863", "province_id": "65545"},
+ {"name": "بندر انزلی", "id": "65864", "province_id": "65545"},
+ {"name": "تالش", "id": "66851", "province_id": "65545"},
+ {"name": "خمام", "id": "66627", "province_id": "65545"},
+ {"name": "خمام", "id": "66904", "province_id": "65545"},
+ {"name": "رشت", "id": "65865", "province_id": "65545"},
+ {"name": "رضوانشهر", "id": "65866", "province_id": "65545"},
+ {"name": "رودبار", "id": "65867", "province_id": "65545"},
+ {"name": "رودسر", "id": "65868", "province_id": "65545"},
+ {"name": "سیاهکل", "id": "65869", "province_id": "65545"},
+ {"name": "شفت", "id": "65870", "province_id": "65545"},
+ {"name": "صومعه سرا", "id": "65871", "province_id": "65545"},
+ {"name": "طوالش", "id": "65872", "province_id": "65545"},
+ {"name": "فومن", "id": "65873", "province_id": "65545"},
+ {"name": "لاهیجان", "id": "65874", "province_id": "65545"},
+ {"name": "لنگرود", "id": "65875", "province_id": "65545"},
+ {"name": "ماسال", "id": "65876", "province_id": "65545"},
+ {"name": "ازنا", "id": "65877", "province_id": "65546"},
+ {"name": "الیگودرز", "id": "65878", "province_id": "65546"},
+ {"name": "بروجرد", "id": "65879", "province_id": "65546"},
+ {"name": "پلدختر", "id": "65880", "province_id": "65546"},
+ {"name": "چگنی", "id": "65883", "province_id": "65546"},
+ {"name": "خرم آباد", "id": "65881", "province_id": "65546"},
+ {"name": "دلفان", "id": "65882", "province_id": "65546"},
+ {"name": "دورود", "id": "65884", "province_id": "65546"},
+ {"name": "رومشکان", "id": "66865", "province_id": "65546"},
+ {"name": "زاهدان", "id": "65749", "province_id": "65536"},
+ {"name": "زرآباد", "id": "66384", "province_id": "65536"},
+ {"name": "زهک", "id": "65750", "province_id": "65536"},
+ {"name": "سراوان", "id": "65751", "province_id": "65536"},
+ {"name": "سرباز", "id": "65752", "province_id": "65536"},
+ {"name": "سیب سوران", "id": "65753", "province_id": "65536"},
+ {"name": "فنوج", "id": "66860", "province_id": "65536"},
+ {"name": "قصرقند", "id": "66861", "province_id": "65536"},
+ {"name": "گلشن", "id": "66896", "province_id": "65536"},
+ {"name": "لاشار", "id": "66390", "province_id": "65536"},
+ {"name": "مهرستان", "id": "65755", "province_id": "65536"},
+ {"name": "میرجاوه", "id": "66862", "province_id": "65536"},
+ {"name": "نیمروز", "id": "66859", "province_id": "65536"},
+ {"name": "نیک شهر", "id": "65756", "province_id": "65536"},
+ {"name": "هامون", "id": "66858", "province_id": "65536"},
+ {"name": "هیرمند", "id": "65757", "province_id": "65536"},
+ {"name": "کنارک", "id": "65754", "province_id": "65536"},
+ {"name": "آباده", "id": "65758", "province_id": "65537"},
+ {"name": "ارسنجان", "id": "65759", "province_id": "65537"},
+ {"name": "استهبان", "id": "65760", "province_id": "65537"},
+ {"name": "اقلید", "id": "65761", "province_id": "65537"},
+ {"name": "اوز", "id": "66442", "province_id": "65537"},
+ {"name": "بختگان", "id": "66880", "province_id": "65537"},
+ {"name": "بوانات", "id": "65762", "province_id": "65537"},
+ {"name": "بیضا", "id": "66416", "province_id": "65537"},
+ {"name": "پاسارگاد", "id": "65763", "province_id": "65537"},
+ {"name": "جهرم", "id": "65764", "province_id": "65537"},
+ {"name": "جویم", "id": "66445", "province_id": "65537"},
+ {"name": "خرامه", "id": "65765", "province_id": "65537"},
+ {"name": "خرم بید", "id": "65766", "province_id": "65537"},
+ {"name": "خفر", "id": "66848", "province_id": "65537"},
+ {"name": "خنج", "id": "65767", "province_id": "65537"},
+ {"name": "داراب", "id": "65768", "province_id": "65537"},
+ {"name": "رستم", "id": "65769", "province_id": "65537"},
+ {"name": "زرقان", "id": "66422", "province_id": "65537"},
+ {"name": "زرین دشت", "id": "65770", "province_id": "65537"},
+ {"name": "سپیدان", "id": "65771", "province_id": "65537"},
+ {"name": "سرچهان", "id": "66843", "province_id": "65537"},
+ {"name": "سروستان", "id": "65772", "province_id": "65537"},
+ {"name": "شیراز", "id": "65773", "province_id": "65537"},
+ {"name": "فراشبند", "id": "65774", "province_id": "65537"},
+ {"name": "فسا", "id": "65775", "province_id": "65537"},
+ {"name": "فیروز آباد", "id": "65776", "province_id": "65537"},
+ {"name": "قیر و کارزین", "id": "66842", "province_id": "65537"},
+ {"name": "گراش", "id": "65780", "province_id": "65537"},
+ {"name": "لارستان", "id": "65781", "province_id": "65537"},
+ {"name": "لامرد", "id": "65782", "province_id": "65537"},
+ {"name": "مرودشت", "id": "65783", "province_id": "65537"},
+ {"name": "ممسنی", "id": "65784", "province_id": "65537"},
+ {"name": "مهر", "id": "65785", "province_id": "65537"},
+ {"name": "نی ریز", "id": "65786", "province_id": "65537"},
+ {"name": "کازرون", "id": "65778", "province_id": "65537"},
+ {"name": "کوار", "id": "65779", "province_id": "65537"},
+ {"name": "کوه چنار", "id": "66881", "province_id": "65537"},
+ {"name": "آبیک", "id": "65787", "province_id": "65538"},
+ {"name": "آوج", "id": "66849", "province_id": "65538"},
+ {"name": "البرز", "id": "65788", "province_id": "65538"},
+ {"name": "بوئین زهرا", "id": "65789", "province_id": "65538"},
+ {"name": "تاکستان", "id": "65790", "province_id": "65538"},
+ {"name": "قزوین", "id": "65791", "province_id": "65538"},
+ {"name": "جعفریه", "id": "66483", "province_id": "65539"},
+ {"name": "خلجستان", "id": "66484", "province_id": "65539"},
+ {"name": "سلفچگان", "id": "66485", "province_id": "65539"},
+ {"name": "قم", "id": "65792", "province_id": "65539"},
+ {"name": "کهک", "id": "66487", "province_id": "65539"},
+ {"name": "بانه", "id": "65793", "province_id": "65540"},
+ {"name": "بیجار", "id": "65794", "province_id": "65540"},
+ {"name": "دهگلان", "id": "65795", "province_id": "65540"},
+ {"name": "دیواندره", "id": "65796", "province_id": "65540"},
+ {"name": "سرو آباد", "id": "65797", "province_id": "65540"},
+ {"name": "سقز", "id": "65798", "province_id": "65540"},
+ {"name": "سنندج", "id": "65799", "province_id": "65540"},
+ {"name": "قروه", "id": "65800", "province_id": "65540"},
+ {"name": "مریوان", "id": "65802", "province_id": "65540"},
+ {"name": "کامیاران", "id": "65801", "province_id": "65540"},
+ {"name": "ارزوئیه", "id": "65803", "province_id": "65541"},
+ {"name": "انار", "id": "65804", "province_id": "65541"},
+ {"name": "بافت", "id": "65805", "province_id": "65541"},
+ {"name": "بردسیر", "id": "65806", "province_id": "65541"},
+ {"name": "بم", "id": "65807", "province_id": "65541"},
+ {"name": "رابر", "id": "65809", "province_id": "65541"},
+ {"name": "راور", "id": "65810", "province_id": "65541"},
+ {"name": "رفسنجان", "id": "65811", "province_id": "65541"},
+ {"name": "ریگان", "id": "65813", "province_id": "65541"},
+ {"name": "زرند", "id": "65814", "province_id": "65541"},
+ {"name": "سیرجان", "id": "65815", "province_id": "65541"},
+ {"name": "شهربابک", "id": "66825", "province_id": "65541"},
+ {"name": "فهرج", "id": "65819", "province_id": "65541"},
+ {"name": "گنبکی", "id": "66900", "province_id": "65541"},
+ {"name": "نرماشیر", "id": "65825", "province_id": "65541"},
+ {"name": "کرمان", "id": "65821", "province_id": "65541"},
+ {"name": "کوهبنان", "id": "65823", "province_id": "65541"},
+ {"name": "اسلام آباد غرب", "id": "65826", "province_id": "65542"},
+ {"name": "پاوه", "id": "65827", "province_id": "65542"},
+ {"name": "رشتخوار", "id": "65687", "province_id": "65531"},
+ {"name": "زاوه", "id": "65688", "province_id": "65531"},
+ {"name": "زبرخان", "id": "66265", "province_id": "65531"},
+ {"name": "سبزوار", "id": "65689", "province_id": "65531"},
+ {"name": "سرخس", "id": "65690", "province_id": "65531"},
+ {"name": "ششتمد", "id": "66246", "province_id": "65531"},
+ {"name": "صالح آباد", "id": "66838", "province_id": "65531"},
+ {"name": "طرقبه", "id": "66837", "province_id": "65531"},
+ {"name": "فریمان", "id": "65691", "province_id": "65531"},
+ {"name": "فیروزه", "id": "66836", "province_id": "65531"},
+ {"name": "قوچان", "id": "65692", "province_id": "65531"},
+ {"name": "گلبهار", "id": "66228", "province_id": "65531"},
+ {"name": "گناباد", "id": "65695", "province_id": "65531"},
+ {"name": "مشهد", "id": "65696", "province_id": "65531"},
+ {"name": "مه ولات", "id": "65697", "province_id": "65531"},
+ {"name": "نیشابور", "id": "65698", "province_id": "65531"},
+ {"name": "کاشمر", "id": "65693", "province_id": "65531"},
+ {"name": "کلات", "id": "65694", "province_id": "65531"},
+ {"name": "کوهسرخ", "id": "66254", "province_id": "65531"},
+ {"name": "اسفراین", "id": "65699", "province_id": "65532"},
+ {"name": "بام و صفی آباد", "id": "66897", "province_id": "65532"},
+ {"name": "بجنورد", "id": "65700", "province_id": "65532"},
+ {"name": "جاجرم", "id": "65701", "province_id": "65532"},
+ {"name": "راز و جرگلان", "id": "66271", "province_id": "65532"},
+ {"name": "سملقان", "id": "65705", "province_id": "65532"},
+ {"name": "شیروان", "id": "65702", "province_id": "65532"},
+ {"name": "فاروج", "id": "65703", "province_id": "65532"},
+ {"name": "گرمه", "id": "65704", "province_id": "65532"},
+ {"name": "مانه", "id": "66898", "province_id": "65532"},
+ {"name": "آبادان", "id": "65706", "province_id": "65533"},
+ {"name": "آغاجری", "id": "66878", "province_id": "65533"},
+ {"name": "امیدیه", "id": "65707", "province_id": "65533"},
+ {"name": "اندیمشک", "id": "65708", "province_id": "65533"},
+ {"name": "اندیکا", "id": "65709", "province_id": "65533"},
+ {"name": "اهواز", "id": "65710", "province_id": "65533"},
+ {"name": "ایذه", "id": "65711", "province_id": "65533"},
+ {"name": "باغ ملک", "id": "65712", "province_id": "65533"},
+ {"name": "باوی", "id": "65713", "province_id": "65533"},
+ {"name": "بندر ماهشهر", "id": "65714", "province_id": "65533"},
+ {"name": "بهبهان", "id": "65715", "province_id": "65533"},
+ {"name": "حمیدیه", "id": "66873", "province_id": "65533"},
+ {"name": "خرمشهر", "id": "65716", "province_id": "65533"},
+ {"name": "دزپارت", "id": "66893", "province_id": "65533"},
+ {"name": "دزفول", "id": "65717", "province_id": "65533"},
+ {"name": "دشت آزادگان", "id": "65718", "province_id": "65533"},
+ {"name": "رامشیر", "id": "65719", "province_id": "65533"},
+ {"name": "رامهرمز", "id": "65720", "province_id": "65533"},
+ {"name": "شادگان", "id": "65721", "province_id": "65533"},
+ {"name": "شوش", "id": "65722", "province_id": "65533"},
+ {"name": "شوشتر", "id": "65723", "province_id": "65533"},
+ {"name": "صیدون", "id": "66902", "province_id": "65533"},
+ {"name": "گتوند", "id": "65724", "province_id": "65533"},
+ {"name": "لالی", "id": "65725", "province_id": "65533"},
+ {"name": "مسجد سلیمان", "id": "65726", "province_id": "65533"},
+ {"name": "هفتگل", "id": "65727", "province_id": "65533"},
+ {"name": "هندیجان", "id": "65728", "province_id": "65533"},
+ {"name": "هویزه", "id": "65729", "province_id": "65533"},
+ {"name": "کارون", "id": "66874", "province_id": "65533"},
+ {"name": "کرخه", "id": "66886", "province_id": "65533"},
+ {"name": "ابهر", "id": "65730", "province_id": "65534"},
+ {"name": "ایجرود", "id": "65731", "province_id": "65534"},
+ {"name": "خدابنده", "id": "65732", "province_id": "65534"},
+ {"name": "خرمدره", "id": "65733", "province_id": "65534"},
+ {"name": "زنجان", "id": "65734", "province_id": "65534"},
+ {"name": "سلطانیه ", "id": "66868", "province_id": "65534"},
+ {"name": "طارم", "id": "65735", "province_id": "65534"},
+ {"name": "ماهنشان", "id": "65736", "province_id": "65534"},
+ {"name": "آرادان", "id": "65737", "province_id": "65535"},
+ {"name": "دامغان", "id": "65738", "province_id": "65535"},
+ {"name": "سرخه", "id": "66840", "province_id": "65535"},
+ {"name": "سمنان", "id": "65739", "province_id": "65535"},
+ {"name": "شاهرود", "id": "65740", "province_id": "65535"},
+ {"name": "گرمسار", "id": "65741", "province_id": "65535"},
+ {"name": "مهدی شهر", "id": "65742", "province_id": "65535"},
+ {"name": "میامی", "id": "65743", "province_id": "65535"},
+ {"name": "ایرانشهر", "id": "65744", "province_id": "65536"},
+ {"name": "بمپور", "id": "66362", "province_id": "65536"},
+ {"name": "تفتان", "id": "66882", "province_id": "65536"},
+ {"name": "چاه بهار", "id": "65745", "province_id": "65536"},
+ {"name": "خاش", "id": "65746", "province_id": "65536"},
+ {"name": "دشتیاری", "id": "66364", "province_id": "65536"},
+ {"name": "دلگان", "id": "65747", "province_id": "65536"},
+ {"name": "راسک", "id": "66883", "province_id": "65536"},
+ {"name": "زابل", "id": "65748", "province_id": "65536"},
+ {"name": "آذرشهر", "id": "65552", "province_id": "65521"},
+ {"name": "اسکو", "id": "65553", "province_id": "65521"},
+ {"name": "اهر", "id": "65554", "province_id": "65521"},
+ {"name": "بستان آباد", "id": "65555", "province_id": "65521"},
+ {"name": "بناب", "id": "65556", "province_id": "65521"},
+ {"name": "تبریز", "id": "65557", "province_id": "65521"},
+ {"name": "جلفا", "id": "65558", "province_id": "65521"},
+ {"name": "چاراویماق", "id": "65559", "province_id": "65521"},
+ {"name": "خدا آفرین", "id": "65560", "province_id": "65521"},
+ {"name": "سراب", "id": "65561", "province_id": "65521"},
+ {"name": "شبستر", "id": "65562", "province_id": "65521"},
+ {"name": "عجب شیر", "id": "65563", "province_id": "65521"},
+ {"name": "مراغه", "id": "65565", "province_id": "65521"},
+ {"name": "مرند", "id": "65566", "province_id": "65521"},
+ {"name": "ملکان", "id": "65567", "province_id": "65521"},
+ {"name": "میانه", "id": "65568", "province_id": "65521"},
+ {"name": "هریس", "id": "65569", "province_id": "65521"},
+ {"name": "هشترود", "id": "65570", "province_id": "65521"},
+ {"name": "هوراند", "id": "65957", "province_id": "65521"},
+ {"name": "ورزقان", "id": "65571", "province_id": "65521"},
+ {"name": "کلیبر", "id": "65564", "province_id": "65521"},
+ {"name": "ارومیه", "id": "65572", "province_id": "65522"},
+ {"name": "اشنویه", "id": "65573", "province_id": "65522"},
+ {"name": "بوکان", "id": "65574", "province_id": "65522"},
+ {"name": "پلدشت", "id": "65575", "province_id": "65522"},
+ {"name": "پیرانشهر", "id": "65576", "province_id": "65522"},
+ {"name": "تکاب", "id": "65577", "province_id": "65522"},
+ {"name": "چاربرج", "id": "66905", "province_id": "65522"},
+ {"name": "چالدران", "id": "65578", "province_id": "65522"},
+ {"name": "چایپاره", "id": "65579", "province_id": "65522"},
+ {"name": "خوی", "id": "65580", "province_id": "65522"},
+ {"name": "سر دشت", "id": "65581", "province_id": "65522"},
+ {"name": "سلماس", "id": "65582", "province_id": "65522"},
+ {"name": "شاهین دژ", "id": "65583", "province_id": "65522"},
+ {"name": "شوط", "id": "65584", "province_id": "65522"},
+ {"name": "ماکو", "id": "65585", "province_id": "65522"},
+ {"name": "مهاباد", "id": "65586", "province_id": "65522"},
+ {"name": "میاندوآب", "id": "65587", "province_id": "65522"},
+ {"name": "نقده", "id": "65588", "province_id": "65522"},
+ {"name": "اردبیل", "id": "65589", "province_id": "65523"},
+ {"name": "اصلاندوز", "id": "66032", "province_id": "65523"},
+ {"name": "انگوت", "id": "66040", "province_id": "65523"},
+ {"name": "بیله سوار", "id": "65590", "province_id": "65523"},
+ {"name": "پارس آباد", "id": "65591", "province_id": "65523"},
+ {"name": "خلخال", "id": "65592", "province_id": "65523"},
+ {"name": "سرعین", "id": "65593", "province_id": "65523"},
+ {"name": "گرمی", "id": "65595", "province_id": "65523"},
+ {"name": "مشگین شهر", "id": "65596", "province_id": "65523"},
+ {"name": "نمین", "id": "65597", "province_id": "65523"},
+ {"name": "نیر", "id": "65598", "province_id": "65523"},
+ {"name": "کوثر", "id": "65594", "province_id": "65523"},
+ {"name": "آران و بیدگل", "id": "65599", "province_id": "65524"},
+ {"name": "اردستان", "id": "65600", "province_id": "65524"},
+ {"name": "اصفهان", "id": "65601", "province_id": "65524"},
+ {"name": "برخوار", "id": "65602", "province_id": "65524"},
+ {"name": "بوئین و میاندشت", "id": "66075", "province_id": "65524"},
+ {"name": "تیران و کرون", "id": "65603", "province_id": "65524"},
+ {"name": "جرقویه", "id": "66887", "province_id": "65524"},
+ {"name": "چادگان", "id": "65604", "province_id": "65524"},
+ {"name": "خمینی شهر", "id": "65605", "province_id": "65524"},
+ {"name": "خوانسار", "id": "65606", "province_id": "65524"},
+ {"name": "خور و بیابانک", "id": "65607", "province_id": "65524"},
+ {"name": "دهاقان", "id": "65608", "province_id": "65524"},
+ {"name": "زرین شهر", "id": "66828", "province_id": "65524"},
+ {"name": "سمیرم", "id": "65609", "province_id": "65524"},
+ {"name": "شاهین شهر", "id": "65610", "province_id": "65524"},
+ {"name": "شهرضا", "id": "65611", "province_id": "65524"},
+ {"name": "فریدن", "id": "65612", "province_id": "65524"},
+ {"name": "فریدونشهر", "id": "65613", "province_id": "65524"},
+ {"name": "فلاورجان", "id": "65614", "province_id": "65524"},
+ {"name": "گلپایگان", "id": "65616", "province_id": "65524"},
+ {"name": "مبارکه", "id": "65618", "province_id": "65524"},
+ {"name": "میمه", "id": "66906", "province_id": "65524"},
+ {"name": "نائین", "id": "65619", "province_id": "65524"},
+ {"name": "نجف آباد", "id": "65620", "province_id": "65524"},
+ {"name": "نطنز", "id": "65621", "province_id": "65524"},
+ {"name": "هرند", "id": "66888", "province_id": "65524"},
+ {"name": "ورزنه", "id": "66889", "province_id": "65524"},
+ {"name": "کاشان", "id": "65615", "province_id": "65524"},
+ {"name": "کوهپایه", "id": "66059", "province_id": "65524"},
+ {"name": "اشتهارد", "id": "66855", "province_id": "65525"},
+ {"name": "چهار باغ", "id": "66096", "province_id": "65525"},
+ {"name": "ساوجبلاغ", "id": "65622", "province_id": "65525"},
+ {"name": "طالقان", "id": "65623", "province_id": "65525"},
+ {"name": "فردیس", "id": "66877", "province_id": "65525"},
+ {"name": "نظر آباد", "id": "65625", "province_id": "65525"},
+ {"name": "کرج", "id": "65624", "province_id": "65525"},
+ {"name": "آبدانان", "id": "65626", "province_id": "65526"},
+ {"name": "ایلام", "id": "65627", "province_id": "65526"},
+ {"name": "ایوان", "id": "65628", "province_id": "65526"},
+ {"name": "بدره", "id": "66830", "province_id": "65526"},
+ {"name": "چرداول", "id": "66829", "province_id": "65526"},
+ {"name": "چوار", "id": "66107", "province_id": "65526"},
+ {"name": "درهشهر", "id": "66831", "province_id": "65526"},
+ {"name": "دهلران", "id": "65630", "province_id": "65526"},
+ {"name": "سیروان", "id": "66832", "province_id": "65526"},
+ {"name": "ملکشاهی", "id": "65632", "province_id": "65526"},
+ {"name": "مهران", "id": "65633", "province_id": "65526"},
+ {"name": "هلیلان", "id": "66118", "province_id": "65526"},
+ {"name": "بوشهر", "id": "65634", "province_id": "65527"},
+ {"name": "تنگستان", "id": "65635", "province_id": "65527"},
+ {"name": "جم", "id": "65636", "province_id": "65527"},
+ {"name": "دشتستان", "id": "65637", "province_id": "65527"},
+ {"name": "دشتی", "id": "65638", "province_id": "65527"},
+ {"name": "دیر", "id": "65639", "province_id": "65527"},
+ {"name": "دیلم", "id": "65640", "province_id": "65527"},
+ {"name": "عسلویه", "id": "66142", "province_id": "65527"},
+ {"name": "گناوه", "id": "65642", "province_id": "65527"},
+ {"name": "کنگان", "id": "65641", "province_id": "65527"},
+ {"name": "اسلامشهر", "id": "65643", "province_id": "65528"},
+ {"name": "بهارستان", "id": "65644", "province_id": "65528"},
+ {"name": "پاکدشت", "id": "65645", "province_id": "65528"},
+ {"name": "پردیس", "id": "66867", "province_id": "65528"},
+ {"name": "پیشوا", "id": "65646", "province_id": "65528"},
+ {"name": "تهران", "id": "65647", "province_id": "65528"},
+ {"name": "دماوند", "id": "65648", "province_id": "65528"},
+ {"name": "رباط کریم", "id": "65649", "province_id": "65528"},
+ {"name": "ری", "id": "65650", "province_id": "65528"},
+ {"name": "شمیرانات", "id": "65651", "province_id": "65528"},
+ {"name": "شهریار", "id": "65652", "province_id": "65528"},
+ {"name": "فیروز کوه", "id": "65653", "province_id": "65528"},
+ {"name": "قدس", "id": "65654", "province_id": "65528"},
+ {"name": "قرچک", "id": "66866", "province_id": "65528"},
+ {"name": "ملارد", "id": "65655", "province_id": "65528"},
+ {"name": "ورامین", "id": "65656", "province_id": "65528"},
+ {"name": "اردل", "id": "65657", "province_id": "65529"},
+ {"name": "بروجن", "id": "65658", "province_id": "65529"},
+ {"name": "بن", "id": "66875", "province_id": "65529"},
+ {"name": "خانمیرزا", "id": "66183", "province_id": "65529"},
+ {"name": "سامان", "id": "66876", "province_id": "65529"},
+ {"name": "شهر کرد", "id": "65659", "province_id": "65529"},
+ {"name": "فارسان", "id": "65660", "province_id": "65529"},
+ {"name": "فرخ شهر", "id": "66907", "province_id": "65529"},
+ {"name": "فلارد", "id": "66184", "province_id": "65529"},
+ {"name": "لردگان", "id": "65663", "province_id": "65529"},
+ {"name": "کوهرنگ", "id": "65661", "province_id": "65529"},
+ {"name": "کیار", "id": "65662", "province_id": "65529"},
+ {"name": "بشرویه", "id": "65664", "province_id": "65530"},
+ {"name": "بیرجند", "id": "65665", "province_id": "65530"},
+ {"name": "خوسف", "id": "66834", "province_id": "65530"},
+ {"name": "درمیان", "id": "65666", "province_id": "65530"},
+ {"name": "زیرکوه", "id": "66863", "province_id": "65530"},
+ {"name": "سرایان", "id": "65667", "province_id": "65530"},
+ {"name": "سربیشه", "id": "65668", "province_id": "65530"},
+ {"name": "طبس", "id": "66864", "province_id": "65530"},
+ {"name": "فردوس", "id": "65669", "province_id": "65530"},
+ {"name": "قائنات", "id": "65670", "province_id": "65530"},
+ {"name": "نهبندان", "id": "65671", "province_id": "65530"},
+ {"name": "باخرز", "id": "65672", "province_id": "65531"},
+ {"name": "بجستان", "id": "65673", "province_id": "65531"},
+ {"name": "بردسکن", "id": "65674", "province_id": "65531"},
+ {"name": "تایباد", "id": "65676", "province_id": "65531"},
+ {"name": "تربت جام", "id": "65678", "province_id": "65531"},
+ {"name": "تربت حیدریه", "id": "65679", "province_id": "65531"},
+ {"name": "جغتای", "id": "65680", "province_id": "65531"},
+ {"name": "جوین", "id": "65681", "province_id": "65531"},
+ {"name": "چناران", "id": "65682", "province_id": "65531"},
+ {"name": "خلیل آباد", "id": "65683", "province_id": "65531"},
+ {"name": "خواف", "id": "65684", "province_id": "65531"},
+ {"name": "خوشاب", "id": "65685", "province_id": "65531"},
+ {"name": "داورزن", "id": "66835", "province_id": "65531"},
+ {"name": "درگز", "id": "65686", "province_id": "65531"}
+]
+iranprovince = [
+ {"name": "همدان", "id": "65550", "address": "https://habackend.rasadyaar.ir/"},
+ {"name": "مرکزی", "id": "65548", "address": "https://mabackend.rasadyaar.ir/"},
+ {"name": "بوشهر", "id": "65527", "address": "https://bubackend.rasadyaar.ir/"},
+ {"name": "آذربایجان شرقی", "id": "65521"},
+ {"name": "آذربایجان غربی", "id": "65522"},
+ {"name": "اردبیل", "id": "65523"},
+ {"name": "اصفهان", "id": "65524"},
+ {"name": "البرز", "id": "65525"},
+ {"name": "ایلام", "id": "65526"},
+ {"name": "تهران", "id": "65528"},
+ {"name": "چهار محال و بختیاری", "id": "65529"},
+ {"name": "خراسان جنوبی", "id": "65530"},
+ {"name": "خراسان رضوی", "id": "65531"},
+ {"name": "خراسان شمالی", "id": "65532"},
+ {"name": "خوزستان", "id": "65533"},
+ {"name": "زنجان", "id": "65534"},
+ {"name": "سمنان", "id": "65535"},
+ {"name": "سیستان و بلوچستان", "id": "65536"},
+ {"name": "فارس", "id": "65537"},
+ {"name": "قزوین", "id": "65538"},
+ {"name": "قم", "id": "65539"},
+ {"name": "کردستان", "id": "65540"},
+ {"name": "کرمان", "id": "65541"},
+ {"name": "کرمانشاه", "id": "65542"},
+ {"name": "کهکیلویه و بویراحمد", "id": "65543"},
+ {"name": "گلستان", "id": "65544"},
+ {"name": "گیلان", "id": "65545"},
+ {"name": "لرستان", "id": "65546"},
+ {"name": "مازندران", "id": "65547"},
+ {"name": "هرمزگان", "id": "65549"},
+ {"name": "یزد", "id": "65551"},
+]
+
+from fuzzywuzzy import fuzz
+import re
+
+
+def normalize_text(text):
+ if not text:
+ return ""
+ text = re.sub(r'[^\w\s]', '', text)
+ text = re.sub(r'\s+', ' ', text).strip()
+ return text
+
+
+def find_best_match_for_province(input_text):
+ input_norm = normalize_text(input_text)
+ best_match = None
+ highest_score = 0
+
+ for item in iranprovince:
+ score = fuzz.ratio(input_norm, normalize_text(item['name']))
+ if score > highest_score and score > 70:
+ highest_score = score
+ best_match = item['name']
+
+ return best_match if highest_score > 0 else None
+
+
+def get_province_id(province_name):
+ for i in iranprovince:
+ if i['name'] == province_name:
+ return i['id']
+ return 0
+
+
+def find_best_match_for_city(input_text, province_name):
+ input_norm = normalize_text(input_text)
+ best_match = None
+ highest_score = 0
+ province_id = get_province_id(province_name)
+
+ city = [city for city in irancity if int(city['province_id']) == int(province_id)]
+
+ for item in city:
+ score = fuzz.ratio(input_norm, normalize_text(item['name']))
+ if score > highest_score and score > 70:
+ highest_score = score
+ best_match = item['name']
+
+ return best_match if highest_score > 0 else None
+
+
+def correct_province(input_text):
+ province_match = find_best_match_for_province(input_text)
+ if province_match:
+ return province_match
+
+ return input_text
+
+
+def correct_city(input_text, province_name):
+ province_match = find_best_match_for_city(input_text, province_name)
+ if province_match:
+ return province_match
+
+ return input_text
+
+# امل درمیان خرمبید دشتستان قائنات گناوه اسلام آباد
+# corrupted_text = "کهگیلویه و بویراحمد"
+# corrected = correct_province(corrupted_text)
+# print(f"ورودی: {corrupted_text} -> خروجی: {corrected}")
+
+
+# #تست شهر
+# province_name = "کهگیلویه و بویراحمد"
+# corrupted_text = "دنا"
+# corrected = correct_city(corrupted_text,province_name)
+# print(f"ورودی: {corrupted_text} -> خروجی: {corrected}")
diff --git a/app/excel_processing.py b/app/excel_processing.py
new file mode 100644
index 0000000..7502e76
--- /dev/null
+++ b/app/excel_processing.py
@@ -0,0 +1,2574 @@
+# import datetime
+import datetime
+from io import BytesIO
+
+import openpyxl
+import requests
+from django.db.models import Sum, Count, Q, F
+from django.http import HttpResponse
+from django.views.decorators.csrf import csrf_exempt
+from openpyxl import Workbook
+from openpyxl.styles import Alignment
+from rest_framework.decorators import api_view, permission_classes
+from rest_framework.permissions import AllowAny
+
+from app.filtersets import TransportingDetailFilterSet, KillHouseFilterSet, HatchingsFilterSet, GuildsFilterSet, \
+ TransportCarcassDetailFilterSet, AllProductsTransportFilterSet
+from app.helper_excel import create_header, create_header_freez, shamsi_date, excel_description, create_value
+from app.models import TransportingDetail, KillHouse, Hatching, TransportCarcassDetail, Guilds, AllProductsTransport
+from app.serializers import TransportingDetailSerializer, HatchingDetailSerializer, \
+ StewardForTransportCarcassSerializer, KillHouseForTransportCarcassSerializer, TransportCarcassDetailSerializer, \
+ GuildsForTransportCarcassSerializer, AllProductsTransportSerializer
+from helpers import build_query
+from app.helper import get_hatching_permit_code
+
+def transporting_detail_excel(request):
+ filterset_class = TransportingDetailFilterSet
+ filters = {}
+ PartIdCode = request.GET['PartIdCode']
+ RequestCode = request.GET.get('RequestCode')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ city = request.GET.get('city')
+ province = request.GET.get('province')
+ if province == 'undefined':
+ province = None
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ filters['Date__date__gte'] = date1
+ filters['Date__date__lte'] = date2
+ if city:
+ filters['City__icontains'] = city
+
+ if province:
+ filters['Province__icontains'] = province
+
+ if RequestCode:
+ filters['hatching__RequestCode'] = RequestCode
+
+ search = request.GET.get('search')
+ value = request.GET.get('value')
+ transports = TransportingDetail.objects.filter(**filters, DesPartIdCode=PartIdCode).order_by("-Date")
+
+ if search:
+ if search != 'undefined' and search.strip():
+ transports = transports.filter(
+ build_query(filterset_class.Meta.fields, value)
+ )
+
+ serializer = TransportingDetailSerializer(transports, many=True).data
+
+ excel_options = [
+ 'ردیف',
+ 'کد رهگیری قرنطینه',
+ 'تاریخ کشتار',
+ 'نام کشتارگاه',
+ 'شناسه یکتا کشتارگاه',
+ 'استان کشتارگاه',
+ 'شهر کشتارگاه',
+ 'تعداد',
+ 'وضعیت',
+ 'مقصد کشتار',
+ 'نام مرغدار',
+ 'شناسه یکتا مرغداری',
+ 'شناسه جوجه ریزی',
+ 'استان مرغدار',
+ 'شهرستان مرغدار',
+ 'سن کشتار',
+ 'نژاد',
+
+ ]
+
+ output = BytesIO()
+ workbook = Workbook()
+ worksheet = workbook.active
+ worksheet.sheet_view.rightToLeft = True
+ worksheet.insert_rows(1)
+ cell = worksheet.cell(row=1, column=1)
+ cell.alignment = Alignment(horizontal='center', vertical='center')
+
+ if 'date1' in request.GET:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ from_date_1 = shamsi_date(date1)
+ to_date_1 = shamsi_date(date2)
+ worksheet['A3'] = f'از تاریخ:({from_date_1}) تا تاریخ:({to_date_1})'
+ if serializer:
+ name = serializer[1]['DesUnitName']
+ else:
+ name = ''
+ excel_description(worksheet, 'A5', f'گزارش اطلاعات بار استان کشتارگاه {name}', color='red', row2='C5')
+
+ header_list2 = [
+ 'تعداد بار',
+ 'حجم بار',
+ 'میانگین سن کشتار',
+ 'میانگین سن کشتار',
+ 'تعداد بار داخل استان',
+ 'حجم بار داخل استان',
+ 'درصد داخل استان',
+ 'تعداد بار خارج استان',
+ 'حجم بار خارج استان',
+ 'درصد خارج استان',
+
+ ]
+ create_header(worksheet, header_list2, 5, 2, height=20.8, border_style='thin')
+ create_header_freez(worksheet, excel_options, 1, 6, 7, 20)
+ l = 5
+ m = 1
+ if serializer:
+ for data in serializer:
+ date = datetime.datetime.strptime(str(data['Date']), '%Y-%m-%dT%H:%M:%SZ')
+ destination = 'داخل استان' if data['Out'] == False else 'خارج استان'
+ list1 = [
+ m,
+ data.get('TrackingCode', '-'),
+ str(shamsi_date(date, in_value=True)),
+ data.get('DesUnitName', '-'),
+ data.get('DesPartIdCode', '-'),
+ data.get('Province', '-'),
+ data.get('City', '-'),
+ data.get('GoodAmount', '-'),
+ data.get('TrackingStatusDescription', '-'),
+ destination,
+ data.get('SourceUnitName', '-'),
+ (data['hatching'] or {}).get('hatching', {}).get('PartIdCode', '-'),
+ (data['hatching'] or {}).get('hatching', {}).get('RequestCode', '-'),
+ (data['hatching'] or {}).get('hatching', {}).get('poultry', {}).get('Province', '-'),
+ (data['hatching'] or {}).get('hatching', {}).get('poultry', {}).get('City', '-'),
+ (data['hatching'] or {}).get('Age', '-'),
+ (data['hatching'] or {}).get('hatching', {}).get('PedigreeName', '-'),
+
+ ]
+ m += 1
+
+ l += 1
+ create_value(worksheet, list1, l + 1, 1)
+ header_data = requests.get(
+ f'https://rsibackend.rasadyar.com/app/transporting-dashboard/?search=&value={value}&province={province}&PartIdCode={PartIdCode}').json()
+ if header_data:
+ value_header_list2 = [
+ header_data['bar_count'],
+ header_data['bar_quantity'],
+ header_data['input_bar_count'],
+ int(header_data['total_bar_killing_age']),
+ header_data['input_bar_count'],
+ header_data['input_bar_quantity'],
+ str(header_data['input_bar_percent']),
+ header_data['output_bar'],
+ header_data['output_bar_quantity'],
+ str(header_data['output_bar_percent']),
+
+ ]
+
+ create_value(worksheet, value_header_list2, 3, 5, border_style='thin')
+ total_quantity = sum(
+ item['GoodAmount'] for item in serializer)
+ list2 = [
+ 'مجموع==>',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ total_quantity,
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+
+ ]
+ create_value(worksheet, list2, l + 3, 1, color='green')
+
+ workbook.save(output)
+ output.seek(0)
+
+ response = HttpResponse(
+ content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+
+ response[
+ 'Content-Disposition'] = f'attachment; filename="گزارش اطلاعات بار کشتارگاه.xlsx"'.encode(
+ 'utf-8')
+ response.write(output.getvalue())
+ return response
+
+
+def total_killhouse_excel(request):
+ filterset_class = KillHouseFilterSet
+ search = request.GET.get('search')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ filters = {"trash": False}
+ if province := request.GET.get('province'):
+ filters['Province'] = province
+
+ if city := request.GET.get('city'):
+ filters['City'] = city
+
+ if kill_houses_name := request.GET.get('name'):
+ filters['UnitName'] = kill_houses_name
+
+ kill_houses = KillHouse.objects.filter(**filters).order_by('id')
+
+ if search:
+ if search != 'undefined' and search.strip():
+ kill_houses = kill_houses.filter(
+ build_query(filterset_class.Meta.fields, search)
+ )
+
+ excel_options = [
+ 'ردیف',
+ 'نام کشتارگاه',
+ 'شناسه یکتا کشتارگاه',
+ 'استان',
+ 'شهرستان',
+ 'تعداد بار',
+ 'حجم بار',
+ 'تعداد بار داخل استان',
+ 'حجم بار داخل استان',
+ 'درصد بار داخل استان',
+ 'تعداد بار خارج استان',
+ 'حجم بار خارج استان',
+ 'درصد بار خارج استان',
+
+ ]
+
+ output = BytesIO()
+ workbook = Workbook()
+ worksheet = workbook.active
+ worksheet.sheet_view.rightToLeft = True
+ worksheet.insert_rows(1)
+ cell = worksheet.cell(row=1, column=1)
+ cell.alignment = Alignment(horizontal='center', vertical='center')
+
+ if 'date1' in request.GET:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ from_date_1 = shamsi_date(date1)
+ to_date_1 = shamsi_date(date2)
+ worksheet['A3'] = f'از تاریخ:({from_date_1}) تا تاریخ:({to_date_1})'
+ excel_description(worksheet, 'A5', f'پایش کشتارگاه ها', color='red', row2='C5')
+
+ header_list2 = [
+ 'تعداد کشتارگاه ها',
+ 'تعداد بار',
+ 'حجم بار',
+ 'تعداد بار داخل استان',
+ 'حجم بار داخل استان',
+ 'درصد بار داخل استان',
+ 'تعداد بار خارج استان',
+ 'حجم بار خارج استان',
+ 'درصد بار خارج استان',
+
+ ]
+ create_header(worksheet, header_list2, 5, 2, height=20.8, border_style='thin')
+ create_header_freez(worksheet, excel_options, 1, 6, 7, 20)
+ l = 5
+ m = 1
+ if kill_houses:
+ part_id_codes = kill_houses.values_list('PartIdCode', flat=True)
+ all_bars = TransportingDetail.objects.filter(DesPartIdCode__in=part_id_codes,trash=False).only('Out', "GoodAmount")
+
+ all_products_transport = AllProductsTransport.objects.filter(
+ jihadi_destination__in=part_id_codes,
+ trash=False,
+ product='مرغ زنده -جهت كشتار'
+ ).only('out', 'quantity')
+
+ if date1:
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ all_bars = all_bars.filter(Date__date__gte=date1, Date__date__lte=date2)
+ all_products_transport = all_products_transport.filter(
+ Q(
+ date__gte=date1,
+ date__lte=date2,
+ date__isnull=False
+ ) |
+ Q(
+ unloading_date__gte=date1,
+ unloading_date__lte=date2,
+ date__isnull=True
+ )
+ )
+
+ for kill_house in kill_houses:
+ bars = all_bars.filter(DesPartIdCode=kill_house.PartIdCode, trash=False).only('GoodAmount',
+ 'Out')
+
+ aggregation_bars = bars.aggregate(
+ total=Sum('GoodAmount'),
+ input_total=Sum('GoodAmount', filter=Q(Out=False)),
+ output_total=Sum('GoodAmount', filter=Q(Out=True)),
+ input_count=Count('id', filter=Q(Out=False)),
+ output_count=Count('id', filter=Q(Out=True)),
+ total_count=Count('id')
+ )
+
+ all_products_for_killhouse = all_products_transport.filter(jihadi_destination=kill_house.PartIdCode)
+ aggregation_all_products = all_products_for_killhouse.aggregate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id')
+ )
+
+ total_count = (aggregation_bars['total_count'] or 0) + (aggregation_all_products['total_count'] or 0)
+ total_bars_quantity = (aggregation_bars['total'] or 0) + (aggregation_all_products['total'] or 0)
+ total_input_bars_quantity = (aggregation_bars['input_total'] or 0) + (aggregation_all_products['input_total'] or 0)
+ total_output_bars_quantity = (aggregation_bars['output_total'] or 0) + (aggregation_all_products['output_total'] or 0)
+ input_bars_count = (aggregation_bars['input_count'] or 0) + (aggregation_all_products['input_count'] or 0)
+ output_bars_count = (aggregation_bars['output_count'] or 0) + (aggregation_all_products['output_count'] or 0)
+
+ if total_count > 0:
+ total_input_bars_percent = round((input_bars_count / total_count) * 100, 1)
+ total_output_bars_percent = round((output_bars_count / total_count) * 100, 1)
+ else:
+ total_input_bars_percent = 0
+ total_output_bars_percent = 0
+
+ list1 = [
+ m,
+ kill_house.UnitName,
+ kill_house.PartIdCode,
+ kill_house.Province,
+ kill_house.City,
+ total_count,
+ total_bars_quantity,
+ input_bars_count,
+ total_input_bars_quantity,
+ total_input_bars_percent,
+ output_bars_count,
+ total_output_bars_quantity,
+ total_output_bars_percent,
+
+ ]
+ m += 1
+
+ l += 1
+ create_value(worksheet, list1, l + 1, 1)
+
+ aggregation1_bars = all_bars.aggregate(
+ total=Sum('GoodAmount'),
+ input_total=Sum('GoodAmount', filter=Q(Out=False)),
+ output_total=Sum('GoodAmount', filter=Q(Out=True)),
+ input_count=Count('id', filter=Q(Out=False)),
+ output_count=Count('id', filter=Q(Out=True)),
+ total_count=Count('id')
+ )
+
+ aggregation1_all_products = all_products_transport.aggregate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id')
+ )
+
+ total_count = (aggregation1_bars['total_count'] or 0) + (aggregation1_all_products['total_count'] or 0)
+ total_bars_quantity = (aggregation1_bars['total'] or 0) + (aggregation1_all_products['total'] or 0)
+ total_input_bars_quantity = (aggregation1_bars['input_total'] or 0) + (aggregation1_all_products['input_total'] or 0)
+ total_output_bars_quantity = (aggregation1_bars['output_total'] or 0) + (aggregation1_all_products['output_total'] or 0)
+ input_bars_count = (aggregation1_bars['input_count'] or 0) + (aggregation1_all_products['input_count'] or 0)
+ output_bars_count = (aggregation1_bars['output_count'] or 0) + (aggregation1_all_products['output_count'] or 0)
+
+ if total_count > 0:
+ total_input_bars_percent = round((input_bars_count / total_count) * 100, 1)
+ total_output_bars_percent = round((output_bars_count / total_count) * 100, 1)
+ else:
+ total_input_bars_percent = 0
+ total_output_bars_percent = 0
+ value_header_list2 = [
+ kill_houses.count(),
+ total_count,
+ total_bars_quantity,
+ input_bars_count,
+ total_input_bars_quantity,
+ total_input_bars_percent,
+ output_bars_count,
+ total_output_bars_quantity,
+ total_output_bars_percent,
+
+ ]
+
+ create_value(worksheet, value_header_list2, 3, 5, border_style='thin')
+
+ list2 = [
+ 'مجموع==>',
+ '',
+ '',
+ '',
+ '',
+ total_count,
+ total_bars_quantity,
+ input_bars_count,
+ total_input_bars_quantity,
+ total_input_bars_percent,
+ output_bars_count,
+ total_output_bars_quantity,
+ total_output_bars_percent,
+
+ ]
+ create_value(worksheet, list2, l + 3, 1, color='green')
+
+ workbook.save(output)
+ output.seek(0)
+
+ response = HttpResponse(
+ content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+
+ response[
+ 'Content-Disposition'] = f'attachment; filename="پایش کشتارگاه ها.xlsx"'.encode(
+ 'utf-8')
+ response.write(output.getvalue())
+ return response
+
+
+def hatching_excel(request):
+ filterset_class = HatchingsFilterSet
+ output = BytesIO()
+ workbook = Workbook()
+ worksheet = workbook.active
+ workbook.remove(worksheet)
+ sheet_list = [
+ 'فعال',
+ 'بایگانی'
+ ]
+ header_list = [
+ 'تعداد دوره جوجه ریزی',
+ 'حجم کل جوجه ریزی',
+ 'تلفات',
+ 'کشتار شده',
+ 'میانگین سن کشتار',
+ 'تعداد بار ها',
+ 'تعداد جوجه ریزی فعال',
+ 'حجم جوجه ریزی فعال',
+ 'کمترین سن',
+ 'بیشترین سن',
+ 'مانده در سالن',
+ ]
+
+ excel_options = [
+ 'ردیف',
+ 'استان',
+ 'شهرستان',
+ 'نام واحد',
+
+ 'نام مالک',
+ 'شماره مجوز',
+ 'شماره گواهی بهداشتی',
+ 'ظرفیت',
+ 'تاریخ جوجه ریزی',
+ 'سن گله',
+ 'تعداد جوجه ریزی',
+ 'دوره جوجه ریزی',
+ 'مجموع تلفات',
+ 'درصد جوجه ریزی به مجوز',
+ 'مانده در سالن',
+ 'میانگین سن کشتار',
+ 'تعداد بارها',
+ 'حجم بارها',
+
+ ]
+ filters = {}
+ system_code = request.GET.get('system_code')
+ city = request.GET.get('city')
+ province = request.GET.get('province')
+ age = request.GET.get('age')
+ killing_age = request.GET.get('killing_age')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ filters['Date__date__gte'] = date1
+ filters['Date__date__lte'] = date2
+
+ if killing_age:
+ filters['KillingAve'] = int(killing_age)
+
+ if age:
+ filters['Age__exact'] = age
+ if city:
+ filters['CityName__icontains'] = city
+ if province:
+ filters['ProvinceName__icontains'] = province
+ hatchings = Hatching.objects.filter(**filters, SystemCode=system_code)
+
+ # hatchings = hatchings.filter(Age__gt=70)
+
+ header = requests.get(f'https://rsibackend.rasadyar.com/app/hatchings-dashboard/?system_code={system_code}').json()
+ value_header_list = [
+ header.get('total_hatching_count', ""), # تعداد دوره جوجه ریزی
+ header.get('total_hatching_quantity', ""), # حجم کل جوجه ریزی
+ header.get('total_hatching_evacuation', ""), # تلفات
+ header.get('total_hatching_killing_quantity', ""), # کشتار شده
+ header.get('total_hatching_killing_age', ""), # میانگین سن کشتار
+ header.get('total_hatching_bars', ""), # تعداد بار ها
+ header.get('total_active_hatching_count', ""), # تعداد جوجه ریزی فعال
+ header.get('total_active_hatching_quantity', ""), # حجم جوجه ریزی فعال
+ header.get('least_age', ""), # کمترین سن
+ header.get('most_age', ""), # بیشترین سن
+ header.get('total_hatching_left_over', "") # مانده در سالن
+ ]
+
+ for sheet_name in sheet_list:
+ worksheet = workbook.create_sheet(sheet_name)
+ if sheet_name == 'فعال':
+ worksheet.sheet_view.rightToLeft = True
+ worksheet.insert_rows(1)
+ cell = worksheet.cell(row=1, column=1)
+ cell.alignment = Alignment(horizontal='center', vertical='center')
+
+ create_header(worksheet, header_list, 4, 2, height=25, width=25, border_style='thin', color='C00000')
+ hatchings_active = hatchings.filter(Age__lte=70)
+ serializer = HatchingDetailSerializer(hatchings_active, many=True).data
+ if serializer:
+ name = serializer[0]['poultry']['UnitName']
+ else:
+ name = ''
+ excel_description(worksheet, 'A1', f'پایش فارم فعال {name}', size=11, color='red', row2='C1')
+
+ m = 1
+
+ create_header_freez(worksheet, excel_options, 1, 6, 7, 20)
+
+ l = 7
+
+ create_value(worksheet, value_header_list, 3, 4, border_style='thin')
+
+ for data in serializer:
+ poultry = data.get('poultry', {})
+ info = data.get('info', {})
+ date = datetime.datetime.strptime(str(data['Date']), '%Y-%m-%dT%H:%M:%SZ')
+ list1 = [
+ m, # ردیف
+ poultry.get('Province', ''), # استان
+ poultry.get('City', ''), # شهرستان
+ poultry.get('UnitName', ''), # نام واحد
+ f"{poultry.get('FirstName', '')} {poultry.get('LastName', '')}".strip(), # نام مالک
+ data.get('RequestCode', ''), # شماره مجوز
+ data.get('CertId', ''), # شماره گواهی بهداشتی
+ data.get('CapacityFemale', ''), # ظرفیت
+ str(shamsi_date(date, in_value=True)),
+ data.get('Age', ''), # سن گله
+ data.get('ChickCountSum', ''), # تعداد جوجه ریزی
+ data.get('Period', ''), # دوره جوجه ریزی
+ data.get('Evacuation', ''), # مجموع تلفات
+ info.get('percent_hatching_license', ''), # درصد جوجه ریزی به مجوز
+ data.get('LeftOver', ''), # مانده در سالن
+ info.get('average_slaughter_age', ''), # میانگین سن کشتار
+ info.get('number_loads', ''), # تعداد بارها
+ info.get('load_volume', ''), # حجم بارها
+ ]
+ create_value(worksheet, list1, l, 1, m=m, border_style='thin', height=35)
+
+ l += 1
+ m += 1
+ if serializer:
+ total_capacity = sum(item.get('CapacityFemale', 0) for item in serializer)
+
+ total_chick_count = sum(item.get('ChickCountSum', 0) for item in serializer)
+
+ total_evacuation = sum(item.get('Evacuation', 0) for item in serializer)
+
+ total_left_over = sum(item.get('LeftOver', 0) for item in serializer)
+
+ total_load_volume = sum(item.get('info', {}).get('load_volume', 0) for item in serializer)
+
+ total_number_loads = sum(item.get('info', {}).get('number_loads', 0) for item in serializer)
+ list2 = [
+ 'مجموع==>',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ total_capacity,
+ '',
+ '',
+ total_chick_count,
+ '',
+ total_evacuation,
+ '',
+ total_left_over,
+ '',
+ total_number_loads,
+ total_load_volume,
+
+ ]
+ create_value(worksheet, list2, l + 3, 1, color='green')
+ else:
+ worksheet.sheet_view.rightToLeft = True
+ worksheet.insert_rows(1)
+ cell = worksheet.cell(row=1, column=1)
+ cell.alignment = Alignment(horizontal='center', vertical='center')
+
+ create_header(worksheet, header_list, 4, 2, height=25, width=25, border_style='thin', color='C00000')
+ hatchings1 = hatchings.filter(Age__gte=70)
+ serializer1 = HatchingDetailSerializer(hatchings1, many=True).data
+ if serializer1:
+ name = serializer1[0]['poultry']['UnitName']
+ else:
+ name = ''
+ excel_description(worksheet, 'A1', f'پایش فارم بایگانی {name}', size=11, color='red', row2='C1')
+
+ m = 1
+
+ create_header_freez(worksheet, excel_options, 1, 6, 7, 20)
+
+ l = 7
+
+ create_value(worksheet, value_header_list, 3, 4, border_style='thin')
+
+ for data in serializer1:
+ poultry = data.get('poultry', {})
+ info = data.get('info', {})
+
+ date = datetime.datetime.strptime(str(data['Date']), '%Y-%m-%dT%H:%M:%SZ')
+ list1 = [
+ m, # ردیف
+ poultry.get('Province', ''), # استان
+ poultry.get('City', ''), # شهرستان
+ poultry.get('UnitName', ''), # نام واحد
+ f"{poultry.get('FirstName', '')} {poultry.get('LastName', '')}".strip(), # نام مالک
+ data.get('RequestCode', ''), # شماره مجوز
+ data.get('CertId', ''), # شماره گواهی بهداشتی
+ data.get('CapacityFemale', ''), # ظرفیت
+ str(shamsi_date(date, in_value=True)),
+ data.get('Age', ''), # سن گله
+ data.get('ChickCountSum', ''), # تعداد جوجه ریزی
+ data.get('Period', ''), # دوره جوجه ریزی
+ data.get('Evacuation', ''), # مجموع تلفات
+ info.get('percent_hatching_license', ''), # درصد جوجه ریزی به مجوز
+ data.get('LeftOver', ''), # مانده در سالن
+ info.get('average_slaughter_age', ''), # میانگین سن کشتار
+ info.get('number_loads', ''), # تعداد بارها
+ info.get('load_volume', ''), # حجم بارها
+ ]
+ create_value(worksheet, list1, l, 1, m=m, border_style='thin', height=35)
+
+ l += 1
+ m += 1
+ if serializer1:
+ total_capacity = sum(item.get('CapacityFemale', 0) for item in serializer1)
+
+ total_chick_count = sum(item.get('ChickCountSum', 0) for item in serializer1)
+
+ total_evacuation = sum(item.get('Evacuation', 0) for item in serializer1)
+
+ total_left_over = sum(item.get('LeftOver', 0) for item in serializer1)
+
+ total_load_volume = sum(item.get('info', {}).get('load_volume', 0) for item in serializer1)
+
+ total_number_loads = sum(item.get('info', {}).get('number_loads', 0) for item in serializer1)
+ list2 = [
+ 'مجموع==>',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ total_capacity,
+ '',
+ '',
+ total_chick_count,
+ '',
+ total_evacuation,
+ '',
+ total_left_over,
+ '',
+ total_number_loads,
+ total_load_volume,
+
+ ]
+ create_value(worksheet, list2, l + 3, 1, color='green')
+ workbook.save(output)
+ output.seek(0)
+
+ response = HttpResponse(
+ content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+ response[
+ 'Content-Disposition'] = f'attachment; filename="پایش جوجه ریزی.xlsx"'.encode(
+ 'utf-8')
+ response.write(output.getvalue())
+ return response
+
+
+def all_hatching_excel(request):
+ filterset_class = HatchingsFilterSet
+ output = BytesIO()
+ workbook = Workbook()
+ worksheet = workbook.active
+ workbook.remove(worksheet)
+ sheet_list = [
+ 'فعال',
+ 'بایگانی'
+ ]
+ header_list = [
+ "تعداد دوره جوجه ریزی",
+ "حجم کل جوجه ریزی",
+ "تلفات",
+ "درصد تلفات",
+ "کشتار شده",
+ "درصد کشتار شده",
+ "میانگین سن کشتار",
+ "تعداد بارها",
+ "کمترین سن",
+ "بیشترین سن",
+ "مانده در سالن",
+ "درصد مانده در سالن نسبت به جوجه ریزی"
+ ]
+
+ header_list2 = [
+ "تعداد دوره جوجه ریزی",
+ "حجم کل جوجه ریزی",
+ "تلفات",
+ "درصد تلفات",
+ "کشتار شده",
+ "درصد کشتار شده",
+ "میانگین سن کشتار",
+ "تعداد بارها",
+ "حجم جوجه ریزی فعال",
+ "کمترین سن",
+ "بیشترین سن",
+ "مانده در سالن",
+ "درصد مانده در سالن",
+ "مانده در سالن آماده به کشتار",
+ "درصد مانده در سالن آماده به کشتار",
+ ]
+
+ excel_options = [
+ 'ردیف',
+ 'استان',
+ 'شهرستان',
+ 'نام واحد',
+
+ 'نام مالک',
+ 'شماره مجوز',
+ 'شماره گواهی بهداشتی',
+ 'ظرفیت',
+ 'تاریخ جوجه ریزی',
+ 'سن گله',
+ 'تعداد جوجه ریزی',
+ 'دوره جوجه ریزی',
+ 'مجموع تلفات',
+ 'درصد جوجه ریزی به مجوز',
+ 'مانده در سالن',
+ 'میانگین سن کشتار',
+ 'تعداد بارها',
+ 'حجم بارها',
+
+ ]
+ filters = {}
+ city = request.GET.get('city')
+ province = request.GET.get('province')
+ age = request.GET.get('age')
+ killing_age = request.GET.get('killing_age')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ filter_and_search='?'
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ filters['Date__date__gte'] = date1
+ filters['Date__date__lte'] = date2
+ filter_and_search+=f'date1={date1}&date2={date2}'
+
+ if killing_age:
+ filters['KillingAve'] = int(killing_age)
+
+ if age:
+ filters['Age__exact'] = age
+ if city:
+ filters['CityName__icontains'] = city
+ if province:
+ filters['ProvinceName__icontains'] = province
+ filter_and_search+=f'&province={province}'
+ hatchings = Hatching.objects.filter(**filters)
+
+ # hatchings = hatchings.filter(Age__gt=70)
+
+ header = requests.get(f'https://rsibackend.rasadyar.com/app/hatchings-dashboard/{filter_and_search}').json()
+ value_header_list = [
+ header.get('total_hatching_count', 0),
+ header.get('total_hatching_quantity', 0),
+ header.get('total_hatching_evacuation', 0),
+ header.get('total_hatching_evacuation', 0),
+ header.get('total_hatching_killing_quantity', 0),
+ header.get('total_hatching_evacuation_percent', 0),
+ header.get('total_hatching_killing_age', 0),
+ header.get('total_hatching_bars', 0),
+ header.get('least_age', 0),
+ header.get('most_age', 0),
+ header.get('total_hatching_left_over', 0),
+ header.get('total_hatching_left_over_percent', 0),
+ ]
+
+ value_header_list2 = [
+ header.get('total_active_hatching_count', 0),
+ header.get('total_hatching_quantity', 0),
+ header.get('total_active_hatching_evacuation', 0),
+ header.get('total_active_hatching_evacuation_percent', 0),
+ header.get('total_active_hatching_killing_quantity', 0),
+ header.get('total_active_hatching_killing_quantity_percent', 0),
+ header.get('total_active_hatching_killing_age', 0),
+ header.get('total_active_hatching_bars', 0),
+ header.get('total_active_hatching_quantity', 0),
+ header.get('least_age', 0),
+ header.get('most_age', 0),
+ header.get('total_active_hatching_left_over', 0),
+ header.get('total_active_hatching_left_over_percent', 0),
+ header.get('total_ready_active_hatching_left_over', 0),
+ header.get('total_ready_hatching_left_over_percent', 0),
+ ]
+
+ for sheet_name in sheet_list:
+ worksheet = workbook.create_sheet(sheet_name)
+ if sheet_name == 'فعال':
+ worksheet.sheet_view.rightToLeft = True
+ worksheet.insert_rows(1)
+ cell = worksheet.cell(row=1, column=1)
+ cell.alignment = Alignment(horizontal='center', vertical='center')
+ create_header(worksheet, header_list, 4, 2, height=25, width=25, border_style='thin', color='C00000')
+
+ create_header(worksheet, header_list2, 4, 5, height=25, width=25, border_style='thin', color='C00000')
+
+ hatchings_active = hatchings.filter(Age__lte=70)
+ serializer = HatchingDetailSerializer(hatchings_active, many=True).data
+ if date1:
+ info = 'از تاریخ {0} تا تاریخ {1}'.format(shamsi_date(date1), shamsi_date(date2))
+ else:
+ info = ''
+ excel_description(worksheet, 'A1', f'پایش فارم فعال {info}', size=11, color='red', row2='C2')
+ excel_description(worksheet, 'E1', f'خلاصه اطلاعات کل جوجه ریزی ها', size=11, color='red', row2='G1')
+ excel_description(worksheet, 'E4', f'خلاصه اطلاعات جوجه ریزی های فعال', size=11, color='red', row2='G4')
+
+ m = 1
+
+ create_header_freez(worksheet, excel_options, 1, 8, 9, 20)
+
+ l = 9
+
+ create_value(worksheet, value_header_list, 3, 4, border_style='thin')
+ create_value(worksheet, value_header_list2, 6, 4, border_style='thin')
+
+ for data in serializer:
+ poultry = data.get('poultry') or {}
+ info = data.get('info') or {}
+ date = datetime.datetime.strptime(str(data['Date']), '%Y-%m-%dT%H:%M:%SZ')
+ list1 = [
+ m,
+ poultry.get('Province') or '',
+ poultry.get('City') or '',
+ poultry.get('UnitName') or '',
+ f"{poultry.get('FirstName') or ''} {poultry.get('LastName') or ''}".strip(),
+ data.get('RequestCode') or '',
+ data.get('CertId') or '',
+ data.get('CapacityFemale') or '',
+ str(shamsi_date(date, in_value=True)),
+ data.get('Age') or '',
+ data.get('ChickCountSum') or '',
+ data.get('Period') or '',
+ data.get('Evacuation') or '',
+ info.get('percent_hatching_license') or '',
+ data.get('LeftOver') or '',
+ info.get('average_slaughter_age') or '',
+ info.get('number_loads') or '',
+ info.get('load_volume') or '',
+ ]
+ create_value(worksheet, list1, l, 1, m=m, border_style='thin', height=35)
+
+ l += 1
+ m += 1
+ if serializer:
+ total_capacity = sum(item.get('CapacityFemale', 0) for item in serializer)
+
+ total_chick_count = sum(item.get('ChickCountSum', 0) for item in serializer)
+
+ total_evacuation = sum(item.get('Evacuation', 0) for item in serializer)
+
+ total_left_over = sum(item.get('LeftOver', 0) for item in serializer)
+
+ total_load_volume = sum(item.get('info', {}).get('load_volume', 0) for item in serializer)
+
+ total_number_loads = sum(item.get('info', {}).get('number_loads', 0) for item in serializer)
+ list2 = [
+ 'مجموع==>',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ total_capacity,
+ '',
+ '',
+ total_chick_count,
+ '',
+ total_evacuation,
+ '',
+ total_left_over,
+ '',
+ total_number_loads,
+ total_load_volume,
+
+ ]
+ create_value(worksheet, list2, l + 3, 1, color='green')
+ else:
+ worksheet.sheet_view.rightToLeft = True
+ worksheet.insert_rows(1)
+ cell = worksheet.cell(row=1, column=1)
+ cell.alignment = Alignment(horizontal='center', vertical='center')
+
+ create_header(worksheet, header_list, 4, 2, height=25, width=25, border_style='thin', color='C00000')
+
+ hatchings1 = hatchings.filter(Age__gte=70)
+ serializer1 = HatchingDetailSerializer(hatchings1, many=True).data
+ if date1:
+ info = 'از تاریخ {0} تا تاریخ {1}'.format(shamsi_date(date1), shamsi_date(date2))
+ else:
+ info = ''
+ excel_description(worksheet, 'A1', f'پایش فارم بایگانی {info}', size=11, color='red', row2='C2')
+ excel_description(worksheet, 'E1', f'خلاصه اطلاعات کل جوجه ریزی ها', size=11, color='red', row2='G1')
+
+ m = 1
+
+ create_header_freez(worksheet, excel_options, 1, 6, 7, 20)
+
+ l = 7
+
+ create_value(worksheet, value_header_list, 3, 4, border_style='thin')
+
+ for data in serializer1:
+ poultry = data.get('poultry') or {}
+ info = data.get('info') or {}
+
+ date = datetime.datetime.strptime(str(data['Date']), '%Y-%m-%dT%H:%M:%SZ')
+ list1 = [
+ m,
+ poultry.get('Province') or '',
+ poultry.get('City') or '',
+ poultry.get('UnitName') or '',
+ f"{poultry.get('FirstName') or ''} {poultry.get('LastName') or ''}".strip(),
+ data.get('RequestCode') or '',
+ data.get('CertId') or '',
+ data.get('CapacityFemale') or '',
+ str(shamsi_date(date, in_value=True)),
+ data.get('Age') or '',
+ data.get('ChickCountSum') or '',
+ data.get('Period') or '',
+ data.get('Evacuation') or '',
+ info.get('percent_hatching_license') or '',
+ data.get('LeftOver') or '',
+ info.get('average_slaughter_age') or '',
+ info.get('number_loads') or '',
+ info.get('load_volume') or '',
+ ]
+ create_value(worksheet, list1, l, 1, m=m, border_style='thin', height=35)
+
+ l += 1
+ m += 1
+ if serializer1:
+ total_capacity = sum(item.get('CapacityFemale', 0) for item in serializer1)
+
+ total_chick_count = sum(item.get('ChickCountSum', 0) for item in serializer1)
+
+ total_evacuation = sum(item.get('Evacuation', 0) for item in serializer1)
+
+ total_left_over = sum(item.get('LeftOver', 0) for item in serializer1)
+
+ total_load_volume = sum(item.get('info', {}).get('load_volume', 0) for item in serializer1)
+
+ total_number_loads = sum(item.get('info', {}).get('number_loads', 0) for item in serializer1)
+ list2 = [
+ 'مجموع==>',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ total_capacity,
+ '',
+ '',
+ total_chick_count,
+ '',
+ total_evacuation,
+ '',
+ total_left_over,
+ '',
+ total_number_loads,
+ total_load_volume,
+
+ ]
+ create_value(worksheet, list2, l + 3, 1, color='green')
+ workbook.save(output)
+ output.seek(0)
+
+ response = HttpResponse(
+ content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+ response[
+ 'Content-Disposition'] = f'attachment; filename="پایش جوجه ریزی ها.xlsx"'.encode(
+ 'utf-8')
+ response.write(output.getvalue())
+ return response
+
+
+def all_send_different_bar_excel(request):
+ filterset_class = TransportingDetailFilterSet
+ filters = {}
+ PartIdCode = request.GET.get('PartIdCode')
+ RequestCode = request.GET.get('RequestCode')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ city = request.GET.get('city')
+ province = request.GET.get('province')
+ if province == 'undefined':
+ province = None
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ filters['Date__date__gte'] = date1
+ filters['Date__date__lte'] = date2
+ if city:
+ filters['City__icontains'] = city
+
+ if province:
+ filters['hatching__poultry__Province__icontains'] = province
+
+ if PartIdCode:
+ filters['DesPartIdCode'] = PartIdCode
+
+ if RequestCode:
+ filters['hatching__RequestCode'] = RequestCode
+
+ search = request.GET.get('search')
+ transports = TransportingDetail.objects.filter(**filters, trash=False).order_by("-Date")
+
+ query_all_products = AllProductsTransport.objects.filter(
+ trash=False,
+ product='مرغ زنده -جهت كشتار'
+ )
+
+
+ if PartIdCode:
+ query_all_products = query_all_products.filter(jihadi_destination=PartIdCode)
+
+ if RequestCode:
+ query_all_products = query_all_products.filter(hatching__RequestCode=RequestCode)
+
+ if date1 and date2:
+ query_all_products = query_all_products.filter(
+ date__gte=date1,
+ date__lte=date2,
+ date__isnull=False
+ )
+
+ if city:
+ query_all_products = query_all_products.filter(destination_city__icontains=city)
+
+ if province:
+ query_all_products = query_all_products.filter(destination_province__icontains=province)
+
+ if search:
+ if search != 'undefined' and search.strip():
+ transports = transports.filter(
+ build_query(filterset_class.Meta.fields, search)
+ )
+ query_all_products = query_all_products.filter(
+ build_query(AllProductsTransportFilterSet.Meta.fields, search)
+ )
+
+ ser_data_transports = TransportingDetailSerializer(transports, many=True).data
+
+ all_products_list = list(query_all_products)
+ ser_data_all_products = []
+ for obj in all_products_list:
+ serializer = AllProductsTransportSerializer(obj)
+ data = serializer.data
+ date_value = obj.date if obj.date else obj.unloading_date
+ if date_value:
+ data['Date'] = datetime.datetime.combine(date_value, datetime.time.min) if isinstance(date_value, datetime.date) else date_value
+ else:
+ data['Date'] = None
+ data['DesPartIdCode'] = obj.jihadi_destination
+ data['TrackingCode'] = obj.tracking
+ data['GoodAmount'] = obj.quantity
+ data['DesUnitName'] = obj.destination
+ data['Province'] = obj.destination_province
+ data['City'] = obj.destination_city
+ data['Out'] = obj.out
+ if hasattr(obj, 'hatching') and obj.hatching:
+ data['hatching'] = {
+ 'poultry': {
+ 'UnitName': getattr(obj.hatching.poultry, 'UnitName', '') if hasattr(obj.hatching, 'poultry') and obj.hatching.poultry else '',
+ 'PartIdCode': getattr(obj.hatching.poultry, 'PartIdCode', '') if hasattr(obj.hatching, 'poultry') and obj.hatching.poultry else '',
+ 'Mobile': getattr(obj.hatching.poultry, 'Mobile', '') if hasattr(obj.hatching, 'poultry') and obj.hatching.poultry else '',
+ 'City': getattr(obj.hatching.poultry, 'City', '') if hasattr(obj.hatching, 'poultry') and obj.hatching.poultry else '',
+ },
+ 'RequestCode': getattr(obj.hatching, 'RequestCode', '') if obj.hatching else '',
+ }
+ else:
+ data['hatching'] = {
+ 'poultry': {
+ 'UnitName': '',
+ 'PartIdCode': '',
+ 'Mobile': '',
+ 'City': '',
+ },
+ 'RequestCode': '',
+ }
+ data['TrackingStatusDescription'] = ''
+ data['Age'] = ''
+ ser_data_all_products.append(data)
+
+ def get_sort_date(data):
+ if data.get('Date'):
+ if isinstance(data['Date'], datetime.datetime):
+ return data['Date'].date()
+ elif isinstance(data['Date'], datetime.date):
+ return data['Date']
+ return datetime.date.min
+
+ combined_data = []
+ for data in ser_data_transports:
+ combined_data.append((get_sort_date(data), data))
+ for data in ser_data_all_products:
+ combined_data.append((get_sort_date(data), data))
+
+ combined_data.sort(key=lambda x: x[0], reverse=True)
+ ser_data = [data for _, data in combined_data]
+ output = BytesIO()
+ workbook = Workbook()
+ worksheet = workbook.active
+ workbook.remove(worksheet)
+ sheet_list = [
+ 'بار های داخل استان',
+ 'بار های خارج استان'
+ ]
+
+ for sheet_name in sheet_list:
+
+ worksheet = workbook.create_sheet(sheet_name)
+ if sheet_name == 'بار های داخل استان':
+
+ worksheet.sheet_view.rightToLeft = True
+ worksheet.insert_rows(1)
+ cell = worksheet.cell(row=1, column=1)
+ cell.alignment = Alignment(horizontal='center', vertical='center')
+
+ header_list = [
+ 'تعداد مرغداران',
+ 'تعداد کشتارگاه ها',
+ 'حجم کشتار',
+
+ ]
+ create_header(worksheet, header_list, 4, 2, height=25, width=25, border_style='thin', color='C00000')
+
+ value_header_list = [
+
+ ]
+ create_value(worksheet, value_header_list, 3, 4, border_style='thin')
+
+ excel_options = [
+ 'ردیف',
+ 'تاریخ کشتار',
+ 'مرغدار',
+ 'شناسه یکتا مرغدار',
+ 'شماره مجوز جوجه ریزی',
+ 'شماره موبایل مرغدار',
+ 'شهر مرغدار',
+ 'دامپزشک فارم',
+ 'تلفن دامپزشک فارم',
+ 'کد قرنطینه',
+ 'وضعیت بار',
+ 'کشتارگاه',
+ 'شناسه یکتا کشتارگاه',
+ 'استان',
+ 'شهر',
+ 'حجم کشتار',
+ 'سن کشتار',
+
+ ]
+
+ m = 1
+
+ create_header_freez(worksheet, excel_options, 1, 6, 7, 20)
+
+ l = 7
+ unique_poultry_out_false = set()
+ unique_slaughterhouses_out_false = set()
+ total_slaughter_out_false = 0
+ excel_description(worksheet, 'A1', f'بارهای داخل استان', color='red',
+ row2='B1')
+
+ if 'date1' in request.GET:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ from_date1 = shamsi_date(date1)
+ from_date2 = shamsi_date(date2)
+ excel_description(worksheet, 'A3', f'از تاریخ {from_date1} تا {from_date2}', color='red', row2='C3')
+ for data in ser_data:
+ if data['Out'] == False:
+ vet_farm_mobile = ''
+ vet_farm_name = ''
+
+ unique_poultry_out_false.add(data['hatching']['poultry']['UnitName'])
+ unique_slaughterhouses_out_false.add(data['DesUnitName'])
+ total_slaughter_out_false += data['GoodAmount']
+ if data['Date']:
+ date_str = str(data['Date']).split('T')[0] # جدا کردن بخش تاریخ
+ date = datetime.datetime.strptime(date_str, '%Y-%m-%d').date()
+ else:
+ date = datetime.date.today()
+ list1 = [
+ m,
+ str(shamsi_date(date, in_value=True)),
+ data['hatching']['poultry']['UnitName'],
+ data['hatching']['poultry']['PartIdCode'],
+ data['hatching']['RequestCode'],
+ data['hatching']['poultry']['Mobile'],
+ data['hatching']['poultry']['City'],
+ vet_farm_name,
+ vet_farm_mobile,
+ data['TrackingCode'],
+ data['TrackingStatusDescription'],
+ data['DesUnitName'],
+ data['DesPartIdCode'],
+ data['Province'],
+ data['City'],
+ data['GoodAmount'],
+ data['Age'],
+
+ ]
+ create_value(worksheet, list1, l, 1, m=m, border_style='thin', height=35)
+
+ l += 1
+ m += 1
+ value_header_list = [
+ len(unique_poultry_out_false),
+ len(unique_slaughterhouses_out_false),
+ total_slaughter_out_false
+ ]
+ create_value(worksheet, value_header_list, 3, 4, border_style='thin')
+ quantity = sum(
+ data['GoodAmount'] for data in ser_data if data['Out'] == False) or 0
+ list2 = [
+ 'مجموع==>',
+ '',
+ '',
+ '',
+
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ quantity,
+ '',
+
+ ]
+
+ create_value(worksheet, list2, l + 1, 1, color='green')
+ else:
+ unique_poultry_out_false = set()
+ unique_slaughterhouses_out_false = set()
+ total_slaughter_out_false = 0
+ worksheet.sheet_view.rightToLeft = True
+ worksheet.insert_rows(1)
+ cell = worksheet.cell(row=1, column=1)
+ cell.alignment = Alignment(horizontal='center', vertical='center')
+
+ header_list = [
+ 'تعداد مرغداران',
+ 'تعداد کشتارگاه ها',
+ 'حجم کشتار',
+
+ ]
+ create_header(worksheet, header_list, 4, 2, height=25, width=25, border_style='thin', color='C00000')
+
+ excel_options = [
+ 'ردیف',
+ 'تاریخ کشتار',
+ 'مرغدار',
+ 'شناسه یکتا مرغدار',
+ 'شماره مجوز جوجه ریزی',
+ 'شماره موبایل مرغدار',
+ 'شهر مرغدار',
+ 'دامپزشک فارم',
+ 'تلفن دامپزشک فارم',
+ 'کد قرنطینه',
+ 'وضعیت بار',
+ 'کشتارگاه',
+ 'شناسه یکتا کشتارگاه',
+ 'استان',
+ 'شهر',
+ 'حجم کشتار',
+ 'سن کشتار',
+
+ ]
+
+ m = 1
+
+ create_header_freez(worksheet, excel_options, 1, 6, 7, 20)
+
+ l = 7
+ unique_poultry = set()
+ unique_slaughterhouses = set()
+ total_slaughter = 0
+ excel_description(worksheet, 'A1', f'بارهای خارج استان', color='red',
+ row2='B1')
+
+ if 'date1' in request.GET:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ from_date1 = shamsi_date(date1)
+ from_date2 = shamsi_date(date2)
+ excel_description(worksheet, 'A3', f'از تاریخ {from_date1} تا {from_date2}', color='red', row2='C3')
+ for data in ser_data:
+ if data['Out'] == True:
+ vet_farm_mobile = ''
+ vet_farm_name = ''
+
+
+ unique_poultry_out_false.add(data['hatching']['poultry']['UnitName'])
+ unique_slaughterhouses_out_false.add(data['DesUnitName'])
+ total_slaughter_out_false += data['GoodAmount']
+ if data['Date']:
+ date_str = str(data['Date']).split('T')[0] # جدا کردن بخش تاریخ
+ date = datetime.datetime.strptime(date_str, '%Y-%m-%d').date()
+ else:
+ date = datetime.date.today()
+ list1 = [
+ m,
+ str(shamsi_date(date, in_value=True)),
+ data['hatching']['poultry']['UnitName'],
+ data['hatching']['poultry']['PartIdCode'],
+ data['hatching']['RequestCode'],
+ data['hatching']['poultry']['Mobile'],
+ data['hatching']['poultry']['City'],
+ vet_farm_mobile,
+ vet_farm_name,
+ data['TrackingCode'],
+ data['TrackingStatusDescription'],
+ data['DesUnitName'],
+ data['DesPartIdCode'],
+ data['Province'],
+ data['City'],
+ data['GoodAmount'],
+ data['Age'],
+
+ ]
+ create_value(worksheet, list1, l, 1, m=m, border_style='thin', height=35)
+
+ l += 1
+ m += 1
+ value_header_list = [
+ len(unique_poultry_out_false),
+ len(unique_slaughterhouses_out_false),
+ total_slaughter_out_false
+ ]
+ create_value(worksheet, value_header_list, 3, 4, border_style='thin')
+ quantity = sum(
+ data['GoodAmount'] for data in ser_data if data['Out'] == True) or 0
+ list2 = [
+ 'مجموع==>',
+ '',
+ '',
+ '',
+
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ quantity,
+ '',
+
+ ]
+
+ create_value(worksheet, list2, l + 1, 1, color='green')
+ workbook.save(output)
+ output.seek(0)
+
+ response = HttpResponse(
+ content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+ response[
+ 'Content-Disposition'] = f'attachment; filename="اطلاعات کلی بار ها.xlsx"'.encode(
+ 'utf-8')
+ response.write(output.getvalue())
+ return response
+
+
+def transport_carcass_detail_excel(request):
+ search = request.GET.get('search')
+
+ type_role = request.GET.get('role')
+ province = request.GET.get('province')
+
+ if province == 'undefined':
+ province = None
+ len_kill_house = 0
+ if type_role == 'KillHouse':
+ filters_kill_house = {}
+ if province:
+ filters_kill_house['Province'] = province
+
+ kill_house_filterset_class = KillHouseFilterSet
+ kill_house = KillHouse.objects.filter(**filters_kill_house).order_by('id')
+ len_kill_house = len(kill_house)
+ if search and search != 'undefined' and search.strip():
+ kill_house = kill_house.filter(
+ build_query(kill_house_filterset_class.Meta.fields, search)
+ )
+
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+
+ bars = TransportCarcassDetail.objects.filter(trash=False).order_by('-product_date')
+ buy_bars = TransportingDetail.objects.filter(trash=False, Date__date__gte='2025-03-21')
+
+ if date1:
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ bars = bars.filter(product_date__gte=date1, product_date__lte=date2)
+ date_1_for_buy_bars = date1 - datetime.timedelta(days=1)
+ date_2_for_buy_bars = date2 - datetime.timedelta(days=1)
+ buy_bars = buy_bars.filter(Date__date__gte=date_1_for_buy_bars, Date__date__lte=date_2_for_buy_bars)
+
+ bars_summary = bars.values('jihadi_origin').annotate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id'),
+ )
+
+ buy_summary = buy_bars.values('DesPartIdCode').annotate(
+ total=Sum('GoodAmount'),
+ input_total=Sum('GoodAmount', filter=Q(Out=False)),
+ output_total=Sum('GoodAmount', filter=Q(Out=True)),
+ input_count=Count('id', filter=Q(Out=False)),
+ output_count=Count('id', filter=Q(Out=True)),
+ total_count=Count('id'),
+ )
+
+ bars_dict = {row['jihadi_origin']: row for row in bars_summary}
+ buy_dict = {row['DesPartIdCode']: row for row in buy_summary}
+ kill_house = list(kill_house)
+ kill_house.sort(
+ key=lambda kh: (bars_dict.get(kh.PartIdCode, {}) or {}).get('total', 0) or 0,
+ reverse=True
+ )
+
+ serializer = KillHouseForTransportCarcassSerializer(
+ kill_house, many=True, context={'request': request, 'bars_dict': bars_dict, 'buy_dict': buy_dict}
+ ).data
+
+ if 'code' in request.GET:
+ output = BytesIO()
+ workbook = Workbook()
+ worksheet = workbook.active
+ worksheet.insert_rows(1)
+ worksheet.sheet_view.rightToLeft = True
+ cell = worksheet.cell(row=1, column=1)
+ cell.alignment = Alignment(horizontal='center', vertical='center')
+ excel_options = [
+ 'ردیف',
+ "تاریخ توزیع",
+ "تاریخ ثبت",
+ "نوع بار",
+ "محصول",
+ "وزن",
+ "کد قرنطینه",
+ "خریدار",
+ "استان خریدار",
+ "شهر خریدار",
+ "راننده",
+ "مالک",
+ "رهگیری خودرو",
+ "پلاک خودرو",
+ "وضعیت",
+
+ ]
+ date1 = request.GET.get('date1')
+ filters = {}
+ if date1:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ filters['product_date__gte'] = date1
+ filters['product_date__lte'] = date2
+ query = TransportCarcassDetail.objects.filter(**filters, jihadi_origin=request.GET.get('code'),
+ trash=False).order_by('-product_date')
+ if search:
+ if search != 'undefined' and search.strip():
+ query = query.filter(
+ build_query(TransportCarcassDetailFilterSet.Meta.fields, search)
+ )
+
+ serializer = TransportCarcassDetailSerializer(query, many=True).data
+
+ header_list = [
+ "تعداد بار ها",
+ "مجموع وزن ها",
+
+ ]
+ create_header(worksheet, header_list, 4, 2, height=25, width=25, border_style='thin', color='C00000')
+ m = 1
+
+ create_header_freez(worksheet, excel_options, 1, 6, 7, 20)
+ name = '-'
+ if query:
+ name = query.first().origin
+ l = 7
+
+ excel_description(worksheet, 'A1', f'توزیع / فروش گوشت {name}', color='red',
+ row2='B1')
+
+ if 'date1' in request.GET:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ from_date1 = shamsi_date(date1)
+ from_date2 = shamsi_date(date2)
+ excel_description(worksheet, 'A3', f'از تاریخ {from_date1} تا {from_date2}', color='red', row2='C3')
+ for data in serializer:
+ date = datetime.datetime.strptime(str(data.get('date')),
+ '%Y-%m-%d').date()
+ list1 = [
+ m,
+ str(shamsi_date(data.get('product_date', '-') or '-', in_value=True) if data.get('product_date',
+ '-') else '-'),
+ str(shamsi_date(date, in_value=True)),
+ "داخل استان" if data.get('out', '-') == False else "خارج استان",
+ data.get('product', '-') or '-',
+ data.get('quantity', 0) or 0,
+ data.get('tracking', '-') or '-',
+ data.get('destination', '-') or '-',
+ data.get('destination_province', '-') or '-',
+ data.get('destination_city', '-') or '-',
+ data.get('driver_name', '-') or '-',
+ data.get('owner', '-') or '-',
+ data.get('car_tracking_code', '-') or '-',
+ data.get('plate', '-') or '-',
+ data.get('unloading', '-') if data.get('unloading', '-') else "در انتظار تخلیه",
+
+ ]
+ create_value(worksheet, list1, l, 1, m=m, border_style='thin', height=35)
+
+ l += 1
+ m += 1
+ quantity = sum(item.get('quantity', 0) for item in serializer)
+
+ value_header_list = [
+ query.count(),
+ quantity
+ ]
+ create_value(worksheet, value_header_list, 3, 4, border_style='thin')
+ list2 = [
+ 'مجموع==>',
+ '',
+ '',
+ '',
+ '',
+ quantity,
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+
+ ]
+
+ create_value(worksheet, list2, l + 1, 1, color='green')
+ workbook.save(output)
+ output.seek(0)
+
+ response = HttpResponse(
+ content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+ response[
+ 'Content-Disposition'] = f'attachment; filename="توزیع / فروش گوشت مرغ.xlsx"'.encode(
+ 'utf-8')
+ response.write(output.getvalue())
+ return response
+ else:
+ output = BytesIO()
+ workbook = Workbook()
+ worksheet = workbook.active
+ workbook.remove(worksheet)
+ sheet_list = [
+ 'توزیع و فروش گوشت مرغ'
+ ]
+ excel_options = [
+ 'ردیف',
+ "نقش",
+ "نام واحد",
+ "شناسه یکتا",
+ "استان",
+ "شهرستان",
+ "محصول",
+ "تعداد خرید داخل استان",
+ "وزن خرید داخل استان",
+ "تعداد خرید خارج استان",
+ "وزن خرید خارج استان",
+ "وزن کل انبار",
+ "وزن کل توزیع",
+ "درصد توزیع نسبت به انبار",
+ "تعداد توزیع داخل استان",
+ "وزن توزیع داخل استان",
+ "درصد توزیع داخل استان",
+ "وزن توزیع خارج استان",
+ "درصد توزیع خارج استان",
+
+ ]
+
+ for sheet_name in sheet_list:
+ worksheet = workbook.create_sheet(sheet_name)
+ if sheet_name == 'توزیع و فروش گوشت مرغ':
+
+ worksheet.sheet_view.rightToLeft = True
+ worksheet.insert_rows(1)
+ cell = worksheet.cell(row=1, column=1)
+ cell.alignment = Alignment(horizontal='center', vertical='center')
+
+ header_list = [
+ "نقش",
+ "تعداد",
+ "محصول",
+ "تعداد خرید داخل استان",
+ "وزن خرید داخل استان",
+ "تعداد خرید خارج استان",
+ "وزن خرید خارج استان",
+ "وزن کل انبار",
+ "وزن کل توزیع",
+ "درصد توزیع نسبت به انبار",
+ "تعداد توزیع داخل استان",
+ "وزن توزیع داخل استان",
+ "درصد توزیع داخل استان",
+ "وزن توزیع خارج استان",
+ "درصد توزیع خارج استان",
+
+ ]
+ create_header(worksheet, header_list, 4, 2, height=25, width=25, border_style='thin',
+ color='C00000')
+ m = 1
+
+ create_header_freez(worksheet, excel_options, 1, 6, 7, 20)
+ l = 7
+ excel_description(worksheet, 'A1', f'توزیع / فروش گوشت مرغ', color='red',
+ row2='B1')
+
+ if 'date1' in request.GET:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ from_date1 = shamsi_date(date1)
+ from_date2 = shamsi_date(date2)
+ excel_description(worksheet, 'A3', f'از تاریخ {from_date1} تا {from_date2}', color='red',
+ row2='C3')
+ for data in serializer:
+ weight = 0
+ if (data.get('info', {}).get('total_ware_house', 0) or 0) > 0:
+ weight = round(((data.get('info', {}).get('total_bars_wight', 0) or 0) /
+ (data.get('info', {}).get('total_ware_house', 0) or 0)) * 100, 1) or 0
+ list1 = [
+ m,
+ data.get('info', {}).get('role', '-') or '-',
+ data.get('UnitName', '-') or '-',
+ data.get('PartIdCode', '-') or '-',
+ data.get('Province', '-') or '-',
+ data.get('City', '-') or '-',
+ "گوشت مرغ زنده",
+ data.get('info', {}).get('total_input_buy_bars_count', 0) or 0,
+ data.get('info', {}).get('total_input_buy_bars_wight', 0) or 0,
+ data.get('info', {}).get('total_output_buy_bars_count', 0) or 0,
+ data.get('info', {}).get('total_output_buy_bars_wight', 0) or 0,
+ data.get('info', {}).get('total_ware_house', 0) or 0,
+ data.get('info', {}).get('total_bars_wight', 0) or 0,
+ weight,
+ data.get('info', {}).get('input_bars', 0) or 0,
+ data.get('info', {}).get('total_input_bars_wight', 0) or 0,
+ data.get('info', {}).get('total_input_bars_percent', 0) or 0,
+ data.get('info', {}).get('total_output_bars_wight', 0) or 0,
+ data.get('info', {}).get('total_output_bars_percent', 0) or 0,
+
+ ]
+ create_value(worksheet, list1, l, 1, m=m, border_style='thin', height=35)
+
+ l += 1
+ m += 1
+ total_input_buy_count = sum(item.get('total_input_buy_bars_count', 0) for item in serializer)
+ total_input_buy_weight = sum(item.get('total_input_buy_bars_wight', 0) for item in serializer)
+ total_output_buy_count = sum(item.get('total_output_buy_bars_count', 0) for item in serializer)
+ total_output_buy_weight = sum(item.get('total_output_buy_bars_wight', 0) for item in serializer)
+ total_warehouse = sum(item.get('total_ware_house', 0) for item in serializer)
+ total_bars_weight = sum(item.get('total_bars_wight', 0) for item in serializer)
+ total_input_count = sum(item.get('input_bars', 0) for item in serializer)
+ total_input_weight = sum(item.get('total_input_bars_wight', 0) for item in serializer)
+ total_input_percent = sum(item.get('total_input_bars_percent', 0) for item in serializer)
+ total_output_weight = sum(item.get('total_output_bars_wight', 0) for item in serializer)
+ total_output_percent = sum(item.get('total_output_bars_percent', 0) for item in serializer)
+
+ percent_distributed = round(
+ (total_bars_weight / total_warehouse) * 100,
+ 1
+ ) if total_warehouse else 0
+
+ value_header_list = [
+ "کشتارگاه" if type_role == "KillHouse" else "مباشر",
+ "گوشت مرغ زنده",
+ len_kill_house,
+ total_input_buy_count,
+ total_input_buy_weight,
+ total_output_buy_count,
+ total_output_buy_weight,
+ total_warehouse,
+ total_bars_weight,
+ percent_distributed,
+ total_input_count,
+ total_input_weight,
+ total_input_percent,
+ total_output_weight,
+ total_output_percent,
+ ]
+ create_value(worksheet, value_header_list, 3, 4, border_style='thin')
+ list2 = [
+ 'مجموع==>',
+ '',
+ '',
+ '',
+ '',
+ '',
+ total_input_buy_count,
+ total_input_buy_weight,
+ total_output_buy_count,
+ total_output_buy_weight,
+ total_warehouse,
+ total_bars_weight,
+ percent_distributed,
+ total_input_count,
+ total_input_weight,
+ total_input_percent,
+ total_output_weight,
+ total_output_percent,
+
+ ]
+
+ create_value(worksheet, list2, l + 1, 1, color='green')
+ workbook.save(output)
+ output.seek(0)
+
+ response = HttpResponse(
+ content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+ response[
+ 'Content-Disposition'] = f'attachment; filename="توزیع / فروش گوشت مرغ.xlsx"'.encode(
+ 'utf-8')
+ response.write(output.getvalue())
+ return response
+
+ else:
+ filters_steward = {}
+ if province:
+ filters_steward['province'] = province
+
+ steward_filterset_class = GuildsFilterSet
+ steward = Guilds.objects.filter(**filters_steward, trash=False, is_steward=True).order_by('id')
+ len_kill_house = len(steward)
+ if search and search != 'undefined' and search.strip():
+ steward = steward.filter(
+ build_query(steward_filterset_class.Meta.fields, search)
+ )
+
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+
+ bars = TransportCarcassDetail.objects.filter(trash=False).order_by('-product_date')
+
+ if date1:
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ bars = bars.filter(product_date__gte=date1, product_date__lte=date2)
+
+ bars_summary = bars.values('jihadi_origin', 'jihadi_destination').annotate(
+ total=Sum('quantity', filter=Q(jihadi_origin=F('jihadi_origin'))),
+ input_total=Sum('quantity', filter=Q(out=False, jihadi_origin=F('jihadi_origin'))),
+ output_total=Sum('quantity', filter=Q(out=True, jihadi_origin=F('jihadi_origin'))),
+ input_count=Count('id', filter=Q(out=False, jihadi_origin=F('jihadi_origin'))),
+ output_count=Count('id', filter=Q(out=True, jihadi_origin=F('jihadi_origin'))),
+ total_count=Count('id', filter=Q(jihadi_origin=F('jihadi_origin'))),
+
+ total_input_buy_bars_wight=Sum('quantity',
+ filter=Q(out=False, jihadi_destination=F('jihadi_destination'))),
+ total_input_buy_bars_count=Count('id',
+ filter=Q(out=False, jihadi_destination=F('jihadi_destination'))),
+ total_output_buy_bars_wight=Sum('quantity',
+ filter=Q(out=True, jihadi_destination=F('jihadi_destination'))),
+ total_output_buy_bars_count=Count('id',
+ filter=Q(out=True, jihadi_destination=F('jihadi_destination'))),
+ )
+
+ bars_dict = {}
+ for row in bars_summary:
+ code = row['jihadi_origin'] or row['jihadi_destination']
+ if code:
+ bars_dict[code] = row
+
+ steward = list(steward)
+ steward.sort(
+ key=lambda s: (bars_dict.get(s.jihadi_code, {}) or {}).get('total', 0) or 0,
+ reverse=True
+ )
+
+ serializer = StewardForTransportCarcassSerializer(
+ steward, many=True, context={'request': request, 'bars_dict': bars_dict}
+ ).data
+ if 'code' in request.GET:
+ output = BytesIO()
+ workbook = Workbook()
+ worksheet = workbook.active
+ worksheet.insert_rows(1)
+ worksheet.sheet_view.rightToLeft = True
+ cell = worksheet.cell(row=1, column=1)
+ cell.alignment = Alignment(horizontal='center', vertical='center')
+ excel_options = [
+ 'ردیف',
+ "تاریخ توزیع",
+ "تاریخ ثبت",
+ "نوع بار",
+ "محصول",
+ "وزن",
+ "کد قرنطینه",
+ "خریدار",
+ "استان خریدار",
+ "شهر خریدار",
+ "راننده",
+ "مالک",
+ "رهگیری خودرو",
+ "پلاک خودرو",
+ "وضعیت",
+
+ ]
+ date1 = request.GET.get('date1')
+ filters = {}
+ if date1:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ filters['product_date__gte'] = date1
+ filters['product_date__lte'] = date2
+ query = TransportCarcassDetail.objects.filter(**filters, jihadi_origin=request.GET.get('code'),
+ trash=False).order_by('-product_date')
+ if search:
+ if search != 'undefined' and search.strip():
+ query = query.filter(
+ build_query(TransportCarcassDetailFilterSet.Meta.fields, search)
+ )
+
+ serializer = TransportCarcassDetailSerializer(query, many=True).data
+
+ header_list = [
+ "تعداد بار ها",
+ "مجموع وزن ها",
+
+ ]
+ create_header(worksheet, header_list, 4, 2, height=25, width=25, border_style='thin', color='C00000')
+ m = 1
+
+ create_header_freez(worksheet, excel_options, 1, 6, 7, 20)
+ name = '-'
+ if query:
+ name = query.first().destination
+ l = 7
+
+ excel_description(worksheet, 'A1', f'توزیع / فروش گوشت {name}', color='red',
+ row2='B1')
+
+ if 'date1' in request.GET:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ from_date1 = shamsi_date(date1)
+ from_date2 = shamsi_date(date2)
+ excel_description(worksheet, 'A3', f'از تاریخ {from_date1} تا {from_date2}', color='red', row2='C3')
+ for data in serializer:
+ if data.get('product_date', '-'):
+ if type(data.get('product_date', '-')) == str:
+ product_date1 = datetime.datetime.strptime(str(data.get('product_date')),
+ '%Y-%m-%d').date()
+ product_date = str(shamsi_date(product_date1))
+ else:
+ product_date = str(shamsi_date(data.get('product_date', '-')))
+ else:
+ product_date = '-'
+ date = datetime.datetime.strptime(str(data.get('date')),
+ '%Y-%m-%d').date()
+ list1 = [
+ m,
+ str(product_date),
+ str(shamsi_date(date, in_value=True)),
+ "داخل استان" if data.get('out', '-') == False else "خارج استان",
+ data.get('product', '-') or '-',
+ data.get('quantity', 0) or 0,
+ data.get('tracking', '-') or '-',
+ data.get('destination', '-') or '-',
+ data.get('destination_province', '-') or '-',
+ data.get('destination_city', '-') or '-',
+ data.get('driver_name', '-') or '-',
+ data.get('owner', '-') or '-',
+ data.get('car_tracking_code', '-') or '-',
+ data.get('plate', '-') or '-',
+ data.get('unloading', '-') if data.get('unloading', '-') else "در انتظار تخلیه",
+
+ ]
+ create_value(worksheet, list1, l, 1, m=m, border_style='thin', height=35)
+
+ l += 1
+ m += 1
+ quantity = sum(item.get('quantity', 0) for item in serializer)
+
+ value_header_list = [
+ query.count(),
+ quantity
+ ]
+ create_value(worksheet, value_header_list, 3, 4, border_style='thin')
+ list2 = [
+ 'مجموع==>',
+ '',
+ '',
+ '',
+ '',
+ quantity,
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+
+ ]
+
+ create_value(worksheet, list2, l + 1, 1, color='green')
+ workbook.save(output)
+ output.seek(0)
+
+ response = HttpResponse(
+ content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+ response[
+ 'Content-Disposition'] = f'attachment; filename="توزیع / فروش گوشت مرغ.xlsx"'.encode(
+ 'utf-8')
+ response.write(output.getvalue())
+ return response
+ else:
+ output = BytesIO()
+ workbook = Workbook()
+ worksheet = workbook.active
+ workbook.remove(worksheet)
+ sheet_list = [
+ 'توزیع و فروش گوشت مرغ'
+ ]
+ excel_options = [
+ 'ردیف',
+ "نقش",
+ "نام واحد",
+ "شناسه یکتا",
+ "استان",
+ "شهرستان",
+ "محصول",
+ "تعداد خرید داخل استان",
+ "وزن خرید داخل استان",
+ "تعداد خرید خارج استان",
+ "وزن خرید خارج استان",
+ "وزن کل انبار",
+ "وزن کل توزیع",
+ "درصد توزیع نسبت به انبار",
+ "تعداد توزیع داخل استان",
+ "وزن توزیع داخل استان",
+ "درصد توزیع داخل استان",
+ "وزن توزیع خارج استان",
+ "درصد توزیع خارج استان",
+
+ ]
+
+ for sheet_name in sheet_list:
+ worksheet = workbook.create_sheet(sheet_name)
+ if sheet_name == 'توزیع و فروش گوشت مرغ':
+
+ worksheet.sheet_view.rightToLeft = True
+ worksheet.insert_rows(1)
+ cell = worksheet.cell(row=1, column=1)
+ cell.alignment = Alignment(horizontal='center', vertical='center')
+
+ header_list = [
+ "نقش",
+ "تعداد",
+ "محصول",
+ "تعداد خرید داخل استان",
+ "وزن خرید داخل استان",
+ "تعداد خرید خارج استان",
+ "وزن خرید خارج استان",
+ "وزن کل انبار",
+ "وزن کل توزیع",
+ "درصد توزیع نسبت به انبار",
+ "تعداد توزیع داخل استان",
+ "وزن توزیع داخل استان",
+ "درصد توزیع داخل استان",
+ "وزن توزیع خارج استان",
+ "درصد توزیع خارج استان",
+
+ ]
+ create_header(worksheet, header_list, 4, 2, height=25, width=25, border_style='thin',
+ color='C00000')
+ m = 1
+
+ create_header_freez(worksheet, excel_options, 1, 6, 7, 20)
+ l = 7
+ excel_description(worksheet, 'A1', f'توزیع / فروش گوشت مرغ', color='red',
+ row2='B1')
+
+ if 'date1' in request.GET:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ from_date1 = shamsi_date(date1)
+ from_date2 = shamsi_date(date2)
+ excel_description(worksheet, 'A3', f'از تاریخ {from_date1} تا {from_date2}', color='red',
+ row2='C3')
+ for data in serializer:
+ weight = 0
+ if (data.get('info', {}).get('total_ware_house', 0) or 0) > 0:
+ weight = round(((data.get('info', {}).get('total_bars_wight', 0) or 0) /
+ (data.get('info', {}).get('total_ware_house', 0) or 0)) * 100, 1) or 0
+ list1 = [
+ m,
+ "مباشر",
+ data.get('name', '-') or '-',
+ data.get('jihadi_code', '-') or '-',
+ data.get('province', '-') or '-',
+ data.get('city', '-') or '-',
+ "گوشت مرغ زنده",
+ data.get('info', {}).get('total_input_buy_bars_count', 0) or 0,
+ data.get('info', {}).get('total_input_buy_bars_wight', 0) or 0,
+ data.get('info', {}).get('total_output_buy_bars_count', 0) or 0,
+ data.get('info', {}).get('total_output_buy_bars_wight', 0) or 0,
+ data.get('info', {}).get('total_ware_house', 0) or 0,
+ data.get('info', {}).get('total_bars_wight', 0) or 0,
+ weight,
+ data.get('info', {}).get('input_bars', 0) or 0,
+ data.get('info', {}).get('total_input_bars_wight', 0) or 0,
+ data.get('info', {}).get('total_input_bars_percent', 0) or 0,
+ data.get('info', {}).get('total_output_bars_wight', 0) or 0,
+ data.get('info', {}).get('total_output_bars_percent', 0) or 0,
+
+ ]
+ create_value(worksheet, list1, l, 1, m=m, border_style='thin', height=35)
+
+ l += 1
+ m += 1
+ total_input_buy_count = sum(item.get('total_input_buy_bars_count', 0) for item in serializer)
+ total_input_buy_weight = sum(item.get('total_input_buy_bars_wight', 0) for item in serializer)
+ total_output_buy_count = sum(item.get('total_output_buy_bars_count', 0) for item in serializer)
+ total_output_buy_weight = sum(item.get('total_output_buy_bars_wight', 0) for item in serializer)
+ total_warehouse = sum(item.get('total_ware_house', 0) for item in serializer)
+ total_bars_weight = sum(item.get('total_bars_wight', 0) for item in serializer)
+ total_input_count = sum(item.get('input_bars', 0) for item in serializer)
+ total_input_weight = sum(item.get('total_input_bars_wight', 0) for item in serializer)
+ total_input_percent = sum(item.get('total_input_bars_percent', 0) for item in serializer)
+ total_output_weight = sum(item.get('total_output_bars_wight', 0) for item in serializer)
+ total_output_percent = sum(item.get('total_output_bars_percent', 0) for item in serializer)
+
+ percent_distributed = round(
+ (total_bars_weight / total_warehouse) * 100,
+ 1
+ ) if total_warehouse else 0
+
+ value_header_list = [
+ "کشتارگاه" if type_role == "KillHouse" else "مباشر",
+ "گوشت مرغ زنده",
+ len_kill_house,
+ total_input_buy_count,
+ total_input_buy_weight,
+ total_output_buy_count,
+ total_output_buy_weight,
+ total_warehouse,
+ total_bars_weight,
+ percent_distributed,
+ total_input_count,
+ total_input_weight,
+ total_input_percent,
+ total_output_weight,
+ total_output_percent,
+ ]
+ create_value(worksheet, value_header_list, 3, 4, border_style='thin')
+ list2 = [
+ 'مجموع==>',
+ '',
+ '',
+ '',
+ '',
+ '',
+ total_input_buy_count,
+ total_input_buy_weight,
+ total_output_buy_count,
+ total_output_buy_weight,
+ total_warehouse,
+ total_bars_weight,
+ percent_distributed,
+ total_input_count,
+ total_input_weight,
+ total_input_percent,
+ total_output_weight,
+ total_output_percent,
+
+ ]
+
+ create_value(worksheet, list2, l + 1, 1, color='green')
+ workbook.save(output)
+ output.seek(0)
+
+ response = HttpResponse(
+ content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+ response[
+ 'Content-Disposition'] = f'attachment; filename="توزیع / فروش گوشت مرغ.xlsx"'.encode(
+ 'utf-8')
+ response.write(output.getvalue())
+ return response
+
+
+def guilds_transport_carcass_detail_excel(request):
+ search = request.GET.get('search')
+ province = request.GET.get('province')
+ if province == 'undefined':
+ province = None
+
+
+ if 'code' in request.GET:
+ output = BytesIO()
+ workbook = Workbook()
+ worksheet = workbook.active
+ worksheet.insert_rows(1)
+ worksheet.sheet_view.rightToLeft = True
+ cell = worksheet.cell(row=1, column=1)
+ cell.alignment = Alignment(horizontal='center', vertical='center')
+ excel_options = [
+ "ردیف",
+ "تاریخ توزیع",
+ "تاریخ ثبت",
+ "نوع بار",
+ "محصول",
+ "وزن",
+ "کد قرنطینه",
+ "فروشنده",
+ "استان فروشنده",
+ "شهر فروشنده",
+ "راننده",
+ "مالک",
+ "رهگیری خودرو",
+ "پلاک خودرو",
+ "وضعیت",
+ ]
+ date1 = request.GET.get('date1')
+ filters = {}
+ if date1:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ filters['product_date__gte'] = date1
+ filters['product_date__lte'] = date2
+
+ query = TransportCarcassDetail.objects.filter(
+ **filters,
+ jihadi_destination=request.GET.get('code'),
+ trash=False
+ ).order_by('-product_date')
+ if search and search != 'undefined' and search.strip():
+ query = query.filter(build_query(TransportCarcassDetailFilterSet.Meta.fields, search))
+
+ serializer = TransportCarcassDetailSerializer(query, many=True).data
+
+ header_list = [
+ "تعداد بار ها",
+ "مجموع وزن ها",
+
+ ]
+ create_header(worksheet, header_list, 4, 2, height=25, width=25, border_style='thin', color='C00000')
+ m = 1
+
+ create_header_freez(worksheet, excel_options, 1, 6, 7, 20)
+ name = '-'
+ if query:
+ name = query.first().origin
+ l = 7
+
+ excel_description(worksheet, 'A1', f'توزیع / فروش گوشت {name}', color='red',
+ row2='B1')
+
+ if 'date1' in request.GET:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ from_date1 = shamsi_date(date1)
+ from_date2 = shamsi_date(date2)
+ excel_description(worksheet, 'A3', f'از تاریخ {from_date1} تا {from_date2}', color='red', row2='C3')
+ for data in serializer:
+ if data.get('product_date', '-'):
+ if type(data.get('product_date', '-')) == str:
+ product_date1 = datetime.datetime.strptime(str(data.get('product_date')),
+ '%Y-%m-%d').date()
+ product_date= str(shamsi_date(product_date1))
+ else :
+ product_date= str(shamsi_date(data.get('product_date', '-')))
+ else:
+ product_date='-'
+ date=datetime.datetime.strptime(str(data.get('date')),
+ '%Y-%m-%d').date()
+ list1 = [
+ m,
+ str(product_date),
+ str(shamsi_date(date, in_value=True)),
+ "داخل استان" if data.get('out', '-') == False else "خارج استان",
+ data.get('product', '-') or '-',
+ data.get('quantity', 0) or 0,
+ data.get('tracking', '-') or '-',
+ data.get('origin', '-') or '-',
+ data.get('origin_province', '-') or '-',
+ data.get('origin_city', '-') or '-',
+ data.get('driver_name', '-') or '-',
+ data.get('owner', '-') or '-',
+ data.get('car_tracking_code', '-') or '-',
+ data.get('plate', '-') or '-',
+ data.get('unloading', '-') if data.get('unloading', '-') else "در انتظار تخلیه",
+
+
+ ]
+ create_value(worksheet, list1, l, 1, m=m, border_style='thin', height=35)
+
+ l += 1
+ m += 1
+ quantity = sum(item.get('quantity', 0) for item in serializer)
+
+ value_header_list = [
+ query.count(),
+ quantity
+ ]
+ create_value(worksheet, value_header_list, 3, 4, border_style='thin')
+ list2 = [
+ 'مجموع==>',
+ '',
+ '',
+ '',
+ '',
+ quantity,
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+
+
+ ]
+
+ create_value(worksheet, list2, l + 1, 1, color='green')
+ workbook.save(output)
+ output.seek(0)
+
+ response = HttpResponse(
+ content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+ response[
+ 'Content-Disposition'] = f'attachment; filename="توزیع / فروش گوشت مرغ.xlsx"'.encode(
+ 'utf-8')
+ response.write(output.getvalue())
+ return response
+ else:
+ output = BytesIO()
+ workbook = Workbook()
+ worksheet = workbook.active
+ workbook.remove(worksheet)
+ sheet_list = [
+ 'خرید صنوف'
+ ]
+ excel_options = [
+ "ردیف",
+ "نقش",
+ "نام واحد",
+ "شناسه یکتا",
+ "استان",
+ "شهرستان",
+ "محصول",
+ "تعداد خرید داخل استان",
+ "وزن خرید داخل استان",
+ "تعداد خرید خارج استان",
+ "وزن خرید خارج استان",
+ "وزن کل خرید",
+ "درصد خرید داخل استان",
+ "درصد خرید خارج استان",
+ ]
+
+ for sheet_name in sheet_list:
+ worksheet = workbook.create_sheet(sheet_name)
+ if sheet_name == 'خرید صنوف':
+ filters_steward = {}
+ if province:
+ filters_steward['province'] = province
+
+ steward = Guilds.objects.filter(**filters_steward, trash=False, is_steward=False).order_by('id')
+ steward_filterset_class = GuildsFilterSet
+ if search and search != 'undefined' and search.strip():
+ steward = steward.filter(build_query(steward_filterset_class.Meta.fields, search))
+
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ bars = TransportCarcassDetail.objects.filter(trash=False).order_by('-product_date')
+ if date1:
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ bars = bars.filter(product_date__gte=date1, product_date__lte=date2)
+
+ bars_summary = bars.values('jihadi_destination').annotate(
+ total_input_buy_bars_wight=Sum('quantity', filter=Q(out=False)),
+ total_output_buy_bars_wight=Sum('quantity', filter=Q(out=True)),
+ total_ware_house=Sum('quantity'),
+ total_count=Count('id'),
+ total_count_input_buy=Count('id', filter=Q(out=False)),
+ total_count_output_buy=Count('id', filter=Q(out=True)),
+ )
+
+ bars_dict = {row['jihadi_destination']: row for row in bars_summary}
+ steward = list(steward)
+ steward.sort(
+ key=lambda st: (bars_dict.get(st.jihadi_code, {}) or {}).get('total_ware_house', 0) or 0,
+ reverse=True
+ )
+
+ serializer = GuildsForTransportCarcassSerializer(steward, many=True,
+ context={'request': request, 'bars_dict': bars_dict})
+ worksheet.sheet_view.rightToLeft = True
+ worksheet.insert_rows(1)
+ cell = worksheet.cell(row=1, column=1)
+ cell.alignment = Alignment(horizontal='center', vertical='center')
+
+ header_list = [
+ "نقش",
+ "تعداد",
+ "محصول",
+ "تعداد خرید داخل استان",
+ "وزن خرید داخل استان",
+ "تعداد خرید خارج استان",
+ "وزن خرید خارج استان",
+ "وزن کل خرید",
+
+ ]
+ create_header(worksheet, header_list, 4, 2, height=25, width=25, border_style='thin', color='C00000')
+ m = 1
+
+ create_header_freez(worksheet, excel_options, 1, 6, 7, 20)
+ l = 7
+ excel_description(worksheet, 'A1', f'خرید صنوف', color='red',
+ row2='B1')
+
+ if 'date1' in request.GET:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ from_date1 = shamsi_date(date1)
+ from_date2 = shamsi_date(date2)
+ excel_description(worksheet, 'A3', f'از تاریخ {from_date1} تا {from_date2}', color='red', row2='C3')
+ for data in serializer:
+
+ list1 = [
+ m,
+ "صنف",
+ data.get('name', '-') or '-',
+ data.get('jihadi_code', '-') or '-',
+ data.get('province', '-') or '-',
+ data.get('city', '-') or '-',
+ "گوشت مرغ تازه",
+ data.get('info', {}).get('total_input_buy_bars_count', 0) or 0,
+ data.get('info', {}).get('total_input_buy_bars_wight', 0) or 0,
+ data.get('info', {}).get('total_output_buy_bars_count', 0) or 0,
+ data.get('info', {}).get('total_output_buy_bars_wight', 0) or 0,
+ data.get('info', {}).get('total_ware_house', 0) or 0,
+ data.get('info', {}).get('total_input_buy_bars_percent', 0) or 0,
+ data.get('info', {}).get('total_output_buy_bars_percent', 0) or 0,
+
+
+ ]
+ create_value(worksheet, list1, l, 1, m=m, border_style='thin', height=35)
+
+ l += 1
+ m += 1
+ total_input_buy_count = sum(item.get('total_input_buy_bars_count', 0) for item in serializer)
+ total_input_buy_weight = sum(item.get('total_input_buy_bars_wight', 0) for item in serializer)
+ total_output_buy_count = sum(item.get('total_output_buy_bars_count', 0) for item in serializer)
+ total_output_buy_weight = sum(item.get('total_output_buy_bars_wight', 0) for item in serializer)
+ total_warehouse = sum(item.get('total_ware_house', 0) for item in serializer)
+
+
+
+
+ value_header_list = [
+ "صنف",
+ "گوشت مرغ تازه",
+ len(steward),
+ total_input_buy_count,
+ total_input_buy_weight,
+ total_output_buy_count,
+ total_output_buy_weight,
+ total_warehouse,
+
+ ]
+ create_value(worksheet, value_header_list, 3, 4, border_style='thin')
+ list2 = [
+ 'مجموع==>',
+ '',
+ '',
+ '',
+ '',
+ '',
+ total_input_buy_count,
+ total_input_buy_weight,
+ total_output_buy_count,
+ total_output_buy_weight,
+ total_warehouse,
+ "",
+ "",
+
+
+ ]
+
+ create_value(worksheet, list2, l + 1, 1, color='green')
+ workbook.save(output)
+ output.seek(0)
+
+ response = HttpResponse(
+ content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+ response[
+ 'Content-Disposition'] = f'attachment; filename="خرید صنوف.xlsx"'.encode(
+ 'utf-8')
+ response.write(output.getvalue())
+ return response
+
+
+@api_view(["POST"])
+@permission_classes([AllowAny])
+@csrf_exempt
+def import_transporting_detail(request):
+ file = request.FILES['file'].read()
+ wb_obj = openpyxl.load_workbook(filename=BytesIO(file))
+ sheet = wb_obj.active
+ created_count = 0
+
+ for i, row in enumerate(sheet.iter_rows(values_only=True)):
+ # ردیف اول هدر است، رد میکنیم
+ if i < 1 or row is None:
+ continue
+
+ # ستونها بر اساس ترتیب در فایل اکسل
+ tracking_code = row[4]
+ issue_date = row[5]
+ product_name = row[7]
+ good_code = row[8]
+ good_amount = row[9]
+ province = row[14]
+ city = row[15]
+ des_unit_name = row[16]
+ source_cert_id = row[17]
+ des_cert_id = row[18]
+ takhlie_date = row[28]
+
+ if not tracking_code:
+ continue # اگر کد رهگیری ندارد، رد شود
+
+ exists = TransportingDetail.objects.filter(TrackingCode=tracking_code).exists()
+ if not exists:
+ new_obj = TransportingDetail(
+ TrackingCode=tracking_code,
+ IssueDatePersian=issue_date,
+ GoodName=product_name,
+ GoodCode=good_code,
+ GoodAmount=good_amount,
+ Province=province,
+ City=city,
+ DesUnitName=des_unit_name,
+ SourceCertId=source_cert_id,
+ DesPartIdCode=des_cert_id,
+ TakhlieDatePersian=takhlie_date,
+ )
+
+ new_obj.save()
+ kill_house = KillHouse.objects.filter(PartIdCode=new_obj.DesPartIdCode, trash=False).first()
+ # Resolve hatching by fetching permit code from quarantine page via tracking code
+ try:
+ permit_map = get_hatching_permit_code(tracking_code)
+ permit_code = permit_map.get(str(tracking_code))
+ if permit_code:
+ hatching = Hatching.objects.filter(RequestCode=permit_code, trash=False).first()
+ if hatching:
+ new_obj.hatching = hatching
+ except Exception:
+ pass
+ if kill_house:
+ new_obj.Province = kill_house.Province
+ new_obj.City = kill_house.City
+ try:
+ if new_obj.hatching and new_obj.hatching.poultry and \
+ new_obj.hatching.poultry.LocationIdProvince != kill_house.ProvinceId:
+ new_obj.Out = True
+ except Exception:
+ pass
+ new_obj.save()
+ created_count += 1
+
+ return HttpResponse(f"{created_count} رکورد جدید اضافه شد ✅")
\ No newline at end of file
diff --git a/app/filtersets.py b/app/filtersets.py
new file mode 100644
index 0000000..22fcf12
--- /dev/null
+++ b/app/filtersets.py
@@ -0,0 +1,332 @@
+import datetime
+
+from django_filters import rest_framework as filters
+
+from app.models import Poultry, PoultryHatching, TransportingChickenDetail, Hatching, TransportingDetail, KillHouse, \
+ TransportCarcassDetail, Driver, Guilds, AllProductsTransport
+
+
+class PoultryFilterSet(filters.FilterSet):
+ class Meta:
+ model = Poultry
+ fields = [
+ 'UserName',
+ 'FirstName',
+ 'LastName',
+ 'UnitName',
+ 'PartIdCode',
+ 'UnitId',
+ 'LocationIdProvince',
+ 'LocationIdCity'
+ ]
+
+
+class PoultryHatchingFilterSet(filters.FilterSet):
+ class Meta:
+ model = PoultryHatching
+ fields = [
+ 'DesCertId',
+ 'RequestCode',
+ 'Mobile',
+ 'PartIdCode',
+ 'EpidemiologicCode',
+ 'LocationNameProvince',
+ 'LocationNameCity',
+ 'poultry__UserName',
+ 'poultry__FirstName',
+ 'poultry__LastName',
+ 'poultry__UnitName',
+ 'poultry__PartIdCode',
+ 'poultry__UnitId',
+ 'LocationIdProvince',
+ 'LocationIdCity',
+ 'LocationNameCity',
+ ]
+
+
+class HatchingsFilterSet(filters.FilterSet):
+ class Meta:
+ model = Hatching
+ fields = [
+ 'UnitName',
+ 'ProvinceName',
+ 'CityName',
+ 'PartIdCode',
+ 'RequestCode',
+ 'poultry__UserName',
+ 'poultry__FirstName',
+ 'poultry__LastName',
+ 'poultry__UnitName',
+ 'poultry__PartIdCode',
+ 'poultry__UnitId',
+ ]
+
+
+class TransportingChickenDetailFilterSet(filters.FilterSet):
+ class Meta:
+ model = TransportingChickenDetail
+ fields = [
+ 'DesUnitName',
+ 'DesPartIdCode',
+ 'SourceUnitPartIdCode',
+ 'SourceUnitName',
+ 'SourceCertId',
+ 'hatching__DesCertId',
+ 'hatching__LocationNameProvince',
+ 'hatching__LocationNameCity',
+ 'hatching__poultry__UserName',
+ 'hatching__poultry__FirstName',
+ 'hatching__poultry__LastName',
+ 'hatching__poultry__UnitName',
+ 'hatching__poultry__PartIdCode',
+ 'hatching__poultry__UnitId',
+ 'hatching__poultry__LocationIdProvince',
+ 'hatching__poultry__LocationIdCity'
+ ]
+
+
+class HatchingFilterSet(filters.FilterSet):
+ class Meta:
+ model = PoultryHatching
+ fields = [
+ 'LocationNameCity',
+ 'LocationNameProvince',
+ 'HatchingAge',
+ ]
+
+
+class PoultryInfoFilterSet(filters.FilterSet):
+ class Meta:
+ model = Poultry
+ fields = [
+ 'LocationNameProvince',
+ 'Province',
+ 'LocationNameCity',
+ 'City',
+ 'FirstName',
+ 'LastName',
+ 'Mobile',
+ 'SystemCode',
+ 'EpidemiologicCode',
+ 'PartIdCode',
+ 'UnitName',
+ ]
+
+
+class HatchingCalculationsFilterSet(filters.FilterSet):
+ class Meta:
+ model = PoultryHatching
+ fields = [
+
+ 'LocationNameProvince',
+ 'LocationNameCity',
+ ]
+
+
+class TransportingDetailFilterSet(filters.FilterSet):
+ class Meta:
+ model = TransportingDetail
+ fields = [
+
+ 'TrackingCode',
+ 'Province',
+ 'City',
+ 'DesPartIdCode',
+ 'Age',
+ 'hatching__poultry__FirstName',
+ 'hatching__poultry__PartIdCode',
+ 'hatching__RequestCode',
+ 'hatching__CityName',
+ 'hatching__ProvinceName',
+ ]
+
+
+class KillHouseFilterSet(filters.FilterSet):
+ class Meta:
+ model = KillHouse
+ fields = [
+ 'PartIdCode',
+ 'UnitName',
+ 'Province',
+ 'City',
+ 'ProvinceId'
+ ]
+
+
+class CustomHatchingsFilterSet(filters.FilterSet):
+ city = filters.CharFilter(field_name='CityName', lookup_expr='icontains')
+ province = filters.CharFilter(field_name='ProvinceName', lookup_expr='icontains')
+ system_code = filters.CharFilter(field_name='SystemCode', lookup_expr='exact')
+
+ age = filters.NumberFilter(field_name='Age', lookup_expr='exact')
+ age__gt = filters.NumberFilter(field_name='Age', lookup_expr='gt')
+ age__gte = filters.NumberFilter(field_name='Age', lookup_expr='gte')
+ age__lt = filters.NumberFilter(field_name='Age', lookup_expr='lt')
+ age__lte = filters.NumberFilter(field_name='Age', lookup_expr='lte')
+
+ killing_age = filters.NumberFilter(field_name='KillingAve', lookup_expr='exact')
+ killing_age__gt = filters.NumberFilter(field_name='KillingAve', lookup_expr='gt')
+ killing_age__gte = filters.NumberFilter(field_name='KillingAve', lookup_expr='gte')
+ killing_age__lt = filters.NumberFilter(field_name='KillingAve', lookup_expr='lt')
+ killing_age__lte = filters.NumberFilter(field_name='KillingAve', lookup_expr='lte')
+
+ leftover__gt = filters.NumberFilter(field_name='LeftOver', lookup_expr='gt')
+ leftover__gte = filters.NumberFilter(field_name='LeftOver', lookup_expr='gte')
+ leftover__lt = filters.NumberFilter(field_name='LeftOver', lookup_expr='lt')
+ leftover__lte = filters.NumberFilter(field_name='LeftOver', lookup_expr='lte')
+ leftover__exact = filters.NumberFilter(field_name='LeftOver', lookup_expr='exact')
+
+ date1__date__gte = filters.DateFilter(method='filter_date_range')
+ date2__date__lte = filters.DateFilter(method='filter_date_range')
+ state = filters.CharFilter(method='filter_state')
+
+ class Meta:
+ model = Hatching
+ fields = [
+ 'city', 'province', 'system_code',
+ 'age', 'age__gt', 'age__gte', 'age__lt', 'age__lte',
+ 'killing_age', 'killing_age__gt', 'killing_age__gte', 'killing_age__lt', 'killing_age__lte',
+ 'leftover__gt', 'leftover__gte', 'leftover__lt', 'leftover__lte', 'leftover__exact',
+ 'date1__date__gte', 'date2__date__lte', 'state'
+ ]
+
+ def filter_date_range(self, queryset, name, value):
+ date1 = self.data.get('date1__date__gte')
+ date2 = self.data.get('date2__date__lte')
+ if date1 and date2:
+ try:
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ return queryset.filter(Date__date__gte=date1, Date__date__lte=date2)
+ except ValueError:
+ return queryset
+ return queryset
+
+ def filter_state(self, queryset, name, value):
+ state = self.data.get('state')
+ if state:
+ if state == 'pending':
+ queryset = queryset.filter(Age__lte=70)
+ else:
+ queryset = queryset.filter(Age__gt=70)
+ return queryset
+
+
+class TransportingDetailCustomFilterSet(filters.FilterSet):
+ unitname = filters.CharFilter(field_name='DesUnitName', lookup_expr='icontains')
+ province = filters.CharFilter(field_name='Province', lookup_expr='icontains')
+ city = filters.CharFilter(field_name='City', lookup_expr='icontains')
+
+ code = filters.CharFilter(field_name='DesPartIdCode', lookup_expr='exact')
+ trackingstatus = filters.CharFilter(field_name='TrackingStatusDescription', lookup_expr='exact')
+
+ quantity__gt = filters.NumberFilter(field_name='GoodAmount', lookup_expr='gt')
+ quantity__gte = filters.NumberFilter(field_name='GoodAmount', lookup_expr='gte')
+ quantity__lt = filters.NumberFilter(field_name='GoodAmount', lookup_expr='lt')
+ quantity__lte = filters.NumberFilter(field_name='GoodAmount', lookup_expr='lte')
+
+ age = filters.NumberFilter(field_name='Age', lookup_expr='exact')
+ age__gt = filters.NumberFilter(field_name='Age', lookup_expr='gt')
+ age__gte = filters.NumberFilter(field_name='Age', lookup_expr='gte')
+ age__lt = filters.NumberFilter(field_name='Age', lookup_expr='lt')
+ age__lte = filters.NumberFilter(field_name='Age', lookup_expr='lte')
+
+ date1__date__gte = filters.DateFilter(method='filter_date_range')
+ date2__date__lte = filters.DateFilter(method='filter_date_range')
+ poultry_unitname = filters.DateFilter(method='filter_poultry_unit_name')
+
+ class Meta:
+ model = TransportingDetail
+ fields = [
+ 'unitname',
+ 'province',
+ 'city',
+ 'code',
+ 'trackingstatus',
+ 'quantity__gt',
+ 'quantity__gte',
+ 'quantity__lt',
+ 'quantity__lte',
+ 'age',
+ 'age__gt',
+ 'age__gte',
+ 'age__lt',
+ 'age__lte',
+ 'date1__date__gte',
+ 'date2__date__lte',
+ 'poultry_unitname',
+ ]
+
+ def filter_date_range(self, queryset, name, value):
+ date1 = self.data.get('date1__date__gte')
+ date2 = self.data.get('date2__date__lte')
+ if date1 and date2:
+ try:
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ return queryset.filter(Date__date__gte=date1, Date__date__lte=date2)
+ except ValueError:
+ return queryset
+ return queryset
+
+ def filter_poultry_unit_name(self, queryset, name, value):
+ return queryset.filter(poultry__UnitName__icontains=value)
+
+
+class TransportCarcassDetailFilterSet(filters.FilterSet):
+ class Meta:
+ model = TransportCarcassDetail
+ fields = [
+ 'tracking',
+ 'origin_province',
+ 'origin_city',
+ 'destination_province',
+ 'destination_city',
+ 'product',
+ 'owner']
+
+
+class DriverFilterSet(filters.FilterSet):
+ class Meta:
+ model = Driver
+ fields = [
+ 'owner_name',
+ 'driver_name',
+ 'city',
+ 'province',
+ ]
+
+
+class GuildsFilterSet(filters.FilterSet):
+ class Meta:
+ model = Guilds
+ fields = [
+ 'name',
+ 'province',
+ 'city',
+ 'jihadi_code'
+ ]
+
+
+class AllProductsTransportFilterSet(filters.FilterSet):
+
+ class Meta:
+ model = AllProductsTransport
+ fields = [
+ 'record_id',
+ 'tracking',
+ 'product',
+ 'items',
+ 'origin_province',
+ 'origin_city',
+ 'destination_province',
+ 'destination_city',
+ 'owner',
+ 'driver_name',
+ 'car_tracking_code',
+ 'out',
+ 'quantity',
+ 'jihadi_destination',
+ 'jihadi_origin',
+
+ ]
diff --git a/app/helper.py b/app/helper.py
new file mode 100644
index 0000000..c9021bf
--- /dev/null
+++ b/app/helper.py
@@ -0,0 +1,157 @@
+import re
+import requests
+from bs4 import BeautifulSoup
+from requests.adapters import HTTPAdapter
+from requests.packages.urllib3.util.ssl_ import create_urllib3_context
+
+from django.db.models import Q
+from django.views.decorators.csrf import csrf_exempt
+from rest_framework import status
+from rest_framework.decorators import api_view, permission_classes
+from rest_framework.permissions import AllowAny
+from rest_framework.response import Response
+
+from app.cityandprovince import correct_province, correct_city, search_city_list, \
+ search_province_list
+from app.models import TransportingDetail, Guilds, AllProductsTransport
+from app.serializers import TransportingDetailForUpdateSerializer, AllProductsTransportSerializer
+
+
+@api_view(["POST"])
+@permission_classes([AllowAny])
+@csrf_exempt
+def get_bar_info(request):
+ kill_houses = request.data
+ kill_houses = dict(kill_houses)['kill_house']
+ bars = AllProductsTransport.objects.filter(trash=False, out=True, jihadi_destination__in=kill_houses,
+ unloading='تخلیه شده.',product='مرغ زنده -جهت كشتار',hatching__isnull=False,
+ date__gte='2025-12-25').order_by('-date')
+ ser_data = AllProductsTransportSerializer(bars, many=True).data
+ return Response(ser_data, status=status.HTTP_200_OK)
+
+
+@api_view(["GET"])
+@permission_classes([AllowAny])
+@csrf_exempt
+def test_city(request):
+ excluded_provinces = search_province_list
+ excluded_city = search_city_list
+ # hatchings = Hatching.objects.filter(
+ # ~Q(ProvinceName__in=excluded_provinces) | ~Q(CityName__in=excluded_city)
+ # )
+ transport = TransportingDetail.objects.filter(
+ ~Q(Province__in=excluded_provinces) | ~Q(City__in=excluded_city)).only('Province', 'City')
+ # hatchings = Hatching.objects.filter(
+ # Q(ProvinceName__contains='�') | Q( CityName__contains='�')
+ # )
+ l = 1
+ for t in transport:
+ print(l)
+ if t.Province not in excluded_provinces:
+ t.Province = correct_province(t.Province)
+ t.save()
+ if t.City not in excluded_city:
+ t.CityName = correct_city(t.City, t.Province)
+ t.save()
+ l += 1
+ # if hatching:
+
+ return Response('lo')
+
+
+class SSLAdapter(HTTPAdapter):
+ def __init__(self, *args, **kwargs):
+ self.context = create_urllib3_context()
+ self.context.options |= 0x4 # OP_LEGACY_SERVER_CONNECT
+ super().__init__(*args, **kwargs)
+
+ def init_poolmanager(self, *args, **kwargs):
+ kwargs['ssl_context'] = self.context
+ return super().init_poolmanager(*args, **kwargs)
+
+ def build_response(self, req, resp):
+ resp = super().build_response(req, resp)
+ return resp
+
+
+def check_quarantine_code(lst):
+ result_code = []
+ for code in lst:
+ session = requests.Session()
+ session.mount('https://', SSLAdapter())
+ data = {'gid': str(code)}
+ m = session.post('https://e.ivo.ir/Rahgiri/Gidprnt.aspx', data=data, verify=False,
+ headers={
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
+ ' (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'})
+ context = BeautifulSoup(m.text, 'html.parser')
+ table = context.find_all('table')
+ if table[5:6]:
+ row = context.find('div', align="right")
+ if row:
+ content = row.get_text(separator=" ", strip=True)
+ date_match = re.search(r'تاريخ:(\d{4}/\d{2}/\d{2})', content)
+ if date_match:
+ for i in table[5:6]:
+ row = i.find_all('tr')
+ for r in row[1:2]:
+ quantity = r.find('td')
+ match = re.search(r'\d+', quantity.text)
+ if match:
+ result_code.append(code)
+ return result_code
+
+
+def get_hatching_permit_code(code):
+ result = {}
+
+ session = requests.Session()
+ session.mount('https://', SSLAdapter())
+ data = {'gid': str(code)}
+ m = session.post(
+ 'https://e.ivo.ir/Rahgiri/Gidprnt.aspx',
+ data=data,
+ verify=False,
+ headers={
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
+ ' (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'
+ },
+ timeout=20,
+ )
+ text = m.text
+ permit = None
+ match = re.search(r'کد\s*مجوز\s*جوجه\s*ریزی\s*[::]\s*([0-9۰-۹]+)', text)
+ if match:
+ raw_permit = match.group(1)
+ trans = str.maketrans('۰۱۲۳۴۵۶۷۸۹', '0123456789')
+ permit = raw_permit.translate(trans)
+ if permit:
+ result[str(code)] = permit
+ return result
+
+
+@api_view(["GET"])
+@permission_classes([AllowAny])
+@csrf_exempt
+def api_get_hatching_permit_code(request):
+ code = request.GET.get('code')
+ if not code:
+ return Response({'detail': 'code query param is required'}, status=status.HTTP_400_BAD_REQUEST)
+ try:
+ data = get_hatching_permit_code(code)
+ return Response(data, status=status.HTTP_200_OK)
+ except Exception as e:
+ return Response({'detail': str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+
+
+def create_guild(**info):
+
+ Guilds(
+ )
+
+
+class SSLAdapter(HTTPAdapter):
+ def __init__(self, *args, **kwargs):
+ self.context = create_urllib3_context()
+ self.context.options |= 0x4 # OP_LEGACY_SERVER_CONNECT
+ super().__init__(*args, **kwargs)
diff --git a/app/helper_excel.py b/app/helper_excel.py
new file mode 100644
index 0000000..4ddaf21
--- /dev/null
+++ b/app/helper_excel.py
@@ -0,0 +1,327 @@
+from io import BytesIO
+
+import jdatetime
+import openpyxl
+from django.http import HttpResponse
+from openpyxl import Workbook
+from openpyxl.chart import LineChart, Reference, BarChart
+from openpyxl.styles import PatternFill, Alignment, Font
+from openpyxl.utils import get_column_letter
+
+correction_dict = {
+ '��ربراکرز (آپلاس)': 'آربراکرز (آپلاس)',
+ 'آ��براکرز (آپلاس)': 'آربراکرز (آپلاس)',
+ 'آر��راکرز (آپلاس)': 'آربراکرز (آپلاس)',
+ 'آرب��اکرز (آپلاس)': 'آربراکرز (آپلاس)',
+ 'آربر��کرز (آپلاس)': 'آربراکرز (آپلاس)',
+ 'آربرا��رز (آپلاس)': 'آربراکرز (آپلاس)',
+ 'آربراک��ز (آپلاس)': 'آربراکرز (آپلاس)',
+ 'آربراکر�� (آپلاس)': 'آربراکرز (آپلاس)',
+ 'آربراکرز�� آپلاس)': 'آربراکرز (آپلاس)',
+ 'آربراکرز (��پلاس)': 'آربراکرز (آپلاس)',
+ 'آربراکرز (آ��لاس)': 'آربراکرز (آپلاس)',
+ 'آربراکرز (آپ��اس)': 'آربراکرز (آپلاس)',
+ 'آربراکرز (آپل��س)': 'آربراکرز (آپلاس)',
+ 'آربراکرز (آپلا��)': 'آربراکرز (آپلاس)',
+ 'آربراکرز (آپلاس��': 'آربراکرز (آپلاس)',
+ 'آربراکرز��(آپلاس)': 'آربراکرز (آپلاس)',
+
+ '��اس': 'راس',
+ 'ر��س': 'راس',
+ 'را��': 'راس',
+
+ '��رین': 'آرین',
+ 'آ��ین': 'آرین',
+ 'آر��ن': 'آرین',
+ 'آری��': 'آرین',
+
+ '��ندین ریور': 'ایندین ریور',
+ 'ای��دین ریور': 'ایندین ریور',
+ 'این��ین ریور': 'ایندین ریور',
+ 'ایند��ن ریور': 'ایندین ریور',
+ 'ایندی�� ریور': 'ایندین ریور',
+ 'ایندین��ریور': 'ایندین ریور',
+ 'ایندین ��یور': 'ایندین ریور',
+ 'ایندین ر��ور': 'ایندین ریور',
+ 'ایندین ری��ر': 'ایندین ریور',
+ 'ایندین ریو��': 'ایندین ریور',
+
+ '��اب': 'کاب',
+ 'ک��ب': 'کاب',
+ 'کا��': 'کاب'
+}
+
+blue_fill = PatternFill(start_color="277358", fill_type="solid")
+Alignment_CELL = Alignment(horizontal='center', vertical='center', wrap_text=True)
+red_font = Font(color="C00000", bold=True)
+GREEN_CELL = PatternFill(start_color="00B050", fill_type="solid")
+RED_CELL = PatternFill(start_color="FCDFDC", fill_type="solid")
+YELLOW_CELL = PatternFill(start_color="FFFF00", fill_type="solid")
+ORANGE_CELL = PatternFill(start_color="FFC000", fill_type="solid")
+BLUE_CELL = PatternFill(start_color="538DD5", fill_type="solid")
+LIGHT_GREEN_CELL = PatternFill(start_color="92D050", fill_type="solid")
+VERY_LIGHT_GREEN_CELL = PatternFill(start_color="5AFC56", fill_type="solid")
+
+
+
+
+def shamsi_date(date,in_value=None):
+ if in_value:
+ sh_date=jdatetime.date.fromgregorian(
+ year=date.year,
+ month=date.month,
+ day=date.day
+ )
+ else:
+ gh_date = jdatetime.date.fromgregorian(
+ year=date.year,
+ month=date.month,
+ day=date.day
+ ).strftime('%Y-%m-%d')
+ reversed_date = reversed(gh_date.split("-"))
+ separate = "-"
+ sh_date = separate.join(reversed_date)
+ return sh_date
+
+
+def create_header(worksheet, list, num, row, height=None, width=None,color=None,text_color=None,border_style=None):
+ for col_num, option in enumerate(list, num):
+ cell = worksheet.cell(row=row, column=col_num, value=option)
+ col_letter = get_column_letter(col_num)
+ cell.alignment = Alignment_CELL
+ if color is not None:
+ if color == 'green':
+ cell.fill=GREEN_CELL
+ elif color == 'orange':
+ cell.fill=ORANGE_CELL
+ elif color == 'blue':
+ cell.fill=BLUE_CELL
+ else:
+ cell.fill = PatternFill(start_color=color, fill_type="solid")
+ else:
+ cell.fill = blue_fill
+ if text_color is not None:
+ cell.font = Font(size=9, bold=True, color=text_color)
+ else:
+ cell.font = Font(size=9, bold=True, color='D9FFFFFF')
+ if height is not None:
+ worksheet.row_dimensions[row].height = height
+ if width is not None:
+ worksheet.column_dimensions[col_letter].width = width
+ if border_style is not None:
+ cell.border = openpyxl.styles.Border(
+ left=openpyxl.styles.Side(style=border_style),
+ right=openpyxl.styles.Side(style=border_style),
+ top=openpyxl.styles.Side(style=border_style),
+ bottom=openpyxl.styles.Side(style=border_style)
+ )
+
+
+def create_header_freez(worksheet, list, num, row, header_row, height=None, width=None,len_with=None,different_cell=None):
+ for col_num, option in enumerate(list, num):
+ col_letter = get_column_letter(col_num)
+ cell = worksheet.cell(row=row, column=col_num, value=option)
+ cell.alignment = Alignment_CELL
+ cell.fill = blue_fill
+ cell.font = Font(size=10, bold=True, color='D9FFFFFF')
+
+ if height is not None:
+ worksheet.row_dimensions[row].height = height
+ if len(option) > worksheet.column_dimensions[col_letter].width:
+ worksheet.column_dimensions[col_letter].width = len(option) + 2
+ if width is not None:
+ worksheet.column_dimensions[col_letter].width = width
+ if len_with is not None:
+ if len(option) > worksheet.column_dimensions[col_letter].width:
+ worksheet.column_dimensions[col_letter].width = len(option) + 3
+ if different_cell is not None:
+ if option == different_cell:
+ cell.fill = PatternFill(start_color="C00000", fill_type="solid")
+ worksheet.freeze_panes = worksheet[f'A{header_row}']
+ max_col = worksheet.max_column
+ range_str = f'A{header_row - 1}:{get_column_letter(max_col)}{worksheet.max_row}'
+ worksheet.auto_filter.ref = range_str
+
+
+def excel_description(worksheet, row1, description, size=None, color=None,my_color=None, row2=None):
+ worksheet[row1] = description
+ worksheet[row1].alignment = Alignment_CELL
+ if size is not None:
+ worksheet[row1].font = Font(size=size)
+ if color is not None:
+ worksheet[row1].font = red_font
+ if my_color is not None:
+ worksheet[row1].font = PatternFill(start_color=my_color, fill_type="solid")
+
+ if row2 is not None:
+ merge_range = f'{row1}:{row2}'
+ worksheet.merge_cells(merge_range)
+
+
+def create_value(worksheet, list, l, num, border_style=None, m=None, height=None, color=None, width=None,
+ different_cell=None, different_value=None, item_num=None, item_color=None):
+
+ color_dict = {
+ 'green': GREEN_CELL,
+ 'yellow': YELLOW_CELL,
+ 'blue': BLUE_CELL,
+ 'red': RED_CELL,
+ 'light_green': LIGHT_GREEN_CELL,
+ 'very_light_green': VERY_LIGHT_GREEN_CELL
+ }
+
+ for item in range(len(list)):
+ cell = worksheet.cell(row=l, column=item + num, value=list[item])
+ cell.alignment = Alignment_CELL
+
+ if border_style:
+ cell.border = openpyxl.styles.Border(
+ left=openpyxl.styles.Side(style=border_style),
+ right=openpyxl.styles.Side(style=border_style),
+ top=openpyxl.styles.Side(style=border_style),
+ bottom=openpyxl.styles.Side(style=border_style)
+ )
+
+ value = list[item]
+ if isinstance(value, (int, float)) and value != 0:
+ cell.number_format = '#,###'
+ else:
+ cell.value = value
+
+ cell.font = Font(size=10, bold=True)
+
+ if m is not None and m % 2 != 0:
+ cell.fill = PatternFill(start_color="D6F6FE", fill_type="solid")
+
+ if height is not None:
+ worksheet.row_dimensions[l + 1].height = height
+
+ if item_num is not None and item == item_num:
+ if item_color:
+ cell.fill = item_color
+ elif color in color_dict:
+ cell.fill = color_dict[color]
+
+ if different_cell is not None and list[different_cell] == different_value:
+ cell.fill = RED_CELL
+
+ if width is not None:
+ worksheet.column_dimensions[openpyxl.utils.get_column_letter(item + num)].width = width
+
+def merge_cells(worksheet, l, s, cell1=None, cell2=None, lst=None):
+ if lst is not None:
+ for col in lst:
+ rng = f'{col}{l}:{col}{l + s}'
+ worksheet.merge_cells(rng)
+ worksheet[col + f'{l}'].alignment = Alignment_CELL
+ else:
+ for col in range(ord(f'{cell1}'), ord(f'{cell2}') + 1):
+ rng = f'{chr(col)}{l}:{chr(col)}{l + s}'
+ worksheet.merge_cells(rng)
+ worksheet[chr(col) + f'{l}'].alignment = Alignment_CELL
+
+def add_header(worksheet):
+ worksheet.oddHeader.center.text = "سامانه رصدیار"
+ worksheet.oddHeader.center.size = 14 # تنظیم اندازه فونت
+ worksheet.oddHeader.center.font = "Arial,Bold" # تنظیم فونت و ضخامت
+
+ # همچنین میتوانید از هدرهای چپ و راست هم استفاده کنید
+ # worksheet.oddHeader.right.text = f"تاریخ: {shamsi_now_date}"
+
+def cell_color_changer(worksheet, row, start_index, end_index, custom_color):
+ for item in range(start_index, end_index):
+ cell = worksheet.cell(row=row, column=item)
+ cell.fill = PatternFill(start_color=custom_color, fill_type="solid")
+
+
+def start_excel():
+ output = BytesIO()
+ workbook = Workbook()
+ worksheet = workbook.active
+ worksheet.sheet_view.rightToLeft = True
+ worksheet.insert_rows(1)
+ return workbook,worksheet,output
+
+def close_excel(name):
+ workbook,worksheet,output=start_excel()
+ workbook.save(output)
+ output.seek(0)
+
+ response = HttpResponse(
+ content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
+ response[
+ 'Content-Disposition'] = f'attachment; filename="{name}.xlsx"'.encode(
+ 'utf-8')
+ response.write(output.getvalue())
+ return response
+
+
+
+def add_chart(
+ worksheet,
+ chart_type,
+ data_columns,
+ category_column,
+ start_row,
+ end_row,
+ chart_position,
+ chart_title,
+ x_axis_title,
+ y_axis_title,
+ chart_width=25, # عرض نمودار پیشفرض (واحد: cm)
+ chart_height=15
+ ):
+ """
+ افزودن نمودار به صفحه اکسل.
+
+ ورودی:
+ worksheet (openpyxl.Worksheet): صفحه اکسل.
+ chart_type (str): نوع نمودار ("line" یا "bar").
+ data_columns (list): لیستی از ستونهای داده.
+ category_column (int): ستون دستهبندیها.
+ start_row (int): ردیف شروع دادهها.
+ end_row (int): ردیف پایان دادهها.
+ chart_position (str): محل قرار گرفتن نمودار.
+ chart_title (str): عنوان نمودار.
+ x_axis_title (str): عنوان محور X.
+ y_axis_title (str): عنوان محور Y.
+ chart_width (float): عرض نمودار (واحد: cm).
+ chart_height (float): ارتفاع نمودار (واحد: cm).
+ """
+
+ if chart_type == 'line':
+ chart = LineChart()
+ chart.style = 20
+ elif chart_type == 'bar':
+ chart = BarChart()
+ else:
+ raise ValueError("chart_type باید 'line' یا 'bar' باشد.")
+
+ chart.title = chart_title
+ chart.y_axis.title = y_axis_title
+ chart.x_axis.title = x_axis_title
+ chart.width = chart_width
+ chart.height = chart_height
+
+ categories = Reference(worksheet, min_col=category_column, min_row=start_row, max_row=end_row)
+ data = Reference(worksheet, min_col=data_columns, min_row=start_row - 1, max_row=end_row)
+ chart.add_data(data, titles_from_data=True)
+ chart.set_categories(categories)
+ for series in chart.series:
+ series.graphicalProperties.line.solidFill = "277358"
+ series.graphicalProperties.line.width = 30000
+
+ worksheet.add_chart(chart, chart_position)
+ #example
+ # add_chart(
+ # worksheet=worksheet,
+ # chart_type='line',
+ # data_columns=7, # ستون وزن وارد شده
+ # category_column=2, # ستون نام سردخانهها
+ # start_row=7,
+ # end_row=l + 1,
+ # chart_position="A12",
+ # chart_title="نمودار تغییرات وزن در سردخانهها",
+ # x_axis_title="سردخانهها",
+ # y_axis_title="وزن (کیلوگرم)"
+ # )
\ No newline at end of file
diff --git a/app/migrations/0001_initial.py b/app/migrations/0001_initial.py
new file mode 100644
index 0000000..7545864
--- /dev/null
+++ b/app/migrations/0001_initial.py
@@ -0,0 +1,138 @@
+# Generated by Django 4.2.19 on 2025-02-26 11:13
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Poultry',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('UserName', models.CharField(max_length=200, null=True)),
+ ('Password', models.CharField(max_length=200, null=True)),
+ ('FirstName', models.CharField(max_length=200, null=True)),
+ ('LastName', models.CharField(max_length=200, null=True)),
+ ('UserGroupName', models.CharField(max_length=200, null=True)),
+ ('UserRoleName', models.CharField(max_length=200, null=True)),
+ ('UserGroupId', models.CharField(max_length=200, null=True)),
+ ('UserRoleId', models.CharField(max_length=200, null=True)),
+ ('Mobile', models.CharField(max_length=200, null=True)),
+ ('Email', models.CharField(max_length=200, null=True)),
+ ('UserIsActive', models.BooleanField(null=True)),
+ ('UserIsActiveDescription', models.CharField(max_length=200, null=True)),
+ ('RegDate', models.CharField(max_length=200, null=True)),
+ ('RegDateShamsi', models.CharField(max_length=200, null=True)),
+ ('RegDateShamsiWithTime', models.CharField(max_length=200, null=True)),
+ ('RegDateShamsiOnlyTime', models.CharField(max_length=200, null=True)),
+ ('StringId', models.CharField(max_length=200, null=True)),
+ ('IsPersisted', models.CharField(max_length=200, null=True)),
+ ('AllowInsert', models.CharField(max_length=200, null=True)),
+ ('AllowUpdate', models.CharField(max_length=200, null=True)),
+ ('ModalCss', models.CharField(max_length=200, null=True)),
+ ('GridContainerParametersModel', models.CharField(max_length=200, null=True)),
+ ('MenuUserAccess', models.CharField(max_length=200, null=True)),
+ ('MenuUserAccessId', models.CharField(max_length=200, null=True)),
+ ('LogTableName', models.CharField(max_length=200, null=True)),
+ ('LogTableAlias', models.CharField(max_length=200, null=True)),
+ ('PageTitle', models.CharField(max_length=200, null=True)),
+ ('UnitName', models.CharField(max_length=200, null=True)),
+ ('SystemCode', models.CharField(max_length=200, null=True)),
+ ('TrackingCode', models.CharField(max_length=200, null=True)),
+ ('EpidemiologicCode', models.CharField(max_length=200, null=True)),
+ ('PartIdCode', models.CharField(max_length=200, null=True)),
+ ('PostalCode', models.CharField(max_length=200, null=True)),
+ ('UnitId', models.CharField(max_length=200, null=True)),
+ ('UnitTypeId', models.CharField(max_length=200, null=True)),
+ ('UnitTypeName', models.CharField(max_length=200, null=True)),
+ ('LocationIdProvince', models.CharField(max_length=200, null=True)),
+ ('LocationIdCity', models.CharField(max_length=200, null=True)),
+ ('UnitIsActive', models.CharField(max_length=200, null=True)),
+ ('UnitIsActiveDescription', models.CharField(max_length=200, null=True)),
+ ('PId', models.CharField(max_length=200, null=True)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='PoultryHatching',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('DesCertId', models.CharField(max_length=200, null=True)),
+ ('UnitId', models.CharField(max_length=200, null=True)),
+ ('BroilerFlockRequestId', models.CharField(max_length=200, null=True)),
+ ('RequestCode', models.CharField(max_length=200, null=True)),
+ ('StartDate', models.CharField(max_length=200, null=True)),
+ ('StartDatePersian', models.CharField(max_length=200, null=True)),
+ ('EndDate', models.CharField(max_length=200, null=True)),
+ ('EndDatePersian', models.CharField(max_length=200, null=True)),
+ ('HatchingDate', models.CharField(max_length=200, null=True)),
+ ('HatchingDatePersian', models.CharField(max_length=200, null=True)),
+ ('MaxHatchingDate', models.CharField(max_length=200, null=True)),
+ ('MaxHatchingDatePersian', models.CharField(max_length=200, null=True)),
+ ('HatchingCount', models.CharField(max_length=200, null=True)),
+ ('HatchingCountInBargiri', models.CharField(max_length=200, null=True)),
+ ('HatchingCountInTakhlie', models.CharField(max_length=200, null=True)),
+ ('TrackingCount', models.CharField(max_length=200, null=True)),
+ ('TrackingBargiriCount', models.CharField(max_length=200, null=True)),
+ ('PercentHamlToMojavez', models.CharField(max_length=200, null=True)),
+ ('PercentTakhlieToBargiri', models.CharField(max_length=200, null=True)),
+ ('FlockAgeDay', models.CharField(max_length=200, null=True)),
+ ('PartIdCode', models.CharField(max_length=200, null=True)),
+ ('UnitName', models.CharField(max_length=200, null=True)),
+ ('PostalCode', models.CharField(max_length=200, null=True)),
+ ('EpidemiologicCode', models.CharField(max_length=200, null=True)),
+ ('CapacityFemale', models.CharField(max_length=200, null=True)),
+ ('PersonFullName', models.CharField(max_length=200, null=True)),
+ ('LocationIdProvince', models.CharField(max_length=200, null=True)),
+ ('LocationNameProvince', models.CharField(max_length=200, null=True)),
+ ('LocationIdCity', models.CharField(max_length=200, null=True)),
+ ('LocationNameCity', models.CharField(max_length=200, null=True)),
+ ('Mobile', models.CharField(max_length=200, null=True)),
+ ('HamlMorghMinDate', models.CharField(max_length=200, null=True)),
+ ('HamlMorghTotalCount', models.CharField(max_length=200, null=True)),
+ ('HamlMorghTakhlieCount', models.CharField(max_length=200, null=True)),
+ ('EvacuationCount', models.CharField(max_length=200, null=True)),
+ ('EvacuationCount_1', models.CharField(max_length=200, null=True)),
+ ('EvacuationCount_2', models.CharField(max_length=200, null=True)),
+ ('EvacuationCount_3', models.CharField(max_length=200, null=True)),
+ ('BaseHatchingCount', models.CharField(max_length=200, null=True)),
+ ('PercentMorghToJoojeTotal', models.CharField(max_length=200, null=True)),
+ ('PercentMorghToJoojeTakhlie', models.CharField(max_length=200, null=True)),
+ ('PercentMorghToJoojeTakhlieWithEvacutaion', models.CharField(max_length=200, null=True)),
+ ('PercentNotDeliverd', models.CharField(max_length=200, null=True)),
+ ('PercentDeliveredForSP', models.CharField(max_length=200, null=True)),
+ ('PercentDeliveredForSPNoExclude', models.CharField(max_length=200, null=True)),
+ ('PercentDeliveredWithoutEvac', models.CharField(max_length=200, null=True)),
+ ('DiffHamlThanTakhlieCount', models.CharField(max_length=200, null=True)),
+ ('DiffTakhlieThanHamlCount', models.CharField(max_length=200, null=True)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ('poultry', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='hatching_poultry', to='app.poultry')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/app/migrations/0002_remove_poultry_allowinsert_and_more.py b/app/migrations/0002_remove_poultry_allowinsert_and_more.py
new file mode 100644
index 0000000..feff8ea
--- /dev/null
+++ b/app/migrations/0002_remove_poultry_allowinsert_and_more.py
@@ -0,0 +1,29 @@
+# Generated by Django 4.2.19 on 2025-02-26 11:46
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='poultry',
+ name='AllowInsert',
+ ),
+ migrations.RemoveField(
+ model_name='poultry',
+ name='AllowUpdate',
+ ),
+ migrations.RemoveField(
+ model_name='poultry',
+ name='IsPersisted',
+ ),
+ migrations.RemoveField(
+ model_name='poultry',
+ name='UnitIsActive',
+ ),
+ ]
diff --git a/app/migrations/0003_poultry_allowinsert_poultry_allowupdate_and_more.py b/app/migrations/0003_poultry_allowinsert_poultry_allowupdate_and_more.py
new file mode 100644
index 0000000..7c772be
--- /dev/null
+++ b/app/migrations/0003_poultry_allowinsert_poultry_allowupdate_and_more.py
@@ -0,0 +1,33 @@
+# Generated by Django 4.2.19 on 2025-02-26 11:47
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0002_remove_poultry_allowinsert_and_more'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='poultry',
+ name='AllowInsert',
+ field=models.BooleanField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultry',
+ name='AllowUpdate',
+ field=models.BooleanField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultry',
+ name='IsPersisted',
+ field=models.BooleanField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultry',
+ name='UnitIsActive',
+ field=models.BooleanField(null=True),
+ ),
+ ]
diff --git a/app/migrations/0004_poultryhatching_date_poultryhatching_hatchingage.py b/app/migrations/0004_poultryhatching_date_poultryhatching_hatchingage.py
new file mode 100644
index 0000000..afed89a
--- /dev/null
+++ b/app/migrations/0004_poultryhatching_date_poultryhatching_hatchingage.py
@@ -0,0 +1,23 @@
+# Generated by Django 4.2.19 on 2025-02-27 08:30
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0003_poultry_allowinsert_poultry_allowupdate_and_more'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='Date',
+ field=models.DateTimeField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='HatchingAge',
+ field=models.IntegerField(default=1),
+ ),
+ ]
diff --git a/app/migrations/0005_remove_poultryhatching_basehatchingcount_and_more.py b/app/migrations/0005_remove_poultryhatching_basehatchingcount_and_more.py
new file mode 100644
index 0000000..a4299c8
--- /dev/null
+++ b/app/migrations/0005_remove_poultryhatching_basehatchingcount_and_more.py
@@ -0,0 +1,117 @@
+# Generated by Django 4.2.19 on 2025-02-27 09:04
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0004_poultryhatching_date_poultryhatching_hatchingage'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='BaseHatchingCount',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='BroilerFlockRequestId',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='CapacityFemale',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='DiffHamlThanTakhlieCount',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='DiffTakhlieThanHamlCount',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='EvacuationCount',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='EvacuationCount_1',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='EvacuationCount_2',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='EvacuationCount_3',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='FlockAgeDay',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='HamlMorghTakhlieCount',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='HamlMorghTotalCount',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='HatchingCount',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='HatchingCountInBargiri',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='HatchingCountInTakhlie',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='PercentDeliveredForSP',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='PercentDeliveredForSPNoExclude',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='PercentDeliveredWithoutEvac',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='PercentHamlToMojavez',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='PercentMorghToJoojeTakhlie',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='PercentMorghToJoojeTakhlieWithEvacutaion',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='PercentMorghToJoojeTotal',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='PercentNotDeliverd',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='PercentTakhlieToBargiri',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='TrackingBargiriCount',
+ ),
+ migrations.RemoveField(
+ model_name='poultryhatching',
+ name='TrackingCount',
+ ),
+ ]
diff --git a/app/migrations/0006_poultryhatching_basehatchingcount_and_more.py b/app/migrations/0006_poultryhatching_basehatchingcount_and_more.py
new file mode 100644
index 0000000..89a54d8
--- /dev/null
+++ b/app/migrations/0006_poultryhatching_basehatchingcount_and_more.py
@@ -0,0 +1,143 @@
+# Generated by Django 4.2.19 on 2025-02-27 09:07
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0005_remove_poultryhatching_basehatchingcount_and_more'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='BaseHatchingCount',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='BroilerFlockRequestId',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='CapacityFemale',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='DiffHamlThanTakhlieCount',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='DiffTakhlieThanHamlCount',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='EvacuationCount',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='EvacuationCount_1',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='EvacuationCount_2',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='EvacuationCount_3',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='FlockAgeDay',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='HamlMorghTakhlieCount',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='HamlMorghTotalCount',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='HatchingCount',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='HatchingCountInBargiri',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='HatchingCountInTakhlie',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='PercentDeliveredForSP',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='PercentDeliveredForSPNoExclude',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='PercentDeliveredWithoutEvac',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='PercentHamlToMojavez',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='PercentMorghToJoojeTakhlie',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='PercentMorghToJoojeTakhlieWithEvacutaion',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='PercentMorghToJoojeTotal',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='PercentNotDeliverd',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='PercentTakhlieToBargiri',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='TrackingBargiriCount',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='TrackingCount',
+ field=models.IntegerField(null=True),
+ ),
+ ]
diff --git a/app/migrations/0007_transportingchickendetail.py b/app/migrations/0007_transportingchickendetail.py
new file mode 100644
index 0000000..4e27bf1
--- /dev/null
+++ b/app/migrations/0007_transportingchickendetail.py
@@ -0,0 +1,34 @@
+# Generated by Django 4.2.19 on 2025-02-27 11:07
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0006_poultryhatching_basehatchingcount_and_more'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='TransportingChickenDetail',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('PedigreeName', models.CharField(max_length=200, null=True)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)),
+ ('hatching', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='transporting_hatching', to='app.poultryhatching')),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/app/migrations/0008_transportingchickendetail_certid_and_more.py b/app/migrations/0008_transportingchickendetail_certid_and_more.py
new file mode 100644
index 0000000..b45ce9f
--- /dev/null
+++ b/app/migrations/0008_transportingchickendetail_certid_and_more.py
@@ -0,0 +1,118 @@
+# Generated by Django 4.2.19 on 2025-02-27 11:12
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0007_transportingchickendetail'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='CertId',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='DesCertId',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='GoodAmount',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='GoodCode',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='GoodName',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='IssueDate',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='IssueDatePersian',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='ParentPartIdCode',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='ParentUnitName',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='ResideDate',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='ResideDatePersian',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='SourceCertId',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='SourcePartIdCode',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='SourceUnitName',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='TakhlieDate',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='TakhlieDatePersian',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='TrackingCode',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='TrackingStatus',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='TrackingStatusDescription',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='issue_date',
+ field=models.DateTimeField(null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='reside_date',
+ field=models.DateTimeField(null=True),
+ ),
+ ]
diff --git a/app/migrations/0009_transportingchickendetail_broilerflockrequestid_and_more.py b/app/migrations/0009_transportingchickendetail_broilerflockrequestid_and_more.py
new file mode 100644
index 0000000..236d135
--- /dev/null
+++ b/app/migrations/0009_transportingchickendetail_broilerflockrequestid_and_more.py
@@ -0,0 +1,38 @@
+# Generated by Django 4.2.19 on 2025-02-27 12:21
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0008_transportingchickendetail_certid_and_more'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='BroilerFlockRequestId',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='DesPartIdCode',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='DesUnitName',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='HealthPermitId',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='SourceUnitPartIdCode',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ ]
diff --git a/app/migrations/0010_transportingchickendetail_province_and_more.py b/app/migrations/0010_transportingchickendetail_province_and_more.py
new file mode 100644
index 0000000..6691541
--- /dev/null
+++ b/app/migrations/0010_transportingchickendetail_province_and_more.py
@@ -0,0 +1,22 @@
+# Generated by Django 4.2.19 on 2025-02-28 10:40
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+ dependencies = [
+ ("app", "0009_transportingchickendetail_broilerflockrequestid_and_more"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="transportingchickendetail",
+ name="Province",
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name="transportingchickendetail",
+ name="ProvinceId",
+ field=models.CharField(max_length=200, null=True),
+ ),
+ ]
diff --git a/app/migrations/0011_poultryhatching_pedigreename.py b/app/migrations/0011_poultryhatching_pedigreename.py
new file mode 100644
index 0000000..c4217cc
--- /dev/null
+++ b/app/migrations/0011_poultryhatching_pedigreename.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.19 on 2025-03-01 11:11
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0010_transportingchickendetail_province_and_more'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='PedigreeName',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ ]
diff --git a/app/migrations/0012_transportingchickendetail_age.py b/app/migrations/0012_transportingchickendetail_age.py
new file mode 100644
index 0000000..fd915fd
--- /dev/null
+++ b/app/migrations/0012_transportingchickendetail_age.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.13 on 2025-03-03 12:07
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0011_poultryhatching_pedigreename'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='transportingchickendetail',
+ name='age',
+ field=models.IntegerField(default=1),
+ ),
+ ]
diff --git a/app/migrations/0013_poultryhatching_leftover.py b/app/migrations/0013_poultryhatching_leftover.py
new file mode 100644
index 0000000..7a67a11
--- /dev/null
+++ b/app/migrations/0013_poultryhatching_leftover.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.19 on 2025-03-09 11:52
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0012_transportingchickendetail_age'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='poultryhatching',
+ name='LeftOver',
+ field=models.IntegerField(null=True),
+ ),
+ ]
diff --git a/app/migrations/0014_poultry_locationnamecity_and_more.py b/app/migrations/0014_poultry_locationnamecity_and_more.py
new file mode 100644
index 0000000..f6e810d
--- /dev/null
+++ b/app/migrations/0014_poultry_locationnamecity_and_more.py
@@ -0,0 +1,23 @@
+# Generated by Django 4.2.19 on 2025-03-11 12:25
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0013_poultryhatching_leftover'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='poultry',
+ name='LocationNameCity',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='poultry',
+ name='LocationNameProvince',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ ]
diff --git a/app/migrations/0015_hatching_transportingdetail.py b/app/migrations/0015_hatching_transportingdetail.py
new file mode 100644
index 0000000..7e53fed
--- /dev/null
+++ b/app/migrations/0015_hatching_transportingdetail.py
@@ -0,0 +1,155 @@
+# Generated by Django 4.2.19 on 2025-04-05 13:43
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0014_poultry_locationnamecity_and_more'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Hatching',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('BroilerFlockRequestId', models.IntegerField(blank=True, null=True)),
+ ('InsertDate', models.CharField(blank=True, max_length=200, null=True)),
+ ('LastChangeStatusDate', models.CharField(blank=True, max_length=200, null=True)),
+ ('LastChangeStatusDateShamsi', models.CharField(blank=True, max_length=200, null=True)),
+ ('FlockRequestUnitName', models.CharField(blank=True, max_length=200, null=True)),
+ ('PedigreeName', models.CharField(blank=True, max_length=200, null=True)),
+ ('StatusId', models.IntegerField(blank=True, null=True)),
+ ('Status', models.IntegerField(blank=True, null=True)),
+ ('StatusName', models.CharField(blank=True, max_length=200, null=True)),
+ ('PedigreeType', models.IntegerField(blank=True, null=True)),
+ ('BroilerPedigreeTypeName', models.CharField(blank=True, max_length=200, null=True)),
+ ('StatusColor', models.CharField(blank=True, max_length=200, null=True)),
+ ('SystemRevocationDate', models.CharField(blank=True, max_length=200, null=True)),
+ ('RemindDays', models.IntegerField(blank=True, null=True)),
+ ('PartyCount', models.IntegerField(blank=True, null=True)),
+ ('GoodCount', models.IntegerField(blank=True, null=True)),
+ ('ShowButtons', models.BooleanField(blank=True, null=True)),
+ ('HasSync', models.BooleanField(blank=True, null=True)),
+ ('BroilerFlockRequestExpireStatus', models.IntegerField(blank=True, null=True)),
+ ('IdWithFormat', models.CharField(blank=True, max_length=200, null=True)),
+ ('ProvinceName', models.CharField(blank=True, max_length=200, null=True)),
+ ('CityName', models.CharField(blank=True, max_length=200, null=True)),
+ ('Address', models.CharField(blank=True, max_length=200, null=True)),
+ ('UnitTel', models.CharField(blank=True, max_length=200, null=True)),
+ ('UnitPostalCode', models.CharField(blank=True, max_length=200, null=True)),
+ ('UnitName', models.CharField(blank=True, max_length=200, null=True)),
+ ('SystemCode', models.CharField(blank=True, max_length=200, null=True)),
+ ('CapacityFemale', models.CharField(blank=True, max_length=200, null=True)),
+ ('EpidemiologicCode', models.CharField(blank=True, max_length=200, null=True)),
+ ('RequestCode', models.CharField(blank=True, max_length=200, null=True)),
+ ('RequestDate', models.CharField(blank=True, max_length=200, null=True)),
+ ('RequestDateFa', models.CharField(blank=True, max_length=200, null=True)),
+ ('RequestCount', models.IntegerField(blank=True, null=True)),
+ ('DeliverDate', models.CharField(blank=True, max_length=200, null=True)),
+ ('DeliverDateFa', models.CharField(blank=True, max_length=200, null=True)),
+ ('UnionName', models.CharField(blank=True, max_length=200, null=True)),
+ ('PersonTypeId', models.IntegerField(blank=True, null=True)),
+ ('PersonType', models.IntegerField(blank=True, null=True)),
+ ('PersonTypeName', models.CharField(blank=True, max_length=200, null=True)),
+ ('PersonFullName', models.CharField(blank=True, max_length=200, null=True)),
+ ('NationalCode', models.CharField(blank=True, max_length=200, null=True)),
+ ('InteractType', models.IntegerField(blank=True, null=True)),
+ ('InteractTypeName', models.CharField(blank=True, max_length=200, null=True)),
+ ('UnionTypeId', models.IntegerField(blank=True, null=True)),
+ ('UnionTypeName', models.CharField(blank=True, max_length=200, null=True)),
+ ('SendDate', models.CharField(blank=True, max_length=200, null=True)),
+ ('SendDateFa', models.CharField(blank=True, max_length=200, null=True)),
+ ('ChickCountSum', models.IntegerField(blank=True, null=True)),
+ ('CalculatedDate', models.CharField(blank=True, max_length=200, null=True)),
+ ('CalculatedDateFa', models.CharField(blank=True, max_length=200, null=True)),
+ ('PartIdCode', models.CharField(blank=True, max_length=200, null=True)),
+ ('CertId', models.CharField(blank=True, max_length=200, null=True)),
+ ('StartDate', models.CharField(blank=True, max_length=200, null=True)),
+ ('StartDateFa', models.CharField(blank=True, max_length=200, null=True)),
+ ('EndDate', models.CharField(blank=True, max_length=200, null=True)),
+ ('EndDateFa', models.CharField(blank=True, max_length=200, null=True)),
+ ('RemainCredit', models.IntegerField(blank=True, null=True)),
+ ('StrRemainCredit', models.CharField(blank=True, max_length=200, null=True)),
+ ('ShowStatus', models.CharField(blank=True, max_length=200, null=True)),
+ ('ValidStatus', models.CharField(blank=True, max_length=200, null=True)),
+ ('ValidStatusName', models.CharField(blank=True, max_length=200, null=True)),
+ ('RegDate', models.CharField(blank=True, max_length=200, null=True)),
+ ('RegDateShamsi', models.CharField(blank=True, max_length=200, null=True)),
+ ('RegDateShamsiWithTime', models.CharField(blank=True, max_length=200, null=True)),
+ ('RegDateShamsiOnlyTime', models.CharField(blank=True, max_length=200, null=True)),
+ ('HatchingId', models.CharField(blank=True, max_length=200, null=True)),
+ ('StringId', models.CharField(blank=True, max_length=200, null=True)),
+ ('IsPersisted', models.BooleanField(blank=True, null=True)),
+ ('AllowInsert', models.BooleanField(blank=True, null=True)),
+ ('AllowUpdate', models.BooleanField(blank=True, null=True)),
+ ('ModalCss', models.CharField(blank=True, max_length=200, null=True)),
+ ('GridContainerParametersModel', models.CharField(blank=True, max_length=200, null=True)),
+ ('MenuUserAccess', models.CharField(blank=True, max_length=200, null=True)),
+ ('MenuUserAccessId', models.IntegerField(blank=True, null=True)),
+ ('LogTableName', models.CharField(blank=True, max_length=200, null=True)),
+ ('LogTableAlias', models.CharField(blank=True, max_length=200, null=True)),
+ ('PageTitle', models.CharField(blank=True, max_length=200, null=True)),
+ ('Evacuation', models.IntegerField(default=0)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ('poultry', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='poultry_hatching_poultry', to='app.poultry')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='TransportingDetail',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('BroilerFlockRequestId', models.IntegerField(blank=True, null=True)),
+ ('TrackingCode', models.CharField(max_length=200, null=True)),
+ ('DesCertId', models.CharField(max_length=200, null=True)),
+ ('IssueDate', models.CharField(max_length=200, null=True)),
+ ('IssueDatePersian', models.CharField(max_length=200, null=True)),
+ ('issue_date', models.DateTimeField(null=True)),
+ ('GoodCode', models.IntegerField(null=True)),
+ ('GoodName', models.CharField(max_length=200, null=True)),
+ ('GoodAmount', models.IntegerField(null=True)),
+ ('TrackingStatus', models.IntegerField(null=True)),
+ ('TrackingStatusDescription', models.CharField(max_length=200, null=True)),
+ ('TakhlieDate', models.CharField(max_length=200, null=True)),
+ ('TakhlieDatePersian', models.CharField(max_length=200, null=True)),
+ ('SourcePartIdCode', models.CharField(max_length=200, null=True)),
+ ('SourceUnitPartIdCode', models.CharField(max_length=200, null=True)),
+ ('SourceUnitName', models.CharField(max_length=200, null=True)),
+ ('SourceCertId', models.CharField(max_length=200, null=True)),
+ ('ParentPartIdCode', models.CharField(max_length=200, null=True)),
+ ('ParentUnitName', models.CharField(max_length=200, null=True)),
+ ('CertId', models.CharField(max_length=200, null=True)),
+ ('ResideDate', models.CharField(max_length=200, null=True)),
+ ('ResideDatePersian', models.CharField(max_length=200, null=True)),
+ ('PedigreeName', models.CharField(max_length=200, null=True)),
+ ('DesUnitName', models.CharField(max_length=200, null=True)),
+ ('DesPartIdCode', models.CharField(max_length=200, null=True)),
+ ('HealthPermitId', models.IntegerField(null=True)),
+ ('ProvinceId', models.CharField(max_length=200, null=True)),
+ ('Province', models.CharField(max_length=200, null=True)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)),
+ ('hatching', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='transport_hatching', to='app.hatching')),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/app/migrations/0016_remove_transportingdetail_certid_and_more.py b/app/migrations/0016_remove_transportingdetail_certid_and_more.py
new file mode 100644
index 0000000..29e5400
--- /dev/null
+++ b/app/migrations/0016_remove_transportingdetail_certid_and_more.py
@@ -0,0 +1,68 @@
+# Generated by Django 4.2.19 on 2025-04-06 07:32
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0015_hatching_transportingdetail'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='transportingdetail',
+ name='CertId',
+ ),
+ migrations.RemoveField(
+ model_name='transportingdetail',
+ name='ParentPartIdCode',
+ ),
+ migrations.RemoveField(
+ model_name='transportingdetail',
+ name='ParentUnitName',
+ ),
+ migrations.RemoveField(
+ model_name='transportingdetail',
+ name='ProvinceId',
+ ),
+ migrations.RemoveField(
+ model_name='transportingdetail',
+ name='issue_date',
+ ),
+ migrations.AddField(
+ model_name='hatching',
+ name='Age',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='hatching',
+ name='Date',
+ field=models.DateTimeField(null=True),
+ ),
+ migrations.AddField(
+ model_name='hatching',
+ name='LeftOver',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='hatching',
+ name='Period',
+ field=models.IntegerField(default=1),
+ ),
+ migrations.AddField(
+ model_name='transportingdetail',
+ name='Age',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingdetail',
+ name='City',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingdetail',
+ name='Date',
+ field=models.DateTimeField(null=True),
+ ),
+ ]
diff --git a/app/migrations/0017_hatching_archivedate.py b/app/migrations/0017_hatching_archivedate.py
new file mode 100644
index 0000000..07e5f99
--- /dev/null
+++ b/app/migrations/0017_hatching_archivedate.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.19 on 2025-04-06 10:43
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0016_remove_transportingdetail_certid_and_more'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='hatching',
+ name='ArchiveDate',
+ field=models.DateTimeField(null=True),
+ ),
+ ]
diff --git a/app/migrations/0018_remove_hatching_capacityfemale.py b/app/migrations/0018_remove_hatching_capacityfemale.py
new file mode 100644
index 0000000..07d3e71
--- /dev/null
+++ b/app/migrations/0018_remove_hatching_capacityfemale.py
@@ -0,0 +1,17 @@
+# Generated by Django 4.2.19 on 2025-04-06 11:59
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0017_hatching_archivedate'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='hatching',
+ name='CapacityFemale',
+ ),
+ ]
diff --git a/app/migrations/0019_hatching_capacityfemale.py b/app/migrations/0019_hatching_capacityfemale.py
new file mode 100644
index 0000000..df59fdb
--- /dev/null
+++ b/app/migrations/0019_hatching_capacityfemale.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.19 on 2025-04-06 11:59
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0018_remove_hatching_capacityfemale'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='hatching',
+ name='CapacityFemale',
+ field=models.IntegerField(blank=True, null=True),
+ ),
+ ]
diff --git a/app/migrations/0020_killhouse.py b/app/migrations/0020_killhouse.py
new file mode 100644
index 0000000..ee8e4c5
--- /dev/null
+++ b/app/migrations/0020_killhouse.py
@@ -0,0 +1,36 @@
+# Generated by Django 3.2.13 on 2025-04-07 05:00
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0019_hatching_capacityfemale'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='KillHouse',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('PartIdCode', models.CharField(blank=True, max_length=250, null=True)),
+ ('UnitName', models.CharField(blank=True, max_length=250, null=True)),
+ ('Province', models.CharField(blank=True, max_length=250, null=True)),
+ ('City', models.CharField(blank=True, max_length=250, null=True)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='killhouse_createdby', to=settings.AUTH_USER_MODEL)),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='killhouse_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/app/migrations/0021_hatching_killingave_transportingdetail_out_and_more.py b/app/migrations/0021_hatching_killingave_transportingdetail_out_and_more.py
new file mode 100644
index 0000000..6797856
--- /dev/null
+++ b/app/migrations/0021_hatching_killingave_transportingdetail_out_and_more.py
@@ -0,0 +1,36 @@
+# Generated by Django 4.2.19 on 2025-04-07 06:21
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0020_killhouse'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='hatching',
+ name='KillingAve',
+ field=models.IntegerField(null=True),
+ ),
+ migrations.AddField(
+ model_name='transportingdetail',
+ name='Out',
+ field=models.BooleanField(default=False),
+ ),
+ migrations.AlterField(
+ model_name='killhouse',
+ name='created_by',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AlterField(
+ model_name='killhouse',
+ name='modified_by',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL),
+ ),
+ ]
diff --git a/app/migrations/0022_alter_hatching_killingave.py b/app/migrations/0022_alter_hatching_killingave.py
new file mode 100644
index 0000000..0e5c3ff
--- /dev/null
+++ b/app/migrations/0022_alter_hatching_killingave.py
@@ -0,0 +1,18 @@
+# Generated by Django 4.2.19 on 2025-04-07 06:44
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0021_hatching_killingave_transportingdetail_out_and_more'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='hatching',
+ name='KillingAve',
+ field=models.IntegerField(default=0),
+ ),
+ ]
diff --git a/app/migrations/0023_killhouse_cityid_killhouse_provinceid.py b/app/migrations/0023_killhouse_cityid_killhouse_provinceid.py
new file mode 100644
index 0000000..badf7a1
--- /dev/null
+++ b/app/migrations/0023_killhouse_cityid_killhouse_provinceid.py
@@ -0,0 +1,23 @@
+# Generated by Django 4.2.19 on 2025-04-07 08:34
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0022_alter_hatching_killingave'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='killhouse',
+ name='CityId',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='killhouse',
+ name='ProvinceId',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ ]
diff --git a/app/migrations/0024_poultry_city_poultry_province.py b/app/migrations/0024_poultry_city_poultry_province.py
new file mode 100644
index 0000000..31b3c12
--- /dev/null
+++ b/app/migrations/0024_poultry_city_poultry_province.py
@@ -0,0 +1,23 @@
+# Generated by Django 4.2.19 on 2025-04-07 09:19
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0023_killhouse_cityid_killhouse_provinceid'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='poultry',
+ name='City',
+ field=models.CharField(blank=True, max_length=500, null=True),
+ ),
+ migrations.AddField(
+ model_name='poultry',
+ name='Province',
+ field=models.CharField(blank=True, max_length=500, null=True),
+ ),
+ ]
diff --git a/app/migrations/0025_delete_transportingdetail.py b/app/migrations/0025_delete_transportingdetail.py
new file mode 100644
index 0000000..105d46f
--- /dev/null
+++ b/app/migrations/0025_delete_transportingdetail.py
@@ -0,0 +1,16 @@
+# Generated by Django 3.2.13 on 2025-04-29 12:52
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0024_poultry_city_poultry_province'),
+ ]
+
+ operations = [
+ migrations.DeleteModel(
+ name='TransportingDetail',
+ ),
+ ]
diff --git a/app/migrations/0026_transportingdetail.py b/app/migrations/0026_transportingdetail.py
new file mode 100644
index 0000000..0d40a39
--- /dev/null
+++ b/app/migrations/0026_transportingdetail.py
@@ -0,0 +1,60 @@
+# Generated by Django 3.2.13 on 2025-04-29 12:57
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0025_delete_transportingdetail'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='TransportingDetail',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('BroilerFlockRequestId', models.IntegerField(blank=True, null=True)),
+ ('TrackingCode', models.CharField(max_length=200, null=True)),
+ ('DesCertId', models.CharField(max_length=200, null=True)),
+ ('IssueDate', models.CharField(max_length=200, null=True)),
+ ('IssueDatePersian', models.CharField(max_length=200, null=True)),
+ ('GoodCode', models.IntegerField(null=True)),
+ ('GoodName', models.CharField(max_length=200, null=True)),
+ ('GoodAmount', models.IntegerField(null=True)),
+ ('TrackingStatus', models.IntegerField(null=True)),
+ ('TrackingStatusDescription', models.CharField(max_length=200, null=True)),
+ ('TakhlieDate', models.CharField(max_length=200, null=True)),
+ ('TakhlieDatePersian', models.CharField(max_length=200, null=True)),
+ ('SourcePartIdCode', models.CharField(max_length=200, null=True)),
+ ('SourceUnitPartIdCode', models.CharField(max_length=200, null=True)),
+ ('SourceUnitName', models.CharField(max_length=200, null=True)),
+ ('SourceCertId', models.CharField(max_length=200, null=True)),
+ ('ResideDate', models.CharField(max_length=200, null=True)),
+ ('ResideDatePersian', models.CharField(max_length=200, null=True)),
+ ('PedigreeName', models.CharField(max_length=200, null=True)),
+ ('DesUnitName', models.CharField(max_length=200, null=True)),
+ ('DesPartIdCode', models.CharField(max_length=200, null=True)),
+ ('HealthPermitId', models.IntegerField(null=True)),
+ ('Province', models.CharField(max_length=200, null=True)),
+ ('City', models.CharField(max_length=200, null=True)),
+ ('Age', models.IntegerField(null=True)),
+ ('Date', models.DateTimeField(null=True)),
+ ('Out', models.BooleanField(default=False)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='transportingdetail_createdby', to=settings.AUTH_USER_MODEL)),
+ ('hatching', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='transport_hatching', to='app.hatching')),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='transportingdetail_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/app/migrations/0027_hatching_samasat_discharge_percentage_and_more.py b/app/migrations/0027_hatching_samasat_discharge_percentage_and_more.py
new file mode 100644
index 0000000..d71e494
--- /dev/null
+++ b/app/migrations/0027_hatching_samasat_discharge_percentage_and_more.py
@@ -0,0 +1,31 @@
+# Generated by Django 4.2.19 on 2025-05-06 08:21
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0026_transportingdetail'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='hatching',
+ name='samasat_discharge_percentage',
+ field=models.IntegerField(default=0),
+ ),
+ migrations.AlterField(
+ model_name='transportingdetail',
+ name='created_by',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AlterField(
+ model_name='transportingdetail',
+ name='modified_by',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL),
+ ),
+ ]
diff --git a/app/migrations/0028_hatching_goodsum.py b/app/migrations/0028_hatching_goodsum.py
new file mode 100644
index 0000000..cd028de
--- /dev/null
+++ b/app/migrations/0028_hatching_goodsum.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.13 on 2025-06-30 12:08
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0027_hatching_samasat_discharge_percentage_and_more'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='hatching',
+ name='GoodSum',
+ field=models.IntegerField(default=0),
+ ),
+ ]
diff --git a/app/migrations/0029_apkinfo.py b/app/migrations/0029_apkinfo.py
new file mode 100644
index 0000000..dbec003
--- /dev/null
+++ b/app/migrations/0029_apkinfo.py
@@ -0,0 +1,34 @@
+# Generated by Django 4.2.19 on 2025-07-20 05:32
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0028_hatching_goodsum'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='ApkInfo',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('info', models.JSONField(null=True)),
+ ('download_link', models.CharField(blank=True, max_length=700, null=True)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/app/migrations/0030_transportcarcassdetail.py b/app/migrations/0030_transportcarcassdetail.py
new file mode 100644
index 0000000..3c7e312
--- /dev/null
+++ b/app/migrations/0030_transportcarcassdetail.py
@@ -0,0 +1,52 @@
+# Generated by Django 3.2.13 on 2025-09-21 10:17
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0029_apkinfo'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='TransportCarcassDetail',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('id_quarantineh', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination_prev', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination_changed', models.CharField(blank=True, max_length=255, null=True)),
+ ('payment', models.CharField(blank=True, max_length=255, null=True)),
+ ('tracking', models.CharField(blank=True, max_length=255, null=True)),
+ ('date', models.DateField(blank=True, null=True)),
+ ('time', models.TimeField(blank=True, null=True)),
+ ('product', models.CharField(blank=True, max_length=255, null=True)),
+ ('items', models.CharField(blank=True, max_length=255, null=True)),
+ ('quantity', models.FloatField(blank=True, null=True)),
+ ('unit', models.CharField(blank=True, max_length=50, null=True)),
+ ('origin_province', models.CharField(blank=True, max_length=255, null=True)),
+ ('origin_city', models.CharField(blank=True, max_length=255, null=True)),
+ ('origin', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination_province', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination_city', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination', models.CharField(blank=True, max_length=255, null=True)),
+ ('jihadi_origin', models.CharField(blank=True, max_length=255, null=True)),
+ ('jihadi_destination', models.CharField(blank=True, max_length=255, null=True)),
+ ('owner', models.CharField(blank=True, max_length=255, null=True)),
+ ('car_tracking_code', models.CharField(blank=True, max_length=255, null=True)),
+ ('driver_name', models.CharField(blank=True, max_length=255, null=True)),
+ ('plate', models.CharField(blank=True, max_length=50, null=True)),
+ ('amount', models.DecimalField(blank=True, decimal_places=2, max_digits=20, null=True)),
+ ('unloading', models.CharField(blank=True, max_length=255, null=True)),
+ ('gross_weight', models.FloatField(blank=True, null=True)),
+ ('tare_weight', models.FloatField(blank=True, null=True)),
+ ('net_weight', models.FloatField(blank=True, null=True)),
+ ('scale_code', models.CharField(blank=True, max_length=255, null=True)),
+ ('scale_name', models.CharField(blank=True, max_length=255, null=True)),
+ ('scale_receipt', models.CharField(blank=True, max_length=255, null=True)),
+ ('unloading_date', models.DateField(blank=True, null=True)),
+ ('out', models.BooleanField(default=False)),
+ ],
+ ),
+ ]
diff --git a/app/migrations/0031_auto_20250921_1447.py b/app/migrations/0031_auto_20250921_1447.py
new file mode 100644
index 0000000..e532e43
--- /dev/null
+++ b/app/migrations/0031_auto_20250921_1447.py
@@ -0,0 +1,49 @@
+# Generated by Django 3.2.13 on 2025-09-21 11:17
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0030_transportcarcassdetail'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='transportcarcassdetail',
+ name='create_date',
+ field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
+ preserve_default=False,
+ ),
+ migrations.AddField(
+ model_name='transportcarcassdetail',
+ name='created_by',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='transportcarcassdetail_createdby', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AddField(
+ model_name='transportcarcassdetail',
+ name='key',
+ field=models.UUIDField(default=uuid.uuid4, editable=False, unique=True),
+ ),
+ migrations.AddField(
+ model_name='transportcarcassdetail',
+ name='modified_by',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='transportcarcassdetail_modifiedby', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AddField(
+ model_name='transportcarcassdetail',
+ name='modify_date',
+ field=models.DateTimeField(auto_now=True),
+ ),
+ migrations.AddField(
+ model_name='transportcarcassdetail',
+ name='trash',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/app/migrations/0032_delete_transportcarcassdetail.py b/app/migrations/0032_delete_transportcarcassdetail.py
new file mode 100644
index 0000000..9ca01c0
--- /dev/null
+++ b/app/migrations/0032_delete_transportcarcassdetail.py
@@ -0,0 +1,16 @@
+# Generated by Django 3.2.13 on 2025-09-21 11:18
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0031_auto_20250921_1447'),
+ ]
+
+ operations = [
+ migrations.DeleteModel(
+ name='TransportCarcassDetail',
+ ),
+ ]
diff --git a/app/migrations/0033_transportcarcassdetail.py b/app/migrations/0033_transportcarcassdetail.py
new file mode 100644
index 0000000..7684476
--- /dev/null
+++ b/app/migrations/0033_transportcarcassdetail.py
@@ -0,0 +1,65 @@
+# Generated by Django 3.2.13 on 2025-09-21 11:18
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0032_delete_transportcarcassdetail'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='TransportCarcassDetail',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('id_quarantineh', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination_prev', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination_changed', models.CharField(blank=True, max_length=255, null=True)),
+ ('payment', models.CharField(blank=True, max_length=255, null=True)),
+ ('tracking', models.CharField(blank=True, max_length=255, null=True)),
+ ('date', models.DateField(blank=True, null=True)),
+ ('time', models.TimeField(blank=True, null=True)),
+ ('product', models.CharField(blank=True, max_length=255, null=True)),
+ ('items', models.CharField(blank=True, max_length=255, null=True)),
+ ('quantity', models.FloatField(blank=True, null=True)),
+ ('unit', models.CharField(blank=True, max_length=50, null=True)),
+ ('origin_province', models.CharField(blank=True, max_length=255, null=True)),
+ ('origin_city', models.CharField(blank=True, max_length=255, null=True)),
+ ('origin', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination_province', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination_city', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination', models.CharField(blank=True, max_length=255, null=True)),
+ ('jihadi_origin', models.CharField(blank=True, max_length=255, null=True)),
+ ('jihadi_destination', models.CharField(blank=True, max_length=255, null=True)),
+ ('owner', models.CharField(blank=True, max_length=255, null=True)),
+ ('car_tracking_code', models.CharField(blank=True, max_length=255, null=True)),
+ ('driver_name', models.CharField(blank=True, max_length=255, null=True)),
+ ('plate', models.CharField(blank=True, max_length=50, null=True)),
+ ('amount', models.DecimalField(blank=True, decimal_places=2, max_digits=20, null=True)),
+ ('unloading', models.CharField(blank=True, max_length=255, null=True)),
+ ('gross_weight', models.FloatField(blank=True, null=True)),
+ ('tare_weight', models.FloatField(blank=True, null=True)),
+ ('net_weight', models.FloatField(blank=True, null=True)),
+ ('scale_code', models.CharField(blank=True, max_length=255, null=True)),
+ ('scale_name', models.CharField(blank=True, max_length=255, null=True)),
+ ('scale_receipt', models.CharField(blank=True, max_length=255, null=True)),
+ ('unloading_date', models.DateField(blank=True, null=True)),
+ ('out', models.BooleanField(default=False)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='transportcarcassdetail_createdby', to=settings.AUTH_USER_MODEL)),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='transportcarcassdetail_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/app/migrations/0034_guilds.py b/app/migrations/0034_guilds.py
new file mode 100644
index 0000000..f1ae914
--- /dev/null
+++ b/app/migrations/0034_guilds.py
@@ -0,0 +1,36 @@
+# Generated by Django 3.2.13 on 2025-09-22 10:08
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0033_transportcarcassdetail'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Guilds',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('name', models.CharField(blank=True, max_length=255, null=True)),
+ ('city', models.CharField(blank=True, max_length=255, null=True)),
+ ('province', models.CharField(blank=True, max_length=255, null=True)),
+ ('jihadi_code', models.CharField(blank=True, max_length=255, null=True)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='guilds_createdby', to=settings.AUTH_USER_MODEL)),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='guilds_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/app/migrations/0035_guilds_is_steward.py b/app/migrations/0035_guilds_is_steward.py
new file mode 100644
index 0000000..183026e
--- /dev/null
+++ b/app/migrations/0035_guilds_is_steward.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.13 on 2025-09-22 10:12
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0034_guilds'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='guilds',
+ name='is_steward',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/app/migrations/0036_driver.py b/app/migrations/0036_driver.py
new file mode 100644
index 0000000..15191f0
--- /dev/null
+++ b/app/migrations/0036_driver.py
@@ -0,0 +1,43 @@
+# Generated by Django 3.2.13 on 2025-09-22 12:22
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0035_guilds_is_steward'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Driver',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('car_id', models.CharField(blank=True, max_length=255, null=True)),
+ ('driver_name', models.CharField(blank=True, max_length=255, null=True)),
+ ('owner_name', models.CharField(blank=True, max_length=255, null=True)),
+ ('city', models.CharField(blank=True, max_length=255, null=True)),
+ ('province', models.CharField(blank=True, max_length=255, null=True)),
+ ('pelak', models.CharField(blank=True, max_length=255, null=True)),
+ ('tracking_code', models.CharField(blank=True, max_length=255, null=True)),
+ ('weight', models.IntegerField(blank=True, null=True)),
+ ('car_type', models.CharField(blank=True, max_length=255, null=True)),
+ ('expire_licence_date', models.DateField(blank=True, null=True)),
+ ('health_permit', models.CharField(blank=True, max_length=255, null=True)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='driver_createdby', to=settings.AUTH_USER_MODEL)),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='driver_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/app/migrations/0037_driver_product.py b/app/migrations/0037_driver_product.py
new file mode 100644
index 0000000..7ba9743
--- /dev/null
+++ b/app/migrations/0037_driver_product.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.13 on 2025-09-22 12:24
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0036_driver'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='driver',
+ name='product',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ ]
diff --git a/app/migrations/0038_auto_20250927_0848.py b/app/migrations/0038_auto_20250927_0848.py
new file mode 100644
index 0000000..cf35e63
--- /dev/null
+++ b/app/migrations/0038_auto_20250927_0848.py
@@ -0,0 +1,23 @@
+# Generated by Django 3.2.13 on 2025-09-27 05:18
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0037_driver_product'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='driver',
+ name='kill_house_name',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AddField(
+ model_name='driver',
+ name='part_id_code',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ ]
diff --git a/app/migrations/0039_alter_transportcarcassdetail_tracking.py b/app/migrations/0039_alter_transportcarcassdetail_tracking.py
new file mode 100644
index 0000000..aa47953
--- /dev/null
+++ b/app/migrations/0039_alter_transportcarcassdetail_tracking.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.13 on 2025-09-27 06:40
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0038_auto_20250927_0848'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='transportcarcassdetail',
+ name='tracking',
+ field=models.CharField(blank=True, max_length=255, null=True, unique=True),
+ ),
+ ]
diff --git a/app/migrations/0040_transportcarcassdetail_product_date.py b/app/migrations/0040_transportcarcassdetail_product_date.py
new file mode 100644
index 0000000..59b9270
--- /dev/null
+++ b/app/migrations/0040_transportcarcassdetail_product_date.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.13 on 2025-09-27 07:39
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0039_alter_transportcarcassdetail_tracking'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='transportcarcassdetail',
+ name='product_date',
+ field=models.DateField(blank=True, null=True),
+ ),
+ ]
diff --git a/app/migrations/0041_transportcarcassdetail_has_product_date.py b/app/migrations/0041_transportcarcassdetail_has_product_date.py
new file mode 100644
index 0000000..896571a
--- /dev/null
+++ b/app/migrations/0041_transportcarcassdetail_has_product_date.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.13 on 2025-09-28 08:05
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('app', '0040_transportcarcassdetail_product_date'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='transportcarcassdetail',
+ name='has_product_date',
+ field=models.BooleanField(default=False),
+ ),
+ ]
diff --git a/app/migrations/0042_alter_driver_created_by_alter_driver_modified_by_and_more.py b/app/migrations/0042_alter_driver_created_by_alter_driver_modified_by_and_more.py
new file mode 100644
index 0000000..896569c
--- /dev/null
+++ b/app/migrations/0042_alter_driver_created_by_alter_driver_modified_by_and_more.py
@@ -0,0 +1,64 @@
+# Generated by Django 4.2.19 on 2025-11-04 07:13
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0041_transportcarcassdetail_has_product_date'),
+ ]
+
+ operations = [
+ migrations.AlterField(
+ model_name='driver',
+ name='created_by',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AlterField(
+ model_name='driver',
+ name='modified_by',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AlterField(
+ model_name='guilds',
+ name='created_by',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AlterField(
+ model_name='guilds',
+ name='modified_by',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AlterField(
+ model_name='transportcarcassdetail',
+ name='created_by',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AlterField(
+ model_name='transportcarcassdetail',
+ name='modified_by',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.CreateModel(
+ name='InquiryCredentials',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('data', models.JSONField(blank=True, null=True)),
+ ('numbers', models.JSONField(blank=True, null=True)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/app/migrations/0043_allproductstransport.py b/app/migrations/0043_allproductstransport.py
new file mode 100644
index 0000000..7c4036e
--- /dev/null
+++ b/app/migrations/0043_allproductstransport.py
@@ -0,0 +1,62 @@
+# Generated by Django 4.2.19 on 2025-11-08 08:21
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0042_alter_driver_created_by_alter_driver_modified_by_and_more'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='AllProductsTransport',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('record_id', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination_prev', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination_changed', models.CharField(blank=True, max_length=255, null=True)),
+ ('tracking', models.CharField(blank=True, max_length=255, null=True, unique=True)),
+ ('date', models.DateField(blank=True, null=True)),
+ ('product', models.CharField(blank=True, max_length=255, null=True)),
+ ('items', models.CharField(blank=True, max_length=255, null=True)),
+ ('quantity', models.FloatField(blank=True, null=True)),
+ ('unit', models.CharField(blank=True, max_length=50, null=True)),
+ ('origin_province', models.CharField(blank=True, max_length=255, null=True)),
+ ('origin_city', models.CharField(blank=True, max_length=255, null=True)),
+ ('origin', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination_province', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination_city', models.CharField(blank=True, max_length=255, null=True)),
+ ('destination', models.CharField(blank=True, max_length=255, null=True)),
+ ('jihadi_origin', models.CharField(blank=True, max_length=255, null=True)),
+ ('jihadi_destination', models.CharField(blank=True, max_length=255, null=True)),
+ ('owner', models.CharField(blank=True, max_length=255, null=True)),
+ ('car_tracking_code', models.CharField(blank=True, max_length=255, null=True)),
+ ('driver_name', models.CharField(blank=True, max_length=255, null=True)),
+ ('gross_weight', models.FloatField(blank=True, null=True)),
+ ('tare_weight', models.FloatField(blank=True, null=True)),
+ ('net_weight', models.FloatField(blank=True, null=True)),
+ ('scale_code', models.CharField(blank=True, max_length=255, null=True)),
+ ('scale_name', models.CharField(blank=True, max_length=255, null=True)),
+ ('scale_receipt', models.CharField(blank=True, max_length=255, null=True)),
+ ('unloading_date', models.DateField(blank=True, null=True)),
+ ('unloading', models.CharField(blank=True, max_length=255, null=True)),
+ ('out', models.BooleanField(default=False)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)),
+ ('hatching', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='all_products_transports', to='app.hatching')),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/app/migrations/0044_evacuationdetail.py b/app/migrations/0044_evacuationdetail.py
new file mode 100644
index 0000000..ea0b737
--- /dev/null
+++ b/app/migrations/0044_evacuationdetail.py
@@ -0,0 +1,56 @@
+# Generated by Django 3.2.13 on 2025-11-08 17:33
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0043_allproductstransport'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='EvacuationDetail',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('BroilerFlockRequestId', models.IntegerField(blank=True, null=True)),
+ ('TrackingCode', models.CharField(blank=True, max_length=200, null=True)),
+ ('IssueDate', models.CharField(blank=True, max_length=200, null=True)),
+ ('IssueDatePersian', models.CharField(blank=True, max_length=200, null=True)),
+ ('issue_date', models.DateTimeField(blank=True, null=True)),
+ ('GoodAmount', models.IntegerField(blank=True, null=True)),
+ ('GoodCode', models.IntegerField(blank=True, null=True)),
+ ('GoodName', models.CharField(blank=True, max_length=200, null=True)),
+ ('TrackingStatus', models.IntegerField(blank=True, null=True)),
+ ('TrackingStatusDescription', models.CharField(blank=True, max_length=200, null=True)),
+ ('TakhlieDate', models.CharField(blank=True, max_length=200, null=True)),
+ ('TakhlieDatePersian', models.CharField(blank=True, max_length=200, null=True)),
+ ('takhlie_date', models.DateTimeField(blank=True, null=True)),
+ ('SourceUnitPartIdCode', models.CharField(blank=True, max_length=200, null=True)),
+ ('SourceUnitName', models.CharField(blank=True, max_length=200, null=True)),
+ ('SourceCertId', models.CharField(blank=True, max_length=200, null=True)),
+ ('SourcePartIdCode', models.CharField(blank=True, max_length=200, null=True)),
+ ('DesUnitName', models.CharField(blank=True, max_length=200, null=True)),
+ ('DesPartIdCode', models.CharField(blank=True, max_length=200, null=True)),
+ ('HealthPermitId', models.IntegerField(blank=True, null=True)),
+ ('ResideDate', models.CharField(blank=True, max_length=200, null=True)),
+ ('ResideDatePersian', models.CharField(blank=True, max_length=200, null=True)),
+ ('reside_date', models.DateTimeField(blank=True, null=True)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='evacuationdetail_createdby', to=settings.AUTH_USER_MODEL)),
+ ('hatching', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='evacuation_details', to='app.hatching')),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='evacuationdetail_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/app/migrations/0045_remove_evacuationdetail_broilerflockrequestid_and_more.py b/app/migrations/0045_remove_evacuationdetail_broilerflockrequestid_and_more.py
new file mode 100644
index 0000000..25bd81c
--- /dev/null
+++ b/app/migrations/0045_remove_evacuationdetail_broilerflockrequestid_and_more.py
@@ -0,0 +1,288 @@
+# Generated by Django 4.2.19 on 2025-11-09 05:14
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0044_evacuationdetail'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='BroilerFlockRequestId',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='DesPartIdCode',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='DesUnitName',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='GoodAmount',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='GoodCode',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='GoodName',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='HealthPermitId',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='IssueDate',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='IssueDatePersian',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='ResideDate',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='ResideDatePersian',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='SourceCertId',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='SourcePartIdCode',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='SourceUnitName',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='SourceUnitPartIdCode',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='TakhlieDate',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='TakhlieDatePersian',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='TrackingCode',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='TrackingStatus',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='TrackingStatusDescription',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='issue_date',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='reside_date',
+ ),
+ migrations.RemoveField(
+ model_name='evacuationdetail',
+ name='takhlie_date',
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='AllowInsert',
+ field=models.BooleanField(default=False),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='AllowUpdate',
+ field=models.BooleanField(default=False),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='ErrorCode',
+ field=models.IntegerField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='ExternalId',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='GoodCount',
+ field=models.IntegerField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='GridContainerParametersModel',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='IsDeleted',
+ field=models.BooleanField(default=False),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='IsPersisted',
+ field=models.BooleanField(default=False),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='LogTableAlias',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='LogTableName',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='MenuUserAccess',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='MenuUserAccessId',
+ field=models.IntegerField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='Message',
+ field=models.TextField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='MoDate',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='MoDateShamsi',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='MoEndDay',
+ field=models.IntegerField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='MoReason',
+ field=models.TextField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='MoReportId',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='MoReportSubId',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='MoStartDay',
+ field=models.IntegerField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='ModalCss',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='PageTitle',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='PartIdCode',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='RegDate',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='RegDateShamsi',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='RegDateShamsiOnlyTime',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='RegDateShamsiWithTime',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='ReportDate',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='ReportDateShamsi',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='ReportStatus',
+ field=models.IntegerField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='ReportType',
+ field=models.IntegerField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='ReportTypeString',
+ field=models.CharField(blank=True, max_length=255, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='RequestId',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AddField(
+ model_name='evacuationdetail',
+ name='StringId',
+ field=models.CharField(blank=True, max_length=200, null=True),
+ ),
+ migrations.AlterField(
+ model_name='evacuationdetail',
+ name='created_by',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL),
+ ),
+ migrations.AlterField(
+ model_name='evacuationdetail',
+ name='modified_by',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL),
+ ),
+ ]
diff --git a/app/migrations/0046_rasadyarappinfo.py b/app/migrations/0046_rasadyarappinfo.py
new file mode 100644
index 0000000..7124388
--- /dev/null
+++ b/app/migrations/0046_rasadyarappinfo.py
@@ -0,0 +1,34 @@
+# Generated by Django 4.2.19 on 2025-12-01 10:28
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ('app', '0045_remove_evacuationdetail_broilerflockrequestid_and_more'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='RasadyarAppInfo',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('info', models.JSONField(blank=True, null=True)),
+ ('file', models.CharField(blank=True, max_length=700, null=True)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/app/migrations/__init__.py b/app/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/app/migrations/__pycache__/0001_initial.cpython-310.pyc b/app/migrations/__pycache__/0001_initial.cpython-310.pyc
new file mode 100644
index 0000000..f40744d
Binary files /dev/null and b/app/migrations/__pycache__/0001_initial.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0001_initial.cpython-311.pyc b/app/migrations/__pycache__/0001_initial.cpython-311.pyc
new file mode 100644
index 0000000..0d4aa53
Binary files /dev/null and b/app/migrations/__pycache__/0001_initial.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0001_initial.cpython-312.pyc b/app/migrations/__pycache__/0001_initial.cpython-312.pyc
new file mode 100644
index 0000000..a5ddbdd
Binary files /dev/null and b/app/migrations/__pycache__/0001_initial.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0001_initial.cpython-39.pyc b/app/migrations/__pycache__/0001_initial.cpython-39.pyc
new file mode 100644
index 0000000..d246d10
Binary files /dev/null and b/app/migrations/__pycache__/0001_initial.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0002_remove_poultry_allowinsert_and_more.cpython-310.pyc b/app/migrations/__pycache__/0002_remove_poultry_allowinsert_and_more.cpython-310.pyc
new file mode 100644
index 0000000..d0886f4
Binary files /dev/null and b/app/migrations/__pycache__/0002_remove_poultry_allowinsert_and_more.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0002_remove_poultry_allowinsert_and_more.cpython-311.pyc b/app/migrations/__pycache__/0002_remove_poultry_allowinsert_and_more.cpython-311.pyc
new file mode 100644
index 0000000..65fa09d
Binary files /dev/null and b/app/migrations/__pycache__/0002_remove_poultry_allowinsert_and_more.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0002_remove_poultry_allowinsert_and_more.cpython-312.pyc b/app/migrations/__pycache__/0002_remove_poultry_allowinsert_and_more.cpython-312.pyc
new file mode 100644
index 0000000..5c823a8
Binary files /dev/null and b/app/migrations/__pycache__/0002_remove_poultry_allowinsert_and_more.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0002_remove_poultry_allowinsert_and_more.cpython-39.pyc b/app/migrations/__pycache__/0002_remove_poultry_allowinsert_and_more.cpython-39.pyc
new file mode 100644
index 0000000..f04bc5c
Binary files /dev/null and b/app/migrations/__pycache__/0002_remove_poultry_allowinsert_and_more.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0003_poultry_allowinsert_poultry_allowupdate_and_more.cpython-310.pyc b/app/migrations/__pycache__/0003_poultry_allowinsert_poultry_allowupdate_and_more.cpython-310.pyc
new file mode 100644
index 0000000..072235c
Binary files /dev/null and b/app/migrations/__pycache__/0003_poultry_allowinsert_poultry_allowupdate_and_more.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0003_poultry_allowinsert_poultry_allowupdate_and_more.cpython-311.pyc b/app/migrations/__pycache__/0003_poultry_allowinsert_poultry_allowupdate_and_more.cpython-311.pyc
new file mode 100644
index 0000000..56de2c7
Binary files /dev/null and b/app/migrations/__pycache__/0003_poultry_allowinsert_poultry_allowupdate_and_more.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0003_poultry_allowinsert_poultry_allowupdate_and_more.cpython-312.pyc b/app/migrations/__pycache__/0003_poultry_allowinsert_poultry_allowupdate_and_more.cpython-312.pyc
new file mode 100644
index 0000000..05a9590
Binary files /dev/null and b/app/migrations/__pycache__/0003_poultry_allowinsert_poultry_allowupdate_and_more.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0003_poultry_allowinsert_poultry_allowupdate_and_more.cpython-39.pyc b/app/migrations/__pycache__/0003_poultry_allowinsert_poultry_allowupdate_and_more.cpython-39.pyc
new file mode 100644
index 0000000..ce071be
Binary files /dev/null and b/app/migrations/__pycache__/0003_poultry_allowinsert_poultry_allowupdate_and_more.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0004_poultryhatching_date_poultryhatching_hatchingage.cpython-310.pyc b/app/migrations/__pycache__/0004_poultryhatching_date_poultryhatching_hatchingage.cpython-310.pyc
new file mode 100644
index 0000000..882553a
Binary files /dev/null and b/app/migrations/__pycache__/0004_poultryhatching_date_poultryhatching_hatchingage.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0004_poultryhatching_date_poultryhatching_hatchingage.cpython-311.pyc b/app/migrations/__pycache__/0004_poultryhatching_date_poultryhatching_hatchingage.cpython-311.pyc
new file mode 100644
index 0000000..b7cf54a
Binary files /dev/null and b/app/migrations/__pycache__/0004_poultryhatching_date_poultryhatching_hatchingage.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0004_poultryhatching_date_poultryhatching_hatchingage.cpython-312.pyc b/app/migrations/__pycache__/0004_poultryhatching_date_poultryhatching_hatchingage.cpython-312.pyc
new file mode 100644
index 0000000..cada48f
Binary files /dev/null and b/app/migrations/__pycache__/0004_poultryhatching_date_poultryhatching_hatchingage.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0004_poultryhatching_date_poultryhatching_hatchingage.cpython-39.pyc b/app/migrations/__pycache__/0004_poultryhatching_date_poultryhatching_hatchingage.cpython-39.pyc
new file mode 100644
index 0000000..465473f
Binary files /dev/null and b/app/migrations/__pycache__/0004_poultryhatching_date_poultryhatching_hatchingage.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0005_remove_poultryhatching_basehatchingcount_and_more.cpython-310.pyc b/app/migrations/__pycache__/0005_remove_poultryhatching_basehatchingcount_and_more.cpython-310.pyc
new file mode 100644
index 0000000..2ccb576
Binary files /dev/null and b/app/migrations/__pycache__/0005_remove_poultryhatching_basehatchingcount_and_more.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0005_remove_poultryhatching_basehatchingcount_and_more.cpython-311.pyc b/app/migrations/__pycache__/0005_remove_poultryhatching_basehatchingcount_and_more.cpython-311.pyc
new file mode 100644
index 0000000..0b0d00e
Binary files /dev/null and b/app/migrations/__pycache__/0005_remove_poultryhatching_basehatchingcount_and_more.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0005_remove_poultryhatching_basehatchingcount_and_more.cpython-312.pyc b/app/migrations/__pycache__/0005_remove_poultryhatching_basehatchingcount_and_more.cpython-312.pyc
new file mode 100644
index 0000000..007b94d
Binary files /dev/null and b/app/migrations/__pycache__/0005_remove_poultryhatching_basehatchingcount_and_more.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0005_remove_poultryhatching_basehatchingcount_and_more.cpython-39.pyc b/app/migrations/__pycache__/0005_remove_poultryhatching_basehatchingcount_and_more.cpython-39.pyc
new file mode 100644
index 0000000..0d3148a
Binary files /dev/null and b/app/migrations/__pycache__/0005_remove_poultryhatching_basehatchingcount_and_more.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0006_poultryhatching_basehatchingcount_and_more.cpython-310.pyc b/app/migrations/__pycache__/0006_poultryhatching_basehatchingcount_and_more.cpython-310.pyc
new file mode 100644
index 0000000..7cc6b86
Binary files /dev/null and b/app/migrations/__pycache__/0006_poultryhatching_basehatchingcount_and_more.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0006_poultryhatching_basehatchingcount_and_more.cpython-311.pyc b/app/migrations/__pycache__/0006_poultryhatching_basehatchingcount_and_more.cpython-311.pyc
new file mode 100644
index 0000000..9961fd3
Binary files /dev/null and b/app/migrations/__pycache__/0006_poultryhatching_basehatchingcount_and_more.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0006_poultryhatching_basehatchingcount_and_more.cpython-312.pyc b/app/migrations/__pycache__/0006_poultryhatching_basehatchingcount_and_more.cpython-312.pyc
new file mode 100644
index 0000000..8ddd843
Binary files /dev/null and b/app/migrations/__pycache__/0006_poultryhatching_basehatchingcount_and_more.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0006_poultryhatching_basehatchingcount_and_more.cpython-39.pyc b/app/migrations/__pycache__/0006_poultryhatching_basehatchingcount_and_more.cpython-39.pyc
new file mode 100644
index 0000000..2458098
Binary files /dev/null and b/app/migrations/__pycache__/0006_poultryhatching_basehatchingcount_and_more.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0007_transportingchickendetail.cpython-310.pyc b/app/migrations/__pycache__/0007_transportingchickendetail.cpython-310.pyc
new file mode 100644
index 0000000..3416f3d
Binary files /dev/null and b/app/migrations/__pycache__/0007_transportingchickendetail.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0007_transportingchickendetail.cpython-311.pyc b/app/migrations/__pycache__/0007_transportingchickendetail.cpython-311.pyc
new file mode 100644
index 0000000..0636a53
Binary files /dev/null and b/app/migrations/__pycache__/0007_transportingchickendetail.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0007_transportingchickendetail.cpython-312.pyc b/app/migrations/__pycache__/0007_transportingchickendetail.cpython-312.pyc
new file mode 100644
index 0000000..aa50d21
Binary files /dev/null and b/app/migrations/__pycache__/0007_transportingchickendetail.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0007_transportingchickendetail.cpython-39.pyc b/app/migrations/__pycache__/0007_transportingchickendetail.cpython-39.pyc
new file mode 100644
index 0000000..7556ceb
Binary files /dev/null and b/app/migrations/__pycache__/0007_transportingchickendetail.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0008_transportingchickendetail_certid_and_more.cpython-310.pyc b/app/migrations/__pycache__/0008_transportingchickendetail_certid_and_more.cpython-310.pyc
new file mode 100644
index 0000000..7aaca75
Binary files /dev/null and b/app/migrations/__pycache__/0008_transportingchickendetail_certid_and_more.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0008_transportingchickendetail_certid_and_more.cpython-311.pyc b/app/migrations/__pycache__/0008_transportingchickendetail_certid_and_more.cpython-311.pyc
new file mode 100644
index 0000000..0e9f151
Binary files /dev/null and b/app/migrations/__pycache__/0008_transportingchickendetail_certid_and_more.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0008_transportingchickendetail_certid_and_more.cpython-312.pyc b/app/migrations/__pycache__/0008_transportingchickendetail_certid_and_more.cpython-312.pyc
new file mode 100644
index 0000000..94f1302
Binary files /dev/null and b/app/migrations/__pycache__/0008_transportingchickendetail_certid_and_more.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0008_transportingchickendetail_certid_and_more.cpython-39.pyc b/app/migrations/__pycache__/0008_transportingchickendetail_certid_and_more.cpython-39.pyc
new file mode 100644
index 0000000..2be0291
Binary files /dev/null and b/app/migrations/__pycache__/0008_transportingchickendetail_certid_and_more.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0009_transportingchickendetail_broilerflockrequestid_and_more.cpython-310.pyc b/app/migrations/__pycache__/0009_transportingchickendetail_broilerflockrequestid_and_more.cpython-310.pyc
new file mode 100644
index 0000000..41984aa
Binary files /dev/null and b/app/migrations/__pycache__/0009_transportingchickendetail_broilerflockrequestid_and_more.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0009_transportingchickendetail_broilerflockrequestid_and_more.cpython-311.pyc b/app/migrations/__pycache__/0009_transportingchickendetail_broilerflockrequestid_and_more.cpython-311.pyc
new file mode 100644
index 0000000..18a9ac4
Binary files /dev/null and b/app/migrations/__pycache__/0009_transportingchickendetail_broilerflockrequestid_and_more.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0009_transportingchickendetail_broilerflockrequestid_and_more.cpython-312.pyc b/app/migrations/__pycache__/0009_transportingchickendetail_broilerflockrequestid_and_more.cpython-312.pyc
new file mode 100644
index 0000000..bb9feee
Binary files /dev/null and b/app/migrations/__pycache__/0009_transportingchickendetail_broilerflockrequestid_and_more.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0009_transportingchickendetail_broilerflockrequestid_and_more.cpython-39.pyc b/app/migrations/__pycache__/0009_transportingchickendetail_broilerflockrequestid_and_more.cpython-39.pyc
new file mode 100644
index 0000000..d3703db
Binary files /dev/null and b/app/migrations/__pycache__/0009_transportingchickendetail_broilerflockrequestid_and_more.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0010_transportingchickendetail_province_and_more.cpython-310.pyc b/app/migrations/__pycache__/0010_transportingchickendetail_province_and_more.cpython-310.pyc
new file mode 100644
index 0000000..caa4032
Binary files /dev/null and b/app/migrations/__pycache__/0010_transportingchickendetail_province_and_more.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0010_transportingchickendetail_province_and_more.cpython-311.pyc b/app/migrations/__pycache__/0010_transportingchickendetail_province_and_more.cpython-311.pyc
new file mode 100644
index 0000000..a4fadb5
Binary files /dev/null and b/app/migrations/__pycache__/0010_transportingchickendetail_province_and_more.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0010_transportingchickendetail_province_and_more.cpython-312.pyc b/app/migrations/__pycache__/0010_transportingchickendetail_province_and_more.cpython-312.pyc
new file mode 100644
index 0000000..f2d5bce
Binary files /dev/null and b/app/migrations/__pycache__/0010_transportingchickendetail_province_and_more.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0010_transportingchickendetail_province_and_more.cpython-39.pyc b/app/migrations/__pycache__/0010_transportingchickendetail_province_and_more.cpython-39.pyc
new file mode 100644
index 0000000..c99dc6f
Binary files /dev/null and b/app/migrations/__pycache__/0010_transportingchickendetail_province_and_more.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0011_poultryhatching_pedigreename.cpython-310.pyc b/app/migrations/__pycache__/0011_poultryhatching_pedigreename.cpython-310.pyc
new file mode 100644
index 0000000..764fe0a
Binary files /dev/null and b/app/migrations/__pycache__/0011_poultryhatching_pedigreename.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0011_poultryhatching_pedigreename.cpython-311.pyc b/app/migrations/__pycache__/0011_poultryhatching_pedigreename.cpython-311.pyc
new file mode 100644
index 0000000..f3354e4
Binary files /dev/null and b/app/migrations/__pycache__/0011_poultryhatching_pedigreename.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0011_poultryhatching_pedigreename.cpython-312.pyc b/app/migrations/__pycache__/0011_poultryhatching_pedigreename.cpython-312.pyc
new file mode 100644
index 0000000..2595756
Binary files /dev/null and b/app/migrations/__pycache__/0011_poultryhatching_pedigreename.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0011_poultryhatching_pedigreename.cpython-39.pyc b/app/migrations/__pycache__/0011_poultryhatching_pedigreename.cpython-39.pyc
new file mode 100644
index 0000000..c1fdae0
Binary files /dev/null and b/app/migrations/__pycache__/0011_poultryhatching_pedigreename.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0012_transportingchickendetail_age.cpython-310.pyc b/app/migrations/__pycache__/0012_transportingchickendetail_age.cpython-310.pyc
new file mode 100644
index 0000000..8225cec
Binary files /dev/null and b/app/migrations/__pycache__/0012_transportingchickendetail_age.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0012_transportingchickendetail_age.cpython-311.pyc b/app/migrations/__pycache__/0012_transportingchickendetail_age.cpython-311.pyc
new file mode 100644
index 0000000..81ebc32
Binary files /dev/null and b/app/migrations/__pycache__/0012_transportingchickendetail_age.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0012_transportingchickendetail_age.cpython-312.pyc b/app/migrations/__pycache__/0012_transportingchickendetail_age.cpython-312.pyc
new file mode 100644
index 0000000..1e4e0ac
Binary files /dev/null and b/app/migrations/__pycache__/0012_transportingchickendetail_age.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0012_transportingchickendetail_age.cpython-39.pyc b/app/migrations/__pycache__/0012_transportingchickendetail_age.cpython-39.pyc
new file mode 100644
index 0000000..9c3ec5f
Binary files /dev/null and b/app/migrations/__pycache__/0012_transportingchickendetail_age.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0013_poultryhatching_leftover.cpython-310.pyc b/app/migrations/__pycache__/0013_poultryhatching_leftover.cpython-310.pyc
new file mode 100644
index 0000000..7309b48
Binary files /dev/null and b/app/migrations/__pycache__/0013_poultryhatching_leftover.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0013_poultryhatching_leftover.cpython-311.pyc b/app/migrations/__pycache__/0013_poultryhatching_leftover.cpython-311.pyc
new file mode 100644
index 0000000..f366dfd
Binary files /dev/null and b/app/migrations/__pycache__/0013_poultryhatching_leftover.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0013_poultryhatching_leftover.cpython-312.pyc b/app/migrations/__pycache__/0013_poultryhatching_leftover.cpython-312.pyc
new file mode 100644
index 0000000..2cb2c8a
Binary files /dev/null and b/app/migrations/__pycache__/0013_poultryhatching_leftover.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0013_poultryhatching_leftover.cpython-39.pyc b/app/migrations/__pycache__/0013_poultryhatching_leftover.cpython-39.pyc
new file mode 100644
index 0000000..9d73eb8
Binary files /dev/null and b/app/migrations/__pycache__/0013_poultryhatching_leftover.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0014_poultry_locationnamecity_and_more.cpython-310.pyc b/app/migrations/__pycache__/0014_poultry_locationnamecity_and_more.cpython-310.pyc
new file mode 100644
index 0000000..6fc8aa1
Binary files /dev/null and b/app/migrations/__pycache__/0014_poultry_locationnamecity_and_more.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0014_poultry_locationnamecity_and_more.cpython-311.pyc b/app/migrations/__pycache__/0014_poultry_locationnamecity_and_more.cpython-311.pyc
new file mode 100644
index 0000000..54d5fdc
Binary files /dev/null and b/app/migrations/__pycache__/0014_poultry_locationnamecity_and_more.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0014_poultry_locationnamecity_and_more.cpython-312.pyc b/app/migrations/__pycache__/0014_poultry_locationnamecity_and_more.cpython-312.pyc
new file mode 100644
index 0000000..ede32fd
Binary files /dev/null and b/app/migrations/__pycache__/0014_poultry_locationnamecity_and_more.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0014_poultry_locationnamecity_and_more.cpython-39.pyc b/app/migrations/__pycache__/0014_poultry_locationnamecity_and_more.cpython-39.pyc
new file mode 100644
index 0000000..fcab7dd
Binary files /dev/null and b/app/migrations/__pycache__/0014_poultry_locationnamecity_and_more.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0015_hatching_transportingdetail.cpython-310.pyc b/app/migrations/__pycache__/0015_hatching_transportingdetail.cpython-310.pyc
new file mode 100644
index 0000000..57b479f
Binary files /dev/null and b/app/migrations/__pycache__/0015_hatching_transportingdetail.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0015_hatching_transportingdetail.cpython-311.pyc b/app/migrations/__pycache__/0015_hatching_transportingdetail.cpython-311.pyc
new file mode 100644
index 0000000..72635c1
Binary files /dev/null and b/app/migrations/__pycache__/0015_hatching_transportingdetail.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0015_hatching_transportingdetail.cpython-312.pyc b/app/migrations/__pycache__/0015_hatching_transportingdetail.cpython-312.pyc
new file mode 100644
index 0000000..a182fbd
Binary files /dev/null and b/app/migrations/__pycache__/0015_hatching_transportingdetail.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0015_hatching_transportingdetail.cpython-39.pyc b/app/migrations/__pycache__/0015_hatching_transportingdetail.cpython-39.pyc
new file mode 100644
index 0000000..2425708
Binary files /dev/null and b/app/migrations/__pycache__/0015_hatching_transportingdetail.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0016_remove_transportingdetail_certid_and_more.cpython-310.pyc b/app/migrations/__pycache__/0016_remove_transportingdetail_certid_and_more.cpython-310.pyc
new file mode 100644
index 0000000..da051ee
Binary files /dev/null and b/app/migrations/__pycache__/0016_remove_transportingdetail_certid_and_more.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0016_remove_transportingdetail_certid_and_more.cpython-311.pyc b/app/migrations/__pycache__/0016_remove_transportingdetail_certid_and_more.cpython-311.pyc
new file mode 100644
index 0000000..877bf34
Binary files /dev/null and b/app/migrations/__pycache__/0016_remove_transportingdetail_certid_and_more.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0016_remove_transportingdetail_certid_and_more.cpython-312.pyc b/app/migrations/__pycache__/0016_remove_transportingdetail_certid_and_more.cpython-312.pyc
new file mode 100644
index 0000000..9c0435b
Binary files /dev/null and b/app/migrations/__pycache__/0016_remove_transportingdetail_certid_and_more.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0016_remove_transportingdetail_certid_and_more.cpython-39.pyc b/app/migrations/__pycache__/0016_remove_transportingdetail_certid_and_more.cpython-39.pyc
new file mode 100644
index 0000000..35bf1dd
Binary files /dev/null and b/app/migrations/__pycache__/0016_remove_transportingdetail_certid_and_more.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0017_hatching_archivedate.cpython-310.pyc b/app/migrations/__pycache__/0017_hatching_archivedate.cpython-310.pyc
new file mode 100644
index 0000000..bd27853
Binary files /dev/null and b/app/migrations/__pycache__/0017_hatching_archivedate.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0017_hatching_archivedate.cpython-311.pyc b/app/migrations/__pycache__/0017_hatching_archivedate.cpython-311.pyc
new file mode 100644
index 0000000..2f8b427
Binary files /dev/null and b/app/migrations/__pycache__/0017_hatching_archivedate.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0017_hatching_archivedate.cpython-312.pyc b/app/migrations/__pycache__/0017_hatching_archivedate.cpython-312.pyc
new file mode 100644
index 0000000..41413e3
Binary files /dev/null and b/app/migrations/__pycache__/0017_hatching_archivedate.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0017_hatching_archivedate.cpython-39.pyc b/app/migrations/__pycache__/0017_hatching_archivedate.cpython-39.pyc
new file mode 100644
index 0000000..2b642b1
Binary files /dev/null and b/app/migrations/__pycache__/0017_hatching_archivedate.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0018_remove_hatching_capacityfemale.cpython-310.pyc b/app/migrations/__pycache__/0018_remove_hatching_capacityfemale.cpython-310.pyc
new file mode 100644
index 0000000..391229e
Binary files /dev/null and b/app/migrations/__pycache__/0018_remove_hatching_capacityfemale.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0018_remove_hatching_capacityfemale.cpython-311.pyc b/app/migrations/__pycache__/0018_remove_hatching_capacityfemale.cpython-311.pyc
new file mode 100644
index 0000000..fa41064
Binary files /dev/null and b/app/migrations/__pycache__/0018_remove_hatching_capacityfemale.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0018_remove_hatching_capacityfemale.cpython-312.pyc b/app/migrations/__pycache__/0018_remove_hatching_capacityfemale.cpython-312.pyc
new file mode 100644
index 0000000..be72a4f
Binary files /dev/null and b/app/migrations/__pycache__/0018_remove_hatching_capacityfemale.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0018_remove_hatching_capacityfemale.cpython-39.pyc b/app/migrations/__pycache__/0018_remove_hatching_capacityfemale.cpython-39.pyc
new file mode 100644
index 0000000..8d7a091
Binary files /dev/null and b/app/migrations/__pycache__/0018_remove_hatching_capacityfemale.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0019_hatching_capacityfemale.cpython-310.pyc b/app/migrations/__pycache__/0019_hatching_capacityfemale.cpython-310.pyc
new file mode 100644
index 0000000..9728a30
Binary files /dev/null and b/app/migrations/__pycache__/0019_hatching_capacityfemale.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0019_hatching_capacityfemale.cpython-311.pyc b/app/migrations/__pycache__/0019_hatching_capacityfemale.cpython-311.pyc
new file mode 100644
index 0000000..e202048
Binary files /dev/null and b/app/migrations/__pycache__/0019_hatching_capacityfemale.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0019_hatching_capacityfemale.cpython-312.pyc b/app/migrations/__pycache__/0019_hatching_capacityfemale.cpython-312.pyc
new file mode 100644
index 0000000..a832a59
Binary files /dev/null and b/app/migrations/__pycache__/0019_hatching_capacityfemale.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0019_hatching_capacityfemale.cpython-39.pyc b/app/migrations/__pycache__/0019_hatching_capacityfemale.cpython-39.pyc
new file mode 100644
index 0000000..9167677
Binary files /dev/null and b/app/migrations/__pycache__/0019_hatching_capacityfemale.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0020_killhouse.cpython-310.pyc b/app/migrations/__pycache__/0020_killhouse.cpython-310.pyc
new file mode 100644
index 0000000..3969642
Binary files /dev/null and b/app/migrations/__pycache__/0020_killhouse.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0020_killhouse.cpython-311.pyc b/app/migrations/__pycache__/0020_killhouse.cpython-311.pyc
new file mode 100644
index 0000000..02533d3
Binary files /dev/null and b/app/migrations/__pycache__/0020_killhouse.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0020_killhouse.cpython-312.pyc b/app/migrations/__pycache__/0020_killhouse.cpython-312.pyc
new file mode 100644
index 0000000..806a0f7
Binary files /dev/null and b/app/migrations/__pycache__/0020_killhouse.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0020_killhouse.cpython-39.pyc b/app/migrations/__pycache__/0020_killhouse.cpython-39.pyc
new file mode 100644
index 0000000..b23307d
Binary files /dev/null and b/app/migrations/__pycache__/0020_killhouse.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0021_hatching_killingave_transportingdetail_out_and_more.cpython-310.pyc b/app/migrations/__pycache__/0021_hatching_killingave_transportingdetail_out_and_more.cpython-310.pyc
new file mode 100644
index 0000000..a9e0a63
Binary files /dev/null and b/app/migrations/__pycache__/0021_hatching_killingave_transportingdetail_out_and_more.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0021_hatching_killingave_transportingdetail_out_and_more.cpython-311.pyc b/app/migrations/__pycache__/0021_hatching_killingave_transportingdetail_out_and_more.cpython-311.pyc
new file mode 100644
index 0000000..8604381
Binary files /dev/null and b/app/migrations/__pycache__/0021_hatching_killingave_transportingdetail_out_and_more.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0021_hatching_killingave_transportingdetail_out_and_more.cpython-312.pyc b/app/migrations/__pycache__/0021_hatching_killingave_transportingdetail_out_and_more.cpython-312.pyc
new file mode 100644
index 0000000..358084b
Binary files /dev/null and b/app/migrations/__pycache__/0021_hatching_killingave_transportingdetail_out_and_more.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0021_hatching_killingave_transportingdetail_out_and_more.cpython-39.pyc b/app/migrations/__pycache__/0021_hatching_killingave_transportingdetail_out_and_more.cpython-39.pyc
new file mode 100644
index 0000000..ea6b3a5
Binary files /dev/null and b/app/migrations/__pycache__/0021_hatching_killingave_transportingdetail_out_and_more.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0022_alter_hatching_killingave.cpython-310.pyc b/app/migrations/__pycache__/0022_alter_hatching_killingave.cpython-310.pyc
new file mode 100644
index 0000000..5470cae
Binary files /dev/null and b/app/migrations/__pycache__/0022_alter_hatching_killingave.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0022_alter_hatching_killingave.cpython-311.pyc b/app/migrations/__pycache__/0022_alter_hatching_killingave.cpython-311.pyc
new file mode 100644
index 0000000..36a19aa
Binary files /dev/null and b/app/migrations/__pycache__/0022_alter_hatching_killingave.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0022_alter_hatching_killingave.cpython-312.pyc b/app/migrations/__pycache__/0022_alter_hatching_killingave.cpython-312.pyc
new file mode 100644
index 0000000..4dce39a
Binary files /dev/null and b/app/migrations/__pycache__/0022_alter_hatching_killingave.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0022_alter_hatching_killingave.cpython-39.pyc b/app/migrations/__pycache__/0022_alter_hatching_killingave.cpython-39.pyc
new file mode 100644
index 0000000..12c21ef
Binary files /dev/null and b/app/migrations/__pycache__/0022_alter_hatching_killingave.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0023_killhouse_cityid_killhouse_provinceid.cpython-310.pyc b/app/migrations/__pycache__/0023_killhouse_cityid_killhouse_provinceid.cpython-310.pyc
new file mode 100644
index 0000000..c2f94a3
Binary files /dev/null and b/app/migrations/__pycache__/0023_killhouse_cityid_killhouse_provinceid.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0023_killhouse_cityid_killhouse_provinceid.cpython-311.pyc b/app/migrations/__pycache__/0023_killhouse_cityid_killhouse_provinceid.cpython-311.pyc
new file mode 100644
index 0000000..f1e1e5b
Binary files /dev/null and b/app/migrations/__pycache__/0023_killhouse_cityid_killhouse_provinceid.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0023_killhouse_cityid_killhouse_provinceid.cpython-312.pyc b/app/migrations/__pycache__/0023_killhouse_cityid_killhouse_provinceid.cpython-312.pyc
new file mode 100644
index 0000000..9899198
Binary files /dev/null and b/app/migrations/__pycache__/0023_killhouse_cityid_killhouse_provinceid.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0023_killhouse_cityid_killhouse_provinceid.cpython-39.pyc b/app/migrations/__pycache__/0023_killhouse_cityid_killhouse_provinceid.cpython-39.pyc
new file mode 100644
index 0000000..633af08
Binary files /dev/null and b/app/migrations/__pycache__/0023_killhouse_cityid_killhouse_provinceid.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0024_poultry_city_poultry_province.cpython-310.pyc b/app/migrations/__pycache__/0024_poultry_city_poultry_province.cpython-310.pyc
new file mode 100644
index 0000000..f55b1ef
Binary files /dev/null and b/app/migrations/__pycache__/0024_poultry_city_poultry_province.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/0024_poultry_city_poultry_province.cpython-311.pyc b/app/migrations/__pycache__/0024_poultry_city_poultry_province.cpython-311.pyc
new file mode 100644
index 0000000..dbde0a5
Binary files /dev/null and b/app/migrations/__pycache__/0024_poultry_city_poultry_province.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0024_poultry_city_poultry_province.cpython-312.pyc b/app/migrations/__pycache__/0024_poultry_city_poultry_province.cpython-312.pyc
new file mode 100644
index 0000000..4426600
Binary files /dev/null and b/app/migrations/__pycache__/0024_poultry_city_poultry_province.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0024_poultry_city_poultry_province.cpython-39.pyc b/app/migrations/__pycache__/0024_poultry_city_poultry_province.cpython-39.pyc
new file mode 100644
index 0000000..f083241
Binary files /dev/null and b/app/migrations/__pycache__/0024_poultry_city_poultry_province.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0025_delete_transportingdetail.cpython-311.pyc b/app/migrations/__pycache__/0025_delete_transportingdetail.cpython-311.pyc
new file mode 100644
index 0000000..70a8c97
Binary files /dev/null and b/app/migrations/__pycache__/0025_delete_transportingdetail.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0025_delete_transportingdetail.cpython-312.pyc b/app/migrations/__pycache__/0025_delete_transportingdetail.cpython-312.pyc
new file mode 100644
index 0000000..c88e24b
Binary files /dev/null and b/app/migrations/__pycache__/0025_delete_transportingdetail.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0025_delete_transportingdetail.cpython-39.pyc b/app/migrations/__pycache__/0025_delete_transportingdetail.cpython-39.pyc
new file mode 100644
index 0000000..33cc70b
Binary files /dev/null and b/app/migrations/__pycache__/0025_delete_transportingdetail.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0026_transportingdetail.cpython-311.pyc b/app/migrations/__pycache__/0026_transportingdetail.cpython-311.pyc
new file mode 100644
index 0000000..7de3a21
Binary files /dev/null and b/app/migrations/__pycache__/0026_transportingdetail.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0026_transportingdetail.cpython-312.pyc b/app/migrations/__pycache__/0026_transportingdetail.cpython-312.pyc
new file mode 100644
index 0000000..662eb62
Binary files /dev/null and b/app/migrations/__pycache__/0026_transportingdetail.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0026_transportingdetail.cpython-39.pyc b/app/migrations/__pycache__/0026_transportingdetail.cpython-39.pyc
new file mode 100644
index 0000000..750e146
Binary files /dev/null and b/app/migrations/__pycache__/0026_transportingdetail.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0027_hatching_samasat_discharge_percentage_and_more.cpython-311.pyc b/app/migrations/__pycache__/0027_hatching_samasat_discharge_percentage_and_more.cpython-311.pyc
new file mode 100644
index 0000000..9f80044
Binary files /dev/null and b/app/migrations/__pycache__/0027_hatching_samasat_discharge_percentage_and_more.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0027_hatching_samasat_discharge_percentage_and_more.cpython-312.pyc b/app/migrations/__pycache__/0027_hatching_samasat_discharge_percentage_and_more.cpython-312.pyc
new file mode 100644
index 0000000..b75b06e
Binary files /dev/null and b/app/migrations/__pycache__/0027_hatching_samasat_discharge_percentage_and_more.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0027_hatching_samasat_discharge_percentage_and_more.cpython-39.pyc b/app/migrations/__pycache__/0027_hatching_samasat_discharge_percentage_and_more.cpython-39.pyc
new file mode 100644
index 0000000..c92ee79
Binary files /dev/null and b/app/migrations/__pycache__/0027_hatching_samasat_discharge_percentage_and_more.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0028_hatching_goodsum.cpython-311.pyc b/app/migrations/__pycache__/0028_hatching_goodsum.cpython-311.pyc
new file mode 100644
index 0000000..05af976
Binary files /dev/null and b/app/migrations/__pycache__/0028_hatching_goodsum.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0028_hatching_goodsum.cpython-312.pyc b/app/migrations/__pycache__/0028_hatching_goodsum.cpython-312.pyc
new file mode 100644
index 0000000..0dbf14a
Binary files /dev/null and b/app/migrations/__pycache__/0028_hatching_goodsum.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0028_hatching_goodsum.cpython-39.pyc b/app/migrations/__pycache__/0028_hatching_goodsum.cpython-39.pyc
new file mode 100644
index 0000000..1bfa0e4
Binary files /dev/null and b/app/migrations/__pycache__/0028_hatching_goodsum.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0029_apkinfo.cpython-311.pyc b/app/migrations/__pycache__/0029_apkinfo.cpython-311.pyc
new file mode 100644
index 0000000..d0bdaaa
Binary files /dev/null and b/app/migrations/__pycache__/0029_apkinfo.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/0029_apkinfo.cpython-312.pyc b/app/migrations/__pycache__/0029_apkinfo.cpython-312.pyc
new file mode 100644
index 0000000..13e1131
Binary files /dev/null and b/app/migrations/__pycache__/0029_apkinfo.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0029_apkinfo.cpython-39.pyc b/app/migrations/__pycache__/0029_apkinfo.cpython-39.pyc
new file mode 100644
index 0000000..e0a22d6
Binary files /dev/null and b/app/migrations/__pycache__/0029_apkinfo.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0030_transportcarcassdetail.cpython-312.pyc b/app/migrations/__pycache__/0030_transportcarcassdetail.cpython-312.pyc
new file mode 100644
index 0000000..262fa7d
Binary files /dev/null and b/app/migrations/__pycache__/0030_transportcarcassdetail.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0030_transportcarcassdetail.cpython-39.pyc b/app/migrations/__pycache__/0030_transportcarcassdetail.cpython-39.pyc
new file mode 100644
index 0000000..ff1c79a
Binary files /dev/null and b/app/migrations/__pycache__/0030_transportcarcassdetail.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0031_auto_20250921_1447.cpython-312.pyc b/app/migrations/__pycache__/0031_auto_20250921_1447.cpython-312.pyc
new file mode 100644
index 0000000..f48b274
Binary files /dev/null and b/app/migrations/__pycache__/0031_auto_20250921_1447.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0031_auto_20250921_1447.cpython-39.pyc b/app/migrations/__pycache__/0031_auto_20250921_1447.cpython-39.pyc
new file mode 100644
index 0000000..ef11270
Binary files /dev/null and b/app/migrations/__pycache__/0031_auto_20250921_1447.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0032_delete_transportcarcassdetail.cpython-312.pyc b/app/migrations/__pycache__/0032_delete_transportcarcassdetail.cpython-312.pyc
new file mode 100644
index 0000000..667865e
Binary files /dev/null and b/app/migrations/__pycache__/0032_delete_transportcarcassdetail.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0032_delete_transportcarcassdetail.cpython-39.pyc b/app/migrations/__pycache__/0032_delete_transportcarcassdetail.cpython-39.pyc
new file mode 100644
index 0000000..a3e1794
Binary files /dev/null and b/app/migrations/__pycache__/0032_delete_transportcarcassdetail.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0033_transportcarcassdetail.cpython-312.pyc b/app/migrations/__pycache__/0033_transportcarcassdetail.cpython-312.pyc
new file mode 100644
index 0000000..13b0136
Binary files /dev/null and b/app/migrations/__pycache__/0033_transportcarcassdetail.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0033_transportcarcassdetail.cpython-39.pyc b/app/migrations/__pycache__/0033_transportcarcassdetail.cpython-39.pyc
new file mode 100644
index 0000000..ff7a798
Binary files /dev/null and b/app/migrations/__pycache__/0033_transportcarcassdetail.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0034_guilds.cpython-312.pyc b/app/migrations/__pycache__/0034_guilds.cpython-312.pyc
new file mode 100644
index 0000000..f06f7a4
Binary files /dev/null and b/app/migrations/__pycache__/0034_guilds.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0034_guilds.cpython-39.pyc b/app/migrations/__pycache__/0034_guilds.cpython-39.pyc
new file mode 100644
index 0000000..f73e0a0
Binary files /dev/null and b/app/migrations/__pycache__/0034_guilds.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0035_guilds_is_steward.cpython-312.pyc b/app/migrations/__pycache__/0035_guilds_is_steward.cpython-312.pyc
new file mode 100644
index 0000000..f2af498
Binary files /dev/null and b/app/migrations/__pycache__/0035_guilds_is_steward.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0035_guilds_is_steward.cpython-39.pyc b/app/migrations/__pycache__/0035_guilds_is_steward.cpython-39.pyc
new file mode 100644
index 0000000..5658ec0
Binary files /dev/null and b/app/migrations/__pycache__/0035_guilds_is_steward.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0036_driver.cpython-312.pyc b/app/migrations/__pycache__/0036_driver.cpython-312.pyc
new file mode 100644
index 0000000..7aeeb52
Binary files /dev/null and b/app/migrations/__pycache__/0036_driver.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0036_driver.cpython-39.pyc b/app/migrations/__pycache__/0036_driver.cpython-39.pyc
new file mode 100644
index 0000000..85ca016
Binary files /dev/null and b/app/migrations/__pycache__/0036_driver.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0037_driver_product.cpython-312.pyc b/app/migrations/__pycache__/0037_driver_product.cpython-312.pyc
new file mode 100644
index 0000000..03d4cfd
Binary files /dev/null and b/app/migrations/__pycache__/0037_driver_product.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0037_driver_product.cpython-39.pyc b/app/migrations/__pycache__/0037_driver_product.cpython-39.pyc
new file mode 100644
index 0000000..c79e1a2
Binary files /dev/null and b/app/migrations/__pycache__/0037_driver_product.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0038_auto_20250927_0848.cpython-312.pyc b/app/migrations/__pycache__/0038_auto_20250927_0848.cpython-312.pyc
new file mode 100644
index 0000000..8b19b96
Binary files /dev/null and b/app/migrations/__pycache__/0038_auto_20250927_0848.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0038_auto_20250927_0848.cpython-39.pyc b/app/migrations/__pycache__/0038_auto_20250927_0848.cpython-39.pyc
new file mode 100644
index 0000000..4b72ad0
Binary files /dev/null and b/app/migrations/__pycache__/0038_auto_20250927_0848.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0039_alter_transportcarcassdetail_tracking.cpython-312.pyc b/app/migrations/__pycache__/0039_alter_transportcarcassdetail_tracking.cpython-312.pyc
new file mode 100644
index 0000000..b026427
Binary files /dev/null and b/app/migrations/__pycache__/0039_alter_transportcarcassdetail_tracking.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0039_alter_transportcarcassdetail_tracking.cpython-39.pyc b/app/migrations/__pycache__/0039_alter_transportcarcassdetail_tracking.cpython-39.pyc
new file mode 100644
index 0000000..76bad83
Binary files /dev/null and b/app/migrations/__pycache__/0039_alter_transportcarcassdetail_tracking.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0040_transportcarcassdetail_product_date.cpython-312.pyc b/app/migrations/__pycache__/0040_transportcarcassdetail_product_date.cpython-312.pyc
new file mode 100644
index 0000000..f7444ed
Binary files /dev/null and b/app/migrations/__pycache__/0040_transportcarcassdetail_product_date.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0040_transportcarcassdetail_product_date.cpython-39.pyc b/app/migrations/__pycache__/0040_transportcarcassdetail_product_date.cpython-39.pyc
new file mode 100644
index 0000000..b823613
Binary files /dev/null and b/app/migrations/__pycache__/0040_transportcarcassdetail_product_date.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0041_transportcarcassdetail_has_product_date.cpython-312.pyc b/app/migrations/__pycache__/0041_transportcarcassdetail_has_product_date.cpython-312.pyc
new file mode 100644
index 0000000..15aa539
Binary files /dev/null and b/app/migrations/__pycache__/0041_transportcarcassdetail_has_product_date.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0041_transportcarcassdetail_has_product_date.cpython-39.pyc b/app/migrations/__pycache__/0041_transportcarcassdetail_has_product_date.cpython-39.pyc
new file mode 100644
index 0000000..616e0a8
Binary files /dev/null and b/app/migrations/__pycache__/0041_transportcarcassdetail_has_product_date.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0042_alter_driver_created_by_alter_driver_modified_by_and_more.cpython-312.pyc b/app/migrations/__pycache__/0042_alter_driver_created_by_alter_driver_modified_by_and_more.cpython-312.pyc
new file mode 100644
index 0000000..591592f
Binary files /dev/null and b/app/migrations/__pycache__/0042_alter_driver_created_by_alter_driver_modified_by_and_more.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/0042_alter_driver_created_by_alter_driver_modified_by_and_more.cpython-39.pyc b/app/migrations/__pycache__/0042_alter_driver_created_by_alter_driver_modified_by_and_more.cpython-39.pyc
new file mode 100644
index 0000000..bedc2b3
Binary files /dev/null and b/app/migrations/__pycache__/0042_alter_driver_created_by_alter_driver_modified_by_and_more.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0043_allproductstransport.cpython-39.pyc b/app/migrations/__pycache__/0043_allproductstransport.cpython-39.pyc
new file mode 100644
index 0000000..6e71007
Binary files /dev/null and b/app/migrations/__pycache__/0043_allproductstransport.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0044_evacuationdetail.cpython-39.pyc b/app/migrations/__pycache__/0044_evacuationdetail.cpython-39.pyc
new file mode 100644
index 0000000..dbd645d
Binary files /dev/null and b/app/migrations/__pycache__/0044_evacuationdetail.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0045_remove_evacuationdetail_broilerflockrequestid_and_more.cpython-39.pyc b/app/migrations/__pycache__/0045_remove_evacuationdetail_broilerflockrequestid_and_more.cpython-39.pyc
new file mode 100644
index 0000000..790ab8f
Binary files /dev/null and b/app/migrations/__pycache__/0045_remove_evacuationdetail_broilerflockrequestid_and_more.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/0046_rasadyarappinfo.cpython-39.pyc b/app/migrations/__pycache__/0046_rasadyarappinfo.cpython-39.pyc
new file mode 100644
index 0000000..4d24685
Binary files /dev/null and b/app/migrations/__pycache__/0046_rasadyarappinfo.cpython-39.pyc differ
diff --git a/app/migrations/__pycache__/__init__.cpython-310.pyc b/app/migrations/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..80244ab
Binary files /dev/null and b/app/migrations/__pycache__/__init__.cpython-310.pyc differ
diff --git a/app/migrations/__pycache__/__init__.cpython-311.pyc b/app/migrations/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000..5d9eb0f
Binary files /dev/null and b/app/migrations/__pycache__/__init__.cpython-311.pyc differ
diff --git a/app/migrations/__pycache__/__init__.cpython-312.pyc b/app/migrations/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..e29035b
Binary files /dev/null and b/app/migrations/__pycache__/__init__.cpython-312.pyc differ
diff --git a/app/migrations/__pycache__/__init__.cpython-39.pyc b/app/migrations/__pycache__/__init__.cpython-39.pyc
new file mode 100644
index 0000000..ca5bdc6
Binary files /dev/null and b/app/migrations/__pycache__/__init__.cpython-39.pyc differ
diff --git a/app/models.py b/app/models.py
new file mode 100644
index 0000000..619461e
--- /dev/null
+++ b/app/models.py
@@ -0,0 +1,720 @@
+import datetime
+import re
+from datetime import timedelta
+from itertools import product
+
+from app.cityandprovince import search_province_list, correct_province, search_city_list, correct_city, normalize_text
+from app.helper_excel import correction_dict
+from authentication.models import BaseModel
+from django.db import models
+
+from helpers import convert_to_miladi
+
+
+class Poultry(BaseModel):
+ UserName = models.CharField(max_length=200, null=True)
+ Password = models.CharField(max_length=200, null=True)
+ FirstName = models.CharField(max_length=200, null=True)
+ LastName = models.CharField(max_length=200, null=True)
+ UserGroupName = models.CharField(max_length=200, null=True)
+ UserRoleName = models.CharField(max_length=200, null=True)
+ UserGroupId = models.CharField(max_length=200, null=True)
+ UserRoleId = models.CharField(max_length=200, null=True)
+ Mobile = models.CharField(max_length=200, null=True)
+ Email = models.CharField(max_length=200, null=True)
+ UserIsActive = models.BooleanField(null=True)
+ UserIsActiveDescription = models.CharField(max_length=200, null=True)
+ RegDate = models.CharField(max_length=200, null=True)
+ RegDateShamsi = models.CharField(max_length=200, null=True)
+ RegDateShamsiWithTime = models.CharField(max_length=200, null=True)
+ RegDateShamsiOnlyTime = models.CharField(max_length=200, null=True)
+ StringId = models.CharField(max_length=200, null=True)
+ IsPersisted = models.BooleanField(null=True)
+ AllowInsert = models.BooleanField(null=True)
+ AllowUpdate = models.BooleanField(null=True)
+ ModalCss = models.CharField(max_length=200, null=True)
+ GridContainerParametersModel = models.CharField(max_length=200, null=True)
+ MenuUserAccess = models.CharField(max_length=200, null=True)
+ MenuUserAccessId = models.CharField(max_length=200, null=True)
+ LogTableName = models.CharField(max_length=200, null=True)
+ LogTableAlias = models.CharField(max_length=200, null=True)
+ PageTitle = models.CharField(max_length=200, null=True)
+ UnitName = models.CharField(max_length=200, null=True)
+ SystemCode = models.CharField(max_length=200, null=True)
+ TrackingCode = models.CharField(max_length=200, null=True)
+ EpidemiologicCode = models.CharField(max_length=200, null=True)
+ PartIdCode = models.CharField(max_length=200, null=True)
+ PostalCode = models.CharField(max_length=200, null=True)
+ UnitId = models.CharField(max_length=200, null=True)
+ UnitTypeId = models.CharField(max_length=200, null=True)
+ UnitTypeName = models.CharField(max_length=200, null=True)
+ LocationIdProvince = models.CharField(max_length=200, null=True)
+ LocationIdCity = models.CharField(max_length=200, null=True)
+ LocationNameProvince = models.CharField(max_length=200, null=True)
+ LocationNameCity = models.CharField(max_length=200, null=True)
+ UnitIsActive = models.BooleanField(null=True)
+ UnitIsActiveDescription = models.CharField(max_length=200, null=True)
+ PId = models.CharField(max_length=200, null=True)
+ Province = models.CharField(max_length=500, null=True, blank=True)
+ City = models.CharField(max_length=500, null=True, blank=True)
+
+ def save(self, *args, **kwargs):
+ super(Poultry, self).save(*args, **kwargs)
+
+
+class PoultryHatching(BaseModel):
+ poultry = models.ForeignKey(
+ Poultry,
+ on_delete=models.CASCADE,
+ related_name="hatching_poultry",
+ null=True
+ )
+ DesCertId = models.CharField(max_length=200, null=True)
+ UnitId = models.CharField(max_length=200, null=True)
+ BroilerFlockRequestId = models.IntegerField(null=True)
+ RequestCode = models.CharField(max_length=200, null=True)
+ StartDate = models.CharField(max_length=200, null=True)
+ StartDatePersian = models.CharField(max_length=200, null=True)
+ EndDate = models.CharField(max_length=200, null=True)
+ EndDatePersian = models.CharField(max_length=200, null=True)
+ HatchingDate = models.CharField(max_length=200, null=True)
+ HatchingDatePersian = models.CharField(max_length=200, null=True)
+ Date = models.DateTimeField(null=True)
+ HatchingAge = models.IntegerField(default=1)
+ MaxHatchingDate = models.CharField(max_length=200, null=True)
+ MaxHatchingDatePersian = models.CharField(max_length=200, null=True)
+ HatchingCount = models.IntegerField(null=True)
+ HatchingCountInBargiri = models.IntegerField(null=True)
+ HatchingCountInTakhlie = models.IntegerField(null=True)
+ TrackingCount = models.IntegerField(null=True)
+ TrackingBargiriCount = models.IntegerField(null=True)
+ PercentHamlToMojavez = models.IntegerField(null=True)
+ PercentTakhlieToBargiri = models.IntegerField(null=True)
+ FlockAgeDay = models.IntegerField(null=True)
+ PartIdCode = models.CharField(max_length=200, null=True)
+ UnitName = models.CharField(max_length=200, null=True)
+ PostalCode = models.CharField(max_length=200, null=True)
+ EpidemiologicCode = models.CharField(max_length=200, null=True)
+ CapacityFemale = models.IntegerField(null=True)
+ PersonFullName = models.CharField(max_length=200, null=True)
+ LocationIdProvince = models.CharField(max_length=200, null=True)
+ LocationNameProvince = models.CharField(max_length=200, null=True)
+ LocationIdCity = models.CharField(max_length=200, null=True)
+ LocationNameCity = models.CharField(max_length=200, null=True)
+ Mobile = models.CharField(max_length=200, null=True)
+ HamlMorghMinDate = models.CharField(max_length=200, null=True)
+ HamlMorghTotalCount = models.IntegerField(null=True)
+ HamlMorghTakhlieCount = models.IntegerField(null=True)
+ EvacuationCount = models.IntegerField(null=True)
+ EvacuationCount_1 = models.IntegerField(null=True)
+ EvacuationCount_2 = models.IntegerField(null=True)
+ EvacuationCount_3 = models.IntegerField(null=True)
+ BaseHatchingCount = models.IntegerField(null=True)
+ PercentMorghToJoojeTotal = models.IntegerField(null=True)
+ PercentMorghToJoojeTakhlie = models.IntegerField(null=True)
+ PercentMorghToJoojeTakhlieWithEvacutaion = models.IntegerField(null=True)
+ PercentNotDeliverd = models.IntegerField(null=True)
+ PercentDeliveredForSP = models.IntegerField(null=True)
+ PercentDeliveredForSPNoExclude = models.IntegerField(null=True)
+ PercentDeliveredWithoutEvac = models.IntegerField(null=True)
+ DiffHamlThanTakhlieCount = models.IntegerField(null=True)
+ DiffTakhlieThanHamlCount = models.IntegerField(null=True)
+ PedigreeName = models.CharField(max_length=200, null=True)
+ LeftOver = models.IntegerField(null=True)
+
+ def save(self, *args, **kwargs):
+ if hasattr(self, 'PedigreeName') and self.PedigreeName in correction_dict:
+ self.PedigreeName = correction_dict[self.PedigreeName]
+ self.LeftOver = self.HatchingCount - self.EvacuationCount
+ hatching_date = self.HatchingDatePersian.split('/')
+ date = convert_to_miladi(
+ year=int(hatching_date[0]),
+ month=int(hatching_date[1]),
+ day=int(hatching_date[2])
+ )
+ self.Date = date
+ super(PoultryHatching, self).save(*args, **kwargs)
+
+
+class Hatching(BaseModel):
+ poultry = models.ForeignKey(
+ Poultry,
+ on_delete=models.CASCADE,
+ related_name="poultry_hatching_poultry",
+ null=True
+ )
+ Date = models.DateTimeField(null=True)
+ ArchiveDate = models.DateTimeField(null=True)
+ BroilerFlockRequestId = models.IntegerField(null=True, blank=True)
+ InsertDate = models.CharField(max_length=200, null=True, blank=True)
+ LastChangeStatusDate = models.CharField(max_length=200, null=True, blank=True)
+ LastChangeStatusDateShamsi = models.CharField(max_length=200, null=True, blank=True)
+ FlockRequestUnitName = models.CharField(max_length=200, null=True, blank=True)
+ PedigreeName = models.CharField(max_length=200, null=True, blank=True)
+ StatusId = models.IntegerField(null=True, blank=True)
+ Status = models.IntegerField(null=True, blank=True)
+ StatusName = models.CharField(max_length=200, null=True, blank=True)
+ PedigreeType = models.IntegerField(null=True, blank=True)
+ BroilerPedigreeTypeName = models.CharField(max_length=200, null=True, blank=True)
+ StatusColor = models.CharField(max_length=200, null=True, blank=True)
+ SystemRevocationDate = models.CharField(max_length=200, null=True, blank=True)
+ RemindDays = models.IntegerField(null=True, blank=True)
+ PartyCount = models.IntegerField(null=True, blank=True)
+ GoodCount = models.IntegerField(null=True, blank=True)
+ ShowButtons = models.BooleanField(null=True, blank=True)
+ HasSync = models.BooleanField(null=True, blank=True)
+ BroilerFlockRequestExpireStatus = models.IntegerField(null=True, blank=True)
+ IdWithFormat = models.CharField(max_length=200, null=True, blank=True)
+ ProvinceName = models.CharField(max_length=200, null=True, blank=True)
+ CityName = models.CharField(max_length=200, null=True, blank=True)
+ Address = models.CharField(max_length=200, null=True, blank=True)
+ UnitTel = models.CharField(max_length=200, null=True, blank=True)
+ UnitPostalCode = models.CharField(max_length=200, null=True, blank=True)
+ UnitName = models.CharField(max_length=200, null=True, blank=True)
+ SystemCode = models.CharField(max_length=200, null=True, blank=True)
+ CapacityFemale = models.IntegerField(null=True, blank=True)
+ EpidemiologicCode = models.CharField(max_length=200, null=True, blank=True)
+ RequestCode = models.CharField(max_length=200, null=True, blank=True)
+ RequestDate = models.CharField(max_length=200, null=True, blank=True)
+ RequestDateFa = models.CharField(max_length=200, null=True, blank=True)
+ RequestCount = models.IntegerField(null=True, blank=True)
+ DeliverDate = models.CharField(max_length=200, null=True, blank=True)
+ DeliverDateFa = models.CharField(max_length=200, null=True, blank=True)
+ UnionName = models.CharField(max_length=200, null=True, blank=True)
+ PersonTypeId = models.IntegerField(null=True, blank=True)
+ PersonType = models.IntegerField(null=True, blank=True)
+ PersonTypeName = models.CharField(max_length=200, null=True, blank=True)
+ PersonFullName = models.CharField(max_length=200, null=True, blank=True)
+ NationalCode = models.CharField(max_length=200, null=True, blank=True)
+ InteractType = models.IntegerField(null=True, blank=True)
+ InteractTypeName = models.CharField(max_length=200, null=True, blank=True)
+ UnionTypeId = models.IntegerField(null=True, blank=True)
+ UnionTypeName = models.CharField(max_length=200, null=True, blank=True)
+ SendDate = models.CharField(max_length=200, null=True, blank=True)
+ SendDateFa = models.CharField(max_length=200, null=True, blank=True)
+ ChickCountSum = models.IntegerField(null=True, blank=True)
+ CalculatedDate = models.CharField(max_length=200, null=True, blank=True)
+ CalculatedDateFa = models.CharField(max_length=200, null=True, blank=True)
+ PartIdCode = models.CharField(max_length=200, null=True, blank=True)
+ CertId = models.CharField(max_length=200, null=True, blank=True)
+ StartDate = models.CharField(max_length=200, null=True, blank=True)
+ StartDateFa = models.CharField(max_length=200, null=True, blank=True)
+ EndDate = models.CharField(max_length=200, null=True, blank=True)
+ EndDateFa = models.CharField(max_length=200, null=True, blank=True)
+ RemainCredit = models.IntegerField(null=True, blank=True)
+ StrRemainCredit = models.CharField(max_length=200, null=True, blank=True)
+ ShowStatus = models.CharField(max_length=200, null=True, blank=True)
+ ValidStatus = models.CharField(max_length=200, null=True, blank=True)
+ ValidStatusName = models.CharField(max_length=200, null=True, blank=True)
+ RegDate = models.CharField(max_length=200, null=True, blank=True)
+ RegDateShamsi = models.CharField(max_length=200, null=True, blank=True)
+ RegDateShamsiWithTime = models.CharField(max_length=200, null=True, blank=True)
+ RegDateShamsiOnlyTime = models.CharField(max_length=200, null=True, blank=True)
+ HatchingId = models.CharField(max_length=200, null=True, blank=True)
+ StringId = models.CharField(max_length=200, null=True, blank=True)
+ IsPersisted = models.BooleanField(null=True, blank=True)
+ AllowInsert = models.BooleanField(null=True, blank=True)
+ AllowUpdate = models.BooleanField(null=True, blank=True)
+ ModalCss = models.CharField(max_length=200, null=True, blank=True)
+ GridContainerParametersModel = models.CharField(max_length=200, null=True, blank=True)
+ MenuUserAccess = models.CharField(max_length=200, null=True, blank=True)
+ MenuUserAccessId = models.IntegerField(null=True, blank=True)
+ LogTableName = models.CharField(max_length=200, null=True, blank=True)
+ LogTableAlias = models.CharField(max_length=200, null=True, blank=True)
+ PageTitle = models.CharField(max_length=200, null=True, blank=True)
+ Evacuation = models.IntegerField(default=0)
+ Age = models.IntegerField(null=True)
+ KillingAve = models.IntegerField(default=0)
+ Period = models.IntegerField(default=1)
+ LeftOver = models.IntegerField(null=True)
+ samasat_discharge_percentage = models.IntegerField(default=0)
+ GoodSum = models.IntegerField(default=0)
+
+ def save(self, *args, **kwargs):
+ if hasattr(self, 'PedigreeName') and self.PedigreeName in correction_dict:
+ self.PedigreeName = correction_dict[self.PedigreeName]
+ try:
+ date1 = AllProductsTransport.objects.filter(hatching__id=self.id, trash=False, product='جوجه یک روزه گوشتی').order_by('date').first().date
+ date = datetime.datetime(year=date1.year, month=date1.month, day=date1.day, hour=10,
+ minute=1,
+ second=1)
+ except:
+ hatching_date = self.SendDateFa.split('/')
+ date = convert_to_miladi(
+ year=int(hatching_date[0]),
+ month=int(hatching_date[1]),
+ day=int(hatching_date[2])
+ )
+ self.Date = date
+ self.Age = (datetime.datetime.now().date() - self.Date.date()).days + 1
+ if self.ProvinceName and self.ProvinceName not in search_province_list:
+ self.ProvinceName = correct_province(self.ProvinceName)
+ if self.CityName and self.CityName not in search_city_list:
+ self.CityName = correct_city(self.CityName, self.ProvinceName)
+ self.ChickCountSum = self.GoodSum
+ super(Hatching, self).save(*args, **kwargs)
+
+
+class TransportingDetail(BaseModel):
+ hatching = models.ForeignKey(
+ Hatching,
+ on_delete=models.CASCADE,
+ related_name="transport_hatching",
+ null=True
+ )
+ BroilerFlockRequestId = models.IntegerField(null=True, blank=True)
+ TrackingCode = models.CharField(max_length=200, null=True)
+ DesCertId = models.CharField(max_length=200, null=True)
+ IssueDate = models.CharField(max_length=200, null=True)
+ IssueDatePersian = models.CharField(max_length=200, null=True)
+ GoodCode = models.IntegerField(null=True)
+ GoodName = models.CharField(max_length=200, null=True)
+ GoodAmount = models.IntegerField(null=True)
+ TrackingStatus = models.IntegerField(null=True)
+ TrackingStatusDescription = models.CharField(max_length=200, null=True)
+ TakhlieDate = models.CharField(max_length=200, null=True)
+ TakhlieDatePersian = models.CharField(max_length=200, null=True)
+ SourcePartIdCode = models.CharField(max_length=200, null=True)
+ SourceUnitPartIdCode = models.CharField(max_length=200, null=True)
+ SourceUnitName = models.CharField(max_length=200, null=True)
+ SourceCertId = models.CharField(max_length=200, null=True)
+ ResideDate = models.CharField(max_length=200, null=True)
+ ResideDatePersian = models.CharField(max_length=200, null=True)
+ PedigreeName = models.CharField(max_length=200, null=True)
+ DesUnitName = models.CharField(max_length=200, null=True)
+ DesPartIdCode = models.CharField(max_length=200, null=True)
+ HealthPermitId = models.IntegerField(null=True)
+ Province = models.CharField(max_length=200, null=True)
+ City = models.CharField(max_length=200, null=True)
+ Age = models.IntegerField(null=True)
+ Date = models.DateTimeField(null=True)
+ Out = models.BooleanField(default=False)
+
+ def save(self, *args, **kwargs):
+ issue = self.IssueDatePersian.split('/')
+ date = convert_to_miladi(
+ year=int(issue[0]),
+ month=int(issue[1]),
+ day=int(issue[2])
+ )
+ self.Date = date
+ kill_house = KillHouse.objects.filter(PartIdCode=self.DesPartIdCode, trash=False).first()
+ if kill_house:
+ self.Province = kill_house.Province
+ self.City = kill_house.City
+ if self.Province and self.Province not in search_province_list:
+ self.Province = correct_province(self.Province)
+ if self.City and self.City not in search_city_list:
+ self.City = correct_city(self.City, self.Province)
+ super(TransportingDetail, self).save(*args, **kwargs)
+
+
+class TransportingChickenDetail(BaseModel):
+ hatching = models.ForeignKey(
+ PoultryHatching,
+ on_delete=models.CASCADE,
+ related_name="transporting_hatching",
+ null=True
+ )
+ TrackingCode = models.CharField(max_length=200, null=True)
+ DesCertId = models.CharField(max_length=200, null=True)
+ IssueDate = models.CharField(max_length=200, null=True)
+ IssueDatePersian = models.CharField(max_length=200, null=True)
+ issue_date = models.DateTimeField(null=True)
+ GoodCode = models.IntegerField(null=True)
+ GoodName = models.CharField(max_length=200, null=True)
+ GoodAmount = models.IntegerField(null=True)
+ TrackingStatus = models.IntegerField(null=True)
+ TrackingStatusDescription = models.CharField(max_length=200, null=True)
+ TakhlieDate = models.CharField(max_length=200, null=True)
+ TakhlieDatePersian = models.CharField(max_length=200, null=True)
+ SourcePartIdCode = models.CharField(max_length=200, null=True)
+ SourceUnitName = models.CharField(max_length=200, null=True)
+ SourceCertId = models.CharField(max_length=200, null=True)
+ ParentPartIdCode = models.CharField(max_length=200, null=True)
+ ParentUnitName = models.CharField(max_length=200, null=True)
+ CertId = models.CharField(max_length=200, null=True)
+ ResideDate = models.CharField(max_length=200, null=True)
+ ResideDatePersian = models.CharField(max_length=200, null=True)
+ reside_date = models.DateTimeField(null=True)
+ PedigreeName = models.CharField(max_length=200, null=True)
+ BroilerFlockRequestId = models.IntegerField(null=True)
+ SourceUnitPartIdCode = models.CharField(max_length=200, null=True)
+ DesUnitName = models.CharField(max_length=200, null=True)
+ DesPartIdCode = models.CharField(max_length=200, null=True)
+ HealthPermitId = models.IntegerField(null=True)
+ ProvinceId = models.CharField(max_length=200, null=True)
+ Province = models.CharField(max_length=200, null=True)
+ age = models.IntegerField(default=1)
+
+ def save(self, *args, **kwargs):
+ Issue = self.IssueDatePersian.split('/')
+ date = convert_to_miladi(
+ year=int(Issue[0]),
+ month=int(Issue[1]),
+ day=int(Issue[2])
+ )
+ self.issue_date = date
+
+ Reside = self.ResideDatePersian.split('/')
+ date = convert_to_miladi(
+ year=int(Reside[0]),
+ month=int(Reside[1]),
+ day=int(Reside[2])
+ )
+ self.reside_date = date
+ if self.hatching:
+ self.age = (self.issue_date.date() - self.hatching.Date.date()).days + 1
+ super(TransportingChickenDetail, self).save(*args, **kwargs)
+
+
+class KillHouse(BaseModel):
+ PartIdCode = models.CharField(max_length=250, null=True, blank=True)
+ UnitName = models.CharField(max_length=250, null=True, blank=True)
+ Province = models.CharField(max_length=250, null=True, blank=True)
+ City = models.CharField(max_length=250, null=True, blank=True)
+ ProvinceId = models.CharField(max_length=200, null=True, blank=True)
+ CityId = models.CharField(max_length=200, null=True, blank=True)
+
+ def save(self, *args, **kwargs):
+ super(KillHouse, self).save(*args, **kwargs)
+
+
+class ApkInfo(BaseModel):
+ info = models.JSONField(null=True)
+ download_link = models.CharField(max_length=700, null=True, blank=True)
+
+ def save(self, *args, **kwargs):
+ super(ApkInfo, self).save(*args, **kwargs)
+
+
+class TransportCarcassDetail(BaseModel): # بارهای لاشه
+ id_quarantineh = models.CharField(max_length=255, null=True, blank=True) # id
+ destination_prev = models.CharField(max_length=255, null=True, blank=True) # مقصد قبلی
+ destination_changed = models.CharField(max_length=255, null=True, blank=True) # تغییر مقصد
+ payment = models.CharField(max_length=255, null=True, blank=True) # پرداخت
+ tracking = models.CharField(max_length=255, null=True, blank=True, unique=True) # رهگیری
+ date = models.DateField(null=True, blank=True) # تاریخ
+ time = models.TimeField(null=True, blank=True) # ساعت
+ product = models.CharField(max_length=255, null=True, blank=True) # محصول
+ items = models.CharField(max_length=255, null=True, blank=True) # اقلام
+ quantity = models.FloatField(null=True, blank=True) # مقدار
+ unit = models.CharField(max_length=50, null=True, blank=True) # واحد
+ origin_province = models.CharField(max_length=255, null=True, blank=True) # استان مبدا
+ origin_city = models.CharField(max_length=255, null=True, blank=True) # شهرستان مبدا
+ origin = models.CharField(max_length=255, null=True, blank=True) # مبدا
+ destination_province = models.CharField(max_length=255, null=True, blank=True) # استان مقصد
+ destination_city = models.CharField(max_length=255, null=True, blank=True) # شهرستان مقصد
+ destination = models.CharField(max_length=255, null=True, blank=True) # مقصد
+ jihadi_origin = models.CharField(max_length=255, null=True, blank=True) # ش جهادی مبدا
+ jihadi_destination = models.CharField(max_length=255, null=True, blank=True) # ش جهادی مقصد
+ owner = models.CharField(max_length=255, null=True, blank=True) # مالک
+ car_tracking_code = models.CharField(max_length=255, null=True, blank=True) # کد رهگیری خودرو
+ driver_name = models.CharField(max_length=255, null=True, blank=True) # نام راننده
+ plate = models.CharField(max_length=50, null=True, blank=True) # پلاک
+ amount = models.DecimalField(max_digits=20, decimal_places=2, null=True, blank=True) # مبلغ
+ unloading = models.CharField(max_length=255, null=True, blank=True) # تخلیه
+ gross_weight = models.FloatField(null=True, blank=True) # وزن پر
+ tare_weight = models.FloatField(null=True, blank=True) # وزن خالی
+ net_weight = models.FloatField(null=True, blank=True) # وزن
+ scale_code = models.CharField(max_length=255, null=True, blank=True) # کد باسکول
+ scale_name = models.CharField(max_length=255, null=True, blank=True) # نام باسکول
+ scale_receipt = models.CharField(max_length=255, null=True, blank=True) # قبض باسکول
+ unloading_date = models.DateField(null=True, blank=True) # تاریخ تخلیه
+ out = models.BooleanField(default=False) # Out (True/False)
+ product_date = models.DateField(null=True, blank=True) # تاریخ
+ has_product_date = models.BooleanField(default=False) # دارای تاریخ
+
+ def save(self, *args, **kwargs):
+ if self.origin_province:
+ self.origin_province = normalize_text(correct_province(self.origin_province))
+ if self.origin_city:
+ self.origin_city = normalize_text(correct_city(self.origin_city, self.origin_province))
+
+ if self.destination_province:
+ self.destination_province = normalize_text(correct_province(self.destination_province))
+ if self.destination_city:
+ self.destination_city = normalize_text(correct_city(self.destination_city, self.destination_province))
+ if isinstance(self.product_date, str):
+ try:
+ self.product_date = datetime.datetime.strptime(self.product_date, '%Y-%m-%d').date()
+ except ValueError:
+ try:
+ self.product_date = datetime.datetime.strptime(self.product_date, '%Y/%m/%d').date()
+ except ValueError:
+ self.product_date = None
+ super(TransportCarcassDetail, self).save(*args, **kwargs)
+
+
+class Guilds(BaseModel):
+ name = models.CharField(max_length=255, null=True, blank=True)
+ city = models.CharField(max_length=255, null=True, blank=True)
+ province = models.CharField(max_length=255, null=True, blank=True)
+ jihadi_code = models.CharField(max_length=255, null=True, blank=True)
+ is_steward = models.BooleanField(default=False)
+
+ def save(self, *args, **kwargs):
+ if self.province:
+ self.province = normalize_text(correct_province(self.province))
+ if self.city:
+ self.city = normalize_text(correct_city(self.city, self.province))
+ super(Guilds, self).save(*args, **kwargs)
+
+
+class Driver(BaseModel):
+ car_id = models.CharField(max_length=255, null=True, blank=True)
+ driver_name = models.CharField(max_length=255, null=True, blank=True)
+ owner_name = models.CharField(max_length=255, null=True, blank=True)
+ city = models.CharField(max_length=255, null=True, blank=True)
+ province = models.CharField(max_length=255, null=True, blank=True)
+ pelak = models.CharField(max_length=255, null=True, blank=True)
+ tracking_code = models.CharField(max_length=255, null=True, blank=True)
+ weight = models.IntegerField(null=True, blank=True)
+ car_type = models.CharField(max_length=255, null=True, blank=True)
+ expire_licence_date = models.DateField(null=True, blank=True)
+ health_permit = models.CharField(max_length=255, null=True, blank=True)
+ product = models.CharField(max_length=255, null=True, blank=True)
+ part_id_code = models.CharField(max_length=255, null=True, blank=True)
+ kill_house_name = models.CharField(max_length=255, null=True, blank=True)
+
+ def save(self, *args, **kwargs):
+ if self.province:
+ self.province = normalize_text(correct_province(self.province))
+ if self.city:
+ self.city = normalize_text(correct_city(self.city, self.province))
+ super(Driver, self).save(*args, **kwargs)
+
+
+class InquiryCredentials(BaseModel):
+ data = models.JSONField(null=True, blank=True)
+ numbers = models.JSONField(null=True, blank=True)
+
+ def save(self, *args, **kwargs):
+ super(InquiryCredentials, self).save(*args, **kwargs)
+
+
+class EvacuationDetail(BaseModel): # گزارشات تلفات جوجه ریزی
+ hatching = models.ForeignKey(
+ Hatching,
+ on_delete=models.CASCADE,
+ related_name="evacuation_details",
+ null=True,
+ blank=True
+ )
+ PartIdCode = models.CharField(max_length=200, null=True, blank=True)
+ RequestId = models.CharField(max_length=200, null=True, blank=True)
+ MoReportId = models.CharField(max_length=200, null=True, blank=True)
+ ReportType = models.IntegerField(null=True, blank=True)
+ ReportTypeString = models.CharField(max_length=255, null=True, blank=True)
+ ReportDate = models.CharField(max_length=200, null=True, blank=True)
+ ReportDateShamsi = models.CharField(max_length=200, null=True, blank=True)
+ MoReason = models.TextField(null=True, blank=True)
+ MoDate = models.CharField(max_length=200, null=True, blank=True)
+ MoDateShamsi = models.CharField(max_length=200, null=True, blank=True)
+ MoStartDay = models.IntegerField(null=True, blank=True)
+ MoEndDay = models.IntegerField(null=True, blank=True)
+ MoReportSubId = models.CharField(max_length=200, null=True, blank=True)
+ ReportStatus = models.IntegerField(null=True, blank=True)
+ GoodCount = models.IntegerField(null=True, blank=True)
+ Message = models.TextField(null=True, blank=True)
+ ErrorCode = models.IntegerField(null=True, blank=True)
+ IsDeleted = models.BooleanField(default=False)
+ RegDate = models.CharField(max_length=200, null=True, blank=True)
+ RegDateShamsi = models.CharField(max_length=200, null=True, blank=True)
+ RegDateShamsiWithTime = models.CharField(max_length=200, null=True, blank=True)
+ RegDateShamsiOnlyTime = models.CharField(max_length=200, null=True, blank=True)
+ ExternalId = models.CharField(max_length=200, null=True, blank=True)
+ StringId = models.CharField(max_length=200, null=True, blank=True)
+ IsPersisted = models.BooleanField(default=False)
+ AllowInsert = models.BooleanField(default=False)
+ AllowUpdate = models.BooleanField(default=False)
+ ModalCss = models.CharField(max_length=255, null=True, blank=True)
+ GridContainerParametersModel = models.CharField(max_length=255, null=True, blank=True)
+ MenuUserAccess = models.CharField(max_length=255, null=True, blank=True)
+ MenuUserAccessId = models.IntegerField(null=True, blank=True)
+ LogTableName = models.CharField(max_length=255, null=True, blank=True)
+ LogTableAlias = models.CharField(max_length=255, null=True, blank=True)
+ PageTitle = models.CharField(max_length=255, null=True, blank=True)
+
+ def save(self, *args, **kwargs):
+ def parse_dotnet_date(dotnet_string):
+ if not dotnet_string:
+ return None
+ try:
+ match = re.search(r'/Date\((\d+)\)/', str(dotnet_string))
+ if match:
+ milliseconds = int(match.group(1))
+ return datetime.datetime.utcfromtimestamp(milliseconds / 1000)
+ except (ValueError, TypeError):
+ pass
+ return None
+
+ def parse_shamsi_date(shamsi_string):
+ if not shamsi_string:
+ return None
+ try:
+ parts = str(shamsi_string).split('/')
+ if len(parts) == 3:
+ year, month, day = map(int, parts)
+ return convert_to_miladi(year=year, month=month, day=day)
+ except (ValueError, TypeError):
+ pass
+ return None
+
+ def parse_shamsi_with_time(value):
+ if not value:
+ return None
+ try:
+ text = str(value)
+ if '(' in text and ')' in text:
+ date_part, time_part = text.split('(')
+ date_part = date_part.strip()
+ time_part = time_part.replace(')', '').strip()
+ base_date = parse_shamsi_date(date_part)
+ if base_date:
+ hour, minute, second = map(int, time_part.split(':'))
+ return datetime.datetime(
+ year=base_date.year,
+ month=base_date.month,
+ day=base_date.day,
+ hour=hour,
+ minute=minute,
+ second=second
+ )
+ except (ValueError, TypeError):
+ pass
+ return None
+
+ def parse_time_only(value):
+ if not value:
+ return None
+ try:
+ hour, minute, second = map(int, value.split(':'))
+ return datetime.time(hour=hour, minute=minute, second=second)
+ except (ValueError, TypeError):
+ pass
+ return None
+
+ dotnet_report = parse_dotnet_date(self.ReportDate)
+ if dotnet_report:
+ self.ReportDate = dotnet_report.strftime('%Y-%m-%d %H:%M:%S')
+
+ dotnet_mo = parse_dotnet_date(self.MoDate)
+ if dotnet_mo:
+ self.MoDate = dotnet_mo.strftime('%Y-%m-%d %H:%M:%S')
+
+ dotnet_reg = parse_dotnet_date(self.RegDate)
+ if dotnet_reg:
+ self.RegDate = dotnet_reg.strftime('%Y-%m-%d %H:%M:%S')
+
+ shamsi_report = parse_shamsi_date(self.ReportDateShamsi)
+ if shamsi_report:
+ self.ReportDateShamsi = shamsi_report.strftime('%Y-%m-%d')
+
+ shamsi_mo = parse_shamsi_date(self.MoDateShamsi)
+ if shamsi_mo:
+ self.MoDateShamsi = shamsi_mo.strftime('%Y-%m-%d')
+
+ shamsi_reg = parse_shamsi_date(self.RegDateShamsi)
+ if shamsi_reg:
+ self.RegDateShamsi = shamsi_reg.strftime('%Y-%m-%d')
+
+ shamsi_with_time = parse_shamsi_with_time(self.RegDateShamsiWithTime)
+ if shamsi_with_time:
+ self.RegDateShamsiWithTime = shamsi_with_time.strftime('%Y-%m-%d %H:%M:%S')
+
+ time_only = parse_time_only(self.RegDateShamsiOnlyTime)
+ if time_only:
+ self.RegDateShamsiOnlyTime = time_only.strftime('%H:%M:%S')
+
+ super(EvacuationDetail, self).save(*args, **kwargs)
+
+
+class AllProductsTransport(BaseModel): # بارهای تمامی محصولات
+ hatching = models.ForeignKey(
+ Hatching,
+ on_delete=models.SET_NULL,
+ related_name="all_products_transports",
+ null=True,
+ blank=True
+ )
+ record_id = models.CharField(max_length=255, null=True, blank=True) # id (نام تغییر یافته)
+ destination_prev = models.CharField(max_length=255, null=True, blank=True) # مقصد قبلی
+ destination_changed = models.CharField(max_length=255, null=True, blank=True) # تغییر مقصد
+ tracking = models.CharField(max_length=255, null=True, blank=True, unique=True) # کد رهگیری
+ date = models.DateField(null=True, blank=True) # تاریخ
+ product = models.CharField(max_length=255, null=True, blank=True) # محصول
+ items = models.CharField(max_length=255, null=True, blank=True) # اقلام
+ quantity = models.FloatField(null=True, blank=True) # مقدار
+ unit = models.CharField(max_length=50, null=True, blank=True) # واحد
+ origin_province = models.CharField(max_length=255, null=True, blank=True) # استان مبدا
+ origin_city = models.CharField(max_length=255, null=True, blank=True) # شهرستان مبدا
+ origin = models.CharField(max_length=255, null=True, blank=True) # مبدا
+ destination_province = models.CharField(max_length=255, null=True, blank=True) # استان مقصد
+ destination_city = models.CharField(max_length=255, null=True, blank=True) # شهرستان مقصد
+ destination = models.CharField(max_length=255, null=True, blank=True) # مقصد
+ jihadi_origin = models.CharField(max_length=255, null=True, blank=True) # ش جهادی مبدا
+ jihadi_destination = models.CharField(max_length=255, null=True, blank=True) # ش جهادی مقصد
+ owner = models.CharField(max_length=255, null=True, blank=True) # مالک
+ car_tracking_code = models.CharField(max_length=255, null=True, blank=True) # کد رهگیری خودرو
+ driver_name = models.CharField(max_length=255, null=True, blank=True) # نام راننده
+ gross_weight = models.FloatField(null=True, blank=True) # وزن پر
+ tare_weight = models.FloatField(null=True, blank=True) # وزن خالی
+ net_weight = models.FloatField(null=True, blank=True) # وزن
+ scale_code = models.CharField(max_length=255, null=True, blank=True) # کد باسکول
+ scale_name = models.CharField(max_length=255, null=True, blank=True) # نام باسکول
+ scale_receipt = models.CharField(max_length=255, null=True, blank=True) # قبض باسکول
+ unloading_date = models.DateField(null=True, blank=True) # تاریخ تخلیه
+ unloading = models.CharField(max_length=255, null=True, blank=True) # تخلیه
+ out = models.BooleanField(default=False) # Out (True/False)
+
+ def save(self, *args, **kwargs):
+ # تصحیح استان/شهر مبدا
+ if self.origin_province:
+ self.origin_province = normalize_text(correct_province(self.origin_province))
+ if self.origin_city:
+ self.origin_city = normalize_text(correct_city(self.origin_city, self.origin_province))
+
+ # تصحیح استان/شهر مقصد
+ if self.destination_province:
+ self.destination_province = normalize_text(correct_province(self.destination_province))
+ if self.destination_city:
+ self.destination_city = normalize_text(correct_city(self.destination_city, self.destination_province))
+
+ # تنظیم out بر اساس تفاوت استان مبدا و مقصد
+ if self.origin_province and self.destination_province:
+ if self.origin_province != self.destination_province:
+ self.out = True
+ else:
+ self.out = False
+
+ # تبدیل تاریخها از string به date در صورت نیاز
+ if isinstance(self.date, str):
+ try:
+ self.date = datetime.datetime.strptime(self.date, '%Y-%m-%d').date()
+ except ValueError:
+ try:
+ self.date = datetime.datetime.strptime(self.date, '%Y/%m/%d').date()
+ except ValueError:
+ self.date = None
+
+ if isinstance(self.unloading_date, str):
+ try:
+ self.unloading_date = datetime.datetime.strptime(self.unloading_date, '%Y-%m-%d').date()
+ except ValueError:
+ try:
+ self.unloading_date = datetime.datetime.strptime(self.unloading_date, '%Y/%m/%d').date()
+ except ValueError:
+ self.unloading_date = None
+
+ super(AllProductsTransport, self).save(*args, **kwargs)
+
+
+class RasadyarAppInfo(BaseModel):
+ info = models.JSONField(null=True, blank=True)
+ file = models.CharField(max_length=700, null=True, blank=True)
+
+ def save(self, *args, **kwargs):
+ super(RasadyarAppInfo, self).save(*args, **kwargs)
+
+
diff --git a/app/scripts.py b/app/scripts.py
new file mode 100644
index 0000000..f8f04a2
--- /dev/null
+++ b/app/scripts.py
@@ -0,0 +1,24 @@
+from rest_framework import status
+from rest_framework.permissions import AllowAny
+from rest_framework.response import Response
+
+from app.models import PoultryHatching, Poultry
+from django.db import transaction
+from django.views.decorators.csrf import csrf_exempt
+from rest_framework.decorators import api_view, permission_classes
+
+
+@api_view(['POST'])
+@permission_classes([AllowAny])
+@csrf_exempt
+def update_poultry_city_province(request):
+ hatchings = PoultryHatching.objects.select_related('poultry').only('poultry', 'LocationNameCity',
+ 'LocationNameProvince').filter(
+ poultry__isnull=False).order_by('id')
+ for hatching in hatchings:
+ hatching.poultry.LocationNameProvince = hatching.LocationNameProvince
+ hatching.poultry.LocationNameCity = hatching.LocationNameCity
+ hatching.poultry.save()
+ print(hatching.id)
+ return Response({'message': 'done'}, status=status.HTTP_200_OK)
+ # return Response({'message': len(hatchings)}, status=status.HTTP_200_OK)
diff --git a/app/serializers.py b/app/serializers.py
new file mode 100644
index 0000000..59d78e6
--- /dev/null
+++ b/app/serializers.py
@@ -0,0 +1,653 @@
+import datetime
+
+from django.db.models import Sum, Avg, Count, Q
+from rest_framework import serializers
+
+from app.models import Poultry, PoultryHatching, TransportingChickenDetail, Hatching, TransportingDetail, KillHouse, \
+ ApkInfo, TransportCarcassDetail, Driver, Guilds, InquiryCredentials, AllProductsTransport, EvacuationDetail, \
+ RasadyarAppInfo
+from authentication.models import Province, City
+from helpers import build_calculation
+
+
+class PoultrySerializer(serializers.ModelSerializer):
+ hatching = serializers.SerializerMethodField('get_hatching')
+
+ class Meta:
+ model = Poultry
+ fields = ['FirstName', 'LastName', 'Mobile', 'UnitName', 'EpidemiologicCode', 'SystemCode', 'TrackingCode',
+ 'UnitIsActiveDescription', 'RegDateShamsi', 'LocationNameProvince', 'LocationNameCity', 'hatching']
+
+ def get_hatching(self, instance):
+ hatching = PoultryHatching.objects.filter(poultry=instance)
+ total_amount = build_calculation(queryset=hatching, column_name='BaseHatchingCount', aggregate_func=Sum)
+ EvacuationCount = build_calculation(queryset=hatching, column_name='EvacuationCount', aggregate_func=Sum)
+ LeftOver = build_calculation(queryset=hatching, column_name='LeftOver', aggregate_func=Sum)
+
+ return {
+ "totalAmount": total_amount,
+ "losses": EvacuationCount,
+ "LeftOver": LeftOver,
+ }
+
+
+# class TransportingForHatchingSerializer(serializers.ModelSerializer):
+# class Meta:
+# model = TransportingChickenDetail
+# fields = '__all__'
+
+
+class PoultryHatchingSerializer(serializers.ModelSerializer):
+ # bars = serializers.SerializerMethodField('get_bars')
+ age = serializers.SerializerMethodField('get_age')
+
+ class Meta:
+ model = PoultryHatching
+ fields = '__all__'
+
+ # def get_bars(self, obj):
+ # transports = TransportingChickenDetail.objects.filter(trash=False, hatching=obj)
+ # serializer = TransportingForHatchingSerializer(transports, many=True)
+ # return serializer.data
+
+ def get_age(self, obj):
+ age = (datetime.datetime.now().date() - obj.Date.date()).days + 1
+ return age
+
+
+class HatchingsSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Hatching
+ fields = '__all__'
+
+
+class TransportingChickenDetailSerializer(serializers.ModelSerializer):
+ hatching = PoultryHatchingSerializer(read_only=True)
+ killing_age = serializers.SerializerMethodField('get_killing_age')
+
+ class Meta:
+ model = TransportingChickenDetail
+ fields = '__all__'
+
+ def get_killing_age(self, obj):
+ age = (obj.reside_date.date() - obj.hatching.Date.date()).days + 1
+ return age
+
+
+class HatchingSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = PoultryHatching
+ fields = '__all__'
+
+
+class TransportingSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = TransportingDetail
+ fields = '__all__'
+
+
+class HatchingCalculationSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = PoultryHatching
+ fields = ['EvacuationCount', 'LocationNameCity', 'LocationNameProvince', 'poultry']
+
+
+class PoultryInfoSerializer(serializers.ModelSerializer):
+ info = serializers.SerializerMethodField('get_info')
+
+ class Meta:
+ model = Poultry
+ fields = ['key', 'FirstName', 'LastName', 'Mobile', 'EpidemiologicCode', 'UnitId', 'SystemCode', 'UnitName',
+ 'UnitIsActive',
+ 'RegDateShamsi', 'info', 'LocationNameCity', 'LocationNameProvince', 'UserIsActiveDescription',
+ 'Province', 'City']
+
+ def get_info(self, obj):
+ hatchings = Hatching.objects.filter(poultry=obj, trash=False)
+ total_hatching = hatchings.aggregate(total=Sum('ChickCountSum'))['total'] or 0
+ total_leftover = hatchings.aggregate(total=Sum('LeftOver'))['total'] or 0
+ total_evacuation = hatchings.aggregate(total=Sum('Evacuation'))['total'] or 0
+ transports = TransportingDetail.objects.filter(hatching__in=hatchings, trash=False)
+ total_killing = transports.aggregate(total=Sum('GoodAmount'))['total'] or 0
+ total_left_over_percent = round((total_leftover / total_hatching) * 100, 2) if total_hatching > 0 else 0
+ total_evacuation_percent = round((total_evacuation / total_hatching) * 100, 2) if total_hatching > 0 else 0
+ total_killing_percent = round((total_killing / total_hatching) * 100, 2) if total_hatching > 0 else 0
+
+ return {
+ "count_hatching": len(hatchings),
+ "total_hatching": total_hatching,
+ "total_leftover": total_leftover,
+ "total_evacuation": total_evacuation,
+ "total_left_over_percent": total_left_over_percent,
+ "total_evacuation_percent": total_evacuation_percent,
+ "total_killing_percent": total_killing_percent,
+ "total_killing": total_killing,
+ "cars_count": len(transports),
+ "total_cars": total_killing,
+ "capacity": hatchings.last().CapacityFemale if hatchings else 0,
+ }
+
+
+class PoultryForHatchingDetailSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Poultry
+ fields = ['City', 'Province', 'FirstName', 'LastName', 'UnitName']
+
+
+class HatchingDetailSerializer(serializers.ModelSerializer):
+ info = serializers.SerializerMethodField('get_info')
+ poultry = PoultryForHatchingDetailSerializer(read_only=True)
+
+ class Meta:
+ model = Hatching
+ fields = ['Date', 'Age', 'KillingAve', 'info', 'CertId',
+ 'RequestCode', 'ChickCountSum', 'Period', 'CapacityFemale', 'LeftOver', 'Evacuation',
+ 'poultry', 'PedigreeName']
+
+ def get_info(self, obj):
+ capacity = obj.CapacityFemale if obj.CapacityFemale else 0
+ transports = TransportingDetail.objects.filter(hatching=obj, trash=False)
+ average_slaughter_age = transports.aggregate(avg_age=Avg('Age'))['avg_age'] or 0
+ load_volume = transports.aggregate(total=Sum('GoodAmount'))['total'] or 0
+ percent_hatching_license = (capacity / obj.ChickCountSum) * 100
+
+ return {
+ "average_slaughter_age": average_slaughter_age,
+ "load_volume": load_volume,
+ "number_loads": len(transports),
+ "percent_hatching_license": round(percent_hatching_license, 2)
+ }
+
+
+class HatchingForUpdateSerializer(serializers.ModelSerializer):
+ poultry = PoultryInfoSerializer(read_only=True)
+ EvacuationDetail = serializers.SerializerMethodField()
+
+ class Meta:
+ model = Hatching
+ fields = '__all__'
+
+ def get_EvacuationDetail(self, obj):
+ queryset = EvacuationDetail.objects.filter(hatching=obj, trash=False)
+ return EvacuationDetailSerializer(queryset, many=True).data
+
+
+class PoultryDetailSerializerForTransport(serializers.ModelSerializer):
+ class Meta:
+ model = Poultry
+ fields = ['UnitId', 'PartIdCode', 'Province', 'City', 'UnitName', 'Mobile']
+
+
+class HatchingDetailSerializerForTransport(serializers.ModelSerializer):
+ poultry = PoultryDetailSerializerForTransport(read_only=True)
+
+ class Meta:
+ model = Hatching
+ fields = ['PartIdCode', 'poultry', 'RequestCode', 'PedigreeName']
+
+
+class TransportingDetailSerializer(serializers.ModelSerializer):
+ hatching = HatchingDetailSerializerForTransport(read_only=True)
+ location_kill_house = serializers.SerializerMethodField('get_location_kill_house')
+ location_poultry = serializers.SerializerMethodField('get_location_poultry')
+
+ class Meta:
+ model = TransportingDetail
+ fields = ['TrackingCode', 'hatching', 'ResideDatePersian', 'DesUnitName', 'DesPartIdCode', 'Province', 'City',
+ 'GoodAmount',
+ 'TrackingStatusDescription', 'SourceUnitName', 'Age', 'PedigreeName', 'Date', 'Out',
+ 'location_kill_house',
+ 'location_poultry']
+
+ def get_location_kill_house(self, obj):
+ resul = {}
+ province_kill_house = Province.objects.filter(name=obj.Province).first()
+ city_kill_house = City.objects.filter(name=obj.City).first()
+ if province_kill_house:
+ resul.update({
+ "provinceLat": province_kill_house.Lat,
+ "provinceLng": province_kill_house.Lng
+ })
+ if city_kill_house:
+ resul.update({
+ "cityLat": city_kill_house.Lat,
+ "cityLng": city_kill_house.Lng
+ })
+ return resul
+
+ def get_location_poultry(self, obj):
+ resul = {}
+ if obj.hatching:
+ province_kill_house = Province.objects.filter(name=obj.hatching.ProvinceName).first()
+ city_kill_house = City.objects.filter(name=obj.hatching.CityName).first()
+ if province_kill_house:
+ resul.update({
+ "provinceLat": province_kill_house.Lat,
+ "provinceLng": province_kill_house.Lng
+ })
+ if city_kill_house:
+ resul.update({
+ "cityLat": city_kill_house.Lat,
+ "cityLng": city_kill_house.Lng
+ })
+ return resul
+
+
+class KillHouseSerializer(serializers.ModelSerializer):
+ info = serializers.SerializerMethodField('get_info')
+
+ class Meta:
+ model = KillHouse
+ fields = '__all__'
+
+ def get_info(self, obj):
+ # Use pre-computed cache if available (optimization to avoid N+1 queries)
+ info_cache = self.context.get('info_cache')
+ if info_cache and obj.PartIdCode in info_cache:
+ return info_cache[obj.PartIdCode]
+
+ # Fallback to original query-based approach if cache not available
+ request = self.context.get('request')
+ date1 = request.GET.get('date1') or None
+ date2 = request.GET.get('date2') or None
+
+ bars = TransportingDetail.objects.filter(DesPartIdCode=obj.PartIdCode, trash=False).only('GoodAmount', 'Out', 'Date')
+ if date1:
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ bars = bars.filter(Date__date__gte=date1, Date__date__lte=date2)
+
+ aggregation = bars.aggregate(
+ total=Sum('GoodAmount'),
+ input_total=Sum('GoodAmount', filter=Q(Out=False)),
+ output_total=Sum('GoodAmount', filter=Q(Out=True)),
+ input_count=Count('id', filter=Q(Out=False)),
+ output_count=Count('id', filter=Q(Out=True)),
+ total_count=Count('id')
+ )
+
+ all_products = AllProductsTransport.objects.filter(
+ jihadi_origin=obj.PartIdCode,
+ trash=False
+ ).only('quantity', 'out', 'date', 'unloading_date')
+
+ if date1:
+ all_products = all_products.filter(
+ Q(
+ date__gte=date1,
+ date__lte=date2,
+ date__isnull=False
+ ) |
+ Q(
+ unloading_date__gte=date1,
+ unloading_date__lte=date2,
+ date__isnull=True
+ )
+ )
+
+ all_products_aggregation = all_products.aggregate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id')
+ )
+
+ aggregation['total'] = (aggregation.get('total') or 0) + (all_products_aggregation.get('total') or 0)
+ aggregation['input_total'] = (aggregation.get('input_total') or 0) + (all_products_aggregation.get('input_total') or 0)
+ aggregation['output_total'] = (aggregation.get('output_total') or 0) + (all_products_aggregation.get('output_total') or 0)
+ aggregation['input_count'] = (aggregation.get('input_count') or 0) + (all_products_aggregation.get('input_count') or 0)
+ aggregation['output_count'] = (aggregation.get('output_count') or 0) + (all_products_aggregation.get('output_count') or 0)
+ aggregation['total_count'] = (aggregation.get('total_count') or 0) + (all_products_aggregation.get('total_count') or 0)
+
+ total_count = aggregation['total_count'] or 0
+ total_bars_quantity = aggregation['total'] or 0
+ total_input_bars_quantity = aggregation['input_total'] or 0
+ total_output_bars_quantity = aggregation['output_total'] or 0
+ input_bars_count = aggregation['input_count'] or 0
+ output_bars_count = aggregation['output_count'] or 0
+
+ if total_count > 0:
+ total_input_bars_percent = round((input_bars_count / total_count) * 100, 1)
+ total_output_bars_percent = round((output_bars_count / total_count) * 100, 1)
+ else:
+ total_input_bars_percent = 0
+ total_output_bars_percent = 0
+
+ return {
+ "bars": total_count,
+ "total_bars_quantity": total_bars_quantity,
+ "input_bars": input_bars_count,
+ "total_input_bars_quantity": total_input_bars_quantity,
+ "total_input_bars_percent": total_input_bars_percent,
+ "output_bars": output_bars_count,
+ "total_output_bars_quantity": total_output_bars_quantity,
+ "total_output_bars_percent": total_output_bars_percent,
+ }
+
+
+class TransportingDetailForUpdateSerializer(serializers.ModelSerializer):
+ hatching = HatchingDetailSerializerForTransport(read_only=True)
+
+ class Meta:
+ model = TransportingDetail
+ fields = '__all__'
+
+
+class HatchingAnalysisSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Hatching
+ fields = ['CityName', 'ProvinceName', 'PedigreeName']
+
+
+class HatchingAnalysisSerializerTwo(serializers.ModelSerializer):
+ class Meta:
+ model = Hatching
+ fields = ['CityName', 'ProvinceName']
+
+
+class TransportingReportDashboard(serializers.ModelSerializer):
+ class Meta:
+ model = TransportingDetail
+ fields = ['age', 'Province', 'City']
+
+
+class TransportingForClearanceCodeSerializer(serializers.ModelSerializer):
+ hatching = HatchingDetailSerializerForTransport(read_only=True)
+
+ class Meta:
+ model = TransportingDetail
+ fields = '__all__'
+
+
+class ApkInfoSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = ApkInfo
+ fields = ['key', 'info', 'download_link']
+
+
+class TransportCarcassDetailSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = TransportCarcassDetail
+ fields = '__all__'
+
+
+class DriverSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Driver
+ fields = '__all__'
+
+
+class KillHouseForTransportCarcassSerializer(serializers.ModelSerializer):
+ info = serializers.SerializerMethodField('get_info')
+
+ class Meta:
+ model = KillHouse
+ fields = '__all__'
+
+ def get_info(self, obj):
+ bars_dict = self.context.get('bars_dict', {})
+ buy_dict = self.context.get('buy_dict', {})
+
+ bars_data = bars_dict.get(obj.PartIdCode, {})
+ buy_data = buy_dict.get(obj.PartIdCode, {})
+
+ total_count = bars_data.get('total_count', 0) or 0
+ total_bars_quantity = bars_data.get('total', 0) or 0
+ total_input_bars_quantity = bars_data.get('input_total', 0) or 0
+ total_output_bars_quantity = bars_data.get('output_total', 0) or 0
+ input_bars_count = bars_data.get('input_count', 0) or 0
+ output_bars_count = bars_data.get('output_count', 0) or 0
+
+ buy_input_total = buy_data.get('input_total', 0) or 0
+ buy_output_total = buy_data.get('output_total', 0) or 0
+ buy_input_count = buy_data.get('input_count', 0) or 0
+ buy_output_count = buy_data.get('output_count', 0) or 0
+
+ if total_count > 0 and (total_input_bars_quantity + total_output_bars_quantity) > 0:
+ total_input_bars_percent = round(
+ (total_input_bars_quantity / (total_input_bars_quantity + total_output_bars_quantity)) * 100, 1
+ )
+ total_output_bars_percent = round(
+ (total_output_bars_quantity / (total_input_bars_quantity + total_output_bars_quantity)) * 100, 1
+ )
+ else:
+ total_input_bars_percent = 0
+ total_output_bars_percent = 0
+
+ return {
+ "role": 'کشتارگاه',
+ "bars": int(total_count),
+ "total_bars_wight": int(total_bars_quantity),
+ "input_bars": int(input_bars_count),
+ "total_input_bars_wight": int(total_input_bars_quantity),
+ "total_input_buy_bars_wight": int(((buy_input_total) * 2.5) * 0.75),
+ "total_input_bars_percent": total_input_bars_percent,
+ "output_bars": int(output_bars_count),
+ "total_output_bars_wight": int(total_output_bars_quantity),
+ "total_output_buy_bars_wight": int(((buy_output_total) * 2.5) * 0.75),
+ "total_output_bars_percent": total_output_bars_percent,
+ "total_ware_house": int(((buy_input_total * 2.5) * 0.75) + ((buy_output_total * 2.5) * 0.75)),
+ "total_input_buy_bars_count": int(buy_input_count),
+ "total_output_buy_bars_count": int(buy_output_count),
+ }
+
+
+class StewardForTransportCarcassSerializer(serializers.ModelSerializer):
+ info = serializers.SerializerMethodField()
+
+ class Meta:
+ model = Guilds
+ fields = '__all__'
+
+ def get_info(self, obj):
+ bars_dict = self.context.get('bars_dict', {})
+ row = bars_dict.get(obj.jihadi_code, {})
+
+ total_count = row.get('total_count', 0) or 0
+ total_bars_quantity = row.get('total', 0) or 0
+ total_input_bars_quantity = row.get('input_total', 0) or 0
+ total_output_bars_quantity = row.get('output_total', 0) or 0
+ input_bars_count = row.get('input_count', 0) or 0
+ output_bars_count = row.get('output_count', 0) or 0
+ total_input_buy_bars_wight = row.get('total_input_buy_bars_wight', 0) or 0
+ total_output_buy_bars_wight = row.get('total_output_buy_bars_wight', 0) or 0
+ total_input_buy_bars_count = row.get('total_input_buy_bars_count', 0) or 0
+ total_output_buy_bars_count = row.get('total_output_buy_bars_count', 0) or 0
+ total_ware_house = total_input_buy_bars_wight + total_output_buy_bars_wight
+
+ if total_count > 0:
+ total_input_bars_percent = round(
+ (total_input_bars_quantity / (total_input_bars_quantity + total_output_bars_quantity)) * 100, 1
+ )
+ total_output_bars_percent = round(
+ (total_output_bars_quantity / (total_input_bars_quantity + total_output_bars_quantity)) * 100, 1
+ )
+ else:
+ total_input_bars_percent = 0
+ total_output_bars_percent = 0
+
+ return {
+ "role": 'مباشر',
+ "bars": int(total_count),
+ "total_bars_wight": int(total_bars_quantity),
+ "input_bars": int(input_bars_count),
+ "total_input_bars_wight": int(total_input_bars_quantity),
+ "total_input_buy_bars_wight": int(total_input_buy_bars_wight),
+ "total_input_bars_percent": total_input_bars_percent,
+ "output_bars": int(output_bars_count),
+ "total_output_bars_wight": int(total_output_bars_quantity),
+ "total_output_buy_bars_wight": int(total_output_buy_bars_wight),
+ "total_output_bars_percent": total_output_bars_percent,
+ "total_ware_house": int(total_ware_house),
+ "total_input_buy_bars_count": int(total_input_buy_bars_count),
+ "total_output_buy_bars_count": int(total_output_buy_bars_count),
+ }
+
+
+class GuildsForTransportCarcassSerializer(serializers.ModelSerializer):
+ info = serializers.SerializerMethodField('get_info')
+
+ class Meta:
+ model = Guilds
+ fields = '__all__'
+
+ def get_info(self, obj):
+ bars_dict = self.context.get('bars_dict', {})
+ data = bars_dict.get(obj.jihadi_code, None)
+
+ if not data:
+ return {
+ "role": 'صنف',
+ "total_input_buy_bars_wight": 0,
+ "total_output_buy_bars_wight": 0,
+ "total_ware_house": 0,
+ "total_input_buy_bars_percent": 0,
+ "total_output_buy_bars_percent": 0,
+ "total_input_buy_bars_count": 0,
+ "total_output_buy_bars_count": 0,
+ }
+
+ total_input = data['total_input_buy_bars_wight'] or 0
+ total_output = data['total_output_buy_bars_wight'] or 0
+ total_wh = data['total_ware_house'] or 0
+ total_count = data['total_count'] or 0
+
+ if total_count > 0 and total_wh > 0:
+ total_input_percent = round((total_input / total_wh) * 100, 1)
+ total_output_percent = round((total_output / total_wh) * 100, 1)
+ else:
+ total_input_percent = 0
+ total_output_percent = 0
+
+ return {
+ "role": 'صنف',
+ "total_input_buy_bars_wight": int(total_input),
+ "total_output_buy_bars_wight": int(total_output),
+ "total_ware_house": int(total_wh),
+ "total_input_buy_bars_percent": total_input_percent,
+ "total_output_buy_bars_percent": total_output_percent,
+ "total_input_buy_bars_count": int(data['total_count_input_buy'] or 0),
+ "total_output_buy_bars_count": int(data['total_count_output_buy'] or 0),
+ }
+
+
+class KillHouseForTransportCarcassForRassadyaarSerializer(serializers.ModelSerializer):
+ info = serializers.SerializerMethodField('get_info')
+
+ class Meta:
+ model = KillHouse
+ fields = '__all__'
+
+ def get_info(self, obj):
+ bars_dict = self.context.get('bars_dict', {})
+ buy_dict = self.context.get('buy_dict', {})
+
+ bars_data = bars_dict.get(obj.PartIdCode, {})
+ buy_data = buy_dict.get(obj.PartIdCode, {})
+
+ total_count = bars_data.get('total_count', 0) or 0
+ total_bars_quantity = bars_data.get('total', 0) or 0
+ total_input_bars_quantity = bars_data.get('input_total', 0) or 0
+ total_output_bars_quantity = bars_data.get('output_total', 0) or 0
+ input_bars_count = bars_data.get('input_count', 0) or 0
+ output_bars_count = bars_data.get('output_count', 0) or 0
+
+ buy_input_total = buy_data.get('input_total', 0) or 0
+ buy_output_total = buy_data.get('output_total', 0) or 0
+ buy_input_count = buy_data.get('input_count', 0) or 0
+ buy_output_count = buy_data.get('output_count', 0) or 0
+
+ if total_count > 0 and (total_input_bars_quantity + total_output_bars_quantity) > 0:
+ total_input_bars_percent = round(
+ (total_input_bars_quantity / (total_input_bars_quantity + total_output_bars_quantity)) * 100, 1
+ )
+ total_output_bars_percent = round(
+ (total_output_bars_quantity / (total_input_bars_quantity + total_output_bars_quantity)) * 100, 1
+ )
+ else:
+ total_input_bars_percent = 0
+ total_output_bars_percent = 0
+
+ return {
+ "bars": int(total_count),
+ "total_bars_wight": int(total_bars_quantity),
+ "input_bars": int(input_bars_count),
+ "total_input_bars_wight": int(total_input_bars_quantity),
+ "total_input_buy_bars_wight": int(buy_input_total),
+ "total_input_bars_percent": total_input_bars_percent,
+ "output_bars": int(output_bars_count),
+ "total_output_bars_wight": int(total_output_bars_quantity),
+ "total_output_buy_bars_wight": int(buy_output_total),
+ "total_output_bars_percent": total_output_bars_percent,
+ "total_ware_house": int(buy_input_total) + int(buy_output_total),
+ "total_input_buy_bars_count": int(buy_input_count),
+ "total_output_buy_bars_count": int(buy_output_count),
+ }
+
+
+class InquiryCredentialsSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = InquiryCredentials
+ fields = '__all__'
+
+
+class AllProductsTransportSerializer(serializers.ModelSerializer):
+ hatching = HatchingDetailSerializerForTransport(read_only=True)
+
+ class Meta:
+ model = AllProductsTransport
+ fields = '__all__'
+
+
+class EvacuationDetailSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = EvacuationDetail
+ fields = '__all__'
+
+
+class RasadyarAppInfoSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = RasadyarAppInfo
+ fields = '__all__'
+
+class AllProductsTransportCustomSerializer(serializers.ModelSerializer):
+ location_origin = serializers.SerializerMethodField('get_location_origin')
+ location_destination = serializers.SerializerMethodField('get_location_destination')
+
+ class Meta:
+ model = AllProductsTransport
+ fields = '__all__'
+
+ def get_location_origin(self, obj):
+ resul = {}
+ province_origin = Province.objects.filter(name=obj.origin_province).first()
+ city_origin = City.objects.filter(name=obj.origin_city).first()
+ if province_origin:
+ resul.update({
+ "provinceLat": province_origin.Lat,
+ "provinceLng": province_origin.Lng
+ })
+ if city_origin:
+ resul.update({
+ "cityLat": city_origin.Lat,
+ "cityLng": city_origin.Lng
+ })
+ return resul
+
+ def get_location_destination(self, obj):
+ resul = {}
+ province_destination = Province.objects.filter(name=obj.destination_province).first()
+ city_destination = City.objects.filter(name=obj.destination_city).first()
+ if province_destination:
+ resul.update({
+ "provinceLat": province_destination.Lat,
+ "provinceLng": province_destination.Lng
+ })
+ if city_destination:
+ resul.update({
+ "cityLat": city_destination.Lat,
+ "cityLng": city_destination.Lng
+ })
+ return resul
diff --git a/app/tests.py b/app/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/app/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/app/urls.py b/app/urls.py
new file mode 100644
index 0000000..137bbd6
--- /dev/null
+++ b/app/urls.py
@@ -0,0 +1,239 @@
+from django.urls import include, path
+from rest_framework.routers import DefaultRouter
+from app import views as app_views
+from app.excel_processing import transporting_detail_excel, total_killhouse_excel, hatching_excel, all_hatching_excel, \
+ all_send_different_bar_excel, transport_carcass_detail_excel, guilds_transport_carcass_detail_excel
+from app.helper import get_bar_info, test_city, api_get_hatching_permit_code
+from app.scripts import update_poultry_city_province
+from app.views import get_transport_to_kill, add_kill_house, update_hatching, get_breeds, dashboard_total_kill_house, \
+ send_different_bar, send_different_bar_with_licence_number, all_province_detail_for_map, \
+ dashboard_province_detail_for_map, TransportCarcassDashboardView, GuildsTransportCarcassDashboardView, \
+ AllProductsTransportDashboardView, AllProductsTransportProductsListView, update_product_date, \
+ send_transport_carcass_detail_for_rasadyaar, delete_free_bar_from_rasadyaar, fix_number, \
+ get_evacuation_detail_by_request_code, get_evacuation_details_by_request_codes, evacuation_report_type_summary, get_all_products_transport_by_code, \
+ get_all_products_transport_dashboard_by_code, get_all_products_transport_products_by_code, \
+ get_all_products_transport_provinces_by_code
+
+router = DefaultRouter()
+
+router.register(
+ r'poultry',
+ app_views.PoultryViewSet,
+ basename="poultry"
+)
+
+router.register(
+ r'poultry-dashboard',
+ app_views.PoultryDashboardViewSet,
+ basename="poultry-dashboard"
+)
+router.register(
+ r'hatching',
+ app_views.PoultryHatchingViewSet,
+ basename="hatching"
+)
+
+router.register(
+ r'hatchings-dashboard',
+ app_views.HatchingDashboardViewSet,
+ basename="hatchings-dashboard"
+)
+
+router.register(
+ r'hatchings',
+ app_views.HatchingsViewSet,
+ basename="hatchings"
+)
+
+router.register(
+ r'hatchings-custom',
+ app_views.HatchingsCustomViewSet,
+ basename="hatchings-custom"
+)
+
+router.register(
+ r'hatching-pedigreename',
+ app_views.PoultryHatchingForUpdatePedigreeNameViewSet,
+ basename="hatching-pedigreename"
+)
+
+router.register(
+ r'transporting',
+ app_views.TransportingChickenDetailViewSet,
+ basename="transporting"
+)
+
+router.register(
+ r'transporting-dashboard',
+ app_views.TransportingDashboardViewSet,
+ basename="transporting-dashboard"
+)
+
+router.register(
+ r'transporting-detail',
+ app_views.TransportingDetailViewSet,
+ basename="transporting-detail"
+)
+
+router.register(
+ r'transporting-detail-custom',
+ app_views.TransportingDetailCustomViewSet,
+ basename="transporting-detail-custom"
+)
+
+router.register(
+ r'hatching-dashboard',
+ app_views.PoultryHatchingDashboardViewSet,
+ basename="hatching-dashboard"
+)
+
+router.register(
+ r'hatching-filtering',
+ app_views.HatchingViewSet,
+ basename="hatching-filtering"
+)
+
+router.register(
+ r'hatching-calculating',
+ app_views.HatchingCalculationsViewSet,
+ basename="hatching-calculating"
+)
+
+router.register(
+ r'poultry-info',
+ app_views.PoultryInfoViewSet,
+ basename="poultry-info"
+)
+
+router.register(
+ r'total-killhouse',
+ app_views.TotalKillHousesViewSet,
+ basename="total-killhouse"
+)
+
+router.register(
+ r'hatching-analysis-pedigree',
+ app_views.HatchingAnalysisPedigreeViewSet,
+ basename="hatching-analysis-pedigree"
+)
+
+router.register(
+ r'hatching-analysis-province',
+ app_views.HatchingAnalysisProvinceView,
+ basename="hatching-analysis-province"
+)
+
+router.register(
+ r'hatchings-analysis-overview',
+ app_views.HatchingAnalysisOverviewViewSet,
+ basename="hatchings-analysis-overview"
+)
+
+router.register(
+ r'transporting-report-dashboard',
+ app_views.TransportingReportDashboardViewSet,
+ basename="transporting-report-dashboard"
+)
+
+router.register(
+ r'transporting-analysis-dashboard',
+ app_views.TransportingAnalysisViewSet,
+ basename="transporting-analysis-dashboard"
+)
+router.register(
+ r'api_send_different_bar',
+ app_views.ApiSendDifferentBar,
+ basename="api_send_different_bar"
+)
+router.register(
+ r'api_send_different_bar-from-hatching',
+ app_views.ApiSendDifferentBarFromHatching,
+ basename="api_send_different_bar-from-hatching"
+)
+
+router.register(
+ r'apk-info',
+ app_views.ApkInfoViewSet,
+ basename="apk-info"
+)
+router.register(
+ r'transport-carcass-detail',
+ app_views.TransportCarcassDetailViewSet,
+ basename="transport-carcass-detail"
+)
+router.register(
+ r'driver',
+ app_views.DriveViewSet,
+ basename="driver"
+)
+router.register(
+ r'guilds-transport-carcass-detail',
+ app_views.GuildsTransportCarcassViewSet,
+ basename="guilds-transport-carcass-detail"
+)
+
+router.register(
+ r'inquiry_credentials',
+ app_views.InquiryCredentialsViewSet,
+ basename="inquiry_credentials"
+)
+
+router.register(
+ r'all-products-transport',
+ app_views.AllProductsTransportViewSet,
+ basename="all-products-transport"
+)
+
+router.register(
+ r'evacuation-detail',
+ app_views.EvacuationDetailViewSet,
+ basename="evacuation-detail"
+)
+
+router.register(
+ r'rasadyar-app-info',
+ app_views.RasadyarAppInfoViewSet,
+ basename="rasadyar-app-info"
+)
+
+
+
+urlpatterns = [
+ path('', include(router.urls)),
+ path('get-transport-to-kill/', get_transport_to_kill),
+ path('add_kill_house/', add_kill_house),
+ path('update_hatching/', update_hatching),
+ path('get_breeds/', get_breeds),
+ path('get_bar_info/', get_bar_info),
+ path('transporting_detail_excel/', transporting_detail_excel),
+ path('total_killhouse_excel/', total_killhouse_excel),
+ path('hatching_excel/', hatching_excel),
+ path('all_hatching_excel/', all_hatching_excel),
+ path('dashboard_total_kill_house/', dashboard_total_kill_house),
+ path('test_city/', test_city),
+ path('send_different_bar/', send_different_bar),
+ path('all_send_different_bar_excel/', all_send_different_bar_excel),
+ path('send_different_bar_with_licence_number/', send_different_bar_with_licence_number),
+ path('all_province_detail_for_map/', all_province_detail_for_map),
+ path('dashboard_province_detail_for_map/', dashboard_province_detail_for_map),
+ path('transport-carcass-dashboard/', TransportCarcassDashboardView.as_view()),
+ path('guilds-transport-carcass-dashboard/', GuildsTransportCarcassDashboardView.as_view()),
+ path('all-products-transport-dashboard/', AllProductsTransportDashboardView.as_view()),
+ path('all-products-transport-products/', AllProductsTransportProductsListView.as_view()),
+ path('update_product_date/', update_product_date),
+ path('transport-carcass-detail-excel/', transport_carcass_detail_excel),
+ path('guilds-transport-carcass-detail-excel/', guilds_transport_carcass_detail_excel),
+ path('send_transport_carcass_detail_for_rasadyaar/', send_transport_carcass_detail_for_rasadyaar),
+ path('delete_free_bar_from_rasadyaar/', delete_free_bar_from_rasadyaar),
+ path('fix_number/', fix_number),
+ path('get-evacuation-detail-by-request-code/', get_evacuation_detail_by_request_code),
+ path('get-evacuation-details-by-request-codes/', get_evacuation_details_by_request_codes),
+ path('api/get-hatching-permit-code/', api_get_hatching_permit_code),
+ path('evacuation-report-type-summary/', evacuation_report_type_summary),
+ path('get-all-products-transport-by-code/', get_all_products_transport_by_code),
+ path('get-all-products-transport-dashboard-by-code/', get_all_products_transport_dashboard_by_code),
+ path('get-all-products-transport-products-by-code/', get_all_products_transport_products_by_code),
+ path('get-all-products-transport-provinces-by-code/', get_all_products_transport_provinces_by_code),
+
+
+]
diff --git a/app/views.py b/app/views.py
new file mode 100644
index 0000000..32b5df7
--- /dev/null
+++ b/app/views.py
@@ -0,0 +1,5891 @@
+import datetime
+import json
+import random
+import string
+from this import d
+import uuid
+from collections import defaultdict
+from io import BytesIO
+
+import boto3
+import jdatetime
+import openpyxl
+import requests
+from bs4 import BeautifulSoup
+from django.db.models import Sum, Count, Avg, Q, Min, Max, Prefetch, F
+from django.http import HttpResponse
+from django.views.decorators.csrf import csrf_exempt
+from rest_framework import viewsets, status
+from rest_framework.decorators import api_view, permission_classes
+from rest_framework.permissions import AllowAny
+from rest_framework.response import Response
+from rest_framework.views import APIView
+
+from app.cityandprovince import iranprovince, irancity
+from app.filtersets import PoultryFilterSet, PoultryHatchingFilterSet, TransportingChickenDetailFilterSet, \
+ PoultryInfoFilterSet, HatchingCalculationsFilterSet, HatchingsFilterSet, TransportingDetailFilterSet, \
+ KillHouseFilterSet, TransportingDetailCustomFilterSet, CustomHatchingsFilterSet, TransportCarcassDetailFilterSet, \
+ DriverFilterSet, GuildsFilterSet, AllProductsTransportFilterSet
+from app.helper import SSLAdapter, get_hatching_permit_code
+from app.models import Poultry, PoultryHatching, TransportingChickenDetail, Hatching, TransportingDetail, KillHouse, \
+ ApkInfo, TransportCarcassDetail, Guilds, Driver, InquiryCredentials, AllProductsTransport, EvacuationDetail, \
+ RasadyarAppInfo
+from app.serializers import PoultrySerializer, PoultryHatchingSerializer, TransportingChickenDetailSerializer, \
+ HatchingSerializer, HatchingCalculationSerializer, PoultryInfoSerializer, HatchingsSerializer, \
+ HatchingDetailSerializer, HatchingForUpdateSerializer, TransportingSerializer, TransportingDetailSerializer, \
+ KillHouseSerializer, HatchingAnalysisSerializer, HatchingAnalysisSerializerTwo, TransportingReportDashboard, \
+ TransportingForClearanceCodeSerializer, ApkInfoSerializer, TransportCarcassDetailSerializer, DriverSerializer, \
+ KillHouseForTransportCarcassSerializer, StewardForTransportCarcassSerializer, \
+ GuildsForTransportCarcassSerializer, KillHouseForTransportCarcassForRassadyaarSerializer, \
+ TransportingDetailForUpdateSerializer, InquiryCredentialsSerializer, AllProductsTransportSerializer, \
+ EvacuationDetailSerializer, RasadyarAppInfoSerializer, AllProductsTransportCustomSerializer
+from authentication.models import Province
+from helpers import CustomPagination, build_query, build_calculation, convert_to_miladi
+
+
+class PoultryViewSet(viewsets.ModelViewSet):
+ queryset = Poultry.objects.all()
+ serializer_class = PoultrySerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = PoultryFilterSet
+
+ def create(self, request, *args, **kwargs):
+ data = request.data.get('Data', [])
+
+ for poultry in data:
+ registered_poultry = Poultry.objects.filter(UnitId=poultry['UnitId']).first()
+ if registered_poultry:
+ for key, value in poultry.items():
+ setattr(registered_poultry, key, value)
+ registered_poultry.save()
+ else:
+ Poultry.objects.create(**poultry)
+
+ return Response({"result": "با موفقیت ثبت شد"}, status=status.HTTP_201_CREATED)
+
+ def list(self, request, *args, **kwargs):
+ poultry = Poultry.objects.filter(trash=False)
+ value = request.GET.get('value')
+ if value:
+ poultry = poultry.filter(
+ build_query(self.filterset_class.Meta.fields, value)
+ )
+
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(poultry)
+ if page is not None:
+ serializer = self.get_serializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+
+ serializer = self.serializer_class(poultry, many=True)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+# class PoultryDashboardViewSet(viewsets.ModelViewSet):
+# queryset = Poultry.objects.filter(trash=False).only('id', 'Province', 'City').order_by('id')
+# serializer_class = PoultrySerializer
+# permission_classes = [AllowAny]
+# filterset_class = PoultryInfoFilterSet
+#
+# def list(self, request, *args, **kwargs):
+# poultries = self.queryset.filter(Province__isnull=False)
+# hatchings = Hatching.objects.filter(trash=False).order_by('id')
+# search = request.GET.get('search')
+# province = request.GET.get('province')
+# city = request.GET.get('city')
+# if province:
+# poultries = poultries.filter(Province__icontains=province)
+# hatchings = Hatching.objects.filter(ProvinceName=province, trash=False).only('CityName',
+# 'ProvinceName').order_by('id')
+#
+# if city:
+# poultries = poultries.filter(City__icontains=city)
+# hatchings = Hatching.objects.filter(CityName=city, trash=False).order_by('id')
+#
+# if search:
+# if search != 'undefined' and search.strip():
+# poultries = poultries.filter(
+# build_query(self.filterset_class.Meta.fields, search)
+# )
+# province = len(set(poultries.values_list('LocationIdProvince', flat=True)))
+# city = len(set(poultries.values_list('LocationIdCity', flat=True)))
+# active_hatchings = hatchings.filter(Age__lte=70, trash=False).order_by('id')
+# bars = TransportingDetail.objects.filter(hatching__in=hatchings, trash=False).order_by('id')
+# active_hatchings_bars = bars.filter(hatching__in=active_hatchings, trash=False).order_by('id')
+# total_active_hatching_quantity = active_hatchings.aggregate(total=Sum('ChickCountSum'))['total'] or 0
+# total_active_hatching_evacuation = active_hatchings.aggregate(total=Sum('Evacuation'))['total'] or 0
+# total_active_hatching_killing_quantity = active_hatchings_bars.aggregate(total=Sum('GoodAmount'))['total'] or 0
+# total_active_hatching_left_over = active_hatchings.aggregate(total=Sum('LeftOver'))['total'] or 0
+# total_hatching_quantity = hatchings.aggregate(total=Sum('ChickCountSum'))['total'] or 0
+# total_hatching_evacuation = hatchings.aggregate(total=Sum('Evacuation'))['total'] or 0
+# total_hatching_killing_quantity = bars.aggregate(total=Sum('GoodAmount'))['total'] or 0
+# total_hatching_left_over = hatchings.aggregate(total=Sum('LeftOver'))['total'] or 0
+#
+# result = {
+# "poultry_count": poultries.count(),
+# "total_hatching_count": hatchings.count(),
+# "total_hatching_quantity": total_hatching_quantity,
+# "total_hatching_evacuation": total_hatching_evacuation,
+# "total_hatching_evacuation_percent": round((total_hatching_evacuation / total_hatching_quantity) * 100, 2),
+# "total_hatching_killing_quantity": total_hatching_killing_quantity,
+# "total_hatching_killing_quantity_percent": round(
+# (total_hatching_killing_quantity / total_hatching_quantity) * 100, 2),
+# "total_hatching_left_over": total_hatching_left_over,
+# "total_hatching_left_over_percent": round(
+# (total_hatching_left_over / total_hatching_quantity) * 100, 2),
+# "total_hatching_killing_age": bars.aggregate(avg_age=Avg('Age'))['avg_age'] or 0,
+# "province_count": province,
+# "city_count": city,
+# "total_active_hatching_count": active_hatchings.count(),
+# "total_active_hatching_quantity": total_active_hatching_quantity,
+# "total_active_hatching_evacuation": total_active_hatching_evacuation,
+# "total_active_hatching_evacuation_percent": round(
+# (total_active_hatching_evacuation / total_active_hatching_quantity) * 100, 2),
+# "total_active_hatching_bars": active_hatchings_bars.count(),
+# "total_active_hatching_killing_quantity": total_active_hatching_killing_quantity,
+# "total_active_hatching_killing_quantity_percent": round(
+# (total_active_hatching_killing_quantity / total_active_hatching_quantity) * 100, 2),
+# "total_active_hatching_killing_age": active_hatchings_bars.aggregate(avg_age=Avg('Age'))['avg_age'] or 0,
+# "total_active_hatching_left_over": active_hatchings.aggregate(total=Sum('LeftOver'))['total'] or 0,
+# "total_active_hatching_left_over_percent": round(
+# (total_active_hatching_left_over / total_active_hatching_quantity) * 100, 2),
+# # "hatching_killing_persent": active_hatchings.aggregate(total=Sum('LeftOver'))['total'] or 0,
+#
+# }
+# return Response(result, status=status.HTTP_200_OK)
+
+
+class PoultryDashboardViewSet(viewsets.ModelViewSet):
+ queryset = Poultry.objects.filter(trash=False).only(
+ 'id', 'Province', 'City', 'LocationIdProvince', 'LocationIdCity'
+ ).order_by('id')
+
+ serializer_class = PoultrySerializer
+ permission_classes = [AllowAny]
+ filterset_class = PoultryInfoFilterSet
+
+ def _optimized_db_hits(self, request):
+
+ province = request.GET.get('province')
+ city = request.GET.get('city')
+ search = request.GET.get('search')
+
+ base_poultries = self.queryset.filter(Province__isnull=False)
+ base_hatchings = Hatching.objects.filter(trash=False)
+
+ if province:
+ base_poultries = base_poultries.filter(Province__icontains=province)
+ base_hatchings = base_hatchings.filter(ProvinceName=province)
+
+ if city:
+ base_poultries = base_poultries.filter(City__icontains=city)
+ base_hatchings = base_hatchings.filter(CityName=city)
+
+ if search and search != 'undefined' and search.strip():
+ base_poultries = base_poultries.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+
+ hatchings_data = base_hatchings.aggregate(
+ total_count=Count('id'),
+ total_quantity=Sum('ChickCountSum'),
+ total_evacuation=Sum('Evacuation'),
+ total_left_over=Sum('LeftOver'),
+ active_count=Count('id', filter=Q(Age__lte=70)),
+ active_quantity=Sum('ChickCountSum', filter=Q(Age__lte=70)),
+ active_evacuation=Sum('Evacuation', filter=Q(Age__lte=70)),
+ active_left_over=Sum('LeftOver', filter=Q(Age__lte=70)),
+ active_ready_left_over=Sum('LeftOver', filter=Q(Age__gte=40, Age__lte=70)),
+
+ )
+
+ transporting_data = TransportingDetail.objects.filter(
+ hatching__in=base_hatchings,
+ trash=False
+ ).aggregate(
+ total_killing=Sum('GoodAmount'),
+ total_killing_age=Avg('Age'),
+ active_killing=Sum('GoodAmount', filter=Q(hatching__Age__lte=70)),
+ active_killing_age=Avg('Age', filter=Q(hatching__Age__lte=70)),
+ )
+
+ location_stats = base_poultries.aggregate(
+ province_count=Count('LocationIdProvince', distinct=True),
+ city_count=Count('LocationIdCity', distinct=True),
+ )
+
+ return {
+ 'poultry_count': base_poultries.count(),
+ **hatchings_data,
+ **transporting_data,
+ **location_stats,
+ 'active_bars_count': base_hatchings.filter(Age__lte=70).count(),
+ }
+
+ def list(self, request, *args, **kwargs):
+ try:
+ data = self._optimized_db_hits(request)
+
+ total_quantity = data.get('total_quantity', 0) or 1
+ active_quantity = data.get('active_quantity', 0) or 1
+
+ result = {
+ "poultry_count": data['poultry_count'],
+ "province_count": data['province_count'],
+ "city_count": data['city_count'],
+
+ "total_hatching_count": data['total_count'],
+ "total_hatching_quantity": data['total_quantity'],
+ "total_hatching_evacuation": data['total_evacuation'],
+ "total_hatching_evacuation_percent": round((data['total_evacuation'] / total_quantity) * 100, 2),
+ "total_hatching_killing_quantity": data['total_killing'],
+ "total_hatching_killing_quantity_percent": round((data['total_killing'] / total_quantity) * 100, 2),
+ "total_hatching_left_over": data['total_left_over'],
+ "total_hatching_left_over_percent": round((data['total_left_over'] / total_quantity) * 100, 2),
+ "total_hatching_killing_age": data['total_killing_age'],
+
+ "total_active_hatching_count": data['active_count'],
+ "total_active_hatching_quantity": data['active_quantity'],
+ "total_active_hatching_evacuation": data['active_evacuation'],
+ "total_active_hatching_evacuation_percent": round((data['active_evacuation'] / active_quantity) * 100,
+ 2),
+ "total_active_hatching_bars": data['active_bars_count'],
+ "total_active_hatching_killing_quantity": data['active_killing'],
+ "total_active_hatching_killing_quantity_percent": round(
+ (data['active_killing'] / active_quantity) * 100, 2),
+ "total_active_hatching_killing_age": data['active_killing_age'],
+ "total_active_hatching_left_over": data['active_left_over'],
+ "total_active_hatching_left_over_percent": round((data['active_left_over'] / active_quantity) * 100, 2),
+ "total_ready_active_hatching_left_over": data['active_ready_left_over'],
+ "total_ready_hatching_left_over_percent": round(
+ (data['active_ready_left_over'] / data['active_left_over']) * 100, 2),
+ }
+
+ return Response(result, status=status.HTTP_200_OK)
+
+ except Exception as e:
+ return Response(
+ {"error": "خطا در پردازش دادهها", "details": str(e)},
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR
+ )
+
+
+@api_view(['GET'])
+@permission_classes([AllowAny])
+@csrf_exempt
+def all_province_detail_for_map(request):
+ provinces = list(Province.objects.values_list('name', flat=True))
+
+ hatchings = Hatching.objects.filter(
+ Age__gte=1,
+ Age__lte=65,
+ trash=False,
+ ProvinceName__in=provinces
+ ).values('ProvinceName').annotate(
+ total_quantity=Sum('ChickCountSum'),
+ total_left_over=Sum('LeftOver')
+ )
+
+ active_hatchings = hatchings.filter(
+ Age__gte=30,
+ Age__lte=65
+ ).values('ProvinceName').annotate(
+ total_left_over=Sum('LeftOver')
+ )
+
+ transportings = TransportingDetail.objects.filter(
+ hatching__in=Hatching.objects.filter(
+ Age__gte=1,
+ Age__lte=65,
+ trash=False,
+ ProvinceName__in=provinces
+ ),
+ trash=False
+ ).values('hatching__ProvinceName').annotate(
+ total_killing=Sum('GoodAmount')
+ )
+
+ hatching_dict = {h['ProvinceName']: h for h in hatchings}
+ active_hatching_dict = {h['ProvinceName']: h for h in active_hatchings}
+ transporting_dict = {t['hatching__ProvinceName']: t for t in transportings}
+
+ results = []
+ for province in provinces:
+ h_data = hatching_dict.get(province, {'total_quantity': 0, 'total_left_over': 0})
+ a_h_data = active_hatching_dict.get(province, {'total_left_over': 0})
+ t_data = transporting_dict.get(province, {'total_killing': 0})
+
+ total_quantity = h_data['total_quantity'] or 0
+ total_left = h_data['total_left_over'] or 0
+ total_active_left = a_h_data['total_left_over'] or 0
+
+ results.append({
+ "total_quantity": total_quantity,
+ "total_left_over": total_left,
+ "total_killed_quantity": t_data['total_killing'] or 0,
+ "total_hatching_left_over_percent": round(
+ (total_left / total_quantity * 100),
+ ) if total_quantity > 0 else 0,
+ "province_name": province,
+ "total_active_left": total_active_left,
+ "total_active_left_percent": round(
+ (total_active_left / total_quantity * 100),
+ ) if total_quantity > 0 else 0,
+ })
+
+ sorted_results = sorted(
+ results,
+ key=lambda x: x['total_quantity'],
+ reverse=True
+ )
+
+ return Response(sorted_results)
+
+
+@api_view(['GET'])
+@permission_classes([AllowAny])
+@csrf_exempt
+def dashboard_province_detail_for_map(request):
+ total_hatching = Hatching.objects.filter(
+ Age__gte=1,
+ Age__lte=65,
+ trash=False
+ ).aggregate(
+ total_quantity=Sum('ChickCountSum'),
+ total_left_over=Sum('LeftOver')
+ )
+
+ total_active = Hatching.objects.filter(
+ Age__gte=30,
+ Age__lte=65,
+ trash=False
+ ).aggregate(
+ total_active_left=Sum('LeftOver')
+ )
+
+ total_transporting = TransportingDetail.objects.filter(
+ hatching__in=Hatching.objects.filter(
+ Age__gte=1,
+ Age__lte=65,
+ trash=False
+ ),
+ trash=False
+ ).aggregate(
+ total_killing=Sum('GoodAmount')
+ )
+
+ total_quantity = total_hatching['total_quantity'] or 0
+ total_left = total_hatching['total_left_over'] or 0
+ total_active_left = total_active['total_active_left'] or 0
+ left_over_percent = round((total_left / total_quantity * 100)) if total_quantity > 0 else 0
+ total_active_left_percent = round((total_active_left / total_quantity * 100)) if total_quantity > 0 else 0
+
+ result = {
+ "total_quantity": total_quantity,
+ "total_left_over": total_left,
+ "total_killed_quantity": total_transporting['total_killing'] or 0,
+ "total_hatching_left_over_percent": left_over_percent,
+ "total_active_left": total_active_left,
+ "total_active_left_percent": total_active_left_percent,
+ }
+
+ return Response(result)
+
+# class HatchingDashboardViewSet(viewsets.ModelViewSet):
+# queryset = Hatching.objects.filter(trash=False).only('KillingAve', 'LeftOver', 'ProvinceName', 'CityName',
+# 'SystemCode').order_by(
+# 'id')
+# serializer_class = HatchingSerializer
+# permission_classes = [AllowAny]
+# filterset_class = HatchingsFilterSet
+#
+# def list(self, request, *args, **kwargs):
+# system_code = request.GET.get('system_code')
+# search = request.GET.get('search')
+# leftover = request.GET.get('leftover')
+# province = request.GET.get('province')
+# city = request.GET.get('city')
+# killing_age = request.GET.get('killing_age')
+#
+# hatchings = self.queryset
+# date1 = self.request.GET.get('date1')
+# date2 = self.request.GET.get('date2')
+# if date1 and date2:
+# date1 = datetime.datetime.strptime(str(self.request.GET['date1']), '%Y-%m-%d').date()
+#
+# date2 = datetime.datetime.strptime(str(self.request.GET['date2']), '%Y-%m-%d').date()
+#
+# hatchings = hatchings.filter(Date__date__gte=date1, Date__date__lte=date2, trash=False)
+#
+# if killing_age:
+# hatchings = hatchings.filter(KillingAve=int(killing_age), trash=False)
+# if leftover:
+# hatchings = hatchings.filter(LeftOver__gt=0, trash=False)
+#
+# if province:
+# hatchings = hatchings.filter(ProvinceName=province)
+#
+# if system_code:
+# hatchings = hatchings.filter(SystemCode=system_code)
+#
+# if city:
+# hatchings = hatchings.filter(CityName=city)
+#
+#
+#
+# if search:
+# if search != 'undefined' and search.strip():
+# hatchings = hatchings.filter(
+# build_query(self.filterset_class.Meta.fields, search)
+# )
+# active_hatchings = hatchings.filter(Age__lte=70, trash=False).order_by('id')
+# bars = TransportingDetail.objects.filter(hatching__in=hatchings, trash=False).order_by('id')
+# active_hatchings_bars = bars.filter(hatching__in=active_hatchings, trash=False).order_by('id')
+# total_hatching_quantity = hatchings.aggregate(total=Sum('ChickCountSum'))['total'] or 0
+# total_hatching_evacuation = hatchings.aggregate(total=Sum('Evacuation'))['total'] or 0
+# total_hatching_killing_quantity = bars.aggregate(total=Sum('GoodAmount'))['total'] or 0
+# total_hatching_left_over = hatchings.aggregate(total=Sum('LeftOver'))['total'] or 0
+# total_active_hatching_quantity = active_hatchings.aggregate(total=Sum('ChickCountSum'))['total'] or 0
+# total_active_hatching_evacuation = active_hatchings.aggregate(total=Sum('Evacuation'))['total'] or 0
+# total_active_hatching_killing_quantity = active_hatchings_bars.aggregate(total=Sum('GoodAmount'))[
+# 'total'] or 0
+# total_active_hatching_left_over = active_hatchings.aggregate(total=Sum('LeftOver'))['total'] or 0
+#
+# result = {
+# "total_hatching_count": hatchings.count(),
+# "total_hatching_quantity": total_hatching_quantity,
+# "total_hatching_evacuation": total_hatching_evacuation,
+# "total_hatching_evacuation_percent": round((total_hatching_evacuation / total_hatching_quantity) * 100, 2) if total_hatching_quantity > 0 else 0,
+# "total_hatching_killing_quantity": total_hatching_killing_quantity,
+# "total_hatching_killing_quantity_percent": round(
+# (total_hatching_killing_quantity / total_hatching_quantity) * 100, 2) if total_hatching_quantity > 0 else 0,
+# "total_hatching_left_over": total_hatching_left_over,
+# "total_hatching_left_over_percent": round((total_hatching_left_over / total_hatching_quantity) * 100, 2) if total_hatching_quantity > 0 else 0,
+# "total_hatching_killing_age": bars.aggregate(avg_age=Avg('Age'))['avg_age'] or 0,
+# "total_hatching_bars": active_hatchings_bars.count(),
+# "total_active_hatching_count": active_hatchings.count(),
+# "total_active_hatching_quantity": total_active_hatching_quantity,
+# "total_active_hatching_evacuation": total_active_hatching_evacuation,
+# "total_active_hatching_evacuation_percent": round(
+# (total_active_hatching_evacuation / total_active_hatching_quantity) * 100, 2) if total_active_hatching_quantity > 0 else 0,
+# "total_active_hatching_bars": active_hatchings_bars.count(),
+# "total_active_hatching_killing_quantity": total_active_hatching_killing_quantity,
+# "total_active_hatching_killing_quantity_percent": round(
+# (total_active_hatching_killing_quantity / total_active_hatching_quantity) * 100, 2) if total_active_hatching_quantity > 0 else 0,
+# "total_active_hatching_left_over": total_active_hatching_left_over,
+# "total_active_hatching_left_over_percent": round(
+# (total_active_hatching_left_over / total_active_hatching_quantity) * 100, 2) if total_active_hatching_quantity > 0 else 0,
+# "total_active_hatching_killing_age": active_hatchings_bars.aggregate(avg_age=Avg('Age'))['avg_age'] or 0,
+# "least_age": active_hatchings.order_by('Age').first().Age if active_hatchings else 0,
+# "most_age": active_hatchings.order_by('Age').last().Age if active_hatchings else 0,
+#
+# }
+# return Response(result, status=status.HTTP_200_OK)
+
+
+class HatchingDashboardViewSet(viewsets.ModelViewSet):
+ queryset = Hatching.objects.filter(trash=False).only(
+ 'id', 'KillingAve', 'LeftOver', 'ProvinceName', 'CityName',
+ 'SystemCode', 'Date', 'Age', 'ChickCountSum', 'Evacuation'
+ ).order_by('id')
+ serializer_class = HatchingSerializer
+ permission_classes = [AllowAny]
+ filterset_class = HatchingsFilterSet
+
+ def _apply_filters(self, queryset, request):
+ system_code = request.GET.get('system_code')
+ search = request.GET.get('search')
+ leftover = request.GET.get('leftover')
+ province = request.GET.get('province')
+ city = request.GET.get('city')
+ killing_age = request.GET.get('killing_age')
+ name = request.GET.get('name')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+
+ if date1 and date2:
+ try:
+ date1 = datetime.datetime.strptime(date1, '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(date2, '%Y-%m-%d').date()
+ queryset = queryset.filter(Date__date__range=[date1, date2])
+ except ValueError:
+ pass
+
+ if killing_age:
+ queryset = queryset.filter(KillingAve=int(killing_age))
+ if leftover:
+ queryset = queryset.filter(LeftOver__gt=0)
+ if province:
+ queryset = queryset.filter(ProvinceName=province)
+ if system_code:
+ queryset = queryset.filter(SystemCode=system_code)
+ if city:
+ queryset = queryset.filter(CityName=city)
+ if name:
+ poultry = Poultry.objects.filter(
+ Q(UserName__contains=name) | Q(FirstName__contains=name) | Q(LastName__contains=name) | Q(
+ Mobile__contains=name) | Q(UnitName__contains=name)
+ , trash=False).first()
+ queryset = queryset.filter(poultry=poultry)
+ if search and search != 'undefined' and search.strip():
+ queryset = queryset.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+
+ return queryset
+
+ def _calculate_metrics(self, hatchings):
+
+ hatchings = hatchings.prefetch_related(
+ Prefetch('transportingdetail_set',
+ queryset=TransportingDetail.objects.filter(trash=False))
+ )
+
+ active_hatchings = hatchings.filter(Age__lte=70)
+
+ main_stats = hatchings.aggregate(
+ total_count=Count('id'),
+ total_quantity=Sum('ChickCountSum'),
+ total_evacuation=Sum('Evacuation'),
+ total_left_over=Sum('LeftOver'),
+ active_count=Count('id', filter=Q(Age__lte=70)),
+ active_quantity=Sum('ChickCountSum', filter=Q(Age__lte=70)),
+ active_evacuation=Sum('Evacuation', filter=Q(Age__lte=70)),
+ active_left_over=Sum('LeftOver', filter=Q(Age__lte=70)),
+ active_ready_left_over=Sum('LeftOver', filter=Q(Age__gte=40, Age__lte=70)),
+
+ )
+
+ bars_stats = TransportingDetail.objects.filter(
+ hatching__in=hatchings,
+ trash=False
+ ).aggregate(
+ total_killing=Sum('GoodAmount'),
+ avg_killing_age=Avg('Age'),
+ active_killing=Sum('GoodAmount', filter=Q(hatching__Age__lte=70)),
+ active_avg_age=Avg('Age', filter=Q(hatching__Age__lte=70)),
+ active_bars_count=Count('id'),
+ min_age=Min('Age'),
+ max_age=Max('Age')
+ )
+
+ return {**main_stats, **bars_stats}
+
+ def list(self, request, *args, **kwargs):
+ # try:
+ filtered_hatchings = self._apply_filters(self.queryset, request)
+
+ metrics = self._calculate_metrics(filtered_hatchings)
+
+ total_quantity = metrics.get('total_quantity', 0) or 1
+ active_quantity = metrics.get('active_quantity', 0) or 1
+ if filtered_hatchings and filtered_hatchings.first().poultry:
+ poultry_name = filtered_hatchings.first().poultry.UnitName
+ poultry_mobile = filtered_hatchings.first().poultry.Mobile
+ poultry_firstName = filtered_hatchings.first().poultry.FirstName
+ poultry_lastName = filtered_hatchings.first().poultry.LastName
+ poultry_userName = filtered_hatchings.first().poultry.UserName
+ else:
+ poultry_name = ''
+ poultry_mobile = ''
+ poultry_firstName = ''
+ poultry_lastName = ''
+ poultry_userName = ''
+ result = {
+ "poultry_name": poultry_name,
+ "poultry_mobile": poultry_mobile,
+ "poultry_firstName": poultry_firstName,
+ "poultry_lastName": poultry_lastName,
+ "poultry_userName": poultry_userName,
+ "total_hatching_count": metrics['total_count'] if metrics['total_count'] else 0,
+ "total_hatching_quantity": metrics['total_quantity'] if metrics['total_quantity'] else 0,
+ "total_hatching_evacuation": metrics['total_evacuation'] if metrics['total_evacuation'] else 0,
+ "total_hatching_evacuation_percent": round((metrics['total_evacuation'] / total_quantity) * 100, 2) if
+ metrics['total_evacuation'] else 0,
+ "total_hatching_killing_quantity": metrics['total_killing'] if metrics['total_killing'] else 0,
+ "total_hatching_killing_quantity_percent": round((metrics['total_killing'] / total_quantity) * 100, 2) if
+ metrics['total_killing'] else 0,
+ "total_hatching_left_over": metrics['total_left_over'] if metrics['total_killing'] else 0,
+ "total_hatching_left_over_percent": round((metrics['total_left_over'] / total_quantity) * 100, 2) if
+ metrics['total_left_over'] else 0,
+ "total_hatching_killing_age": metrics['avg_killing_age'] if metrics['avg_killing_age'] else 0,
+ "total_hatching_bars": metrics.get('active_bars_count', 0),
+
+ "total_active_hatching_count": metrics['active_count'] if metrics['active_count'] else 0,
+ "total_active_hatching_quantity": metrics['active_quantity'] if metrics['active_quantity'] else 0,
+ "total_active_hatching_evacuation": metrics['active_evacuation'] if metrics['active_evacuation'] else 0,
+ "total_active_hatching_evacuation_percent": round(
+ (metrics['active_evacuation'] / active_quantity) * 100, 2) if metrics['active_evacuation'] else 0,
+ "total_active_hatching_bars": metrics.get('active_bars_count', 0),
+ "total_active_hatching_killing_quantity": metrics['active_killing'] if metrics['active_killing'] else 0,
+ "total_active_hatching_killing_quantity_percent": round(
+ (metrics['active_killing'] / active_quantity) * 100, 2) if metrics['active_killing'] else 0,
+ "total_active_hatching_left_over": metrics['active_left_over'] if metrics['active_left_over'] else 0,
+ "total_active_hatching_left_over_percent": round((metrics['active_left_over'] / active_quantity) * 100,
+ 2) if metrics['active_left_over'] else 0,
+ "total_ready_active_hatching_left_over": metrics['active_ready_left_over'] if metrics[
+ 'active_ready_left_over'] else 0,
+ "total_ready_hatching_left_over_percent": round(
+ (metrics['active_ready_left_over'] / metrics['active_left_over']) * 100,
+ 2) if metrics['active_ready_left_over'] else 0,
+ "total_active_hatching_killing_age": metrics['active_avg_age'] or 0,
+ "least_age": metrics.get('min_age', 0),
+ "most_age": metrics.get('max_age', 0),
+ }
+
+ return Response(result, status=status.HTTP_200_OK)
+
+ # except Exception as e:
+ # return Response(
+ # {"error": str(e)},
+ # status=status.HTTP_500_INTERNAL_SERVER_ERROR
+ # )
+
+
+class PoultryHatchingDashboardViewSet(viewsets.ModelViewSet):
+ queryset = PoultryHatching.objects.all()
+ serializer_class = PoultryHatchingSerializer
+ permission_classes = [AllowAny]
+ filterset_class = PoultryHatchingFilterSet
+
+ def list(self, request, *args, **kwargs):
+ hatchings = PoultryHatching.objects.filter(trash=False).order_by('Date')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ hatchings = hatchings.filter(Date__date__gte=date1, Date__date__lte=date2)
+ search = request.GET.get('search')
+ if search:
+ if search != 'undefined' and search.strip():
+ hatchings = hatchings.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+
+ poultry = Poultry.objects.filter(pk__in=hatchings.values_list('poultry', flat=True), trash=False)
+ transports = TransportingChickenDetail.objects.filter(hatching__isnull=False, trash=False)
+ hatching_quantity = hatchings.aggregate(total=Sum('HatchingCount'))['total'] or 0
+ evacuation_count = hatchings.aggregate(total=Sum('EvacuationCount'))['total'] or 0
+ bars_quantity = transports.aggregate(total=Sum('GoodAmount'))['total'] or 0
+ result = {
+ "poultry": len(poultry),
+ "hatchings": len(hatchings),
+ "hatching_quantity": hatching_quantity,
+ "evacuation_count": evacuation_count,
+ "hatching_remain_quantity": hatching_quantity - evacuation_count,
+ "bars": len(transports),
+ "bars_quantity": bars_quantity,
+
+ }
+
+ return Response(result, status=status.HTTP_200_OK)
+
+
+class PoultryHatchingViewSet(viewsets.ModelViewSet):
+ queryset = PoultryHatching.objects.all()
+ serializer_class = PoultryHatchingSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = PoultryHatchingFilterSet
+
+ def create(self, request, *args, **kwargs):
+ data = request.data['Data']
+ request.data.pop('Data')
+ for hatching_data in data:
+ registered_hatching = PoultryHatching.objects.filter(DesCertId=hatching_data['DesCertId']).first()
+ if registered_hatching:
+ for key, value in hatching_data.items():
+ setattr(registered_hatching, key, value)
+ registered_hatching.save()
+
+ else:
+ hatching = PoultryHatching.objects.create(**hatching_data)
+ poultry = Poultry.objects.filter(UnitId=hatching_data['UnitId']).first()
+ if poultry:
+ hatching.poultry = poultry
+ hatching.save()
+
+ return Response({"result": "با موفقیت ثبت شد"}, status=status.HTTP_201_CREATED)
+
+ def list(self, request, *args, **kwargs):
+ hatchings = PoultryHatching.objects.filter(trash=False).order_by('Date')
+ # hatchings = PoultryHatching.objects.filter(
+ # pk__in=TransportingChickenDetail.objects.filter(trash=False).values_list('hatching', flat=True),
+ # trash=False).order_by('Date')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ hatchings = hatchings.filter(Date__date__gte=date1, Date__date__lte=date2)
+
+ search = request.GET.get('search')
+ if search:
+ if search != 'undefined' and search.strip():
+ hatchings = hatchings.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(hatchings)
+ if page is not None:
+ serializer = self.get_serializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+
+ serializer = self.serializer_class(hatchings, many=True)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+class HatchingsViewSet(viewsets.ModelViewSet):
+ queryset = Hatching.objects.filter(trash=False)
+ serializer_class = HatchingsSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = HatchingsFilterSet
+
+ def set_filters(self):
+ filters = {}
+ system_code = self.request.GET.get('system_code')
+ city = self.request.GET.get('city')
+ province = self.request.GET.get('province')
+ age = self.request.GET.get('age')
+ killing_age = self.request.GET.get('killing_age')
+ date1 = self.request.GET.get('date1')
+ date2 = self.request.GET.get('date2')
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(self.request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(self.request.GET['date2']), '%Y-%m-%d').date()
+ filters['Date__date__gte'] = date1
+ filters['Date__date__lte'] = date2
+
+ if killing_age:
+ filters['KillingAve'] = int(killing_age)
+ if system_code:
+ filters['SystemCode'] = system_code
+ if age:
+ filters['Age__exact'] = age
+ if city:
+ filters['CityName__icontains'] = city
+ if province:
+ filters['ProvinceName__icontains'] = province
+
+ return filters
+
+ def create(self, request, *args, **kwargs):
+ BREED_STANDARDIZATION = {
+ 'آربراکرز (آ��لاس)': 'آربراکرز (آپلاس)',
+ 'آربراک��ز (آپلاس)': 'آربراکرز (آپلاس)',
+ 'آربر��کرز (آپلاس)': 'آربراکرز (آپلاس)',
+ 'را��': 'راس',
+ 'ر��س': 'راس',
+ '��اس': 'راس',
+ 'آ��ین': 'آرین',
+ 'آر��ن': 'آرین',
+ 'ایندین ریو��': 'ایندین ریور',
+ 'ایند��ن ریور': 'ایندین ریور',
+ 'ک��ب': 'کاب'
+ }
+ if int(request.data['GoodSum']) > 0:
+ bars = request.data.get('Transports', [])
+ if 'Transports' in request.data:
+ request.data.pop('Transports')
+ evacuation_details = request.data.get('EvacuationDetail', [])
+ if 'EvacuationDetail' in request.data:
+ request.data.pop('EvacuationDetail')
+
+ hatching = Hatching.objects.filter(RequestCode=request.data['RequestCode']).first()
+ print({'1': request.data['ProvinceName']})
+ if hatching:
+ for key, value in request.data.items():
+ setattr(hatching, key, value)
+ hatching.save()
+
+ else:
+ poultry = Poultry.objects.filter(SystemCode=request.data['SystemCode']).first()
+ hatchings = Hatching.objects.filter(poultry=poultry).order_by('id').last()
+ period = hatchings.Period + 1 if hatchings else 1
+ request.data['Period'] = period
+ hatching = Hatching.objects.create(**request.data)
+ hatching.ArchiveDate = hatching.Date + datetime.timedelta(days=76)
+
+ if poultry:
+ hatching.poultry = poultry
+ hatching.save()
+ allowed_evacuation_fields = {
+ 'PartIdCode', 'RequestId', 'MoReportId', 'ReportType', 'ReportTypeString',
+ 'ReportDate', 'ReportDateShamsi', 'MoReason', 'MoDate', 'MoDateShamsi',
+ 'MoStartDay', 'MoEndDay', 'MoReportSubId', 'ReportStatus', 'GoodCount',
+ 'Message', 'ErrorCode', 'IsDeleted', 'RegDate', 'RegDateShamsi',
+ 'RegDateShamsiWithTime', 'RegDateShamsiOnlyTime', 'ExternalId', 'StringId',
+ 'IsPersisted', 'AllowInsert', 'AllowUpdate', 'ModalCss',
+ 'GridContainerParametersModel', 'MenuUserAccess', 'MenuUserAccessId',
+ 'LogTableName', 'LogTableAlias', 'PageTitle'
+ }
+
+ if evacuation_details:
+ cleaned_payload = []
+ external_ids = []
+ for evacuation_data in evacuation_details:
+ clean_data = {
+ k: v for k, v in evacuation_data.items()
+ if k in allowed_evacuation_fields or k == 'Id'
+ }
+ external_id = clean_data.pop('Id', None)
+ if external_id is not None:
+ clean_data['ExternalId'] = external_id
+ external_ids.append(external_id)
+ cleaned_payload.append((external_id, clean_data))
+
+ existing_map = {}
+ if external_ids:
+ existing_qs = EvacuationDetail.objects.filter(
+ ExternalId__in=external_ids,
+ trash=False
+ )
+ existing_map = {ev.ExternalId: ev for ev in existing_qs}
+
+ bulk_create = []
+ for external_id, clean_data in cleaned_payload:
+ if external_id:
+ evacuation = existing_map.get(external_id)
+ if evacuation and 'GoodCount' in clean_data:
+ evacuation.GoodCount = clean_data['GoodCount']
+ evacuation.hatching = hatching
+ evacuation.save(update_fields=['GoodCount', 'hatching'])
+ continue
+ clean_data['hatching'] = hatching
+ bulk_create.append(EvacuationDetail(**clean_data))
+
+ if bulk_create:
+ EvacuationDetail.objects.bulk_create(bulk_create)
+
+ if bars:
+ for transport_data in bars:
+ transport = TransportingDetail.objects.filter(
+ TrackingCode=transport_data['TrackingCode']).first()
+ if transport:
+ for key, value in transport_data.items():
+ setattr(transport, key, value)
+ transport.save()
+
+ else:
+ transport = TransportingDetail.objects.create(**transport_data)
+ kill_house = KillHouse.objects.filter(PartIdCode=transport.DesPartIdCode, trash=False).first()
+ transport.hatching = hatching
+ if kill_house:
+ transport.Province = kill_house.Province
+ transport.City = kill_house.City
+ if hatching.poultry.LocationIdProvince != kill_house.ProvinceId:
+ transport.Out = True
+ transport.save()
+ transport.Age = (transport.Date.date() - transport.hatching.Date.date()).days + 1
+ transport.save()
+ bars = TransportingDetail.objects.filter(trash=False, hatching=hatching)
+ bars_quantity = bars.filter(TrackingStatusDescription='تایید تخلیه').aggregate(total=Sum('GoodAmount'))[
+ 'total'] or 0
+ ave_age = int(bars.aggregate(avg_age=Avg('Age'))[
+ 'avg_age'] or 0)
+ hatching.LeftOver = hatching.ChickCountSum - (hatching.Evacuation + bars_quantity) if (
+ hatching.ChickCountSum - (
+ hatching.Evacuation + bars_quantity)) > 0 else 0
+ print({'2': hatching.ProvinceName})
+ if hatching.PedigreeName in BREED_STANDARDIZATION:
+ hatching.PedigreeName = BREED_STANDARDIZATION[hatching.PedigreeName]
+ hatching.KillingAve = ave_age
+ hatching.samasat_discharge_percentage = int(
+ ((hatching.Evacuation + bars_quantity) / hatching.ChickCountSum) * 100)
+ hatching.save()
+ else:
+ print('تعداد حمل صفر است')
+ return Response({"result": "با موفقیت ثبت شد"}, status=status.HTTP_201_CREATED)
+
+ def list(self, request, *args, **kwargs):
+ state = self.request.GET.get('state')
+ hatchings = Hatching.objects.filter(**self.set_filters())
+ if state:
+ if state == 'pending':
+ hatchings = hatchings.filter(Age__lte=70)
+
+ else:
+ hatchings = hatchings.filter(Age__gt=70)
+
+ search = request.GET.get('search')
+ if search:
+ if search != 'undefined' and search.strip():
+ hatchings = hatchings.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(hatchings)
+ if page is not None:
+ serializer = HatchingDetailSerializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+
+ serializer = HatchingDetailSerializer(hatchings, many=True)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+class HatchingsCustomViewSet(viewsets.ModelViewSet):
+ queryset = Hatching.objects.filter(trash=False)
+ serializer_class = HatchingsSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = CustomHatchingsFilterSet
+
+ def create(self, request, *args, **kwargs):
+ BREED_STANDARDIZATION = {
+ 'آربراکرز (آ��لاس)': 'آربراکرز (آپلاس)',
+ 'آربراک��ز (آپلاس)': 'آربراکرز (آپلاس)',
+ 'آربر��کرز (آپلاس)': 'آربراکرز (آپلاس)',
+ 'را��': 'راس',
+ 'ر��س': 'راس',
+ '��اس': 'راس',
+ 'آ��ین': 'آرین',
+ 'آر��ن': 'آرین',
+ 'ایندین ریو��': 'ایندین ریور',
+ 'ایند��ن ریور': 'ایندین ریور',
+ 'ک��ب': 'کاب'
+ }
+
+ bars = request.data.get('Transports', [])
+ if 'Transports' in request.data:
+ request.data.pop('Transports')
+
+ evacuation_details = request.data.get('EvacuationDetail', [])
+ if 'EvacuationDetail' in request.data:
+ request.data.pop('EvacuationDetail')
+
+ hatching = Hatching.objects.filter(RequestCode=request.data['RequestCode']).first()
+ if hatching:
+ for key, value in request.data.items():
+ setattr(hatching, key, value)
+ hatching.save()
+
+ else:
+ poultry = Poultry.objects.filter(SystemCode=request.data['SystemCode']).first()
+ hatchings = Hatching.objects.filter(poultry=poultry).order_by('id').last()
+ period = hatchings.Period + 1 if hatchings else 1
+ request.data['Period'] = period
+ hatching = Hatching.objects.create(**request.data)
+ hatching.ArchiveDate = hatching.Date + datetime.timedelta(days=76)
+
+ if poultry:
+ hatching.poultry = poultry
+ hatching.save()
+
+ allowed_evacuation_fields = {
+ 'PartIdCode', 'RequestId', 'MoReportId', 'ReportType', 'ReportTypeString',
+ 'ReportDate', 'ReportDateShamsi', 'MoReason', 'MoDate', 'MoDateShamsi',
+ 'MoStartDay', 'MoEndDay', 'MoReportSubId', 'ReportStatus', 'GoodCount',
+ 'Message', 'ErrorCode', 'IsDeleted', 'RegDate', 'RegDateShamsi',
+ 'RegDateShamsiWithTime', 'RegDateShamsiOnlyTime', 'ExternalId', 'StringId',
+ 'IsPersisted', 'AllowInsert', 'AllowUpdate', 'ModalCss',
+ 'GridContainerParametersModel', 'MenuUserAccess', 'MenuUserAccessId',
+ 'LogTableName', 'LogTableAlias', 'PageTitle'
+ }
+
+ if evacuation_details:
+ cleaned_payload = []
+ external_ids = []
+ for evacuation_data in evacuation_details:
+ clean_data = {
+ k: v for k, v in evacuation_data.items()
+ if k in allowed_evacuation_fields or k == 'Id'
+ }
+ external_id = clean_data.pop('Id', None)
+ if external_id is not None:
+ clean_data['ExternalId'] = external_id
+ external_ids.append(external_id)
+ cleaned_payload.append((external_id, clean_data))
+
+ existing_map = {}
+ if external_ids:
+ existing_qs = EvacuationDetail.objects.filter(
+ ExternalId__in=external_ids,
+ )
+ existing_map = {ev.ExternalId: ev for ev in existing_qs}
+
+ bulk_create = []
+ for external_id, clean_data in cleaned_payload:
+ if external_id and external_id in existing_map:
+ evacuation = existing_map[external_id]
+ for key, value in clean_data.items():
+ setattr(evacuation, key, value)
+ evacuation.hatching = hatching
+ evacuation.save()
+ else:
+ clean_data['hatching'] = hatching
+ bulk_create.append(EvacuationDetail(**clean_data))
+
+ if bulk_create:
+ EvacuationDetail.objects.bulk_create(bulk_create)
+
+ if bars:
+ for transport_data in bars:
+ transport = TransportingDetail.objects.filter(
+ TrackingCode=transport_data['TrackingCode']).first()
+ if transport:
+ for key, value in transport_data.items():
+ setattr(transport, key, value)
+ transport.save()
+
+ else:
+ transport = TransportingDetail.objects.create(**transport_data)
+ kill_house = KillHouse.objects.filter(PartIdCode=transport.DesPartIdCode, trash=False).first()
+ transport.hatching = hatching
+ if kill_house:
+ transport.Province = kill_house.Province
+ transport.City = kill_house.City
+ if hatching.poultry.LocationIdProvince != kill_house.ProvinceId:
+ transport.Out = True
+ transport.save()
+ transport.Age = (transport.Date.date() - transport.hatching.Date.date()).days + 1
+ transport.save()
+ bars = TransportingDetail.objects.filter(trash=False, hatching=hatching)
+ bars_quantity = bars.aggregate(total=Sum('GoodAmount'))['total'] or 0
+ ave_age = int(bars.aggregate(avg_age=Avg('Age'))[
+ 'avg_age'] or 0)
+ hatching.LeftOver = hatching.ChickCountSum - (hatching.Evacuation + bars_quantity) if (
+ hatching.ChickCountSum - (
+ hatching.Evacuation + bars_quantity)) > 0 else 0
+
+ if hatching.PedigreeName in BREED_STANDARDIZATION:
+ hatching.PedigreeName = BREED_STANDARDIZATION[hatching.PedigreeName]
+ hatching.KillingAve = ave_age
+ hatching.save()
+
+ return Response({"result": "با موفقیت ثبت شد"}, status=status.HTTP_201_CREATED)
+
+ def list(self, request, *args, **kwargs):
+ queryset = self.filter_queryset(self.get_queryset())
+ page_size = request.query_params.get('page_size')
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(queryset)
+ if page is not None:
+ serializer = HatchingDetailSerializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+
+ serializer = HatchingDetailSerializer(queryset, many=True)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+class PoultryHatchingForUpdatePedigreeNameViewSet(viewsets.ModelViewSet):
+ queryset = PoultryHatching.objects.all()
+ serializer_class = PoultryHatchingSerializer
+ permission_classes = [AllowAny]
+
+ def create(self, request, *args, **kwargs):
+ data = request.data['Data']
+ request.data.pop('Data')
+ for hatching_data in data:
+ hatching = PoultryHatching.objects.filter(PartIdCode=hatching_data['PartIdCode']).first()
+ if hatching:
+ if hatching.PedigreeName:
+ hatching.PedigreeName = 'ترکیبی'
+ else:
+ hatching.PedigreeName = hatching_data['PedigreeName']
+
+ hatching.save()
+
+ return Response({"result": "با موفقیت ثبت شد"}, status=status.HTTP_201_CREATED)
+
+
+class TransportingChickenDetailViewSet(viewsets.ModelViewSet):
+ queryset = TransportingChickenDetail.objects.all()
+ serializer_class = TransportingChickenDetailSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = TransportingChickenDetailFilterSet
+
+ def create(self, request, *args, **kwargs):
+ data = request.data['Data']
+ request.data.pop('Data')
+ for transport_data in data:
+ transport = TransportingChickenDetail.objects.filter(TrackingCode=transport_data['TrackingCode']).first()
+ if transport:
+ for key, value in transport_data.items():
+ setattr(transport, key, value)
+ transport.save()
+
+ else:
+ transport = TransportingChickenDetail.objects.create(**transport_data)
+ hatching = PoultryHatching.objects.filter(DesCertId=transport_data['CertId']).first()
+ if hatching:
+ transport.hatching = hatching
+ transport.save()
+
+ return Response({"result": "با موفقیت ثبت شد"}, status=status.HTTP_201_CREATED)
+
+ def list(self, request, *args, **kwargs):
+ transports = TransportingChickenDetail.objects.filter(hatching__isnull=False, trash=False).order_by(
+ '-issue_date')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ transports = transports.filter(reside_date__date__gte=date1, reside_date__date__lte=date2)
+ search = request.GET.get('search')
+ if search:
+ if search != 'undefined' and search.strip():
+ transports = transports.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(transports)
+ if page is not None:
+ serializer = self.get_serializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+
+ serializer = self.serializer_class(transports, many=True)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+class HatchingViewSet(viewsets.ModelViewSet):
+ queryset = PoultryHatching.objects.all()
+ serializer_class = HatchingSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+
+ def list(self, request, *args, **kwargs):
+ hatchings = PoultryHatching.objects.filter(trash=False)
+ city = request.GET.get('city')
+ province = request.GET.get('province')
+ age = request.GET.get('age')
+ filters = {}
+ if age:
+ filters['HatchingAge__exact'] = age
+ if city:
+ filters['LocationNameCity__icontains'] = city
+ if province:
+ filters['LocationNameProvince__icontains'] = province
+ hatchings = hatchings.filter(**filters)
+
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(hatchings)
+ if page is not None:
+ serializer = self.get_serializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+
+ serializer = self.serializer_class(hatchings, many=True)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+# class TransportingDashboardViewSet(viewsets.ModelViewSet):
+# queryset = TransportingDetail.objects.filter(trash=False).only('id').order_by('id')
+# serializer_class = TransportingSerializer
+# permission_classes = [AllowAny]
+# filterset_class = TransportingDetailFilterSet
+#
+# def list(self, request, *args, **kwargs):
+# bars = self.queryset
+# filters = {}
+# RequestCode = request.GET.get('RequestCode')
+# date1 = request.GET.get('date1')
+# date2 = request.GET.get('date2')
+# city = request.GET.get('city')
+# search = request.GET.get('search')
+# province = request.GET.get('province')
+# if date1 and date2:
+# date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+#
+# date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+# filters['Date__date__gte'] = date1
+# filters['Date_date__lte'] = date2
+# if city:
+# filters['City__icontains'] = city
+#
+# if province:
+# filters['Province__icontains'] = province
+#
+# if RequestCode:
+# filters['hatching__RequestCode'] = RequestCode
+#
+#
+# bars = bars.filter(**filters)
+#
+# if search:
+# if search != 'undefined' and search.strip():
+# bars = bars.filter(
+# build_query(self.filterset_class.Meta.fields, search)
+# )
+# bar_quantity = bars.aggregate(total=Sum('GoodAmount'))['total'] or 0
+# total_bar_killing_age = bars.aggregate(avg_age=Avg('Age'))['avg_age'] or 0
+# input_bar = bars.filter(Out=False)
+# input_bar__percent = input_bar.count() / bars.count() * 100
+# input_bar_quantity = input_bar.aggregate(total=Sum('GoodAmount'))['total'] or 0
+# output_bar = bars.filter(Out=True)
+# output_bar_quantity = output_bar.aggregate(total=Sum('GoodAmount'))['total'] or 0
+# output_bar_percent = output_bar.count() / bars.count() * 100
+# result = {
+# "bar_count": bars.count(),
+# "bar_quantity": bar_quantity,
+# "total_bar_killing_age": total_bar_killing_age,
+# "input_bar_count": input_bar.count(),
+# "input_bar_quantity": input_bar_quantity,
+# "input_bar_percent": input_bar__percent,
+# "output_bar": output_bar.count(),
+# "output_bar_quantity": output_bar_quantity,
+# "output_bar_percent": output_bar_percent,
+#
+# }
+# return Response(result, status=status.HTTP_200_OK)
+
+class TransportingDashboardViewSet(viewsets.ModelViewSet):
+ queryset = TransportingDetail.objects.filter(trash=False)
+ serializer_class = TransportingSerializer
+ permission_classes = [AllowAny]
+ filterset_class = TransportingDetailFilterSet
+
+ def _apply_filters(self, queryset, request):
+ filters = {}
+
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ if date1 and date2:
+ try:
+ date1 = datetime.datetime.strptime(date1, '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(date2, '%Y-%m-%d').date()
+ filters['Date__date__range'] = [date1, date2]
+ except ValueError:
+ pass
+
+ if city := request.GET.get('city'):
+ filters['City__icontains'] = city
+ if province := request.GET.get('province'):
+ if province == 'undefined':
+ province = None
+ filters['hatching__poultry__Province__icontains'] = province
+ if RequestCode := request.GET.get('RequestCode'):
+ filters['hatching__RequestCode'] = RequestCode
+
+ if DesPartIdCode := request.GET.get('PartIdCode'):
+ filters['DesPartIdCode'] = DesPartIdCode
+
+ queryset = queryset.filter(**filters)
+
+ if search := request.GET.get('search'):
+ if search != 'undefined' and search.strip():
+ queryset = queryset.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+
+ return queryset
+
+ def _apply_filters_all_products(self, queryset, request):
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ if date1 and date2:
+ try:
+ date1 = datetime.datetime.strptime(date1, '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(date2, '%Y-%m-%d').date()
+ queryset = queryset.filter(
+ date__gte=date1,
+ date__lte=date2,
+ date__isnull=False
+
+ )
+ except ValueError:
+ pass
+
+ if city := request.GET.get('city'):
+ queryset = queryset.filter(destination_city__icontains=city)
+ if province := request.GET.get('province'):
+ if province == 'undefined':
+ province = None
+ if province:
+ queryset = queryset.filter(destination_province__icontains=province)
+ if RequestCode := request.GET.get('RequestCode'):
+ queryset = queryset.filter(hatching__RequestCode=RequestCode)
+
+ if DesPartIdCode := request.GET.get('PartIdCode'):
+ queryset = queryset.filter(jihadi_destination=DesPartIdCode)
+
+ if search := request.GET.get('search'):
+ if search != 'undefined' and search.strip():
+ queryset = queryset.filter(
+ build_query(AllProductsTransportFilterSet.Meta.fields, search)
+ )
+
+ return queryset
+
+ def _calculate_metrics(self, queryset, all_products_queryset):
+ stats = queryset.aggregate(
+ total_count=Count('id'),
+ total_quantity=Sum('GoodAmount'),
+ avg_age=Avg('Age'),
+ input_count=Count('id', filter=Q(Out=False)),
+ input_quantity=Sum('GoodAmount', filter=Q(Out=False)),
+ output_count=Count('id', filter=Q(Out=True)),
+ output_quantity=Sum('GoodAmount', filter=Q(Out=True)),
+ )
+
+ all_products_stats = all_products_queryset.aggregate(
+ total_count=Count('id'),
+ total_quantity=Sum('quantity'),
+ input_count=Count('id', filter=Q(out=False)),
+ input_quantity=Sum('quantity', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ output_quantity=Sum('quantity', filter=Q(out=True)),
+ )
+
+ stats['total_count'] = (stats.get('total_count') or 0) + (all_products_stats.get('total_count') or 0)
+ stats['total_quantity'] = (stats.get('total_quantity') or 0) + (all_products_stats.get('total_quantity') or 0)
+ stats['input_count'] = (stats.get('input_count') or 0) + (all_products_stats.get('input_count') or 0)
+ stats['input_quantity'] = (stats.get('input_quantity') or 0) + (all_products_stats.get('input_quantity') or 0)
+ stats['output_count'] = (stats.get('output_count') or 0) + (all_products_stats.get('output_count') or 0)
+ stats['output_quantity'] = (stats.get('output_quantity') or 0) + (all_products_stats.get('output_quantity') or 0)
+
+ total_count = stats['total_count'] or 1
+ stats.update({
+ 'input_percent': (stats['input_count'] / total_count) * 100,
+ 'output_percent': (stats['output_count'] / total_count) * 100,
+ })
+
+ return stats
+
+ def list(self, request, *args, **kwargs):
+ try:
+ filtered_bars = self._apply_filters(self.queryset, request)
+
+ query_all_products = AllProductsTransport.objects.filter(
+ trash=False,
+ product='مرغ زنده -جهت كشتار'
+ )
+ filtered_all_products = self._apply_filters_all_products(query_all_products, request)
+
+ metrics = self._calculate_metrics(filtered_bars, filtered_all_products)
+
+ result = {
+ "bar_count": metrics['total_count'],
+ "bar_quantity": metrics['total_quantity'] or 0,
+ "total_bar_killing_age": metrics['avg_age'] or 0,
+ "input_bar_count": metrics['input_count'],
+ "input_bar_quantity": metrics['input_quantity'] or 0,
+ "input_bar_percent": round(metrics['input_percent'], 2),
+ "output_bar": metrics['output_count'],
+ "output_bar_quantity": metrics['output_quantity'] or 0,
+ "output_bar_percent": round(metrics['output_percent'], 2),
+ }
+
+ return Response(result, status=status.HTTP_200_OK)
+
+ except Exception as e:
+ return Response(
+ {"error": str(e)},
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR
+ )
+
+
+class TransportingDetailViewSet(viewsets.ModelViewSet):
+ queryset = TransportingDetail.objects.filter(trash=False)
+ serializer_class = TransportingDetailSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = TransportingDetailFilterSet
+
+ def list(self, request, *args, **kwargs):
+ filters = {}
+ PartIdCode = request.GET.get('PartIdCode')
+ RequestCode = request.GET.get('RequestCode')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ city = request.GET.get('city')
+ province = request.GET.get('province')
+ if province == 'undefined':
+ province = None
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ filters['Date__date__gte'] = date1
+ filters['Date__date__lte'] = date2
+ if city:
+ filters['City__icontains'] = city
+
+ if province:
+ # filters['Province__icontains'] = province
+ filters['hatching__poultry__Province__icontains'] = province
+
+ if PartIdCode:
+ filters['DesPartIdCode'] = PartIdCode
+
+ if RequestCode:
+ filters['hatching__RequestCode'] = RequestCode
+
+ search = request.GET.get('search')
+ transports = TransportingDetail.objects.filter(**filters, trash=False).order_by("-Date")
+
+ # Query برای AllProductsTransport با product='مرغ زنده -جهت كشتار'
+ query_all_products = AllProductsTransport.objects.filter(
+ trash=False,
+ product='مرغ زنده -جهت كشتار'
+ )
+
+ # اعمال فیلترها روی AllProductsTransport
+ if PartIdCode:
+ query_all_products = query_all_products.filter(jihadi_destination=PartIdCode)
+
+ if RequestCode:
+ query_all_products = query_all_products.filter(hatching__RequestCode=RequestCode)
+
+ if date1 and date2:
+ query_all_products = query_all_products.filter(
+ date__gte=date1,
+ date__lte=date2,
+ date__isnull=False
+
+ )
+
+ if city:
+ query_all_products = query_all_products.filter(destination_city__icontains=city)
+
+ if province:
+ query_all_products = query_all_products.filter(destination_province__icontains=province)
+
+ if search:
+ if search != 'undefined' and search.strip():
+ transports = transports.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+ # اعمال search روی AllProductsTransport
+ query_all_products = query_all_products.filter(
+ build_query(AllProductsTransportFilterSet.Meta.fields, search)
+ )
+
+ # تبدیل به list و ترکیب
+ transports_list = list(transports)
+ all_products_list = list(query_all_products)
+
+ # تبدیل AllProductsTransport به TransportingDetail-like objects برای sort
+ def get_sort_date(obj):
+ if hasattr(obj, 'Date') and obj.Date:
+ if isinstance(obj.Date, datetime.datetime):
+ return obj.Date.date()
+ elif isinstance(obj.Date, datetime.date):
+ return obj.Date
+ elif hasattr(obj, 'date') and obj.date:
+ return obj.date
+ elif hasattr(obj, 'unloading_date') and obj.unloading_date:
+ return obj.unloading_date
+ return datetime.date.min
+
+ combined_list = []
+ for obj in transports_list:
+ combined_list.append((get_sort_date(obj), 'transporting', obj))
+ for obj in all_products_list:
+ combined_list.append((get_sort_date(obj), 'all_products', obj))
+
+ combined_list.sort(key=lambda x: x[0], reverse=True)
+ sorted_objects = [obj for _, _, obj in combined_list]
+
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ paginator = self.pagination_class()
+ page = paginator.paginate_queryset(sorted_objects, request)
+
+ if page is not None:
+ serialized_data = []
+ for obj in page:
+ if hasattr(obj, 'Date'): # TransportingDetail
+ serializer = TransportingDetailSerializer(obj, context={'request': request})
+ serialized_data.append(serializer.data)
+ else: # AllProductsTransport
+ serializer = AllProductsTransportSerializer(obj, context={'request': request})
+ data = serializer.data
+ date_value = obj.date if obj.date else obj.unloading_date
+ if date_value:
+ data['Date'] = datetime.datetime.combine(date_value, datetime.time.min) if isinstance(date_value, datetime.date) else date_value
+ else:
+ data['Date'] = None
+ data['DesPartIdCode'] = obj.jihadi_destination
+ data['TrackingCode'] = obj.tracking
+ data['GoodAmount'] = obj.quantity
+ data['DesUnitName'] = obj.destination
+ data['Province'] = obj.destination_province
+ data['City'] = obj.destination_city
+ data['Out'] = obj.out
+ serialized_data.append(data)
+ return paginator.get_paginated_response(serialized_data)
+
+ serialized_data = []
+ for obj in sorted_objects:
+ if hasattr(obj, 'Date'): # TransportingDetail
+ serializer = TransportingDetailSerializer(obj, context={'request': request})
+ serialized_data.append(serializer.data)
+ else: # AllProductsTransport
+ serializer = AllProductsTransportSerializer(obj, context={'request': request})
+ data = serializer.data
+ date_value = obj.date if obj.date else obj.unloading_date
+ if date_value:
+ data['Date'] = datetime.datetime.combine(date_value, datetime.time.min) if isinstance(date_value, datetime.date) else date_value
+ else:
+ data['Date'] = None
+ data['DesPartIdCode'] = obj.jihadi_destination
+ data['TrackingCode'] = obj.tracking
+ data['GoodAmount'] = obj.quantity
+ data['DesUnitName'] = obj.destination
+ data['Province'] = obj.destination_province
+ data['City'] = obj.destination_city
+ data['Out'] = obj.out
+ serialized_data.append(data)
+ return Response(serialized_data, status=status.HTTP_200_OK)
+
+
+class HatchingCalculationsViewSet(viewsets.ModelViewSet):
+ queryset = PoultryHatching.objects.all()
+ serializer_class = HatchingCalculationSerializer
+ permission_classes = [AllowAny]
+ filterset_class = HatchingCalculationsFilterSet
+
+ def list(self, request, *args, **kwargs):
+ city = request.GET.get('city')
+ province = request.GET.get('province')
+ value = request.GET.get('value')
+ filters = {'trash': False}
+ if city:
+ filters['LocationNameCity__in'] = [city]
+ if province:
+ filters['LocationNameProvince__in'] = [province]
+
+ hatching_dict = PoultryHatching.objects.filter(**filters)
+ if value:
+ hatching_dict = hatching_dict.filter(
+ build_query(self.filterset_class.Meta.fields, value)
+ )
+ total_evacuation = build_calculation(queryset=hatching_dict, column_name='EvacuationCount', aggregate_func=Sum)
+ total_poultry_hatching = build_calculation(queryset=hatching_dict, column_name='HatchingCount',
+ aggregate_func=Sum)
+ total_poultry = hatching_dict.values('poultry').distinct().count()
+ total_leftover = build_calculation(queryset=hatching_dict, column_name='LeftOver', aggregate_func=Sum)
+
+ return Response({"evacuation_sum": total_evacuation, "hatching_count": total_poultry_hatching,
+ "total_poultry": total_poultry, "leftover": total_leftover
+ },
+ status=status.HTTP_200_OK)
+
+
+class PoultryInfoViewSet(viewsets.ModelViewSet):
+ queryset = Poultry.objects.all()
+ serializer_class = PoultryInfoSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = PoultryInfoFilterSet
+
+ def list(self, request, *args, **kwargs):
+ poultry = Poultry.objects.filter(trash=False).order_by('id')
+ search = request.GET.get('search')
+ leftover = request.GET.get('leftover')
+ province = request.GET.get('province')
+ city = request.GET.get('city')
+ if leftover:
+ poultry = poultry.filter(
+ pk__in=Hatching.objects.filter(LeftOver__gt=0, trash=False).values_list('poultry',
+ flat=True))
+
+ if province:
+ poultry = poultry.filter(LocationNameProvince__icontains=province)
+
+ if city:
+ poultry = poultry.filter(LocationNameCity__icontains=city)
+
+ if search:
+ if search != 'undefined' and search.strip():
+ poultry = poultry.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(poultry)
+ if page is not None:
+ serializer = self.get_serializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+
+ serializer = self.serializer_class(poultry, many=True)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+class TotalKillHousesViewSet(viewsets.ModelViewSet):
+ queryset = KillHouse.objects.filter(trash=False).only('id', 'PartIdCode', 'UnitName', 'Province', 'City').order_by('id')
+ serializer_class = KillHouseSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = KillHouseFilterSet
+
+ def list(self, request, *args, **kwargs):
+ search = request.GET.get('search')
+ filters = {"trash": False}
+ if province := request.GET.get('province'):
+ filters['Province'] = province
+
+ if city := request.GET.get('city'):
+ filters['City'] = city
+
+ if kill_houses_name := request.GET.get('name'):
+ filters['UnitName'] = kill_houses_name
+
+ kill_houses = self.queryset.filter(**filters)
+
+ if search:
+ if search != 'undefined' and search.strip():
+ kill_houses = kill_houses.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(kill_houses)
+
+ items_to_serialize = page if page is not None else kill_houses
+ part_id_codes = list(kill_houses.values_list('PartIdCode', flat=True).distinct())
+ info_cache = {}
+
+ if part_id_codes:
+ date1 = request.GET.get('date1') or None
+ date2 = request.GET.get('date2') or None
+
+ bars_query = TransportingDetail.objects.filter(
+ DesPartIdCode__in=part_id_codes,
+ trash=False
+ ).only('DesPartIdCode', 'GoodAmount', 'Out', 'Date')
+
+ if date1:
+ date1_obj = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2_obj = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ bars_query = bars_query.filter(Date__date__gte=date1_obj, Date__date__lte=date2_obj)
+
+ bars_aggregations = bars_query.values('DesPartIdCode').annotate(
+ total=Sum('GoodAmount'),
+ input_total=Sum('GoodAmount', filter=Q(Out=False)),
+ output_total=Sum('GoodAmount', filter=Q(Out=True)),
+ input_count=Count('id', filter=Q(Out=False)),
+ output_count=Count('id', filter=Q(Out=True)),
+ total_count=Count('id')
+ )
+
+ all_products_query = AllProductsTransport.objects.filter(
+ jihadi_destination__in=part_id_codes,
+ trash=False,
+ product='مرغ زنده -جهت كشتار'
+ ).only('jihadi_destination', 'quantity', 'out', 'date', 'unloading_date')
+
+ if date1:
+ all_products_query = all_products_query.filter(
+ date__gte=date1_obj,
+ date__lte=date2_obj,
+ date__isnull=False
+
+ )
+
+ all_products_aggregations = all_products_query.values('jihadi_destination').annotate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id')
+ )
+
+ bars_dict = {item['DesPartIdCode']: item for item in bars_aggregations}
+ all_products_dict = {item['jihadi_destination']: item for item in all_products_aggregations}
+
+ for part_id_code in part_id_codes:
+ bars_data = bars_dict.get(part_id_code, {})
+ all_products_data = all_products_dict.get(part_id_code, {})
+
+ total_count = (bars_data.get('total_count') or 0) + (all_products_data.get('total_count') or 0)
+ total_bars_quantity = (bars_data.get('total') or 0) + (all_products_data.get('total') or 0)
+ total_input_bars_quantity = (bars_data.get('input_total') or 0) + (all_products_data.get('input_total') or 0)
+ total_output_bars_quantity = (bars_data.get('output_total') or 0) + (all_products_data.get('output_total') or 0)
+ input_bars_count = (bars_data.get('input_count') or 0) + (all_products_data.get('input_count') or 0)
+ output_bars_count = (bars_data.get('output_count') or 0) + (all_products_data.get('output_count') or 0)
+
+ if total_count > 0:
+ total_input_bars_percent = round((input_bars_count / total_count) * 100, 1)
+ total_output_bars_percent = round((output_bars_count / total_count) * 100, 1)
+ else:
+ total_input_bars_percent = 0
+ total_output_bars_percent = 0
+
+ info_cache[part_id_code] = {
+ "bars": total_count,
+ "total_bars_quantity": total_bars_quantity,
+ "input_bars": input_bars_count,
+ "total_input_bars_quantity": total_input_bars_quantity,
+ "total_input_bars_percent": total_input_bars_percent,
+ "output_bars": output_bars_count,
+ "total_output_bars_quantity": total_output_bars_quantity,
+ "total_output_bars_percent": total_output_bars_percent,
+ }
+
+ if page is not None:
+ serializer = self.get_serializer(page, many=True, context={'request': request, 'info_cache': info_cache})
+ return self.get_paginated_response(serializer.data)
+
+ serializer = self.serializer_class(kill_houses, many=True, context={'request': request, 'info_cache': info_cache})
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+class HatchingAnalysisPedigreeViewSet(viewsets.ModelViewSet):
+ queryset = Hatching.objects.filter(trash=False, poultry__isnull=False, PedigreeName__isnull=False)
+ serializer_class = HatchingAnalysisSerializer
+ permission_classes = [AllowAny]
+
+ def list(self, request, *args, **kwargs):
+ filters = {}
+ pedigree = request.GET.get('pedigree')
+ city = request.GET.get('city')
+ province = request.GET.get('province')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ state = request.GET.get('state')
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ filters['Date__date__gte'] = date1
+ filters['Date__date__lte'] = date2
+ if city:
+ filters['CityName__icontains'] = city
+
+ if province:
+ filters['ProvinceName__icontains'] = province
+
+ if pedigree:
+ filters['PedigreeName__icontains'] = pedigree
+
+ if state == 'active':
+ filters['Age__lte'] = 70
+ else:
+ pass
+
+ hatchings = self.queryset.filter(**filters)
+ pedigree_name_poultry = hatchings.values('PedigreeName').annotate(
+ poultry_count=Count('poultry', distinct=True)).order_by('-poultry_count')
+
+ pedigree_name_hatching_count = hatchings.values('PedigreeName').annotate(
+ chick_count=Count('ChickCountSum')).order_by(
+ '-chick_count')
+ pedigree_name_hatching_sum = hatchings.values('PedigreeName').annotate(
+ chick_sum=Sum('ChickCountSum')).order_by(
+ '-chick_sum')
+ pedigree_name_leftover = hatchings.values('PedigreeName').annotate(left_over=Sum('LeftOver')).order_by(
+ '-left_over')
+
+ pedigree_name_pedigree = hatchings.values('PedigreeName').annotate(pedigree_name=Sum('id')).order_by(
+ '-pedigree_name')
+
+ pedigree_name_average_age = hatchings.values(
+ 'PedigreeName').annotate(average_age=Avg('KillingAve')).order_by('-average_age')
+ for item in pedigree_name_average_age:
+ item['average_age'] = int(item['average_age']) if item['average_age'] is not None else 0
+
+ pedigree_name_active_hatching_count = hatchings.filter(Age__lte=70).values('PedigreeName').annotate(
+ chick_count=Count('ChickCountSum')).order_by(
+ '-chick_count')
+
+ pedigree_name_active_hatching_sum = hatchings.filter(Age__lte=70).values('PedigreeName').annotate(
+ chick_count=Sum('ChickCountSum')).order_by(
+ '-chick_count')
+ hatchings_by_province = hatchings.values('ProvinceName', 'PedigreeName').annotate(
+ hatching_sum=Sum('ChickCountSum')).order_by('ProvinceName', '-hatching_sum')
+ provinces_dict = defaultdict(lambda: {'total_sum': 0, 'breeds': []})
+
+ for h in hatchings_by_province:
+ province = h['ProvinceName']
+ pedigree = h['PedigreeName']
+ hatching_sum = h['hatching_sum']
+
+ provinces_dict[province]['breeds'].append({
+ 'pedigree': pedigree,
+ 'hatching_sum': hatching_sum
+ })
+ provinces_dict[province]['total_sum'] += hatching_sum
+
+ province_result = [
+ {
+ 'province': province,
+ 'hatching_sum_all': values['total_sum'],
+ 'breeds': values['breeds']
+ }
+ for province, values in provinces_dict.items()
+ ]
+
+ pedigree_name_evacuation_sum = hatchings.values('PedigreeName').annotate(
+ evacuation=Sum('Evacuation')).order_by(
+ '-evacuation')
+ pedigree_name_bars_sum = hatchings.values('PedigreeName').annotate(
+ count_bars=Sum('transport_hatching__GoodAmount')).order_by(
+ '-count_bars')
+
+ evacuation_dict = {
+ item['PedigreeName']: item['evacuation'] or 0
+ for item in pedigree_name_evacuation_sum
+ }
+ hatching_dict = {
+ item['PedigreeName']: item['chick_sum'] or 0
+ for item in pedigree_name_hatching_sum
+ }
+ total_hatching = hatchings.aggregate(total=Sum('ChickCountSum'))['total'] or 1
+
+ evacuation_hatching_percent = []
+ for item in pedigree_name_bars_sum:
+ pedigree = item['PedigreeName']
+ evacuation_sum = evacuation_dict.get(pedigree, 0)
+ hatching_sum = hatching_dict.get(pedigree, 1)
+
+ percent_by_pedigree = round((evacuation_sum / hatching_sum) * 100, 2)
+ percent_by_total = round((evacuation_sum / total_hatching) * 100, 2)
+
+ evacuation_hatching_percent.append({
+ "pedigree_name": pedigree,
+ "pedigree": percent_by_pedigree,
+ "total": percent_by_total
+ })
+
+ result = {
+ "pedigree_name_poultry": pedigree_name_poultry,
+ "pedigree_name_hatching_count": pedigree_name_hatching_count,
+ "pedigree_name_hatching_sum": pedigree_name_hatching_sum,
+ "pedigree_name_leftover": pedigree_name_leftover,
+ "pedigree_name_pedigree": pedigree_name_pedigree,
+ "pedigree_name_average_age": pedigree_name_average_age,
+ "pedigree_name_active_hatching_count": pedigree_name_active_hatching_count,
+ "pedigree_name_active_hatching_sum": pedigree_name_active_hatching_sum,
+ "province_result": province_result,
+ "pedigree_name_evacuation_sum": pedigree_name_evacuation_sum,
+ "pedigree_name_bars_sum": pedigree_name_bars_sum,
+ "pedigree_evacuation_hatching_percent": evacuation_hatching_percent,
+ }
+ return Response(result, status=status.HTTP_200_OK)
+
+
+class HatchingAnalysisProvinceView(viewsets.ModelViewSet):
+ queryset = Hatching.objects.filter(trash=False, poultry__isnull=False)
+ serializer_class = HatchingAnalysisSerializerTwo
+ permission_classes = [AllowAny]
+
+ def list(self, request, *args, **kwargs):
+ filters = {}
+ state = request.GET.get('state')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ filters['Date__date__gte'] = date1
+ filters['Date__date__lte'] = date2
+
+ if state == 'active':
+ filters['Age__lte'] = 70
+ else:
+ pass
+
+ hatchings = self.queryset.filter(**filters)
+ province_sum_transporting = hatchings.filter(transport_hatching__GoodAmount__isnull=False).values(
+ 'ProvinceName').annotate(
+ good_amount=Sum('transport_hatching__GoodAmount')).order_by(
+ '-good_amount')
+
+ province_hatching_sum = hatchings.values('ProvinceName').annotate(
+ chick_sum=Sum('ChickCountSum')).order_by(
+ '-chick_sum')
+
+ province_name_leftover = hatchings.values('ProvinceName').annotate(left_over=Sum('LeftOver')).order_by(
+ '-left_over')
+
+ province_name_average_age = hatchings.values(
+ 'ProvinceName').annotate(average_age=Avg('KillingAve')).order_by('-average_age')
+
+ province_name_poultry = hatchings.values('ProvinceName').annotate(
+ poultry_count=Count('poultry', distinct=True)).order_by('-poultry_count')
+
+ for item in province_name_average_age:
+ item['average_age'] = int(item['average_age']) if item['average_age'] is not None else 0
+
+ province_data = hatchings.values('ProvinceName').annotate(
+ total_evacuation=Sum('Evacuation'),
+ total_chicks=Sum('ChickCountSum')
+ ).order_by('-ProvinceName')
+ province_evacuation_hatching_percent = []
+
+ for item in province_data:
+ total_chicks = item['total_chicks']
+ total_evacuation = item['total_evacuation']
+
+ if total_chicks:
+ percent = round((total_evacuation / total_chicks) * 100, 2)
+ else:
+ percent = 0
+
+ province_evacuation_hatching_percent.append({
+ 'ProvinceName': item['ProvinceName'],
+ 'evacuation_hatching': percent
+ })
+
+ result = {
+ "province_sum_transporting": province_sum_transporting,
+ "province_hatching_sum": province_hatching_sum,
+ "province_name_poultry": province_name_poultry,
+ "province_name_leftover": province_name_leftover,
+ "province_name_average_age": province_name_average_age,
+ "province_evacuation_hatching_percent": province_evacuation_hatching_percent
+ }
+ return Response(result, status=status.HTTP_200_OK)
+
+
+class TransportingReportDashboardViewSet(viewsets.ModelViewSet):
+ queryset = TransportingDetail.objects.filter(trash=False, Province__isnull=False).select_related('hatching')
+ serializer_class = TransportingReportDashboard
+ permission_classes = [AllowAny]
+
+ def list(self, request, *args, **kwargs):
+ filters = {}
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ location = request.GET.get('location')
+
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ filters['Date__date__gte'] = date1
+ filters['Date__date__lte'] = date2
+ transports = self.queryset.filter(**filters)
+ if location:
+ transports = transports.filter(
+ Q(City__icontains=location) | Q(Province__icontains=location)
+ )
+
+ average_age_killing = transports.aggregate(average_age=Avg('Age'))['average_age']
+
+ minimum_maximum_age_killing = transports.filter(Age__lte=70, Age__gte=0).aggregate(minimum_age=Min('Age'),
+ maximum_age=Max('Age'))
+ active_hatching = \
+ transports.filter(Age__lte=70, Age__gte=0).aggregate(good_count=Sum('hatching__ChickCountSum'))[
+ 'good_count'] or 0
+
+ average_age_killing_province = transports.values(
+ 'Province').annotate(average_age=Avg('Age')).order_by('-average_age')
+
+ for age in average_age_killing_province:
+ age['average_age'] = int(age['average_age']) if age['average_age'] is not None else 0
+
+ hatching_killing_province = transports.values(
+ 'Province').annotate(
+ chick_sum=Sum('hatching__ChickCountSum')).order_by(
+ '-chick_sum')
+
+ country_hatching = transports.aggregate(
+ good_count=Sum('hatching__ChickCountSum')
+ )['good_count'] or 0
+
+ killing_name_hatching = transports.filter(DesUnitName__isnull=False).values(
+ 'DesUnitName').annotate(
+ good_amount=Sum('GoodAmount')).order_by(
+ '-good_amount')[:15]
+
+ maximum_killing_province = transports.values(
+ 'Province').annotate(sum_killing=Sum('GoodAmount')).order_by('-sum_killing').first()
+
+ result = {
+ 'average_age_killing': int(average_age_killing) if average_age_killing is not None else 0,
+ 'minimum_maximum_age_killing': minimum_maximum_age_killing,
+ 'average_age_killing_province': average_age_killing_province,
+ 'hatching_killing_province': hatching_killing_province,
+ 'killing_name_hatching': killing_name_hatching,
+ 'active_hatching': active_hatching,
+ 'country_hatching': country_hatching,
+ 'transport_car_count': transports.count(),
+ 'maximum_killing_province': maximum_killing_province,
+ }
+ return Response(result, status=status.HTTP_200_OK)
+
+
+class TransportingAnalysisViewSet(viewsets.ModelViewSet):
+ queryset = TransportingDetail.objects.filter(trash=False, Province__isnull=False)
+ serializer_class = TransportingSerializer
+ permission_classes = [AllowAny]
+
+ def list(self, request, *args, **kwargs):
+ qs = self.queryset
+
+ total_killing_province = qs.values('Province').annotate(total_good_amount=Sum('GoodAmount'))
+ total_killing_dict = {item['Province']: item['total_good_amount'] for item in total_killing_province}
+
+ total_good_amount_in_and_out = qs.values('Province', 'DesUnitName').annotate(
+ total_good_amount=Sum('GoodAmount')
+ )
+
+ total_max_amount = {}
+ total_min_amount = {}
+ max_unit_name_in_and_out = {}
+ min_unit_name_in_and_out = {}
+ for item in total_good_amount_in_and_out:
+ province = item['Province']
+ amount = item['total_good_amount']
+ unit_name = item['DesUnitName']
+ if province not in total_max_amount or amount > total_max_amount[province]:
+ total_max_amount[province] = amount
+ max_unit_name_in_and_out[province] = unit_name
+
+ if province not in total_min_amount or amount < total_min_amount[province]:
+ total_min_amount[province] = amount
+ min_unit_name_in_and_out[province] = unit_name
+
+ all_data = qs.filter(Out=True).values('Province', 'DesUnitName').annotate(
+ total_good_amount=Sum('GoodAmount')
+ )
+
+ unit_lookup = {}
+ for item in all_data:
+ province = item['Province']
+ unit_name = item['DesUnitName']
+ total_good_amount = item['total_good_amount']
+
+ if province not in unit_lookup:
+ unit_lookup[province] = []
+
+ unit_lookup[province].append({
+ 'unit_name': unit_name,
+ 'total_good_amount': total_good_amount
+ })
+
+ result = []
+ for province in total_killing_dict.keys():
+ out_units = unit_lookup.get(province, [])
+ if out_units:
+ max_unit = max(out_units, key=lambda x: x['total_good_amount'])
+ min_unit = min(out_units, key=lambda x: x['total_good_amount'])
+ max_unit_name = max_unit['unit_name']
+ max_unit_amount = max_unit['total_good_amount']
+ min_unit_name = min_unit['unit_name']
+ min_unit_amount = min_unit['total_good_amount']
+ else:
+ max_unit_name = None
+ max_unit_amount = 0
+ min_unit_name = None
+ min_unit_amount = 0
+
+ result.append({
+ "province": province,
+ "max_out_province_slaughterhouse": max_unit_name,
+ "max_out_amount": max_unit_amount,
+ "min_out_province_slaughterhouse": min_unit_name,
+ "min_out_amount": min_unit_amount,
+ "total_killing_province": total_killing_dict.get(province, 0),
+ "total_max_amount": total_max_amount.get(province, 0),
+ "total_min_amount": total_min_amount.get(province, 0),
+ "total_max_amount_unit_name": max_unit_name_in_and_out.get(province),
+ "total_min_amount_unit_name": min_unit_name_in_and_out.get(province),
+ })
+
+ return Response(result, status=status.HTTP_200_OK)
+
+
+@api_view(['GET'])
+@permission_classes([AllowAny])
+@csrf_exempt
+def get_transport_to_kill(request):
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ filters = {"trash": False}
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ filters['issue_date__date__gte'] = date1
+ filters['issue_date__date__lte'] = date2
+ transports = TransportingChickenDetail.objects.filter(**filters)
+ total_amount = build_calculation(queryset=transports, column_name='GoodAmount', aggregate_func=Sum)
+ return Response({"total_amount": total_amount,
+ 'len_transports': transports.count()
+ }, status=status.HTTP_200_OK)
+
+
+@api_view(["POST"])
+@permission_classes([AllowAny])
+@csrf_exempt
+def add_kill_house(request):
+ file = request.FILES['file'].read()
+ wb_obj = openpyxl.load_workbook(filename=BytesIO(file))
+ sheet = wb_obj.active
+ list1 = []
+ for i, row in enumerate(sheet.iter_rows(values_only=True)):
+ if i < 1 or row is None:
+ continue
+ PartIdCode = row[0]
+ UnitName = row[1]
+ City = row[2]
+ Province = row[3]
+ Province_id = row[4]
+ if not KillHouse.objects.filter(PartIdCode=PartIdCode, trash=False):
+ kill_house = KillHouse(
+ PartIdCode=PartIdCode,
+ UnitName=UnitName,
+ Province=Province.rstrip(),
+ City=City.rstrip(),
+ ProvinceId=Province_id
+ )
+ kill_house.save()
+
+ # print(date.date())
+ # for kill in kill_req:
+ # if kill.poultry_name == poultry and kill.quantity== quantity and \
+ # kill.live_weight== wieght and kill.date.date() ==date and kill.acceptor_rejector == accepterde:
+ # print(kill.id)
+ return HttpResponse(list1)
+
+
+@api_view(["GET"])
+@permission_classes([AllowAny])
+@csrf_exempt
+def update_hatching(request):
+ filters = {}
+ province = request.GET.get('province')
+ if province == 'ha':
+ filters['ProvinceName'] = 'همدان'
+
+ elif province == 'ma':
+ filters['ProvinceName'] = 'مرکزی'
+ elif province == 'ku':
+ filters['ProvinceName'] = 'کردستان'
+ else:
+ return Response({'result':'شهر اشتباه است'}, status=status.HTTP_403_FORBIDDEN)
+
+ hatching = Hatching.objects.filter(**filters, trash=False, Age__lte=150)
+ ser_data = HatchingForUpdateSerializer(hatching, many=True).data
+ return Response(ser_data, status=status.HTTP_200_OK)
+
+
+@api_view(["GET"])
+@permission_classes([AllowAny])
+@csrf_exempt
+def get_breeds(request):
+ BREED_STANDARDIZATION = {
+ 'آربراکرز (آ��لاس)': 'آربراکرز (آپلاس)',
+ 'آربراک��ز (آپلاس)': 'آربراکرز (آپلاس)',
+ 'آربر��کرز (آپلاس)': 'آربراکرز (آپلاس)',
+ 'را��': 'راس',
+ 'ر��س': 'راس',
+ '��اس': 'راس',
+ 'آ��ین': 'آرین',
+ 'آر��ن': 'آرین',
+ 'ایندین ریو��': 'ایندین ریور',
+ 'ایند��ن ریور': 'ایندین ریور',
+ 'ک��ب': 'کاب'
+ }
+
+ hatchings = Hatching.objects.filter(
+ trash=False,
+ PedigreeName__in=list(BREED_STANDARDIZATION.keys())
+ ).only('id', 'PedigreeName')
+
+ updated_count = 0
+
+ for hatching in hatchings:
+ original_name = hatching.PedigreeName
+
+ if original_name in BREED_STANDARDIZATION:
+ hatching.PedigreeName = BREED_STANDARDIZATION[original_name]
+ hatching.save(update_fields=['PedigreeName'])
+ updated_count += 1
+
+ return Response({
+ 'status': 'success',
+ 'updated_records': updated_count,
+ 'skipped_records': len(hatchings) - updated_count,
+ 'message': f'Updated {updated_count} records. Skipped {len(hatchings) - updated_count}.'
+ })
+
+
+@api_view(["GET"])
+@permission_classes([AllowAny])
+@csrf_exempt
+def dashboard_total_kill_house(request):
+ filterset_class = KillHouseFilterSet
+ search = request.GET.get('search')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ filters = {"trash": False}
+
+ if province := request.GET.get('province'):
+ filters['Province'] = province
+
+ if city := request.GET.get('city'):
+ filters['City'] = city
+
+ if kill_houses_name := request.GET.get('name'):
+ filters['UnitName'] = kill_houses_name
+
+ kill_houses = KillHouse.objects.filter(**filters).order_by('id')
+
+ if search:
+ if search != 'undefined' and search.strip():
+ kill_houses = kill_houses.filter(
+ build_query(filterset_class.Meta.fields, search)
+ )
+ part_id_codes = list(kill_houses.values_list('PartIdCode', flat=True))
+
+ if not part_id_codes:
+ return Response({
+ "killHouseCount": 0,
+ "bars": 0,
+ "total_bars_quantity": 0,
+ "total_bars_output_count": 0,
+ "total_bars_input_count": 0,
+ "total_input_bars_quantity": 0,
+ "total_output_bars_quantity": 0,
+ "top_kill_house_name": None,
+ "top_kill_house_amount": None,
+ "low_kill_house_name": None,
+ "low_kill_house_amount": None,
+ })
+
+ # بهینهسازی: استفاده از only() برای کاهش دادههای خوانده شده
+ bars = TransportingDetail.objects.filter(DesPartIdCode__in=part_id_codes, trash=False).only('DesPartIdCode', 'GoodAmount', 'Out', 'Date')
+
+ all_products_transport = AllProductsTransport.objects.filter(
+ jihadi_destination__in=part_id_codes,
+ trash=False,
+ product='مرغ زنده -جهت كشتار'
+ ).only('jihadi_destination', 'quantity', 'out', 'date', 'unloading_date')
+
+ if date1:
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ bars = bars.filter(Date__date__gte=date1, Date__date__lte=date2)
+ all_products_transport = all_products_transport.filter(
+ date__gte=date1,
+ date__lte=date2,
+ date__isnull=False
+
+ )
+
+ # بهینهسازی: ترکیب aggregation ها در یک query
+ kill_house_stats_bars = bars.values('DesPartIdCode').annotate(
+ total_amount=Sum('GoodAmount')
+ )
+
+ kill_house_stats_all_products = all_products_transport.values('jihadi_destination').annotate(
+ total_amount=Sum('quantity')
+ )
+
+ # بهینهسازی: استفاده از dictionary comprehension
+ stats_dict = {stat['DesPartIdCode']: (stat['total_amount'] or 0) for stat in kill_house_stats_bars}
+
+ for stat in kill_house_stats_all_products:
+ part_id = stat['jihadi_destination']
+ stats_dict[part_id] = stats_dict.get(part_id, 0) + (stat['total_amount'] or 0)
+
+ kill_house_stats = [
+ {'DesPartIdCode': k, 'total_amount': v}
+ for k, v in sorted(stats_dict.items(), key=lambda x: x[1], reverse=True)
+ ]
+
+ top_kill_house_info = None
+ low_kill_house_info = None
+ if kill_house_stats:
+ top_stat = kill_house_stats[0]
+ low_stat = kill_house_stats[-1]
+
+ # بهینهسازی: یک query برای هر دو kill house
+ kill_house_part_ids = [top_stat['DesPartIdCode'], low_stat['DesPartIdCode']]
+ kill_houses_dict = {kh.PartIdCode: kh for kh in KillHouse.objects.filter(PartIdCode__in=kill_house_part_ids).only('PartIdCode', 'UnitName')}
+
+ top_kill_house = kill_houses_dict.get(top_stat['DesPartIdCode'])
+ low_kill_house = kill_houses_dict.get(low_stat['DesPartIdCode'])
+
+ if top_kill_house:
+ top_kill_house_info = {
+ "name": top_kill_house.UnitName,
+ "amount": top_stat['total_amount']
+ }
+
+ if low_kill_house:
+ low_kill_house_info = {
+ "name": low_kill_house.UnitName,
+ "amount": low_stat['total_amount']
+ }
+
+ aggregation_bars = bars.aggregate(
+ total=Sum('GoodAmount'),
+ input_total=Sum('GoodAmount', filter=Q(Out=False)),
+ output_total=Sum('GoodAmount', filter=Q(Out=True)),
+ input_count=Count('id', filter=Q(Out=False)),
+ output_count=Count('id', filter=Q(Out=True)),
+ total_count=Count('id')
+ )
+
+ aggregation_all_products = all_products_transport.aggregate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id')
+ )
+
+ total_count = (aggregation_bars['total_count'] or 0) + (aggregation_all_products['total_count'] or 0)
+ total_bars_quantity = (aggregation_bars['total'] or 0) + (aggregation_all_products['total'] or 0)
+ total_input_bars_quantity = (aggregation_bars['input_total'] or 0) + (aggregation_all_products['input_total'] or 0)
+ total_output_bars_quantity = (aggregation_bars['output_total'] or 0) + (aggregation_all_products['output_total'] or 0)
+ input_count = (aggregation_bars['input_count'] or 0) + (aggregation_all_products['input_count'] or 0)
+ output_count = (aggregation_bars['output_count'] or 0) + (aggregation_all_products['output_count'] or 0)
+
+ return Response({
+ "killHouseCount": len(part_id_codes),
+ "bars": total_count,
+ "total_bars_quantity": total_bars_quantity,
+ "total_bars_output_count": output_count,
+ "total_bars_input_count": input_count,
+ "total_input_bars_quantity": total_input_bars_quantity,
+ "total_output_bars_quantity": total_output_bars_quantity,
+ "top_kill_house_name": top_kill_house_info.get('name') if top_kill_house_info else None,
+ "top_kill_house_amount": top_kill_house_info.get('amount') if top_kill_house_info else None,
+ "low_kill_house_name": low_kill_house_info.get('name') if low_kill_house_info else None,
+ "low_kill_house_amount": low_kill_house_info.get('amount') if low_kill_house_info else None,
+
+ })
+
+
+class HatchingAnalysisOverviewViewSet(viewsets.ModelViewSet):
+ queryset = Hatching.objects.filter(trash=False, poultry__isnull=False, PedigreeName__isnull=False,
+
+ )
+ serializer_class = HatchingAnalysisSerializer
+ permission_classes = [AllowAny]
+
+ def list(self, request, *args, **kwargs):
+ filters = {}
+ pedigree = request.GET.get('pedigree')
+ city = request.GET.get('city')
+ province = request.GET.get('province')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ state = request.GET.get('state')
+
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ filters['Date__date__gte'] = date1
+ filters['Date__date__lte'] = date2
+ if city:
+ filters['CityName__icontains'] = city
+ if province:
+ filters['ProvinceName__icontains'] = province
+ if pedigree:
+ filters['PedigreeName__icontains'] = pedigree
+ if state == 'active':
+ filters['Age__lte'] = 70
+
+ hatchings = self.queryset.filter(**filters)
+
+ kill_houses_overview = hatchings.filter(transport_hatching__DesUnitName__isnull=False).values(
+ 'transport_hatching__DesUnitName', 'PedigreeName'
+ ).annotate(
+ total_amount=Sum('transport_hatching__GoodAmount')
+ ).order_by('-total_amount')
+
+ hatching_total_poultry_overview = hatchings.filter(poultry__UnitName__isnull=False).values('poultry__UnitName',
+ 'PedigreeName').annotate(
+ chick_count=Sum('ChickCountSum')
+ ).order_by('-chick_count')
+
+ evacuation_total_poultry_overview = hatchings.filter(poultry__UnitName__isnull=False).values(
+ 'poultry__UnitName', 'PedigreeName').annotate(
+ evacuation=Sum('Evacuation')
+ ).order_by('-evacuation')
+
+ kill_house_total = defaultdict(int)
+ kill_house_total_pedigrees = defaultdict(list)
+ hatching_total = defaultdict(int)
+ hatching_total_pedigrees = defaultdict(list)
+ evacuation_total = defaultdict(int)
+ evacuation_total_pedigrees = defaultdict(list)
+
+ for item in kill_houses_overview:
+ kill_house_name = item['transport_hatching__DesUnitName']
+ pedigree_name = item['PedigreeName']
+ total_amount = item['total_amount'] or 0
+ kill_house_total[kill_house_name] += total_amount
+ kill_house_total_pedigrees[kill_house_name].append({pedigree_name: total_amount})
+
+ max_kill_house_name = max(kill_house_total, key=kill_house_total.get)
+ min_kill_house_name = min(kill_house_total, key=kill_house_total.get)
+
+ for data in hatching_total_poultry_overview:
+ poultry_unit_name = data['poultry__UnitName']
+ pedigree_name = data['PedigreeName']
+ chick_count_sum = data['chick_count'] or 0
+ hatching_total[poultry_unit_name] += chick_count_sum
+ hatching_total_pedigrees[poultry_unit_name].append({pedigree_name: chick_count_sum})
+
+ max_poultry_unit_name = max(hatching_total, key=hatching_total.get)
+ min_poultry_unit_name = min(hatching_total, key=hatching_total.get)
+
+ for info in evacuation_total_poultry_overview:
+ poultry_unit_name = info['poultry__UnitName']
+ pedigree_name = info['PedigreeName']
+ evacuation_sum = info['evacuation'] or 0
+ evacuation_total[poultry_unit_name] += evacuation_sum
+ evacuation_total_pedigrees[poultry_unit_name].append({pedigree_name: evacuation_sum})
+
+ max_evacuation_total_poultry = max(evacuation_total, key=evacuation_total.get)
+ min_evacuation_total_poultry = min(evacuation_total, key=evacuation_total.get)
+
+ overview = {
+ "kill_house": {
+ "max": {
+ "name": max_kill_house_name,
+ "total_amount": kill_house_total[max_kill_house_name],
+ "pedigrees": kill_house_total_pedigrees[max_kill_house_name]
+ },
+ "min": {
+ "name": min_kill_house_name,
+ "total_amount": kill_house_total[min_kill_house_name],
+ "pedigrees": kill_house_total_pedigrees[min_kill_house_name]
+ }
+ },
+ "hatching_poultry": {
+ "max": {
+ "name": max_poultry_unit_name,
+ "total_chick_count": hatching_total[max_poultry_unit_name],
+ "pedigrees": hatching_total_pedigrees[max_poultry_unit_name]
+ },
+ "min": {
+ "name": min_poultry_unit_name,
+ "total_chick_count": hatching_total[min_poultry_unit_name],
+ "pedigrees": hatching_total_pedigrees[min_poultry_unit_name]
+ }
+ },
+ "evacuation_poultry": {
+ "max": {
+ "name": max_evacuation_total_poultry,
+ "total_evacuation": evacuation_total[max_evacuation_total_poultry],
+ "pedigrees": evacuation_total_pedigrees[max_evacuation_total_poultry]
+ },
+ "min": {
+ "name": min_evacuation_total_poultry,
+ "total_evacuation": evacuation_total[min_evacuation_total_poultry],
+ "pedigrees": evacuation_total_pedigrees[min_evacuation_total_poultry]
+ }
+ }
+ }
+
+ result = {
+ "overview": overview
+ }
+ return Response(result, status=status.HTTP_200_OK)
+
+
+class TransportingDetailCustomViewSet(viewsets.ModelViewSet):
+ queryset = TransportingDetail.objects.filter(trash=False)
+ serializer_class = TransportingDetailSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = TransportingDetailCustomFilterSet
+
+ def list(self, request, *args, **kwargs):
+ transports = self.filter_queryset(self.get_queryset())
+ search = request.GET.get('search')
+ if search:
+ if search != 'undefined' and search.strip():
+ transports = transports.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(transports)
+ if page is not None:
+ serializer = self.get_serializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+
+ serializer = self.serializer_class(transports, many=True)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+@api_view(['POST'])
+@permission_classes([AllowAny])
+@csrf_exempt
+def send_different_bar(request):
+ filters = {'trash': False}
+ filters_province = {'trash': False}
+ filters_bar = {'trash': False}
+ province = request.GET.get('province')
+ if province:
+ if province == 'ha':
+ filters['poultry__LocationNameProvince'] = 'همدان'
+ filters_province['poultry__Province'] = 'همدان'
+ elif province == 'ma':
+ filters['poultry__LocationNameProvince'] = 'مرکزی'
+ filters_province['poultry__Province'] = 'مرکزی'
+ elif province == 'ku':
+ filters['poultry__LocationNameProvince'] = 'کردستان'
+ filters_province['poultry__Province'] = 'کردستان'
+ else:
+ filters['poultry__LocationNameProvince'] = 'بوشهر'
+ filters_province['poultry__Province'] = 'بوشهر'
+
+ if request.GET.get('date1') != 'None':
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ filters_bar['Date__date__gte'] = date1
+ filters_bar['Date__date__lte'] = date2
+
+ poultry = Hatching.objects.filter(Q(**filters) | Q(**filters_province))
+ bar = TransportingDetail.objects.filter(**filters_bar, hatching__in=poultry, TrackingStatusDescription__in=(
+ 'تایید تخلیه', 'بارگیری'
+ )).exclude(
+ TrackingCode__in=request.data
+ ).order_by('-Date')
+ quarantine_code = bar.values_list('TrackingCode', flat=True).distinct()
+ bar = bar.filter(TrackingCode__in=quarantine_code).order_by('-Date')
+ ser_data = TransportingForClearanceCodeSerializer(bar, many=True).data
+ return Response(ser_data)
+
+
+class ApiSendDifferentBar(viewsets.ModelViewSet):
+ queryset = TransportingDetail.objects.filter(trash=False)
+ serializer_class = TransportingForClearanceCodeSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = TransportingDetailFilterSet
+
+ def list(self, request, *args, **kwargs):
+ filters = {'trash': False}
+ filters_bar = {'trash': False}
+ province = request.GET.get('province')
+ if province == 'ha':
+ filters['poultry__LocationNameProvince'] = 'همدان'
+ elif province == 'ma':
+ filters['poultry__LocationNameProvince'] = 'مرکزی'
+ elif province == 'ku':
+ filters['poultry__LocationNameProvince'] = 'کردستان'
+ else:
+ filters['poultry__LocationNameProvince'] = 'بوشهر'
+ if request.GET.get('date1') != 'None':
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ filters_bar['Date__date__gte'] = date1
+ filters_bar['Date__date__lte'] = date2
+
+ poultry = Hatching.objects.filter(**filters)
+ bar = TransportingDetail.objects.filter(**filters_bar, hatching__in=poultry, TrackingStatusDescription__in=(
+ 'تایید تخلیه', 'بارگیری'
+ )).exclude(
+ TrackingCode__in=request.data
+ ).order_by('-Date')
+ quarantine_code = bar.values_list('TrackingCode', flat=True).distinct()
+ bar = bar.filter(TrackingCode__in=quarantine_code).order_by('-Date')
+ search = request.GET.get('search')
+ value = request.GET.get('value')
+ if search:
+ if search != 'undefined' and value.strip():
+ bar = bar.filter(
+ build_query(self.filterset_class.Meta.fields, value)
+ )
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(bar)
+ if page is not None:
+ serializer = self.get_serializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+ ser_data = TransportingForClearanceCodeSerializer(bar, many=True).data
+ return Response(ser_data)
+
+
+class ApkInfoViewSet(viewsets.ModelViewSet):
+ queryset = ApkInfo.objects.filter(trash=False)
+ serializer_class = ApkInfoSerializer
+ permission_classes = [AllowAny]
+
+ def get_object(self):
+ return self.queryset.first()
+
+ def list(self, request, *args, **kwargs):
+ instance = ApkInfo.objects.filter(trash=False).first()
+ serializer = self.serializer_class(instance)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+ def update(self, request, *args, **kwargs):
+ instance = ApkInfo.objects.filter(trash=False).first()
+ serializer = self.serializer_class(instance, data=request.data, partial=True)
+ serializer.is_valid(raise_exception=True)
+ serializer.save()
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+RASADYAR_ENDPOINT = 'https://s3.rasadyar.com'
+RASADYAR_BUCKET_NAME = 'rasadyar'
+RASADYAR_ACCESS_KEY = "zG3ewsbYsTqCmuws"
+RASADYAR_SECRET_KEY = 'RInUMB78zlQZp6CNf8+sRoSh2cNDHcGQhXrLnTJ1AuI='
+
+
+def upload_to_liara(image, name):
+ s3 = boto3.client(
+ 's3',
+ endpoint_url=RASADYAR_ENDPOINT,
+ aws_access_key_id=RASADYAR_ACCESS_KEY,
+ aws_secret_access_key=RASADYAR_SECRET_KEY
+ )
+ s3.upload_fileobj(
+ image,
+ RASADYAR_BUCKET_NAME,
+ name,
+ ExtraArgs={'ACL': 'public-read'}
+ )
+ return f"{RASADYAR_ENDPOINT}/{RASADYAR_BUCKET_NAME}/{name}"
+
+
+class RasadyarAppInfoViewSet(viewsets.ModelViewSet):
+ queryset = RasadyarAppInfo.objects.filter(trash=False)
+ serializer_class = RasadyarAppInfoSerializer
+ permission_classes = [AllowAny]
+
+ def get_object(self):
+ return self.queryset.first()
+
+ def list(self, request, *args, **kwargs):
+ instance = RasadyarAppInfo.objects.filter(trash=False).first()
+ if not instance:
+ instance = RasadyarAppInfo.objects.create()
+ serializer = self.serializer_class(instance)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+ def create(self, request, *args, **kwargs):
+ instance = RasadyarAppInfo.objects.filter(trash=False).first()
+ if not instance:
+ instance = RasadyarAppInfo()
+
+ if 'file' in request.FILES:
+ ran = ''.join(random.choices(string.ascii_uppercase + string.digits, k=15))
+
+ file_obj = request.FILES.get('file')
+ file_name = file_obj.name
+ file_extension = file_name.split('.')[-1] if '.' in file_name else ''
+ file_name = f"{ran}.{file_extension}"
+ file_url = upload_to_liara(file_obj, file_name)
+ instance.file = file_url
+ if 'info' in request.data:
+ info_data = request.data.get('info')
+ if isinstance(info_data, str):
+ try:
+ instance.info = json.loads(info_data)
+ except json.JSONDecodeError:
+ instance.info = info_data
+ else:
+ instance.info = info_data
+
+ instance.save()
+ serializer = self.serializer_class(instance)
+ return Response(serializer.data, status=status.HTTP_201_CREATED)
+
+ def update(self, request, *args, **kwargs):
+ instance = RasadyarAppInfo.objects.filter(trash=False).first()
+ if not instance:
+ instance = RasadyarAppInfo()
+
+ if 'file' in request.FILES:
+ uploaded_file = request.FILES['file']
+ file_extension = uploaded_file.name.split('.')[-1] if '.' in uploaded_file.name else ''
+ unique_filename = f"rasadyar_app_info_{uuid.uuid4().hex}.{file_extension}" if file_extension else f"rasadyar_app_info_{uuid.uuid4().hex}"
+
+ file_url = upload_to_liara(uploaded_file, unique_filename)
+ instance.file = file_url
+
+ if 'info' in request.data:
+ info_data = request.data.get('info')
+ if isinstance(info_data, str):
+ try:
+ instance.info = json.loads(info_data)
+ except json.JSONDecodeError:
+ instance.info = info_data
+ else:
+ instance.info = info_data
+
+ instance.save()
+ serializer = self.serializer_class(instance)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+@api_view(['POST'])
+@permission_classes([AllowAny])
+@csrf_exempt
+def send_different_bar_with_licence_number(request):
+ filters_bar = {'trash': False}
+ if request.GET.get('date1') != 'None':
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ filters_bar['Date__date__gte'] = date1
+ filters_bar['Date__date__lte'] = date2
+
+ hatching = Hatching.objects.filter(RequestCode=request.GET['licence_number'])
+
+ bar = TransportingDetail.objects.filter(**filters_bar, hatching__in=hatching, TrackingStatusDescription__in=(
+ 'تایید تخلیه', 'بارگیری'
+ )).order_by('-Date')
+ ser_data = TransportingForClearanceCodeSerializer(bar, many=True).data
+ return Response(ser_data)
+
+
+class TransportCarcassDetailViewSet(viewsets.ModelViewSet):
+ queryset = TransportCarcassDetail.objects.filter(trash=False)
+ serializer_class = TransportCarcassDetailSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = TransportCarcassDetailFilterSet
+
+ def list(self, request, *args, **kwargs):
+ search = request.GET.get('search')
+
+ type_role = request.GET.get('role')
+ province = request.GET.get('province')
+
+ if province == 'undefined':
+ province = None
+ if 'code' in request.GET:
+ date1 = request.GET.get('date1')
+ code = request.GET.get('code')
+ filters={}
+
+ # Query برای TransportCarcassDetail
+ query_carcass = self.queryset.filter(jihadi_origin=code)
+
+ # Query برای AllProductsTransport با product='گوشت مرغ تازه'
+ query_all_products = AllProductsTransport.objects.filter(
+ trash=False,
+ product='گوشت مرغ تازه',
+ jihadi_origin=code
+ )
+
+ if date1:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ query_carcass = query_carcass.filter(
+ Q(
+ product_date__gte=date1,
+ product_date__lte=date2,
+ product_date__isnull=False
+ ) |
+ Q(
+ date__gte=date1,
+ date__lte=date2,
+ product_date__isnull=True
+ )
+ )
+ query_all_products = query_all_products.filter(
+
+ date__gte=date1,
+ date__lte=date2,
+ date__isnull=False
+
+ )
+
+ if search:
+ if search != 'undefined' and search.strip():
+ query_carcass = query_carcass.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+ # اعمال search روی AllProductsTransport
+ query_all_products = query_all_products.filter(
+ build_query(AllProductsTransportFilterSet.Meta.fields, search)
+ )
+
+ # تبدیل به list و ترکیب
+ carcass_list = list(query_carcass)
+ all_products_list = list(query_all_products)
+
+ # تبدیل AllProductsTransport به TransportCarcassDetail-like objects برای sort
+ def get_sort_date(obj):
+ if hasattr(obj, 'product_date') and obj.product_date:
+ return obj.product_date
+ elif hasattr(obj, 'date') and obj.date:
+ return obj.date
+ elif hasattr(obj, 'unloading_date') and obj.unloading_date:
+ return obj.unloading_date
+ return datetime.date.min
+
+ # ایجاد یک list ترکیبی با tuple (date, source_type, obj)
+ combined_list = []
+ for obj in carcass_list:
+ combined_list.append((get_sort_date(obj), 'carcass', obj))
+ for obj in all_products_list:
+ combined_list.append((get_sort_date(obj), 'all_products', obj))
+
+ # sort بر اساس date (از جدید به قدیم)
+ combined_list.sort(key=lambda x: x[0], reverse=True)
+
+ # جدا کردن objects
+ sorted_objects = [obj for _, _, obj in combined_list]
+
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ # Pagination روی list
+ paginator = self.pagination_class()
+ page = paginator.paginate_queryset(sorted_objects, request)
+
+ if page is not None:
+ # Serialize هر object با serializer مناسب
+ serialized_data = []
+ for obj in page:
+ if hasattr(obj, 'product_date'): # TransportCarcassDetail
+ serializer = TransportCarcassDetailSerializer(obj, context={'request': request})
+ serialized_data.append(serializer.data)
+ else: # AllProductsTransport
+ # تبدیل به dict و اضافه کردن فیلدهای مشترک
+ serializer = AllProductsTransportSerializer(obj, context={'request': request})
+ data = serializer.data
+ # اضافه کردن فیلدهای مشابه TransportCarcassDetail برای سازگاری
+ data['product_date'] = obj.date
+ data['id_quarantineh'] = obj.record_id
+ serialized_data.append(data)
+ return paginator.get_paginated_response(serialized_data)
+
+ # اگر pagination نبود
+ serialized_data = []
+ for obj in sorted_objects:
+ if hasattr(obj, 'product_date'): # TransportCarcassDetail
+ serializer = TransportCarcassDetailSerializer(obj, context={'request': request})
+ serialized_data.append(serializer.data)
+ else: # AllProductsTransport
+ serializer = AllProductsTransportSerializer(obj, context={'request': request})
+ data = serializer.data
+ data['product_date'] = obj.date
+ data['id_quarantineh'] = obj.record_id
+ serialized_data.append(data)
+ return Response(serialized_data, status=status.HTTP_200_OK)
+ if type_role:
+ if type_role == 'KillHouse':
+ filters_kill_house = {}
+ if province:
+ filters_kill_house['Province'] = province
+
+ kill_house_filterset_class = KillHouseFilterSet
+ kill_house = KillHouse.objects.filter(**filters_kill_house).order_by('id')
+
+ if search and search != 'undefined' and search.strip():
+ kill_house = kill_house.filter(
+ build_query(kill_house_filterset_class.Meta.fields, search)
+ )
+
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+
+ bars = TransportCarcassDetail.objects.filter(trash=False)
+ buy_bars = TransportingDetail.objects.filter(trash=False, Date__date__gte='2025-03-21',
+ TrackingStatusDescription__in=("تایید تخلیه",'بارگیری'))
+ all_products_transport_carcass = AllProductsTransport.objects.filter(trash=False, product='گوشت مرغ تازه',jihadi_origin__in=kill_house.values_list('PartIdCode', flat=True))
+ all_products_transport = AllProductsTransport.objects.filter(trash=False, product='مرغ زنده -جهت كشتار',jihadi_destination__in=kill_house.values_list('PartIdCode', flat=True))
+
+ if date1:
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ bars = bars.filter(Q(product_date__gte=date1, product_date__lte=date2, product_date__isnull=False) |
+ Q(date__gte=date1, date__lte=date2, product_date__isnull=True))
+ all_products_transport_carcass = all_products_transport_carcass.filter(
+ date__gte=date1, date__lte=date2, date__isnull=False
+ )
+ date_1_for_buy_bars = date1 - datetime.timedelta(days=1)
+ date_2_for_buy_bars = date2 - datetime.timedelta(days=1)
+ buy_bars = buy_bars.filter(Date__date__gte=date_1_for_buy_bars, Date__date__lte=date_2_for_buy_bars)
+ all_products_transport = all_products_transport.filter(
+ date__gte=date_1_for_buy_bars, date__lte=date_2_for_buy_bars, date__isnull=False
+ )
+
+
+ bars_summary = bars.values('jihadi_origin').annotate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id'),
+ )
+
+ all_products_transport_carcass_summary = all_products_transport_carcass.values('jihadi_origin').annotate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id'),
+ )
+
+ buy_summary = buy_bars.values('DesPartIdCode').annotate(
+ total=Sum('GoodAmount'),
+ input_total=Sum('GoodAmount', filter=Q(Out=False)),
+ output_total=Sum('GoodAmount', filter=Q(Out=True)),
+ input_count=Count('id', filter=Q(Out=False)),
+ output_count=Count('id', filter=Q(Out=True)),
+ total_count=Count('id'),
+ )
+
+ all_products_transport_summary = all_products_transport.values('jihadi_destination').annotate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id'),
+ )
+
+ # ادغام bars_summary و all_products_transport_carcass_summary
+ bars_dict = {row['jihadi_origin']: row for row in bars_summary if row.get('jihadi_origin')}
+ for row in all_products_transport_carcass_summary:
+ key = row.get('jihadi_origin')
+ if not key:
+ continue
+ if key in bars_dict:
+ bars_dict[key]['total'] = (bars_dict[key].get('total', 0) or 0) + (row.get('total', 0) or 0)
+ bars_dict[key]['input_total'] = (bars_dict[key].get('input_total', 0) or 0) + (row.get('input_total', 0) or 0)
+ bars_dict[key]['output_total'] = (bars_dict[key].get('output_total', 0) or 0) + (row.get('output_total', 0) or 0)
+ bars_dict[key]['input_count'] = (bars_dict[key].get('input_count', 0) or 0) + (row.get('input_count', 0) or 0)
+ bars_dict[key]['output_count'] = (bars_dict[key].get('output_count', 0) or 0) + (row.get('output_count', 0) or 0)
+ bars_dict[key]['total_count'] = (bars_dict[key].get('total_count', 0) or 0) + (row.get('total_count', 0) or 0)
+ else:
+ bars_dict[key] = row
+
+ # ادغام buy_summary و all_products_transport_summary
+ buy_dict = {row['DesPartIdCode']: row for row in buy_summary if row.get('DesPartIdCode')}
+ for row in all_products_transport_summary:
+ key = row.get('jihadi_destination') # jihadi_destination همان DesPartIdCode است
+ if not key:
+ continue
+ if key in buy_dict:
+ buy_dict[key]['total'] = (buy_dict[key].get('total', 0) or 0) + (row.get('total', 0) or 0)
+ buy_dict[key]['input_total'] = (buy_dict[key].get('input_total', 0) or 0) + (row.get('input_total', 0) or 0)
+ buy_dict[key]['output_total'] = (buy_dict[key].get('output_total', 0) or 0) + (row.get('output_total', 0) or 0)
+ buy_dict[key]['input_count'] = (buy_dict[key].get('input_count', 0) or 0) + (row.get('input_count', 0) or 0)
+ buy_dict[key]['output_count'] = (buy_dict[key].get('output_count', 0) or 0) + (row.get('output_count', 0) or 0)
+ buy_dict[key]['total_count'] = (buy_dict[key].get('total_count', 0) or 0) + (row.get('total_count', 0) or 0)
+ else:
+ # تبدیل jihadi_destination به DesPartIdCode برای سازگاری
+ buy_dict[key] = {
+ 'DesPartIdCode': key,
+ 'total': row.get('total', 0) or 0,
+ 'input_total': row.get('input_total', 0) or 0,
+ 'output_total': row.get('output_total', 0) or 0,
+ 'input_count': row.get('input_count', 0) or 0,
+ 'output_count': row.get('output_count', 0) or 0,
+ 'total_count': row.get('total_count', 0) or 0,
+ }
+
+ kill_house = list(kill_house)
+ kill_house.sort(
+ key=lambda kh: (bars_dict.get(kh.PartIdCode, {}) or {}).get('total', 0) or 0,
+ reverse=True
+ )
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(kill_house)
+ if page is not None:
+ serializer = KillHouseForTransportCarcassSerializer(
+ page, many=True, context={'request': request, 'bars_dict': bars_dict, 'buy_dict': buy_dict}
+ )
+ return self.get_paginated_response(serializer.data)
+
+ serializer = KillHouseForTransportCarcassSerializer(
+ kill_house, many=True, context={'request': request, 'bars_dict': bars_dict, 'buy_dict': buy_dict}
+ )
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+ else:
+ filters_steward = {}
+ if province:
+ filters_steward['province'] = province
+
+ steward_filterset_class = GuildsFilterSet
+ steward = Guilds.objects.filter(**filters_steward, trash=False, is_steward=True).order_by('id')
+
+ if search and search != 'undefined' and search.strip():
+ steward = steward.filter(
+ build_query(steward_filterset_class.Meta.fields, search)
+ )
+
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+
+ bars = TransportCarcassDetail.objects.filter(trash=False).order_by('-product_date')
+
+ if date1:
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ bars = bars.filter(product_date__gte=date1, product_date__lte=date2)
+
+
+ bars_summary = bars.values('jihadi_origin', 'jihadi_destination').annotate(
+ total=Sum('quantity', filter=Q(jihadi_origin=F('jihadi_origin'))),
+ input_total=Sum('quantity', filter=Q(out=False, jihadi_origin=F('jihadi_origin'))),
+ output_total=Sum('quantity', filter=Q(out=True, jihadi_origin=F('jihadi_origin'))),
+ input_count=Count('id', filter=Q(out=False, jihadi_origin=F('jihadi_origin'))),
+ output_count=Count('id', filter=Q(out=True, jihadi_origin=F('jihadi_origin'))),
+ total_count=Count('id', filter=Q(jihadi_origin=F('jihadi_origin'))),
+
+ total_input_buy_bars_wight=Sum('quantity',
+ filter=Q(out=False, jihadi_destination=F('jihadi_destination'))),
+ total_input_buy_bars_count=Count('id',
+ filter=Q(out=False, jihadi_destination=F('jihadi_destination'))),
+ total_output_buy_bars_wight=Sum('quantity',
+ filter=Q(out=True, jihadi_destination=F('jihadi_destination'))),
+ total_output_buy_bars_count=Count('id',
+ filter=Q(out=True, jihadi_destination=F('jihadi_destination'))),
+ )
+
+ bars_dict = {}
+ for row in bars_summary:
+ code = row['jihadi_origin'] or row['jihadi_destination']
+ if code:
+ bars_dict[code] = row
+
+ steward = list(steward)
+ steward.sort(
+ key=lambda s: (bars_dict.get(s.jihadi_code, {}) or {}).get('total', 0) or 0,
+ reverse=True
+ )
+
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(steward)
+ if page is not None:
+ serializer = StewardForTransportCarcassSerializer(
+ page, many=True, context={'request': request, 'bars_dict': bars_dict}
+ )
+ return self.get_paginated_response(serializer.data)
+
+ serializer = StewardForTransportCarcassSerializer(
+ steward, many=True, context={'request': request, 'bars_dict': bars_dict}
+ )
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+ return Response('ok', status=status.HTTP_200_OK)
+
+ def create(self, request, *args, **kwargs):
+ file = request.FILES['file'].read()
+ wb_obj = openpyxl.load_workbook(filename=BytesIO(file))
+ sheet = wb_obj.active
+
+ # گرفتن مقادیر اولیه برای چک کردن وجود داشتن
+ kill_house_code = set(
+ KillHouse.objects.only('PartIdCode').values_list('PartIdCode', flat=True)
+ )
+ steward_jihadi_code = set(
+ Guilds.objects.filter(trash=False, is_steward=True).values_list('jihadi_code', flat=True)
+ )
+
+ headers = [str(cell).strip() for cell in next(sheet.iter_rows(values_only=True))]
+
+ field_map = {
+ "id": "id_quarantineh",
+ "مقصد قبلی": "destination_prev",
+ "تغییر مقصد": "destination_changed",
+ "پرداخت": "payment",
+ "رهگیری": "tracking",
+ "تاریخ": "date",
+ "ساعت": "time",
+ "محصول": "product",
+ "اقلام": "items",
+ "مقدار": "quantity",
+ "واحد": "unit",
+ "استان مبدا": "origin_province",
+ "شهرستان مبدا": "origin_city",
+ "مبدا": "origin",
+ "استان مقصد": "destination_province",
+ "شهرستان مقصد": "destination_city",
+ "مقصد": "destination",
+ "ش جهادی مبدا": "jihadi_origin",
+ "ش جهادی مقصد": "jihadi_destination",
+ "مالک": "owner",
+ "کد رهگیری خودرو": "car_tracking_code",
+ "نام راننده": "driver_name",
+ "پلاک": "plate",
+ "مبلغ": "amount",
+ "تخلیه": "unloading",
+ "وزن پر": "gross_weight",
+ "وزن خالی": "tare_weight",
+ "وزن": "net_weight",
+ "کد باسکول": "scale_code",
+ "نام باسکول": "scale_name",
+ "قبض باسکول": "scale_receipt",
+ "تاریخ تخلیه": "unloading_date",
+ "Out": "out",
+ }
+
+ out_flag = True if request.data.get('out') == 'true' else False
+
+ bulk_guilds_create = []
+ bulk_records_create = []
+ record_updates = {}
+
+ for row in sheet.iter_rows(min_row=2, values_only=True):
+ if not row:
+ continue
+
+ row_data = dict(zip(headers, row))
+ tracking_val = row_data.get("رهگیری")
+ if not tracking_val:
+ continue
+
+ steward_code = row_data.get("ش جهادی مبدا")
+ guilds_code = row_data.get("ش جهادی مقصد")
+
+ # گیلد مبدا
+ if steward_code and steward_code not in kill_house_code and steward_code not in steward_jihadi_code:
+ bulk_guilds_create.append(Guilds(
+ jihadi_code=steward_code,
+ name=row_data.get("مبدا"),
+ city=row_data.get("شهرستان مبدا"),
+ province=row_data.get("استان مبدا"),
+ is_steward=True
+ ))
+ steward_jihadi_code.add(steward_code)
+
+ # گیلد مقصد
+ if guilds_code and guilds_code not in kill_house_code and guilds_code not in steward_jihadi_code:
+ bulk_guilds_create.append(Guilds(
+ jihadi_code=guilds_code,
+ name=row_data.get("مقصد"),
+ city=row_data.get("شهرستان مقصد"),
+ province=row_data.get("استان مقصد"),
+ is_steward=False
+ ))
+ steward_jihadi_code.add(guilds_code)
+
+ record_data = {}
+ for col_name, model_field in field_map.items():
+ if col_name in row_data:
+ value = row_data[col_name]
+
+ if model_field in ["date", "unloading_date"] and value:
+ try:
+ if isinstance(value, str):
+ y, m, d = map(int, value.split("/"))
+ value = convert_to_miladi(y, m, d)
+ elif hasattr(value, "year"):
+ value = convert_to_miladi(value.year, value.month, value.day)
+ except:
+ value = None
+
+ if model_field == "out":
+ record_data[model_field] = out_flag
+ else:
+ record_data[model_field] = value
+
+ record_updates[tracking_val] = record_data
+
+ # ذخیره گیلدها
+ if bulk_guilds_create:
+ Guilds.objects.bulk_create(bulk_guilds_create, ignore_conflicts=True)
+
+ # بررسی رکوردهای موجود
+ existing_trackings = set(
+ TransportCarcassDetail.objects.filter(
+ tracking__in=record_updates.keys()
+ ).values_list('tracking', flat=True)
+ )
+
+ for tracking_val, data in record_updates.items():
+ data["out"] = out_flag
+ if tracking_val in existing_trackings:
+ TransportCarcassDetail.objects.filter(tracking=tracking_val).update(**data)
+ else:
+ data["tracking"] = tracking_val
+ bulk_records_create.append(TransportCarcassDetail(**data))
+
+ if bulk_records_create:
+ TransportCarcassDetail.objects.bulk_create(bulk_records_create)
+
+ return Response({'result': 'ok'}, status=status.HTTP_201_CREATED)
+
+
+class DriveViewSet(viewsets.ModelViewSet):
+ queryset = Driver.objects.filter(trash=False)
+ serializer_class = DriverSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = DriverFilterSet
+
+ def list(self, request, *args, **kwargs):
+ if 'code' in request.GET:
+ query = self.queryset.filter(tracking_code__regex=r'^\d{7}$').only('tracking_code').values_list('tracking_code',flat=True).distinct()
+ return Response({'codes':query}, status=status.HTTP_200_OK)
+ if 'all' in request.GET:
+ query = self.queryset.filter(tracking_code__regex=r'^\d{7}$',trash=False,pelak__isnull=False).order_by('part_id_code')
+ serializer = self.serializer_class(query, many=True)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+ filters = {}
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ city = request.GET.get('city')
+ province = request.GET.get('province')
+ if province == 'undefined':
+ province = None
+ if date1 and date2:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ filters['date__gte'] = date1
+ filters['date__lte'] = date2
+
+ search = request.GET.get('search')
+ transports = self.queryset.filter(**filters).order_by("-date")
+
+ if search:
+ if search != 'undefined' and search.strip():
+ transports = transports.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(transports)
+ if page is not None:
+ serializer = self.get_serializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+
+ serializer = self.serializer_class(transports, many=True)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+ def create(self, request, *args, **kwargs):
+ file = request.FILES['file'].read()
+ wb_obj = openpyxl.load_workbook(filename=BytesIO(file))
+ sheet = wb_obj.active
+ headers = [str(cell).strip() for cell in next(sheet.iter_rows(values_only=True))]
+
+ field_map = {
+ "کد رهگیری خودرو": "tracking_code",
+ # "استان مقصد": "province",
+ # "شهرستان مقصد": "city",
+ # "محصول": "product",
+ "مقصد": "kill_house_name",
+ "ش جهادی مقصد": "part_id_code",
+
+ }
+ created_list = []
+
+ for row in sheet.iter_rows(min_row=2, values_only=True):
+ if row is None:
+ continue
+
+ row_data = dict(zip(headers, row))
+ driver_name = row_data.get("نام راننده")
+
+ record_data = {}
+ for col_name, model_field in field_map.items():
+ if col_name in row_data:
+ value = row_data[col_name]
+
+ record_data[model_field] = value
+
+ obj, created = Driver.objects.update_or_create(
+ driver_name=driver_name,
+ defaults=record_data,
+ )
+
+ created_list.append({
+ "driver_name": obj.driver_name,
+ })
+
+ return Response({'result':'ok'},status=status.HTTP_201_CREATED)
+
+ def update(self, request, *args, **kwargs):
+ tracking_code = request.data.get('tracking_code')
+
+ driver = Driver.objects.filter(
+ tracking_code=tracking_code,
+ ).first()
+ if driver:
+ date_str = request.data.get('expire_licence_date')
+ expire_date = None
+ if date_str and isinstance(date_str, str):
+ try:
+ parts = [int(x) for x in date_str.split("/")]
+ if len(parts) == 3:
+ expire_date = convert_to_miladi(parts[0], parts[1], parts[2])
+ except (ValueError, IndexError) as e:
+ pass
+ update_fields = [
+ 'car_id', 'driver_name', 'owner_name', 'pelak',
+ 'weight', 'car_type', 'health_permit'
+ ]
+
+ for field in update_fields:
+ if field in request.data:
+ setattr(driver, field, request.data[field])
+
+ if expire_date is not None:
+ driver.expire_licence_date = expire_date
+
+ driver.save()
+
+ return Response({'result':'ok'},status=status.HTTP_200_OK)
+
+
+class TransportCarcassDashboardView(APIView):
+ permission_classes = [AllowAny]
+
+ def get(self,request):
+ search = request.GET.get('search')
+
+ type_role = request.GET.get('role')
+ province = request.GET.get('province')
+
+ if province == 'undefined':
+ province = None
+ date1 = request.GET.get('date1') or None
+ date2 = request.GET.get('date2') or None
+ if type_role:
+ if type_role == 'KillHouse':
+ filters_kill_house = {}
+ if province:
+ filters_kill_house['Province'] = province
+ kill_house_filterset_class = KillHouseFilterSet
+ kill_house = KillHouse.objects.filter(**filters_kill_house).order_by('id')
+ if search:
+ if search != 'undefined' and search.strip():
+ kill_house = kill_house.filter(
+ build_query(kill_house_filterset_class.Meta.fields, search)
+ )
+ kill_house = kill_house.values_list('PartIdCode',flat=True)
+ bars = TransportCarcassDetail.objects.filter(jihadi_origin__in=kill_house, trash=False)
+ buy_bars = TransportingDetail.objects.filter(DesPartIdCode__in=kill_house,
+ trash=False,Date__date__gte='2025-03-21',
+ TrackingStatusDescription__in=("تایید تخلیه",'بارگیری')).only(
+ 'GoodAmount', 'Out')
+ all_products_transport_carcass = AllProductsTransport.objects.filter(
+ trash=False,
+ product='گوشت مرغ تازه',
+ jihadi_origin__in=kill_house
+ )
+ all_products_transport = AllProductsTransport.objects.filter(
+ trash=False,
+ product='مرغ زنده -جهت كشتار',
+ jihadi_destination__in=kill_house
+ )
+
+ if date1:
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ bars = bars.filter(Q(product_date__gte=date1, product_date__lte=date2, product_date__isnull=False) |
+ Q(date__gte=date1, date__lte=date2, product_date__isnull=True))
+ all_products_transport_carcass = all_products_transport_carcass.filter(
+ date__gte=date1, date__lte=date2, date__isnull=False
+ )
+ date_1_for_buy_bars = date1 - datetime.timedelta(days=1)
+ date_2_for_buy_bars = date2 - datetime.timedelta(days=1)
+ buy_bars = buy_bars.filter(Date__date__gte=date_1_for_buy_bars, Date__date__lte=date_2_for_buy_bars)
+ all_products_transport = all_products_transport.filter(
+ date__gte=date_1_for_buy_bars, date__lte=date_2_for_buy_bars, date__isnull=False
+ )
+
+ aggregation = bars.aggregate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id'),
+
+ )
+ all_products_transport_carcass_aggregation = all_products_transport_carcass.aggregate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id'),
+ )
+ buy_aggregation = buy_bars.aggregate(
+ total=Sum('GoodAmount'),
+ input_total=Sum('GoodAmount', filter=Q(Out=False)),
+ output_total=Sum('GoodAmount', filter=Q(Out=True)),
+ input_count=Count('id', filter=Q(Out=False)),
+ output_count=Count('id', filter=Q(Out=True)),
+ total_count=Count('id')
+ )
+ all_products_transport_aggregation = all_products_transport.aggregate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id'),
+ )
+
+ # ادغام aggregationها
+ aggregation['total'] = (aggregation.get('total') or 0) + (all_products_transport_carcass_aggregation.get('total') or 0)
+ aggregation['input_total'] = (aggregation.get('input_total') or 0) + (all_products_transport_carcass_aggregation.get('input_total') or 0)
+ aggregation['output_total'] = (aggregation.get('output_total') or 0) + (all_products_transport_carcass_aggregation.get('output_total') or 0)
+ aggregation['input_count'] = (aggregation.get('input_count') or 0) + (all_products_transport_carcass_aggregation.get('input_count') or 0)
+ aggregation['output_count'] = (aggregation.get('output_count') or 0) + (all_products_transport_carcass_aggregation.get('output_count') or 0)
+ aggregation['total_count'] = (aggregation.get('total_count') or 0) + (all_products_transport_carcass_aggregation.get('total_count') or 0)
+
+ buy_aggregation['total'] = (buy_aggregation.get('total') or 0) + (all_products_transport_aggregation.get('total') or 0)
+ buy_aggregation['input_total'] = (buy_aggregation.get('input_total') or 0) + (all_products_transport_aggregation.get('input_total') or 0)
+ buy_aggregation['output_total'] = (buy_aggregation.get('output_total') or 0) + (all_products_transport_aggregation.get('output_total') or 0)
+ buy_aggregation['input_count'] = (buy_aggregation.get('input_count') or 0) + (all_products_transport_aggregation.get('input_count') or 0)
+ buy_aggregation['output_count'] = (buy_aggregation.get('output_count') or 0) + (all_products_transport_aggregation.get('output_count') or 0)
+ buy_aggregation['total_count'] = (buy_aggregation.get('total_count') or 0) + (all_products_transport_aggregation.get('total_count') or 0)
+
+ total_count = aggregation['total_count'] or 0
+ total_bars_quantity = aggregation['total'] or 0
+ total_input_bars_quantity = aggregation['input_total'] or 0
+ total_output_bars_quantity = aggregation['output_total'] or 0
+ input_bars_count = aggregation['input_count'] or 0
+ output_bars_count = aggregation['output_count'] or 0
+
+
+ if total_count > 0:
+ total_input_bars_percent = round(
+ (total_input_bars_quantity / (total_input_bars_quantity + total_output_bars_quantity)) * 100, 1)
+ total_output_bars_percent = round(
+ (total_output_bars_quantity / (total_input_bars_quantity + total_output_bars_quantity)) * 100,
+ 1)
+ else:
+ total_input_bars_percent = 0
+ total_output_bars_percent = 0
+
+ return Response({
+ "role": 'کشتارگاه',
+ "product": 'مرغ گرم',
+ "bars": int(total_count),
+ "total_bars_wight": int(total_bars_quantity),
+ "input_bars": int(input_bars_count),
+ "total_input_bars_wight": int(total_input_bars_quantity),
+ "total_input_buy_bars_wight": int(((buy_aggregation['input_total'] or 0) * 2.5) *0.75),
+ "total_input_bars_percent": total_input_bars_percent,
+ "output_bars": int(output_bars_count),
+ "total_output_bars_wight": int(total_output_bars_quantity),
+ "total_output_buy_bars_wight": int(((buy_aggregation['output_total'] or 0) * 2.5) *0.75),
+ "total_output_bars_percent": total_output_bars_percent,
+ "total_ware_house": int((((buy_aggregation['input_total'] or 0) * 2.5) *0.75) + (((buy_aggregation['output_total'] or 0) * 2.5) *0.75)),
+ "total_input_buy_bars_count": buy_aggregation['input_count'] or 0,
+ "total_output_buy_bars_count": buy_aggregation['output_count'] or 0,
+ "total_count_kill_house": len(kill_house),
+ "last_update": self._get_last_update_date_for_dashboard(bars, all_products_transport_carcass),
+
+ })
+
+
+ else:
+ filters_steward = {}
+ if province:
+ filters_steward['province'] = province
+ steward = Guilds.objects.filter(**filters_steward,trash=False, is_steward=True).order_by('id')
+ steward_filterset_class = GuildsFilterSet
+ if search:
+ if search != 'undefined' and search.strip():
+ steward = steward.filter(
+ build_query(steward_filterset_class.Meta.fields, search)
+ )
+ steward = steward.values_list('jihadi_code',flat=True)
+ date1 = request.GET.get('date1') or None
+ date2 = request.GET.get('date2') or None
+
+ bars = TransportCarcassDetail.objects.filter(
+ Q(jihadi_origin__in=steward) | Q(jihadi_destination__in=steward)
+ , trash=False).order_by('-modify_date')
+ all_products_transport_steward = AllProductsTransport.objects.filter(
+ trash=False,
+ product='گوشت مرغ تازه',
+ ).filter(
+ Q(jihadi_origin__in=steward) | Q(jihadi_destination__in=steward)
+ )
+ if date1:
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ bars = bars.filter(product_date__gte=date1, product_date__lte=date2)
+ all_products_transport_steward = all_products_transport_steward.filter(
+ date__gte=date1, date__lte=date2, date__isnull=False
+ )
+
+ aggregation = bars.aggregate(
+ total=Sum('quantity', filter=Q(jihadi_origin__in=steward)),
+ input_total=Sum('quantity', filter=Q(out=False, jihadi_origin__in=steward)),
+ output_total=Sum('quantity', filter=Q(out=True, jihadi_origin__in=steward)),
+ input_count=Count('id', filter=Q(out=False, jihadi_origin__in=steward)),
+ output_count=Count('id', filter=Q(out=True, jihadi_origin__in=steward)),
+ total_count=Count('id', filter=Q(jihadi_origin__in=steward)),
+ total_input_buy_bars_wight=Sum('quantity', filter=Q(jihadi_destination__in=steward, out=False)),
+ total_input_buy_bars_count=Count('id', filter=Q(jihadi_destination__in=steward, out=False)),
+ total_output_buy_bars_wight=Sum('quantity', filter=Q(jihadi_destination__in=steward, out=True)),
+ total_output_buy_bars_count=Count('id', filter=Q(jihadi_destination__in=steward, out=True)),
+ total_ware_house=Sum('quantity', filter=Q(jihadi_origin__in=steward)),
+
+ )
+ all_products_transport_steward_aggregation = all_products_transport_steward.aggregate(
+ total=Sum('quantity', filter=Q(jihadi_origin__in=steward)),
+ input_total=Sum('quantity', filter=Q(out=False, jihadi_origin__in=steward)),
+ output_total=Sum('quantity', filter=Q(out=True, jihadi_origin__in=steward)),
+ input_count=Count('id', filter=Q(out=False, jihadi_origin__in=steward)),
+ output_count=Count('id', filter=Q(out=True, jihadi_origin__in=steward)),
+ total_count=Count('id', filter=Q(jihadi_origin__in=steward)),
+ total_input_buy_bars_wight=Sum('quantity', filter=Q(jihadi_destination__in=steward, out=False)),
+ total_input_buy_bars_count=Count('id', filter=Q(jihadi_destination__in=steward, out=False)),
+ total_output_buy_bars_wight=Sum('quantity', filter=Q(jihadi_destination__in=steward, out=True)),
+ total_output_buy_bars_count=Count('id', filter=Q(jihadi_destination__in=steward, out=True)),
+ total_ware_house=Sum('quantity', filter=Q(jihadi_origin__in=steward)),
+ )
+
+ # ادغام aggregationها
+ aggregation['total'] = (aggregation.get('total') or 0) + (all_products_transport_steward_aggregation.get('total') or 0)
+ aggregation['input_total'] = (aggregation.get('input_total') or 0) + (all_products_transport_steward_aggregation.get('input_total') or 0)
+ aggregation['output_total'] = (aggregation.get('output_total') or 0) + (all_products_transport_steward_aggregation.get('output_total') or 0)
+ aggregation['input_count'] = (aggregation.get('input_count') or 0) + (all_products_transport_steward_aggregation.get('input_count') or 0)
+ aggregation['output_count'] = (aggregation.get('output_count') or 0) + (all_products_transport_steward_aggregation.get('output_count') or 0)
+ aggregation['total_count'] = (aggregation.get('total_count') or 0) + (all_products_transport_steward_aggregation.get('total_count') or 0)
+ aggregation['total_input_buy_bars_wight'] = (aggregation.get('total_input_buy_bars_wight') or 0) + (all_products_transport_steward_aggregation.get('total_input_buy_bars_wight') or 0)
+ aggregation['total_input_buy_bars_count'] = (aggregation.get('total_input_buy_bars_count') or 0) + (all_products_transport_steward_aggregation.get('total_input_buy_bars_count') or 0)
+ aggregation['total_output_buy_bars_wight'] = (aggregation.get('total_output_buy_bars_wight') or 0) + (all_products_transport_steward_aggregation.get('total_output_buy_bars_wight') or 0)
+ aggregation['total_output_buy_bars_count'] = (aggregation.get('total_output_buy_bars_count') or 0) + (all_products_transport_steward_aggregation.get('total_output_buy_bars_count') or 0)
+ aggregation['total_ware_house'] = (aggregation.get('total_ware_house') or 0) + (all_products_transport_steward_aggregation.get('total_ware_house') or 0)
+
+ total_count = aggregation['total_count'] or 0
+ total_bars_quantity = aggregation['total'] or 0
+ total_input_bars_quantity = aggregation['input_total'] or 0
+ total_output_bars_quantity = aggregation['output_total'] or 0
+ input_bars_count = aggregation['input_count'] or 0
+ output_bars_count = aggregation['output_count'] or 0
+ total_input_buy_bars_wight = aggregation['total_input_buy_bars_wight'] or 0
+ total_output_buy_bars_wight = aggregation['total_output_buy_bars_wight'] or 0
+ total_ware_house = total_input_buy_bars_wight + total_output_buy_bars_wight
+ total_input_buy_bars_count = aggregation['total_input_buy_bars_count'] or 0
+ total_output_buy_bars_count = aggregation['total_output_buy_bars_count'] or 0
+
+ if total_count > 0:
+ total_input_bars_percent = round((total_input_bars_quantity / (total_input_bars_quantity +total_output_bars_quantity) ) * 100, 1)
+ total_output_bars_percent = round((total_output_bars_quantity / (total_input_bars_quantity +total_output_bars_quantity)) * 100, 1)
+ else:
+ total_input_bars_percent = 0
+ total_output_bars_percent = 0
+
+ return Response({
+ "role": 'مباشر',
+ "product": 'گوشت مرغ تازه',
+ "bars": int(total_count),
+ "total_bars_wight": int(total_bars_quantity),
+ "input_bars": int(input_bars_count),
+ "total_input_bars_wight": int(total_input_bars_quantity),
+ "total_input_buy_bars_wight": int(total_input_buy_bars_wight),
+ "total_input_bars_percent": total_input_bars_percent,
+ "output_bars": int(output_bars_count),
+ "total_output_bars_wight": int(total_output_bars_quantity),
+ "total_output_buy_bars_wight": int(total_output_buy_bars_wight),
+ "total_output_bars_percent": total_output_bars_percent,
+ "total_ware_house": int(total_ware_house),
+ "total_count_steward": len(steward),
+ "last_update": self._get_last_update_date_for_dashboard(bars, all_products_transport_steward),
+ "total_input_buy_bars_count": int(total_input_buy_bars_count),
+ "total_output_buy_bars_count": int(total_output_buy_bars_count),
+
+ })
+
+ return Response('ok', status=status.HTTP_200_OK)
+
+ def _get_last_update_date_for_dashboard(self, queryset1, queryset2):
+ """تابع helper برای دریافت آخرین تاریخ بهروزرسانی از دو queryset"""
+ dates = []
+
+ if queryset1.exists():
+ first_obj = queryset1.first()
+ if first_obj and hasattr(first_obj, 'modify_date') and first_obj.modify_date:
+ dates.append(first_obj.modify_date)
+
+ if queryset2.exists():
+ first_obj = queryset2.first()
+ if first_obj and hasattr(first_obj, 'modify_date') and first_obj.modify_date:
+ dates.append(first_obj.modify_date)
+
+ return max(dates) if dates else None
+
+
+class GuildsTransportCarcassViewSet(viewsets.ModelViewSet):
+ queryset = Guilds.objects.filter(trash=False)
+ serializer_class = GuildsForTransportCarcassSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = TransportCarcassDetailFilterSet
+
+
+ def list(self, request, *args, **kwargs):
+ search = request.GET.get('search')
+ province = request.GET.get('province')
+ if province == 'undefined':
+ province = None
+
+ if 'code' in request.GET:
+ code = request.GET.get('code')
+ date1 = request.GET.get('date1')
+ filters={}
+
+ query_carcass = TransportCarcassDetail.objects.filter(
+ jihadi_destination=code,
+ trash=False
+ )
+
+ query_all_products = AllProductsTransport.objects.filter(
+ trash=False,
+ product='گوشت مرغ تازه',
+ jihadi_destination=code
+ )
+
+ if date1:
+ date1 = datetime.datetime.strptime(str(request.GET['date1']), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']), '%Y-%m-%d').date()
+ query_carcass = query_carcass.filter(
+ Q(
+ product_date__gte=date1,
+ product_date__lte=date2,
+ product_date__isnull=False
+ ) |
+ Q(
+ date__gte=date1,
+ date__lte=date2,
+ product_date__isnull=True
+ )
+ )
+ query_all_products = query_all_products.filter(
+ date__gte=date1,
+ date__lte=date2,
+ date__isnull=False
+
+ )
+
+ if search and search != 'undefined' and search.strip():
+ query_carcass = query_carcass.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+ query_all_products = query_all_products.filter(
+ build_query(AllProductsTransportFilterSet.Meta.fields, search)
+ )
+
+ carcass_list = list(query_carcass)
+ all_products_list = list(query_all_products)
+
+ def get_sort_date(obj):
+ if hasattr(obj, 'product_date') and obj.product_date:
+ return obj.product_date
+ elif hasattr(obj, 'date') and obj.date:
+ return obj.date
+ elif hasattr(obj, 'unloading_date') and obj.unloading_date:
+ return obj.unloading_date
+ return datetime.date.min
+
+ combined_list = []
+ for obj in carcass_list:
+ combined_list.append((get_sort_date(obj), 'carcass', obj))
+ for obj in all_products_list:
+ combined_list.append((get_sort_date(obj), 'all_products', obj))
+
+ combined_list.sort(key=lambda x: x[0], reverse=True)
+ sorted_objects = [obj for _, _, obj in combined_list]
+
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ paginator = self.pagination_class()
+ page = paginator.paginate_queryset(sorted_objects, request)
+
+ if page is not None:
+ serialized_data = []
+ for obj in page:
+ if hasattr(obj, 'product_date'):
+ serializer = TransportCarcassDetailSerializer(obj, context={'request': request})
+ serialized_data.append(serializer.data)
+ else:
+ serializer = AllProductsTransportSerializer(obj, context={'request': request})
+ data = serializer.data
+ data['product_date'] = obj.date
+ data['product'] = obj.product
+ data['quantity'] = obj.quantity
+ data['jihadi_destination'] = obj.jihadi_destination
+ serialized_data.append(data)
+ return paginator.get_paginated_response(serialized_data)
+
+ serialized_data = []
+ for obj in sorted_objects:
+ if hasattr(obj, 'product_date'):
+ serializer = TransportCarcassDetailSerializer(obj, context={'request': request})
+ serialized_data.append(serializer.data)
+ else:
+ serializer = AllProductsTransportSerializer(obj, context={'request': request})
+ data = serializer.data
+ data['product_date'] = obj.date
+ data['product'] = obj.product
+ data['quantity'] = obj.quantity
+ data['jihadi_destination'] = obj.jihadi_destination
+ serialized_data.append(data)
+ return Response(serialized_data, status=status.HTTP_200_OK)
+
+ filters_steward = {}
+ if province:
+ filters_steward['province'] = province
+
+ steward = Guilds.objects.filter(**filters_steward, trash=False, is_steward=False).order_by('id')
+ steward_filterset_class = GuildsFilterSet
+ if search and search != 'undefined' and search.strip():
+ steward = steward.filter(build_query(steward_filterset_class.Meta.fields, search))
+
+ steward_jihadi_codes = list(steward.values_list('jihadi_code', flat=True))
+ steward_jihadi_codes = [code for code in steward_jihadi_codes if code]
+
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ bars = TransportCarcassDetail.objects.filter(
+ trash=False,
+ jihadi_destination__in=steward_jihadi_codes
+ ).order_by('-product_date')
+ all_products_transport = AllProductsTransport.objects.filter(
+ trash=False,
+ product='گوشت مرغ تازه',
+ jihadi_destination__in=steward_jihadi_codes
+ )
+
+ if date1:
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ bars = bars.filter(product_date__gte=date1, product_date__lte=date2)
+ all_products_transport = all_products_transport.filter(
+ date__gte=date1, date__lte=date2, date__isnull=False
+ )
+
+ bars_summary = bars.values('jihadi_destination').annotate(
+ total_input_buy_bars_wight=Sum('quantity', filter=Q(out=False)),
+ total_output_buy_bars_wight=Sum('quantity', filter=Q(out=True)),
+ total_ware_house=Sum('quantity'),
+ total_count=Count('id'),
+ total_count_input_buy=Count('id', filter=Q(out=False)),
+ total_count_output_buy=Count('id', filter=Q(out=True)),
+ )
+
+ all_products_summary = all_products_transport.values('jihadi_destination').annotate(
+ total_input_buy_bars_wight=Sum('quantity', filter=Q(out=False)),
+ total_output_buy_bars_wight=Sum('quantity', filter=Q(out=True)),
+ total_ware_house=Sum('quantity'),
+ total_count=Count('id'),
+ total_count_input_buy=Count('id', filter=Q(out=False)),
+ total_count_output_buy=Count('id', filter=Q(out=True)),
+ )
+
+ bars_dict = {}
+ for row in bars_summary:
+ code = row['jihadi_destination']
+ if code:
+ if code not in bars_dict:
+ bars_dict[code] = {
+ 'total_input_buy_bars_wight': 0,
+ 'total_output_buy_bars_wight': 0,
+ 'total_ware_house': 0,
+ 'total_count': 0,
+ 'total_count_input_buy': 0,
+ 'total_count_output_buy': 0,
+ }
+ bars_dict[code]['total_input_buy_bars_wight'] += row['total_input_buy_bars_wight'] or 0
+ bars_dict[code]['total_output_buy_bars_wight'] += row['total_output_buy_bars_wight'] or 0
+ bars_dict[code]['total_ware_house'] += row['total_ware_house'] or 0
+ bars_dict[code]['total_count'] += row['total_count'] or 0
+ bars_dict[code]['total_count_input_buy'] += row['total_count_input_buy'] or 0
+ bars_dict[code]['total_count_output_buy'] += row['total_count_output_buy'] or 0
+
+ for row in all_products_summary:
+ code = row['jihadi_destination']
+ if code:
+ if code not in bars_dict:
+ bars_dict[code] = {
+ 'total_input_buy_bars_wight': 0,
+ 'total_output_buy_bars_wight': 0,
+ 'total_ware_house': 0,
+ 'total_count': 0,
+ 'total_count_input_buy': 0,
+ 'total_count_output_buy': 0,
+ }
+ bars_dict[code]['total_input_buy_bars_wight'] += row['total_input_buy_bars_wight'] or 0
+ bars_dict[code]['total_output_buy_bars_wight'] += row['total_output_buy_bars_wight'] or 0
+ bars_dict[code]['total_ware_house'] += row['total_ware_house'] or 0
+ bars_dict[code]['total_count'] += row['total_count'] or 0
+ bars_dict[code]['total_count_input_buy'] += row['total_count_input_buy'] or 0
+ bars_dict[code]['total_count_output_buy'] += row['total_count_output_buy'] or 0
+ seen_jihadi_codes = set()
+ unique_stewards = []
+ for st in steward:
+ if st.jihadi_code and st.jihadi_code not in seen_jihadi_codes:
+ seen_jihadi_codes.add(st.jihadi_code)
+ unique_stewards.append(st)
+
+ unique_stewards.sort(
+ key=lambda st: (bars_dict.get(st.jihadi_code, {}) or {}).get('total_ware_house', 0) or 0,
+ reverse=True
+ )
+ steward = unique_stewards
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(steward)
+ if page is not None:
+ serializer = self.serializer_class(page, many=True, context={'request': request, 'bars_dict': bars_dict})
+ return self.get_paginated_response(serializer.data)
+
+ serializer = self.serializer_class(steward, many=True, context={'request': request, 'bars_dict': bars_dict})
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+
+class GuildsTransportCarcassDashboardView(APIView):
+
+ permission_classes = [AllowAny]
+ def get(self,request):
+ search = request.GET.get('search')
+
+ province = request.GET.get('province')
+
+ if province == 'undefined':
+ province = None
+
+ filters_steward = {}
+ if province:
+ filters_steward['province'] = province
+ steward = Guilds.objects.filter(**filters_steward,trash=False, is_steward=False).order_by('id')
+ steward_filterset_class = GuildsFilterSet
+ if search:
+ if search != 'undefined' and search.strip():
+ steward = steward.filter(
+ build_query(steward_filterset_class.Meta.fields, search)
+ )
+ steward = steward.values_list('jihadi_code', flat=True)
+ date1 = request.GET.get('date1') or None
+ date2 = request.GET.get('date2') or None
+
+ bars = TransportCarcassDetail.objects.filter(
+ jihadi_destination__in=steward,
+ trash=False
+ )
+ all_products_transport = AllProductsTransport.objects.filter(
+ trash=False,
+ product='گوشت مرغ تازه',
+ jihadi_destination__in=steward
+ )
+
+ if date1:
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ bars = bars.filter(product_date__gte=date1, product_date__lte=date2)
+ all_products_transport = all_products_transport.filter(
+ date__gte=date1, date__lte=date2, date__isnull=False
+ )
+
+ aggregation = bars.aggregate(
+ total_input_buy_bars_wight=Sum('quantity', filter=Q(out=False)),
+ total_output_buy_bars_wight=Sum('quantity', filter=Q(out=True)),
+ total_ware_house=Sum('quantity'),
+ total_count=Count('id'),
+ total_count_input_buy=Count('id', filter=Q(out=False)),
+ total_count_output_buy=Count('id', filter=Q(out=True)),
+ last_update_carcass=Max('modify_date'),
+ )
+
+ all_products_aggregation = all_products_transport.aggregate(
+ total_input_buy_bars_wight=Sum('quantity', filter=Q(out=False)),
+ total_output_buy_bars_wight=Sum('quantity', filter=Q(out=True)),
+ total_ware_house=Sum('quantity'),
+ total_count=Count('id'),
+ total_count_input_buy=Count('id', filter=Q(out=False)),
+ total_count_output_buy=Count('id', filter=Q(out=True)),
+ last_update_all_products=Max('modify_date'),
+ )
+
+ total_input_buy_bars_wight = (aggregation['total_input_buy_bars_wight'] or 0) + (all_products_aggregation['total_input_buy_bars_wight'] or 0)
+ total_output_buy_bars_wight = (aggregation['total_output_buy_bars_wight'] or 0) + (all_products_aggregation['total_output_buy_bars_wight'] or 0)
+ total_ware_house = (aggregation['total_ware_house'] or 0) + (all_products_aggregation['total_ware_house'] or 0)
+ total_count = (aggregation['total_count'] or 0) + (all_products_aggregation['total_count'] or 0)
+ total_count_input_buy = (aggregation['total_count_input_buy'] or 0) + (all_products_aggregation['total_count_input_buy'] or 0)
+ total_count_output_buy = (aggregation['total_count_output_buy'] or 0) + (all_products_aggregation['total_count_output_buy'] or 0)
+
+ last_update_carcass = aggregation.get('last_update_carcass')
+ last_update_all_products = all_products_aggregation.get('last_update_all_products')
+ last_update = None
+ if last_update_carcass and last_update_all_products:
+ last_update = max(last_update_carcass, last_update_all_products)
+ elif last_update_carcass:
+ last_update = last_update_carcass
+ elif last_update_all_products:
+ last_update = last_update_all_products
+
+ if total_count > 0:
+ total_input_bars_percent = round((total_input_buy_bars_wight / total_ware_house) * 100, 1)
+ total_output_bars_percent = round((total_output_buy_bars_wight / total_ware_house) * 100, 1)
+ else:
+ total_input_bars_percent = 0
+ total_output_bars_percent = 0
+
+ return Response({
+ "role": 'صنف',
+ "product": 'گوشت مرغ تازه',
+ "total_input_buy_bars_wight": int(total_input_buy_bars_wight),
+ "total_output_buy_bars_wight": int(total_output_buy_bars_wight),
+ "total_ware_house": int(total_ware_house),
+ "total_input_buy_bars_percent": total_input_bars_percent,
+ "total_output_buy_bars_percent": total_output_bars_percent,
+ "total_count_guild": len(steward),
+ "last_update": last_update,
+ "total_input_buy_bars_count": int(total_count_input_buy),
+ "total_output_buy_bars_count": int(total_count_output_buy),
+ })
+
+
+class ApiSendDifferentBarFromHatching(viewsets.ModelViewSet):
+ queryset = TransportingDetail.objects.filter(trash=False)
+ serializer_class = TransportingForClearanceCodeSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = TransportingDetailFilterSet
+
+ def list(self, request, *args, **kwargs):
+ filters = {'trash': False}
+ filters_bar = {'trash': False}
+ if request.GET.get('date1') != 'None':
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ filters_bar['Date__date__gte'] = date1
+ filters_bar['Date__date__lte'] = date2
+
+ poultry = Hatching.objects.filter(**filters, PartIdCode=request.GET['licence_number'])
+ if poultry.exists():
+ bar = TransportingDetail.objects.filter(**filters_bar, hatching__in=poultry, TrackingStatusDescription__in=(
+ 'تایید تخلیه', 'بارگیری'
+ )).exclude(
+ TrackingCode__in=request.data
+ ).order_by('-Date')
+ quarantine_code = bar.values_list('TrackingCode', flat=True).distinct()
+ bar = bar.filter(TrackingCode__in=quarantine_code).order_by('-Date')
+ search = request.GET.get('search')
+ value = request.GET.get('value')
+ if search:
+ if search != 'undefined' and value.strip():
+ bar = bar.filter(
+ build_query(self.filterset_class.Meta.fields, value)
+ )
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(bar)
+ if page is not None:
+ serializer = self.get_serializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+ ser_data = TransportingForClearanceCodeSerializer(bar, many=True).data
+ return Response(ser_data)
+ return Response([])
+
+
+def fix_province():
+ province_list = [p['name'] for p in iranprovince]
+
+ wrong_records = TransportCarcassDetail.objects.filter(
+ ~Q(origin_province__in=province_list) | ~Q(destination_province__in=province_list),
+ trash=False
+ )
+
+ for record in wrong_records:
+ record.save()
+
+
+def fix_city():
+ province_list = [p['name'] for p in irancity]
+
+ wrong_records = TransportCarcassDetail.objects.filter(
+ ~Q(origin_city__in=province_list) | ~Q(destination_city__in=province_list),
+ trash=False
+ )
+
+ for record in wrong_records:
+ record.save()
+
+
+def update_product_date(request):
+ date1 = datetime.datetime.strptime(str(request.GET['date1']),
+ '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(request.GET['date2']),
+ '%Y-%m-%d').date()
+ bars = TransportCarcassDetail.objects.filter(trash=False, product_date__isnull=True, date__gte=date1,date__lte=date2,
+ has_product_date=False)[:1000]
+ for bar in bars:
+ quarantine = bar.tracking
+ session = requests.Session()
+ session.mount('https://', SSLAdapter())
+ data = {'gid': str(quarantine)}
+ m = session.post('https://e.ivo.ir/Rahgiri/Gidprnt.aspx', data=data, verify=False,
+ headers={
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'})
+ context = BeautifulSoup(m.text, 'html.parser')
+ try:
+ table = context.find_all('table')
+ if table[5:6]:
+ for i in table[5:6]:
+ row = i.find_all('tr')
+ for r in row[1:2]:
+ td = r.find('td')
+ shamsi_date =td.text.strip().split(':')[1]
+ year, month, day = map(int, shamsi_date.split('/'))
+ gregorian_date = jdatetime.date(year, month, day).togregorian()
+ bar.product_date = gregorian_date
+ bar.has_product_date = True
+ bar.save()
+
+
+ except:
+ bar.product_date=bar.date
+ bar.has_product_date = True
+ bar.save()
+
+ return HttpResponse(len(bars))
+
+def update_product_date_cron():
+ bars = TransportCarcassDetail.objects.filter(trash=False, product_date__isnull=True,
+ has_product_date=False
+ ).order_by('-date')[:600]
+ for bar in bars:
+ quarantine = bar.tracking
+ session = requests.Session()
+ session.mount('https://', SSLAdapter())
+ data = {'gid': str(quarantine)}
+ m = session.post('https://e.ivo.ir/Rahgiri/Gidprnt.aspx', data=data, verify=False,
+ headers={
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'})
+ context = BeautifulSoup(m.text, 'html.parser')
+ bar.has_product_date=True
+ try:
+ table = context.find_all('table')
+ if table[5:6]:
+ for i in table[5:6]:
+ row = i.find_all('tr')
+ for r in row[1:2]:
+ td = r.find('td')
+ shamsi_date = td.text.strip().split(':')[1]
+ year, month, day = map(int, shamsi_date.split('/'))
+ gregorian_date = jdatetime.date(year, month, day).togregorian()
+ bar.product_date = gregorian_date
+ bar.has_product_date = True
+ bar.save()
+
+ except:
+ bar.product_date = bar.date
+ bar.has_product_date = True
+ bar.save()
+
+
+@api_view(['GET'])
+@permission_classes([AllowAny])
+@csrf_exempt
+def send_transport_carcass_detail_for_rasadyaar(request):
+ code = request.GET['code']
+ code = code.split(',')
+ kill_house = KillHouse.objects.filter(trash=False,PartIdCode__in=code).order_by('id')
+
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+
+ date1 = datetime.datetime.strptime(str(date1), '%Y-%m-%d').date()
+ date2 = datetime.datetime.strptime(str(date2), '%Y-%m-%d').date()
+ bars = TransportCarcassDetail.objects.filter(
+ Q(product_date__gte=date1, product_date__lte=date2, product_date__isnull=False) |
+ Q(date__gte=date1, date__lte=date2, product_date__isnull=True),
+ trash=False
+ ).only('quantity','out','id','jihadi_origin','tracking')
+ date_1_for_buy_bars = date1 - datetime.timedelta(days=1)
+ date_2_for_buy_bars = date2 - datetime.timedelta(days=1)
+ buy_bars = TransportingDetail.objects.filter(
+ Date__date__gte=date_1_for_buy_bars,
+ Date__date__lte=date_2_for_buy_bars,
+ trash=False,
+ TrackingStatusDescription__in=("تایید تخلیه",'بارگیری')
+ ).only('GoodAmount','Out','id','DesPartIdCode','TrackingCode')
+ quarantine_bars = list(bars.values_list('tracking',flat=True).distinct())
+ quarantine_buy = list(buy_bars.values_list('TrackingCode',flat=True).distinct())
+ all_track = quarantine_bars + quarantine_buy
+
+ all_products_carcass = AllProductsTransport.objects.filter(
+ date__gte=date1,
+ date__lte=date2,
+ trash=False,
+ product='گوشت مرغ تازه'
+ ).exclude(tracking__in=all_track).only('quantity','out','id','jihadi_origin', 'tracking')
+
+ all_products_live = AllProductsTransport.objects.filter(
+ date__gte=date_1_for_buy_bars, date__lte=date_2_for_buy_bars, date__isnull=False,
+ trash=False,
+ product='مرغ زنده -جهت كشتار'
+ ).exclude(tracking__in=all_track).only('quantity','out','id','jihadi_destination', 'tracking')
+
+ bars_summary = bars.values('jihadi_origin').annotate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id'),
+ )
+
+ all_products_carcass_summary = all_products_carcass.values('jihadi_origin').annotate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id'),
+ )
+
+ buy_summary = buy_bars.values('DesPartIdCode').annotate(
+ total=Sum('GoodAmount'),
+ input_total=Sum('GoodAmount', filter=Q(Out=False)),
+ output_total=Sum('GoodAmount', filter=Q(Out=True)),
+ input_count=Count('id', filter=Q(Out=False)),
+ output_count=Count('id', filter=Q(Out=True)),
+ total_count=Count('id'),
+ )
+
+ all_products_live_summary = all_products_live.values('jihadi_destination').annotate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id'),
+ )
+
+ bars_dict = {}
+ for row in bars_summary:
+ key = row['jihadi_origin']
+ if key:
+ if key not in bars_dict:
+ bars_dict[key] = {
+ 'total': 0,
+ 'input_total': 0,
+ 'output_total': 0,
+ 'input_count': 0,
+ 'output_count': 0,
+ 'total_count': 0,
+ }
+ bars_dict[key]['total'] += row['total'] or 0
+ bars_dict[key]['input_total'] += row['input_total'] or 0
+ bars_dict[key]['output_total'] += row['output_total'] or 0
+ bars_dict[key]['input_count'] += row['input_count'] or 0
+ bars_dict[key]['output_count'] += row['output_count'] or 0
+ bars_dict[key]['total_count'] += row['total_count'] or 0
+
+ for row in all_products_carcass_summary:
+ key = row['jihadi_origin']
+ if key:
+ if key not in bars_dict:
+ bars_dict[key] = {
+ 'total': 0,
+ 'input_total': 0,
+ 'output_total': 0,
+ 'input_count': 0,
+ 'output_count': 0,
+ 'total_count': 0,
+ }
+ bars_dict[key]['total'] += row['total'] or 0
+ bars_dict[key]['input_total'] += row['input_total'] or 0
+ bars_dict[key]['output_total'] += row['output_total'] or 0
+ bars_dict[key]['input_count'] += row['input_count'] or 0
+ bars_dict[key]['output_count'] += row['output_count'] or 0
+ bars_dict[key]['total_count'] += row['total_count'] or 0
+
+ buy_dict = {}
+ for row in buy_summary:
+ key = row['DesPartIdCode']
+ if key not in buy_dict:
+ buy_dict[key] = {
+ 'total': 0,
+ 'input_total': 0,
+ 'output_total': 0,
+ 'input_count': 0,
+ 'output_count': 0,
+ 'total_count': 0,
+ }
+ buy_dict[key]['total'] += row['total'] or 0
+ buy_dict[key]['input_total'] += row['input_total'] or 0
+ buy_dict[key]['output_total'] += row['output_total'] or 0
+ buy_dict[key]['input_count'] += row['input_count'] or 0
+ buy_dict[key]['output_count'] += row['output_count'] or 0
+ buy_dict[key]['total_count'] += row['total_count'] or 0
+
+ for row in all_products_live_summary:
+ key = row['jihadi_destination']
+ if key:
+ if key not in buy_dict:
+ buy_dict[key] = {
+ 'total': 0,
+ 'input_total': 0,
+ 'output_total': 0,
+ 'input_count': 0,
+ 'output_count': 0,
+ 'total_count': 0,
+ }
+ buy_dict[key]['total'] += row['total'] or 0
+ buy_dict[key]['input_total'] += row['input_total'] or 0
+ buy_dict[key]['output_total'] += row['output_total'] or 0
+ buy_dict[key]['input_count'] += row['input_count'] or 0
+ buy_dict[key]['output_count'] += row['output_count'] or 0
+ buy_dict[key]['total_count'] += row['total_count'] or 0
+ kill_house = list(kill_house)
+ kill_house.sort(
+ key=lambda kh: (bars_dict.get(kh.PartIdCode, {}) or {}).get('total', 0) or 0,
+ reverse=True
+ )
+
+ serializer = KillHouseForTransportCarcassForRassadyaarSerializer(
+ kill_house, many=True, context={'request': request, 'bars_dict': bars_dict, 'buy_dict': buy_dict}
+ )
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+@api_view(["POST"])
+@permission_classes([AllowAny])
+@csrf_exempt
+def delete_free_bar_from_rasadyaar(request):
+ code = request.data
+ code = dict(code)['code']
+ bars = TransportingDetail.objects.filter(trash=False, Out=True, TrackingCode__in=code,
+ ).exclude(TrackingStatusDescription__in=(
+ 'تایید تخلیه', 'بارگیری'))
+
+ ser_data = TransportingDetailForUpdateSerializer(bars, many=True).data
+ return Response(ser_data, status=status.HTTP_200_OK)
+
+
+@api_view(["POST"])
+@permission_classes([AllowAny])
+@csrf_exempt
+def fix_number(request):
+ data = request.data
+ poultreis = Poultry.objects.filter(trash=False, PartIdCode__in=list(data.keys()))
+ result_list=[]
+ for k, v in data.items():
+ poultry = poultreis.filter(PartIdCode=k).first()
+ if poultry and poultry.Mobile != v:
+ result_list.append({k:poultry.Mobile})
+
+ return Response(result_list)
+
+
+@api_view(["POST"])
+@permission_classes([AllowAny])
+@csrf_exempt
+def get_evacuation_detail_by_request_code(request):
+ request_code = request.data.get('RequestCode')
+ if not request_code:
+ return Response({'detail': 'RequestCode is required'}, status=status.HTTP_400_BAD_REQUEST)
+
+ hatching = Hatching.objects.filter(RequestCode=request_code, trash=False).first()
+ if not hatching:
+ return Response([], status=status.HTTP_200_OK)
+
+ evacuations = EvacuationDetail.objects.filter(hatching=hatching, trash=False).order_by('-create_date')
+ serializer = EvacuationDetailSerializer(evacuations, many=True)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+@api_view(['POST'])
+@permission_classes([AllowAny])
+def get_evacuation_details_by_request_codes(request):
+ request_codes = request.data.get('codes', [])
+ if not request_codes or not isinstance(request_codes, list):
+ return Response({'detail': 'RequestCodes list is required'}, status=status.HTTP_400_BAD_REQUEST)
+
+ result = {}
+ hatchings = Hatching.objects.filter(RequestCode__in=request_codes, trash=False)
+
+ for hatching in hatchings:
+ evacuations = EvacuationDetail.objects.filter(hatching=hatching, trash=False).order_by('-create_date')
+ serializer = EvacuationDetailSerializer(evacuations, many=True)
+ result[hatching.RequestCode] = serializer.data
+
+ for code in request_codes:
+ if code not in result:
+ result[code] = []
+
+ return Response(result, status=status.HTTP_200_OK)
+
+
+class InquiryCredentialsViewSet(viewsets.ModelViewSet):
+ queryset = InquiryCredentials.objects.all()
+ serializer_class = InquiryCredentialsSerializer
+ permission_classes = [AllowAny]
+
+ def list(self, request, *args, **kwargs):
+ inquiry_credentials = InquiryCredentials.objects.filter(trash=False).first()
+ # inquiry_credentials, created = InquiryCredentials.objects.get_or_create(trash=False)
+ serializer = self.serializer_class(inquiry_credentials)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+ def update(self, request, *args, **kwargs):
+ inquiry_credentials = InquiryCredentials.objects.filter(trash=False).first()
+ serializer = self.serializer_class(inquiry_credentials, data=request.data, partial=True)
+ serializer.is_valid(raise_exception=True)
+ serializer.save()
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+class EvacuationDetailViewSet(viewsets.ModelViewSet):
+ queryset = EvacuationDetail.objects.filter(trash=False)
+ serializer_class = EvacuationDetailSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+
+ def create(self, request, *args, **kwargs):
+ data_list = request.data if isinstance(request.data, list) else [request.data]
+
+ total_created = 0
+ total_updated = 0
+
+ for item_data in data_list:
+ evacuation_details = item_data.get('EvacuationDetail', [])
+ if not evacuation_details or len(evacuation_details) == 0:
+ continue
+
+ request_code = item_data.get('RequestCode')
+ if not request_code:
+ continue
+
+ hatching = Hatching.objects.filter(RequestCode=request_code, trash=False).first()
+ if not hatching:
+ continue
+
+ for evacuation_data in evacuation_details:
+ clean_data = evacuation_data.copy()
+ external_id = clean_data.pop('Id', None)
+ if external_id is not None:
+ clean_data['ExternalId'] = external_id
+ evacuation = None
+ if external_id:
+ evacuation = EvacuationDetail.objects.filter(
+ ExternalId=external_id,
+ trash=False
+ ).first()
+
+ if evacuation:
+ for key, value in clean_data.items():
+ setattr(evacuation, key, value)
+ evacuation.hatching = hatching
+ evacuation.save()
+ total_updated += 1
+ else:
+ clean_data['hatching'] = hatching
+ EvacuationDetail.objects.create(**clean_data)
+ total_created += 1
+
+ return Response({
+ "result": "با موفقیت ثبت شد",
+ "created": total_created,
+ "updated": total_updated
+ }, status=status.HTTP_201_CREATED)
+
+ def list(self, request, *args, **kwargs):
+ evacuations = self.filter_queryset(self.get_queryset())
+
+ search = request.GET.get('search')
+ if search:
+ if search != 'undefined' and search.strip():
+ evacuations = evacuations.filter(
+ build_query(['TrackingCode', 'SourceUnitName', 'DesUnitName'], search)
+ )
+
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(evacuations.order_by('-issue_date', '-create_date'))
+ if page is not None:
+ serializer = self.get_serializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+
+ serializer = self.serializer_class(evacuations.order_by('-issue_date', '-create_date'), many=True)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+
+@api_view(['GET'])
+@permission_classes([AllowAny])
+@csrf_exempt
+def evacuation_report_type_summary(request):
+ queryset = EvacuationDetail.objects.filter(trash=False).exclude(
+ ReportTypeString__isnull=True
+ ).exclude(
+ ReportTypeString=''
+ )
+
+ report_type_counter = queryset.values('ReportTypeString').annotate(
+ count=Count('id')
+ )
+
+ type_relations = defaultdict(set)
+ for item in queryset.values('ReportTypeString', 'ReportType').distinct():
+ report_type_string = item['ReportTypeString']
+ report_type_value = item['ReportType']
+ if report_type_value is not None:
+ type_relations[report_type_string].add(report_type_value)
+
+ report_types = []
+ for entry in report_type_counter.order_by('-count', 'ReportTypeString'):
+ report_type_string = entry['ReportTypeString']
+ associated_types = sorted(type_relations.get(report_type_string, []))
+ report_types.append({
+ "report_type_string": report_type_string,
+ "count": entry['count'],
+ "report_types": associated_types,
+ })
+
+ distinct_count = len(report_types)
+ mapping_is_unique = all(len(values) <= 1 for values in type_relations.values())
+
+ return Response({
+ "total_report_types": distinct_count,
+ "report_types": report_types,
+ "report_type_matches_report_type_string": mapping_is_unique,
+ }, status=status.HTTP_200_OK)
+
+
+class AllProductsTransportViewSet(viewsets.ModelViewSet):
+ queryset = AllProductsTransport.objects.filter(trash=False)
+ serializer_class = AllProductsTransportCustomSerializer
+ permission_classes = [AllowAny]
+ pagination_class = CustomPagination
+ filterset_class = AllProductsTransportFilterSet
+
+
+ def list(self, request, *args, **kwargs):
+ transports = self.filter_queryset(self.get_queryset())
+
+ product_type = request.GET.get('product_type')
+ destination_province = request.GET.get('destination_province')
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ search = request.GET.get('search')
+
+ if product_type and product_type != 'undefined':
+ transports = transports.filter(product=product_type)
+
+ if destination_province and destination_province != 'undefined':
+ transports = transports.filter(destination_province=destination_province)
+
+ if date1 and date2 and date1 != 'undefined' and date2 != 'undefined':
+ try:
+ start_date = datetime.datetime.strptime(str(date1), '%Y-%m-%d')
+ end_date = datetime.datetime.strptime(str(date2), '%Y-%m-%d')
+ transports = transports.filter(date__gte=start_date, date__lte=end_date)
+ except ValueError:
+ pass
+
+ if search:
+ if search != 'undefined' and search.strip():
+ transports = transports.filter(
+ build_query(self.filterset_class.Meta.fields, search)
+ )
+
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ self.pagination_class.page_size = int(page_size)
+
+ page = self.paginate_queryset(transports.order_by('-date', '-create_date'))
+ if page is not None:
+ serializer = self.get_serializer(page, many=True)
+ return self.get_paginated_response(serializer.data)
+
+ serializer = self.serializer_class(transports.order_by('-date', '-create_date'), many=True)
+ return Response(serializer.data, status=status.HTTP_200_OK)
+
+ def create(self, request, *args, **kwargs):
+ if 'file' in request.FILES:
+ return self._import_from_excel(request)
+ data = request.data
+ if isinstance(data, list):
+ # Bulk create
+ created_objects = []
+ for item in data:
+ tracking = item.get('tracking')
+ if tracking:
+ obj, created = AllProductsTransport.objects.update_or_create(
+ tracking=tracking,
+ defaults=item
+ )
+ created_objects.append(obj)
+ serializer = self.get_serializer(created_objects, many=True)
+ return Response(serializer.data, status=status.HTTP_201_CREATED)
+ else:
+ # Single create
+ return super().create(request, *args, **kwargs)
+
+ def _import_from_excel(self, request):
+ """Import data from Excel file"""
+ file = request.FILES['file'].read()
+ wb_obj = openpyxl.load_workbook(filename=BytesIO(file))
+ sheet = wb_obj.active
+
+ headers = [str(cell).strip() if cell else '' for cell in next(sheet.iter_rows(values_only=True))]
+
+ field_map = {
+ "id": "record_id",
+ "مقصد قبلی": "destination_prev",
+ "تغییر مقصد": "destination_changed",
+ "کد رهگیری": "tracking",
+ "رهگیری": "tracking",
+ "تاریخ": "date",
+ "محصول": "product",
+ "اقلام": "items",
+ "مقدار": "quantity",
+ "واحد": "unit",
+ "استان مبدا": "origin_province",
+ "شهرستان مبدا": "origin_city",
+ "مبدا": "origin",
+ "استان مقصد": "destination_province",
+ "شهرستان مقصد": "destination_city",
+ "مقصد": "destination",
+ "ش جهادی مبدا": "jihadi_origin",
+ "ش جهادی مقصد": "jihadi_destination",
+ "مالک": "owner",
+ "کد رهگیری خودرو": "car_tracking_code",
+ "نام راننده": "driver_name",
+ "وزن پر": "gross_weight",
+ "وزن خالی": "tare_weight",
+ "وزن": "net_weight",
+ "کد باسکول": "scale_code",
+ "نام باسکول": "scale_name",
+ "قبض باسکول": "scale_receipt",
+ "تاریخ تخلیه": "unloading_date",
+ "تخلیه": "unloading",
+ }
+
+ created_count = 0
+ updated_count = 0
+ skipped_count = 0
+
+ for row in sheet.iter_rows(min_row=2, values_only=True):
+ if not row or all(cell is None for cell in row):
+ continue
+
+ row_data = dict(zip(headers, row))
+
+ tracking_val = row_data.get("کد رهگیری") or row_data.get("رهگیری")
+ province_name = row_data.get("شهرستان مقصد") or row_data.get("شهرستان مقصد")
+ jihadi_origin_code = row_data.get("ش جهادی مبدا") or row_data.get("ش جهادی مبدا")
+ print(province_name)
+ if not tracking_val:
+ continue
+
+ existing_record = AllProductsTransport.objects.filter(tracking=tracking_val, trash=False).first()
+
+ unloading_val = row_data.get("تخلیه", "").strip() if row_data.get("تخلیه") else ""
+ if unloading_val in ["تخلیه.", "تخلیه"] and existing_record:
+ skipped_count += 1
+ continue
+
+ record_data = {}
+ for col_name, model_field in field_map.items():
+ if col_name in row_data:
+ value = row_data[col_name]
+
+ if model_field in ["date", "unloading_date"] and value:
+ try:
+ if isinstance(value, str):
+ if '/' in value:
+ parts = value.split('/')
+ if len(parts) == 3:
+ y, m, d = map(int, parts)
+ value = convert_to_miladi(y, m, d)
+ else:
+ value = datetime.datetime.strptime(value, '%Y-%m-%d').date()
+ elif hasattr(value, "year"):
+ value = convert_to_miladi(value.year, value.month, value.day)
+ except (ValueError, AttributeError, IndexError):
+ value = None
+
+ if model_field == "quantity" and value:
+ try:
+ if isinstance(value, str):
+ value = float(value.replace(',', ''))
+ else:
+ value = float(value)
+ except (ValueError, TypeError):
+ value = None
+
+ if model_field in ["gross_weight", "tare_weight", "net_weight"] and value:
+ try:
+ if isinstance(value, str):
+ value = float(value.replace(',', ''))
+ else:
+ value = float(value)
+ except (ValueError, TypeError):
+ value = None
+
+ record_data[model_field] = value
+
+ hatching_obj = None
+ try:
+ permit_map = get_hatching_permit_code(tracking_val)
+ permit_code = permit_map.get(str(tracking_val))
+ if permit_code:
+ hatching_obj = Hatching.objects.filter(PartIdCode=jihadi_origin_code, trash=False).last()
+ except Exception:
+ pass
+
+ if existing_record:
+ for key, value in record_data.items():
+ setattr(existing_record, key, value)
+ if hatching_obj:
+ existing_record.hatching = hatching_obj
+ existing_record.save()
+
+ if existing_record.destination_province and existing_record.origin_province:
+ if existing_record.destination_province != existing_record.origin_province:
+ existing_record.out = True
+ existing_record.save()
+
+ updated_count += 1
+ else:
+ record_data["tracking"] = tracking_val
+ new_record = AllProductsTransport(**record_data)
+ if hatching_obj:
+ new_record.hatching = hatching_obj
+ new_record.save()
+
+ if new_record.destination_province and new_record.origin_province:
+ if new_record.destination_province != new_record.origin_province:
+ new_record.out = True
+ new_record.save()
+
+ created_count += 1
+
+ return Response({
+ 'result': 'ok',
+ 'created': created_count,
+ 'updated': updated_count,
+ 'skipped': skipped_count,
+ 'message': f'{created_count} رکورد جدید اضافه شد، {updated_count} رکورد بهروزرسانی شد، {skipped_count} رکورد رد شد (تخلیه شده)'
+ }, status=status.HTTP_201_CREATED)
+
+
+class AllProductsTransportDashboardView(APIView):
+ permission_classes = [AllowAny]
+
+ def get(self, request):
+ role = request.GET.get('role')
+ province = request.GET.get('province')
+ search = request.GET.get('search')
+ product_type = request.GET.get('product_type')
+ date1 = request.GET.get('date1') or None
+ date2 = request.GET.get('date2') or None
+
+ if province == 'undefined':
+ province = None
+
+ queryset = AllProductsTransport.objects.filter(trash=False)
+
+ if product_type and product_type != 'undefined':
+ queryset = queryset.filter(product=product_type)
+
+ if date1 and date2 and date1 != 'undefined' and date2 != 'undefined':
+ try:
+ start_date = datetime.datetime.strptime(str(date1), '%Y-%m-%d')
+ end_date = datetime.datetime.strptime(str(date2), '%Y-%m-%d')
+ queryset = queryset.filter(date__gte=start_date, date__lte=end_date)
+ except ValueError:
+ pass
+ destination_province = request.GET.get('destination_province')
+ kill_house_filterset_class = AllProductsTransportFilterSet
+
+ if search and search != 'undefined' and search.strip():
+ queryset = queryset.filter(
+ build_query(kill_house_filterset_class.Meta.fields, search)
+ )
+ if role:
+ if role == 'KillHouse':
+ filters_kill_house = {}
+ if province:
+ filters_kill_house['Province'] = province
+ kill_house_filterset_class = AllProductsTransportFilterSet
+ kill_house = KillHouse.objects.filter(**filters_kill_house).order_by('id')
+
+ if search and search != 'undefined' and search.strip():
+ kill_house = kill_house.filter(
+ build_query(kill_house_filterset_class.Meta.fields, search)
+ )
+
+ kill_house_codes = kill_house.values_list('PartIdCode', flat=True)
+ bars = queryset.filter(jihadi_origin__in=kill_house_codes)
+
+ if destination_province and destination_province != 'undefined':
+ bars = bars.filter(destination_province=destination_province)
+
+
+ aggregation = bars.aggregate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id'),
+ )
+
+ total_count = aggregation['total_count'] or 0
+ total_quantity = aggregation['total'] or 0
+ input_quantity = aggregation['input_total'] or 0
+ output_quantity = aggregation['output_total'] or 0
+ input_count = aggregation['input_count'] or 0
+ output_count = aggregation['output_count'] or 0
+
+ if total_count > 0 and (input_quantity + output_quantity) > 0:
+ input_percent = round((input_quantity / (input_quantity + output_quantity)) * 100, 1)
+ output_percent = round((output_quantity / (input_quantity + output_quantity)) * 100, 1)
+ else:
+ input_percent = 0
+ output_percent = 0
+
+ last_update = bars.order_by('-modify_date').values_list('modify_date', flat=True).first()
+
+ return Response({
+ "role": 'کشتارگاه',
+ "product": product_type,
+ "bars": int(total_count),
+ "total_bars_wight": int(total_quantity),
+ "input_bars": int(input_count),
+ "total_input_bars_wight": int(input_quantity),
+ "total_input_bars_percent": input_percent,
+ "output_bars": int(output_count),
+ "total_output_bars_wight": int(output_quantity),
+ "total_output_bars_percent": output_percent,
+ "total_count_kill_house": kill_house.count(),
+ "last_update": last_update,
+ }, status=status.HTTP_200_OK)
+
+ else:
+ filters_steward = {}
+ if province:
+ filters_steward['province'] = province
+ kill_house_filterset_class = AllProductsTransportFilterSet
+ steward = Guilds.objects.filter(**filters_steward, trash=False, is_steward=True).order_by('id')
+
+ if search and search != 'undefined' and search.strip():
+ steward = steward.filter(
+ build_query(kill_house_filterset_class.Meta.fields, search)
+ )
+
+ steward_codes = steward.values_list('jihadi_code', flat=True)
+
+ bars = queryset.filter(
+ Q(jihadi_origin__in=steward_codes) | Q(jihadi_destination__in=steward_codes)
+ ).order_by('-modify_date')
+
+ if destination_province and destination_province != 'undefined':
+ bars = bars.filter(destination_province=destination_province)
+
+ aggregation = bars.aggregate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id'),
+ )
+
+ total_count = aggregation['total_count'] or 0
+ total_quantity = aggregation['total'] or 0
+ input_quantity = aggregation['input_total'] or 0
+ output_quantity = aggregation['output_total'] or 0
+ input_count = aggregation['input_count'] or 0
+ output_count = aggregation['output_count'] or 0
+
+ if total_count > 0 and (input_quantity + output_quantity) > 0:
+ input_percent = round((input_quantity / (input_quantity + output_quantity)) * 100, 1)
+ output_percent = round((output_quantity / (input_quantity + output_quantity)) * 100, 1)
+ else:
+ input_percent = 0
+ output_percent = 0
+
+ last_update = bars.values_list('modify_date', flat=True).first()
+
+ return Response({
+ "product": product_type,
+ "bars": int(total_count),
+ "total_bars_wight": int(total_quantity),
+ "input_bars": int(input_count),
+ "total_input_bars_wight": int(input_quantity),
+ "total_input_bars_percent": input_percent,
+ "output_bars": int(output_count),
+ "total_output_bars_wight": int(output_quantity),
+ "total_output_bars_percent": output_percent,
+ "total_count_steward": steward.count(),
+ "last_update": last_update,
+ }, status=status.HTTP_200_OK)
+
+ if destination_province and destination_province != 'undefined':
+ queryset = queryset.filter(destination_province=destination_province)
+
+ aggregation = queryset.aggregate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id'),
+ )
+
+ total_count = aggregation['total_count'] or 0
+ total_quantity = aggregation['total'] or 0
+ input_quantity = aggregation['input_total'] or 0
+ output_quantity = aggregation['output_total'] or 0
+ input_count = aggregation['input_count'] or 0
+ output_count = aggregation['output_count'] or 0
+
+ if total_count > 0 and (input_quantity + output_quantity) > 0:
+ input_percent = round((input_quantity / (input_quantity + output_quantity)) * 100, 1)
+ output_percent = round((output_quantity / (input_quantity + output_quantity)) * 100, 1)
+ else:
+ input_percent = 0
+ output_percent = 0
+
+ last_update = queryset.order_by('-modify_date').values_list('modify_date', flat=True).first()
+
+ return Response({
+ "role": 'all',
+ "product": product_type,
+ "bars": int(total_count),
+ "total_bars_wight": int(total_quantity),
+ "input_bars": int(input_count),
+ "total_input_bars_wight": int(input_quantity),
+ "total_input_bars_percent": input_percent,
+ "output_bars": int(output_count),
+ "total_output_bars_wight": int(output_quantity),
+ "total_output_bars_percent": output_percent,
+ "last_update": last_update,
+ }, status=status.HTTP_200_OK)
+
+
+class AllProductsTransportProductsListView(APIView):
+ permission_classes = [AllowAny]
+
+ def get(self, request):
+ products = AllProductsTransport.objects.filter(
+ trash=False,
+ product__isnull=False
+ ).exclude(product='').values_list('product', flat=True).distinct().order_by('product')
+
+ return Response({
+ "products": list(products)
+ }, status=status.HTTP_200_OK)
+
+
+def _convert_transporting_detail_to_unified(obj):
+ """تبدیل TransportingDetail به فرمت یکپارچه"""
+ hatching_data = None
+ if obj.hatching:
+ poultry_data = None
+ if obj.hatching.poultry:
+ poultry_data = {
+ 'UnitId': obj.hatching.poultry.UnitId,
+ 'PartIdCode': obj.hatching.poultry.PartIdCode,
+ 'Province': obj.hatching.poultry.Province,
+ 'City': obj.hatching.poultry.City,
+ 'UnitName': obj.hatching.poultry.UnitName,
+ 'Mobile': obj.hatching.poultry.Mobile,
+ }
+ hatching_data = {
+ 'PartIdCode': obj.hatching.PartIdCode,
+ 'poultry': poultry_data,
+ 'RequestCode': obj.hatching.RequestCode,
+ 'PedigreeName': obj.hatching.PedigreeName,
+ }
+
+ return {
+ 'id': obj.id,
+ 'key': str(obj.key),
+ 'source': 'TransportingDetail',
+ 'record_id': str(obj.id),
+ 'tracking': obj.TrackingCode,
+ 'date': obj.Date.date() if obj.Date else None,
+ 'product': obj.GoodName,
+ 'items': obj.GoodName,
+ 'quantity': obj.GoodAmount,
+ 'unit': 'قطعه',
+ 'origin_province': obj.hatching.ProvinceName if obj.hatching else None,
+ 'origin_city': obj.hatching.CityName if obj.hatching else None,
+ 'origin': obj.SourceUnitName,
+ 'destination_province': obj.Province,
+ 'destination_city': obj.City,
+ 'destination': obj.DesUnitName,
+ 'jihadi_origin': obj.SourcePartIdCode,
+ 'jihadi_destination': obj.DesPartIdCode,
+ 'owner': obj.hatching.PersonFullName if obj.hatching else None,
+ 'car_tracking_code': None,
+ 'driver_name': None,
+ 'gross_weight': None,
+ 'tare_weight': None,
+ 'net_weight': None,
+ 'scale_code': None,
+ 'scale_name': None,
+ 'scale_receipt': None,
+ 'unloading_date': obj.Date.date() if obj.Date else None,
+ 'unloading': obj.TrackingStatusDescription,
+ 'out': obj.Out,
+ 'hatching': hatching_data,
+ 'create_date': obj.create_date,
+ 'modify_date': obj.modify_date,
+ }
+
+
+def _convert_transport_carcass_to_unified(obj):
+ """تبدیل TransportCarcassDetail به فرمت یکپارچه"""
+ return {
+ 'id': obj.id,
+ 'key': str(obj.key),
+ 'source': 'TransportCarcassDetail',
+ 'record_id': obj.id_quarantineh,
+ 'tracking': obj.tracking,
+ 'date': obj.date,
+ 'product': obj.product,
+ 'items': obj.items,
+ 'quantity': obj.quantity,
+ 'unit': obj.unit,
+ 'origin_province': obj.origin_province,
+ 'origin_city': obj.origin_city,
+ 'origin': obj.origin,
+ 'destination_province': obj.destination_province,
+ 'destination_city': obj.destination_city,
+ 'destination': obj.destination,
+ 'jihadi_origin': obj.jihadi_origin,
+ 'jihadi_destination': obj.jihadi_destination,
+ 'owner': obj.owner,
+ 'car_tracking_code': obj.car_tracking_code,
+ 'driver_name': obj.driver_name,
+ 'gross_weight': obj.gross_weight,
+ 'tare_weight': obj.tare_weight,
+ 'net_weight': obj.net_weight,
+ 'scale_code': obj.scale_code,
+ 'scale_name': obj.scale_name,
+ 'scale_receipt': obj.scale_receipt,
+ 'unloading_date': obj.unloading_date,
+ 'unloading': obj.unloading,
+ 'out': obj.out,
+ 'hatching': None,
+ 'create_date': obj.create_date,
+ 'modify_date': obj.modify_date,
+ }
+
+
+def _convert_all_products_to_unified(obj):
+ """تبدیل AllProductsTransport به فرمت یکپارچه"""
+ hatching_data = None
+ if obj.hatching:
+ poultry_data = None
+ if obj.hatching.poultry:
+ poultry_data = {
+ 'UnitId': obj.hatching.poultry.UnitId,
+ 'PartIdCode': obj.hatching.poultry.PartIdCode,
+ 'Province': obj.hatching.poultry.Province,
+ 'City': obj.hatching.poultry.City,
+ 'UnitName': obj.hatching.poultry.UnitName,
+ 'Mobile': obj.hatching.poultry.Mobile,
+ }
+ hatching_data = {
+ 'PartIdCode': obj.hatching.PartIdCode,
+ 'poultry': poultry_data,
+ 'RequestCode': obj.hatching.RequestCode,
+ 'PedigreeName': obj.hatching.PedigreeName,
+ }
+
+ return {
+ 'id': obj.id,
+ 'key': str(obj.key),
+ 'source': 'AllProductsTransport',
+ 'record_id': obj.record_id,
+ 'tracking': obj.tracking,
+ 'date': obj.date,
+ 'product': obj.product,
+ 'items': obj.items,
+ 'quantity': obj.quantity,
+ 'unit': obj.unit,
+ 'origin_province': obj.origin_province,
+ 'origin_city': obj.origin_city,
+ 'origin': obj.origin,
+ 'destination_province': obj.destination_province,
+ 'destination_city': obj.destination_city,
+ 'destination': obj.destination,
+ 'jihadi_origin': obj.jihadi_origin,
+ 'jihadi_destination': obj.jihadi_destination,
+ 'owner': obj.owner,
+ 'car_tracking_code': obj.car_tracking_code,
+ 'driver_name': obj.driver_name,
+ 'gross_weight': obj.gross_weight,
+ 'tare_weight': obj.tare_weight,
+ 'net_weight': obj.net_weight,
+ 'scale_code': obj.scale_code,
+ 'scale_name': obj.scale_name,
+ 'scale_receipt': obj.scale_receipt,
+ 'unloading_date': obj.unloading_date,
+ 'unloading': obj.unloading,
+ 'out': obj.out,
+ 'hatching': hatching_data,
+ 'create_date': obj.create_date,
+ 'modify_date': obj.modify_date,
+ }
+
+
+@api_view(['GET'])
+@permission_classes([AllowAny])
+@csrf_exempt
+def get_all_products_transport_by_code(request):
+ code = request.GET.get('code')
+ if not code:
+ return Response(
+ {'detail': 'کد الزامی است'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ transport_type = request.GET.get('type')
+ if transport_type not in ['in', 'out']:
+ return Response(
+ {'detail': 'نوع باید in یا out باشد'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ from_source = request.GET.get('from')
+ if from_source and from_source not in ['Poultry', 'KillHouse']:
+ return Response(
+ {'detail': 'from باید Poultry یا KillHouse باشد'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ start_date = None
+ end_date = None
+ if date1 and date2 and date1 != 'undefined' and date2 != 'undefined':
+ try:
+ start_date = datetime.datetime.strptime(str(date1), '%Y-%m-%d')
+ end_date = datetime.datetime.strptime(str(date2), '%Y-%m-%d')
+ except ValueError:
+ pass
+
+ province = request.GET.get('province')
+ product_type = request.GET.get('product')
+ search = request.GET.get('search')
+
+ unified_results = []
+ seen_tracking_codes = set()
+
+ def add_if_not_duplicate(item):
+ tracking = item.get('tracking')
+ if tracking and tracking in seen_tracking_codes:
+ return False
+ if tracking:
+ seen_tracking_codes.add(tracking)
+ unified_results.append(item)
+ return True
+
+ if from_source == 'Poultry':
+ if transport_type == 'out':
+ all_products = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_origin=code,
+ )
+ if start_date and end_date:
+ all_products = all_products.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ all_products = all_products.filter(destination_province=province)
+ if product_type and product_type != 'undefined':
+ all_products = all_products.filter(product=product_type)
+
+ for obj in all_products:
+ add_if_not_duplicate(_convert_all_products_to_unified(obj))
+
+ transport_details = TransportingDetail.objects.filter(
+ trash=False,
+ hatching__poultry__PartIdCode=code,
+ TrackingStatusDescription__in=(
+ 'تایید تخلیه', 'بارگیری')).order_by('-Date')
+ if start_date and end_date:
+ transport_details = transport_details.filter(Date__gte=start_date, Date__lte=end_date)
+ if province and province != 'undefined':
+ transport_details = transport_details.filter(Province=province)
+
+ for obj in transport_details:
+ add_if_not_duplicate(_convert_transporting_detail_to_unified(obj))
+ else:
+ all_products = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_destination=code
+ ).order_by('-date')
+ if start_date and end_date:
+ all_products = all_products.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ all_products = all_products.filter(origin_province=province)
+ if product_type and product_type != 'undefined':
+ all_products = all_products.filter(product=product_type)
+
+ for obj in all_products:
+ add_if_not_duplicate(_convert_all_products_to_unified(obj))
+
+ elif from_source == 'KillHouse':
+ if transport_type == 'out':
+ carcass_details = TransportCarcassDetail.objects.filter(
+ trash=False,
+ jihadi_origin=code,
+ )
+ if start_date and end_date:
+ carcass_details = carcass_details.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ carcass_details = carcass_details.filter(destination_province=province)
+ if product_type and product_type != 'undefined':
+ carcass_details = carcass_details.filter(product=product_type)
+
+ for obj in carcass_details:
+ add_if_not_duplicate(_convert_transport_carcass_to_unified(obj))
+ else:
+ transport_details = TransportingDetail.objects.filter(
+ trash=False,
+ DesPartIdCode=code,
+ TrackingStatusDescription__in=(
+ 'تایید تخلیه', 'بارگیری')).select_related('hatching', 'hatching__poultry')
+ if start_date and end_date:
+ transport_details = transport_details.filter(Date__gte=start_date, Date__lte=end_date)
+ if province and province != 'undefined':
+ transport_details = transport_details.filter(hatching__ProvinceName=province)
+
+ for obj in transport_details:
+ add_if_not_duplicate(_convert_transporting_detail_to_unified(obj))
+
+ else:
+ if transport_type == 'out':
+ bars = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_origin=code
+ )
+ else:
+ bars = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_destination=code
+ )
+
+ if start_date and end_date:
+ bars = bars.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ if transport_type == 'out':
+ bars = bars.filter(destination_province=province)
+ else:
+ bars = bars.filter(origin_province=province)
+ if product_type and product_type != 'undefined':
+ bars = bars.filter(product=product_type)
+
+ for obj in bars:
+ add_if_not_duplicate(_convert_all_products_to_unified(obj))
+
+ if search and search != 'undefined' and search.strip():
+ search_lower = search.lower()
+ unified_results = [
+ r for r in unified_results
+ if (r.get('tracking') and search_lower in str(r['tracking']).lower()) or
+ (r.get('product') and search_lower in str(r['product']).lower()) or
+ (r.get('origin') and search_lower in str(r['origin']).lower()) or
+ (r.get('destination') and search_lower in str(r['destination']).lower()) or
+ (r.get('driver_name') and search_lower in str(r['driver_name']).lower()) or
+ (r.get('owner') and search_lower in str(r['owner']).lower())
+ ]
+
+ unified_results.sort(key=lambda x: x.get('date') or datetime.date.min, reverse=True)
+
+ paginator = CustomPagination()
+ page_size = request.query_params.get('page_size', None)
+ if page_size:
+ paginator.page_size = int(page_size)
+
+ page_number = int(request.query_params.get('page', 1))
+ start_index = (page_number - 1) * paginator.page_size
+ end_index = start_index + paginator.page_size
+ paginated_results = unified_results[start_index:end_index]
+
+ return Response({
+ 'count': len(unified_results),
+ 'next': f"?page={page_number + 1}" if end_index < len(unified_results) else None,
+ 'previous': f"?page={page_number - 1}" if page_number > 1 else None,
+ 'results': paginated_results
+ }, status=status.HTTP_200_OK)
+
+
+@api_view(['GET'])
+@permission_classes([AllowAny])
+@csrf_exempt
+def get_all_products_transport_products_by_code(request):
+ code = request.GET.get('code')
+ if not code:
+ return Response(
+ {'detail': 'کد الزامی است'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ transport_type = request.GET.get('type')
+ if transport_type not in ['in', 'out']:
+ return Response(
+ {'detail': 'نوع باید in یا out باشد'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ from_source = request.GET.get('from')
+ if from_source and from_source not in ['Poultry', 'KillHouse']:
+ return Response(
+ {'detail': 'from باید Poultry یا KillHouse باشد'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ start_date = None
+ end_date = None
+ if date1 and date2 and date1 != 'undefined' and date2 != 'undefined':
+ try:
+ start_date = datetime.datetime.strptime(str(date1), '%Y-%m-%d')
+ end_date = datetime.datetime.strptime(str(date2), '%Y-%m-%d')
+ except ValueError:
+ pass
+
+ province = request.GET.get('province')
+ product_type = request.GET.get('product')
+ search = request.GET.get('search')
+
+ products_set = set()
+
+ if from_source == 'Poultry':
+ if transport_type == 'out':
+ all_products = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_origin=code,
+ )
+ if start_date and end_date:
+ all_products = all_products.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ all_products = all_products.filter(destination_province=province)
+ if product_type and product_type != 'undefined':
+ all_products = all_products.filter(product=product_type)
+
+ products_set.update(
+ all_products.filter(product__isnull=False)
+ .exclude(product='')
+ .values_list('product', flat=True)
+ .distinct()
+ )
+
+ transport_details = TransportingDetail.objects.filter(
+ trash=False,
+ hatching__poultry__PartIdCode=code,
+ TrackingStatusDescription__in=('تایید تخلیه', 'بارگیری')
+ ).select_related('hatching', 'hatching__poultry')
+ if start_date and end_date:
+ transport_details = transport_details.filter(Date__gte=start_date, Date__lte=end_date)
+ if province and province != 'undefined':
+ transport_details = transport_details.filter(Province=province)
+
+ products_set.update(
+ transport_details.filter(GoodName__isnull=False)
+ .exclude(GoodName='')
+ .values_list('GoodName', flat=True)
+ .distinct()
+ )
+ else:
+ all_products = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_destination=code
+ )
+ if start_date and end_date:
+ all_products = all_products.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ all_products = all_products.filter(origin_province=province)
+ if product_type and product_type != 'undefined':
+ all_products = all_products.filter(product=product_type)
+
+ products_set.update(
+ all_products.filter(product__isnull=False)
+ .exclude(product='')
+ .values_list('product', flat=True)
+ .distinct()
+ )
+
+ elif from_source == 'KillHouse':
+ if transport_type == 'out':
+ carcass_details = TransportCarcassDetail.objects.filter(
+ trash=False,
+ jihadi_origin=code,
+ )
+ if start_date and end_date:
+ carcass_details = carcass_details.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ carcass_details = carcass_details.filter(destination_province=province)
+ if product_type and product_type != 'undefined':
+ carcass_details = carcass_details.filter(product=product_type)
+
+ products_set.update(
+ carcass_details.filter(product__isnull=False)
+ .exclude(product='')
+ .values_list('product', flat=True)
+ .distinct()
+ )
+ else:
+ transport_details = TransportingDetail.objects.filter(
+ trash=False,
+ DesPartIdCode=code,
+ TrackingStatusDescription__in=('تایید تخلیه', 'بارگیری')
+ ).select_related('hatching', 'hatching__poultry')
+ if start_date and end_date:
+ transport_details = transport_details.filter(Date__gte=start_date, Date__lte=end_date)
+ if province and province != 'undefined':
+ transport_details = transport_details.filter(hatching__ProvinceName=province)
+
+ products_set.update(
+ transport_details.filter(GoodName__isnull=False)
+ .exclude(GoodName='')
+ .values_list('GoodName', flat=True)
+ .distinct()
+ )
+
+ else:
+ if transport_type == 'out':
+ bars = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_origin=code
+ )
+ else:
+ bars = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_destination=code
+ )
+
+ if start_date and end_date:
+ bars = bars.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ if transport_type == 'out':
+ bars = bars.filter(destination_province=province)
+ else:
+ bars = bars.filter(origin_province=province)
+ if product_type and product_type != 'undefined':
+ bars = bars.filter(product=product_type)
+
+ products_set.update(
+ bars.filter(product__isnull=False)
+ .exclude(product='')
+ .values_list('product', flat=True)
+ .distinct()
+ )
+
+ if search and search != 'undefined' and search.strip():
+ search_lower = search.lower()
+ products_set = {
+ p for p in products_set
+ if p and search_lower in str(p).lower()
+ }
+
+ products = sorted([p for p in products_set if p], key=str)
+
+ return Response({
+ "products": products
+ }, status=status.HTTP_200_OK)
+
+
+@api_view(['GET'])
+@permission_classes([AllowAny])
+@csrf_exempt
+def get_all_products_transport_provinces_by_code(request):
+ code = request.GET.get('code')
+ if not code:
+ return Response(
+ {'detail': 'کد الزامی است'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ transport_type = request.GET.get('type')
+ if transport_type not in ['in', 'out']:
+ return Response(
+ {'detail': 'نوع باید in یا out باشد'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ from_source = request.GET.get('from')
+ if from_source and from_source not in ['Poultry', 'KillHouse']:
+ return Response(
+ {'detail': 'from باید Poultry یا KillHouse باشد'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ start_date = None
+ end_date = None
+ if date1 and date2 and date1 != 'undefined' and date2 != 'undefined':
+ try:
+ start_date = datetime.datetime.strptime(str(date1), '%Y-%m-%d')
+ end_date = datetime.datetime.strptime(str(date2), '%Y-%m-%d')
+ except ValueError:
+ pass
+
+ province = request.GET.get('province')
+ product_type = request.GET.get('product')
+ search = request.GET.get('search')
+ provinces_set = set()
+
+ if from_source == 'Poultry':
+ if transport_type == 'out':
+ all_products = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_origin=code,
+ )
+ if start_date and end_date:
+ all_products = all_products.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ all_products = all_products.filter(destination_province=province)
+ if product_type and product_type != 'undefined':
+ all_products = all_products.filter(product=product_type)
+
+ provinces_set.update(
+ all_products.filter(destination_province__isnull=False)
+ .exclude(destination_province='')
+ .values_list('destination_province', flat=True)
+ .distinct()
+ )
+
+ transport_details = TransportingDetail.objects.filter(
+ trash=False,
+ hatching__poultry__PartIdCode=code,
+ TrackingStatusDescription__in=('تایید تخلیه', 'بارگیری')
+ ).select_related('hatching', 'hatching__poultry')
+ if start_date and end_date:
+ transport_details = transport_details.filter(Date__gte=start_date, Date__lte=end_date)
+ if province and province != 'undefined':
+ transport_details = transport_details.filter(Province=province)
+
+ provinces_set.update(
+ transport_details.filter(Province__isnull=False)
+ .exclude(Province='')
+ .values_list('Province', flat=True)
+ .distinct()
+ )
+ else:
+ all_products = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_destination=code
+ )
+ if start_date and end_date:
+ all_products = all_products.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ all_products = all_products.filter(origin_province=province)
+ if product_type and product_type != 'undefined':
+ all_products = all_products.filter(product=product_type)
+
+ provinces_set.update(
+ all_products.filter(origin_province__isnull=False)
+ .exclude(origin_province='')
+ .values_list('origin_province', flat=True)
+ .distinct()
+ )
+
+ elif from_source == 'KillHouse':
+ if transport_type == 'out':
+ carcass_details = TransportCarcassDetail.objects.filter(
+ trash=False,
+ jihadi_origin=code,
+ )
+ if start_date and end_date:
+ carcass_details = carcass_details.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ carcass_details = carcass_details.filter(destination_province=province)
+ if product_type and product_type != 'undefined':
+ carcass_details = carcass_details.filter(product=product_type)
+
+ provinces_set.update(
+ carcass_details.filter(destination_province__isnull=False)
+ .exclude(destination_province='')
+ .values_list('destination_province', flat=True)
+ .distinct()
+ )
+ else:
+ transport_details = TransportingDetail.objects.filter(
+ trash=False,
+ DesPartIdCode=code,
+ TrackingStatusDescription__in=('تایید تخلیه', 'بارگیری')
+ ).select_related('hatching', 'hatching__poultry')
+ if start_date and end_date:
+ transport_details = transport_details.filter(Date__gte=start_date, Date__lte=end_date)
+ if province and province != 'undefined':
+ transport_details = transport_details.filter(hatching__ProvinceName=province)
+
+ provinces_set.update(
+ transport_details.filter(hatching__ProvinceName__isnull=False)
+ .exclude(hatching__ProvinceName='')
+ .values_list('hatching__ProvinceName', flat=True)
+ .distinct()
+ )
+
+ else:
+ if transport_type == 'out':
+ bars = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_origin=code
+ )
+ else:
+ bars = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_destination=code
+ )
+
+ if start_date and end_date:
+ bars = bars.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ if transport_type == 'out':
+ bars = bars.filter(destination_province=province)
+ else:
+ bars = bars.filter(origin_province=province)
+ if product_type and product_type != 'undefined':
+ bars = bars.filter(product=product_type)
+
+ if transport_type == 'out':
+ provinces_set.update(
+ bars.filter(destination_province__isnull=False)
+ .exclude(destination_province='')
+ .values_list('destination_province', flat=True)
+ .distinct()
+ )
+ else:
+ provinces_set.update(
+ bars.filter(origin_province__isnull=False)
+ .exclude(origin_province='')
+ .values_list('origin_province', flat=True)
+ .distinct()
+ )
+
+ if search and search != 'undefined' and search.strip():
+ search_lower = search.lower()
+ provinces_set = {
+ p for p in provinces_set
+ if p and search_lower in str(p).lower()
+ }
+
+ provinces = sorted([p for p in provinces_set if p], key=str)
+
+ return Response({
+ "provinces": provinces
+ }, status=status.HTTP_200_OK)
+
+
+@api_view(['GET'])
+@permission_classes([AllowAny])
+@csrf_exempt
+def get_all_products_transport_dashboard_by_code(request):
+ code = request.GET.get('code')
+ if not code:
+ return Response(
+ {'detail': 'کد الزامی است'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ transport_type = request.GET.get('type')
+ if transport_type not in ['in', 'out']:
+ return Response(
+ {'detail': 'نوع باید in یا out باشد'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ from_source = request.GET.get('from')
+ if from_source and from_source not in ['Poultry', 'KillHouse']:
+ return Response(
+ {'detail': 'from باید Poultry یا KillHouse باشد'},
+ status=status.HTTP_400_BAD_REQUEST
+ )
+
+ date1 = request.GET.get('date1')
+ date2 = request.GET.get('date2')
+ start_date = None
+ end_date = None
+ if date1 and date2 and date1 != 'undefined' and date2 != 'undefined':
+ try:
+ start_date = datetime.datetime.strptime(str(date1), '%Y-%m-%d')
+ end_date = datetime.datetime.strptime(str(date2), '%Y-%m-%d')
+ except ValueError:
+ pass
+
+ province = request.GET.get('province')
+ product_type = request.GET.get('product')
+ search = request.GET.get('search')
+
+ total_count = 0
+ total_quantity = 0
+ input_quantity = 0
+ output_quantity = 0
+ input_count = 0
+ output_count = 0
+ last_update = None
+
+ if from_source == 'Poultry':
+ if transport_type == 'out':
+ all_products = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_origin=code,
+ )
+ if start_date and end_date:
+ all_products = all_products.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ all_products = all_products.filter(destination_province=province)
+ if product_type and product_type != 'undefined':
+ all_products = all_products.filter(product=product_type)
+
+ agg = all_products.aggregate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_cnt=Count('id', filter=Q(out=False)),
+ output_cnt=Count('id', filter=Q(out=True)),
+ total_cnt=Count('id'),
+ last_mod=Max('modify_date'),
+ )
+ total_count += agg['total_cnt'] or 0
+ total_quantity += agg['total'] or 0
+ input_quantity += agg['input_total'] or 0
+ output_quantity += agg['output_total'] or 0
+ input_count += agg['input_cnt'] or 0
+ output_count += agg['output_cnt'] or 0
+ if agg['last_mod'] and (not last_update or agg['last_mod'] > last_update):
+ last_update = agg['last_mod']
+
+ transport_details = TransportingDetail.objects.filter(
+ trash=False,
+ hatching__poultry__PartIdCode=code,
+ TrackingStatusDescription__in=('تایید تخلیه', 'بارگیری')
+ ).select_related('hatching', 'hatching__poultry')
+ if start_date and end_date:
+ transport_details = transport_details.filter(Date__gte=start_date, Date__lte=end_date)
+ if province and province != 'undefined':
+ transport_details = transport_details.filter(Province=province)
+
+ agg = transport_details.aggregate(
+ total=Sum('GoodAmount'),
+ input_total=Sum('GoodAmount', filter=Q(Out=False)),
+ output_total=Sum('GoodAmount', filter=Q(Out=True)),
+ input_cnt=Count('id', filter=Q(Out=False)),
+ output_cnt=Count('id', filter=Q(Out=True)),
+ total_cnt=Count('id'),
+ last_mod=Max('modify_date'),
+ )
+ total_count += agg['total_cnt'] or 0
+ total_quantity += agg['total'] or 0
+ input_quantity += agg['input_total'] or 0
+ output_quantity += agg['output_total'] or 0
+ input_count += agg['input_cnt'] or 0
+ output_count += agg['output_cnt'] or 0
+ if agg['last_mod'] and (not last_update or agg['last_mod'] > last_update):
+ last_update = agg['last_mod']
+ else:
+ all_products = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_destination=code
+ )
+ if start_date and end_date:
+ all_products = all_products.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ all_products = all_products.filter(origin_province=province)
+ if product_type and product_type != 'undefined':
+ all_products = all_products.filter(product=product_type)
+
+ agg = all_products.aggregate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_cnt=Count('id', filter=Q(out=False)),
+ output_cnt=Count('id', filter=Q(out=True)),
+ total_cnt=Count('id'),
+ last_mod=Max('modify_date'),
+ )
+ total_count += agg['total_cnt'] or 0
+ total_quantity += agg['total'] or 0
+ input_quantity += agg['input_total'] or 0
+ output_quantity += agg['output_total'] or 0
+ input_count += agg['input_cnt'] or 0
+ output_count += agg['output_cnt'] or 0
+ if agg['last_mod'] and (not last_update or agg['last_mod'] > last_update):
+ last_update = agg['last_mod']
+
+ elif from_source == 'KillHouse':
+ if transport_type == 'out':
+ carcass_details = TransportCarcassDetail.objects.filter(
+ trash=False,
+ jihadi_origin=code,
+ )
+ if start_date and end_date:
+ carcass_details = carcass_details.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ carcass_details = carcass_details.filter(destination_province=province)
+ if product_type and product_type != 'undefined':
+ carcass_details = carcass_details.filter(product=product_type)
+
+ agg = carcass_details.aggregate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_cnt=Count('id', filter=Q(out=False)),
+ output_cnt=Count('id', filter=Q(out=True)),
+ total_cnt=Count('id'),
+ last_mod=Max('modify_date'),
+ )
+ total_count += agg['total_cnt'] or 0
+ total_quantity += agg['total'] or 0
+ input_quantity += agg['input_total'] or 0
+ output_quantity += agg['output_total'] or 0
+ input_count += agg['input_cnt'] or 0
+ output_count += agg['output_cnt'] or 0
+ if agg['last_mod'] and (not last_update or agg['last_mod'] > last_update):
+ last_update = agg['last_mod']
+ else:
+ transport_details = TransportingDetail.objects.filter(
+ trash=False,
+ DesPartIdCode=code,
+ TrackingStatusDescription__in=('تایید تخلیه', 'بارگیری')
+ ).select_related('hatching', 'hatching__poultry')
+ if start_date and end_date:
+ transport_details = transport_details.filter(Date__gte=start_date, Date__lte=end_date)
+ if province and province != 'undefined':
+ transport_details = transport_details.filter(hatching__ProvinceName=province)
+
+ agg = transport_details.aggregate(
+ total=Sum('GoodAmount'),
+ input_total=Sum('GoodAmount', filter=Q(Out=False)),
+ output_total=Sum('GoodAmount', filter=Q(Out=True)),
+ input_cnt=Count('id', filter=Q(Out=False)),
+ output_cnt=Count('id', filter=Q(Out=True)),
+ total_cnt=Count('id'),
+ last_mod=Max('modify_date'),
+ )
+ total_count += agg['total_cnt'] or 0
+ total_quantity += agg['total'] or 0
+ input_quantity += agg['input_total'] or 0
+ output_quantity += agg['output_total'] or 0
+ input_count += agg['input_cnt'] or 0
+ output_count += agg['output_cnt'] or 0
+ if agg['last_mod'] and (not last_update or agg['last_mod'] > last_update):
+ last_update = agg['last_mod']
+
+ else:
+ if transport_type == 'out':
+ all_bars = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_origin=code
+ )
+ else:
+ all_bars = AllProductsTransport.objects.filter(
+ trash=False,
+ jihadi_destination=code
+ )
+
+ if start_date and end_date:
+ all_bars = all_bars.filter(date__gte=start_date, date__lte=end_date)
+ if province and province != 'undefined':
+ if transport_type == 'out':
+ all_bars = all_bars.filter(destination_province=province)
+ else:
+ all_bars = all_bars.filter(origin_province=province)
+ if product_type and product_type != 'undefined':
+ all_bars = all_bars.filter(product=product_type)
+
+ if search and search != 'undefined' and search.strip():
+ all_bars = all_bars.filter(
+ build_query(AllProductsTransportFilterSet.Meta.fields, search)
+ )
+
+ aggregation = all_bars.aggregate(
+ total=Sum('quantity'),
+ input_total=Sum('quantity', filter=Q(out=False)),
+ output_total=Sum('quantity', filter=Q(out=True)),
+ input_count=Count('id', filter=Q(out=False)),
+ output_count=Count('id', filter=Q(out=True)),
+ total_count=Count('id'),
+ last_mod=Max('modify_date'),
+ )
+
+ total_count = aggregation['total_count'] or 0
+ total_quantity = aggregation['total'] or 0
+ input_quantity = aggregation['input_total'] or 0
+ output_quantity = aggregation['output_total'] or 0
+ input_count = aggregation['input_count'] or 0
+ output_count = aggregation['output_count'] or 0
+ last_update = aggregation['last_mod']
+
+ if total_count > 0 and (input_quantity + output_quantity) > 0:
+ input_percent = round((input_quantity / (input_quantity + output_quantity)) * 100, 1)
+ output_percent = round((output_quantity / (input_quantity + output_quantity)) * 100, 1)
+ else:
+ input_percent = 0
+ output_percent = 0
+
+ return Response({
+ "bars": int(total_count),
+ "input_bars": int(input_count),
+ "last_update": last_update,
+ "output_bars": int(output_count),
+ "product": product_type,
+ "role": "all",
+ "total_bars_wight": int(total_quantity),
+ "total_input_bars_percent": input_percent,
+ "total_input_bars_wight": int(input_quantity),
+ "total_output_bars_percent": output_percent,
+ "total_output_bars_wight": int(output_quantity),
+ }, status=status.HTTP_200_OK)
\ No newline at end of file
diff --git a/authentication/__init__.py b/authentication/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/authentication/__pycache__/__init__.cpython-310.pyc b/authentication/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..8aded22
Binary files /dev/null and b/authentication/__pycache__/__init__.cpython-310.pyc differ
diff --git a/authentication/__pycache__/__init__.cpython-311.pyc b/authentication/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000..c80e5ac
Binary files /dev/null and b/authentication/__pycache__/__init__.cpython-311.pyc differ
diff --git a/authentication/__pycache__/__init__.cpython-312.pyc b/authentication/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..944ecb1
Binary files /dev/null and b/authentication/__pycache__/__init__.cpython-312.pyc differ
diff --git a/authentication/__pycache__/__init__.cpython-39.pyc b/authentication/__pycache__/__init__.cpython-39.pyc
new file mode 100644
index 0000000..ccc34e1
Binary files /dev/null and b/authentication/__pycache__/__init__.cpython-39.pyc differ
diff --git a/authentication/__pycache__/admin.cpython-310.pyc b/authentication/__pycache__/admin.cpython-310.pyc
new file mode 100644
index 0000000..c6462f4
Binary files /dev/null and b/authentication/__pycache__/admin.cpython-310.pyc differ
diff --git a/authentication/__pycache__/admin.cpython-311.pyc b/authentication/__pycache__/admin.cpython-311.pyc
new file mode 100644
index 0000000..033b59e
Binary files /dev/null and b/authentication/__pycache__/admin.cpython-311.pyc differ
diff --git a/authentication/__pycache__/admin.cpython-312.pyc b/authentication/__pycache__/admin.cpython-312.pyc
new file mode 100644
index 0000000..4d6d9ee
Binary files /dev/null and b/authentication/__pycache__/admin.cpython-312.pyc differ
diff --git a/authentication/__pycache__/admin.cpython-39.pyc b/authentication/__pycache__/admin.cpython-39.pyc
new file mode 100644
index 0000000..8f0af95
Binary files /dev/null and b/authentication/__pycache__/admin.cpython-39.pyc differ
diff --git a/authentication/__pycache__/apps.cpython-310.pyc b/authentication/__pycache__/apps.cpython-310.pyc
new file mode 100644
index 0000000..065b93d
Binary files /dev/null and b/authentication/__pycache__/apps.cpython-310.pyc differ
diff --git a/authentication/__pycache__/apps.cpython-311.pyc b/authentication/__pycache__/apps.cpython-311.pyc
new file mode 100644
index 0000000..14170b2
Binary files /dev/null and b/authentication/__pycache__/apps.cpython-311.pyc differ
diff --git a/authentication/__pycache__/apps.cpython-312.pyc b/authentication/__pycache__/apps.cpython-312.pyc
new file mode 100644
index 0000000..2dd6a1c
Binary files /dev/null and b/authentication/__pycache__/apps.cpython-312.pyc differ
diff --git a/authentication/__pycache__/apps.cpython-39.pyc b/authentication/__pycache__/apps.cpython-39.pyc
new file mode 100644
index 0000000..03c1b08
Binary files /dev/null and b/authentication/__pycache__/apps.cpython-39.pyc differ
diff --git a/authentication/__pycache__/models.cpython-310.pyc b/authentication/__pycache__/models.cpython-310.pyc
new file mode 100644
index 0000000..1b07383
Binary files /dev/null and b/authentication/__pycache__/models.cpython-310.pyc differ
diff --git a/authentication/__pycache__/models.cpython-311.pyc b/authentication/__pycache__/models.cpython-311.pyc
new file mode 100644
index 0000000..03877ec
Binary files /dev/null and b/authentication/__pycache__/models.cpython-311.pyc differ
diff --git a/authentication/__pycache__/models.cpython-312.pyc b/authentication/__pycache__/models.cpython-312.pyc
new file mode 100644
index 0000000..be90410
Binary files /dev/null and b/authentication/__pycache__/models.cpython-312.pyc differ
diff --git a/authentication/__pycache__/models.cpython-39.pyc b/authentication/__pycache__/models.cpython-39.pyc
new file mode 100644
index 0000000..21670e0
Binary files /dev/null and b/authentication/__pycache__/models.cpython-39.pyc differ
diff --git a/authentication/__pycache__/serializers.cpython-310.pyc b/authentication/__pycache__/serializers.cpython-310.pyc
new file mode 100644
index 0000000..8c8f839
Binary files /dev/null and b/authentication/__pycache__/serializers.cpython-310.pyc differ
diff --git a/authentication/__pycache__/serializers.cpython-311.pyc b/authentication/__pycache__/serializers.cpython-311.pyc
new file mode 100644
index 0000000..f9363e8
Binary files /dev/null and b/authentication/__pycache__/serializers.cpython-311.pyc differ
diff --git a/authentication/__pycache__/serializers.cpython-312.pyc b/authentication/__pycache__/serializers.cpython-312.pyc
new file mode 100644
index 0000000..b84f5aa
Binary files /dev/null and b/authentication/__pycache__/serializers.cpython-312.pyc differ
diff --git a/authentication/__pycache__/serializers.cpython-39.pyc b/authentication/__pycache__/serializers.cpython-39.pyc
new file mode 100644
index 0000000..1502365
Binary files /dev/null and b/authentication/__pycache__/serializers.cpython-39.pyc differ
diff --git a/authentication/__pycache__/urls.cpython-310.pyc b/authentication/__pycache__/urls.cpython-310.pyc
new file mode 100644
index 0000000..a80cd0e
Binary files /dev/null and b/authentication/__pycache__/urls.cpython-310.pyc differ
diff --git a/authentication/__pycache__/urls.cpython-311.pyc b/authentication/__pycache__/urls.cpython-311.pyc
new file mode 100644
index 0000000..12caebf
Binary files /dev/null and b/authentication/__pycache__/urls.cpython-311.pyc differ
diff --git a/authentication/__pycache__/urls.cpython-312.pyc b/authentication/__pycache__/urls.cpython-312.pyc
new file mode 100644
index 0000000..1c74aa0
Binary files /dev/null and b/authentication/__pycache__/urls.cpython-312.pyc differ
diff --git a/authentication/__pycache__/urls.cpython-39.pyc b/authentication/__pycache__/urls.cpython-39.pyc
new file mode 100644
index 0000000..4443b9e
Binary files /dev/null and b/authentication/__pycache__/urls.cpython-39.pyc differ
diff --git a/authentication/__pycache__/views.cpython-310.pyc b/authentication/__pycache__/views.cpython-310.pyc
new file mode 100644
index 0000000..21eb922
Binary files /dev/null and b/authentication/__pycache__/views.cpython-310.pyc differ
diff --git a/authentication/__pycache__/views.cpython-311.pyc b/authentication/__pycache__/views.cpython-311.pyc
new file mode 100644
index 0000000..ba06080
Binary files /dev/null and b/authentication/__pycache__/views.cpython-311.pyc differ
diff --git a/authentication/__pycache__/views.cpython-312.pyc b/authentication/__pycache__/views.cpython-312.pyc
new file mode 100644
index 0000000..515b16e
Binary files /dev/null and b/authentication/__pycache__/views.cpython-312.pyc differ
diff --git a/authentication/__pycache__/views.cpython-39.pyc b/authentication/__pycache__/views.cpython-39.pyc
new file mode 100644
index 0000000..2a92cd0
Binary files /dev/null and b/authentication/__pycache__/views.cpython-39.pyc differ
diff --git a/authentication/admin.py b/authentication/admin.py
new file mode 100644
index 0000000..8c38f3f
--- /dev/null
+++ b/authentication/admin.py
@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.
diff --git a/authentication/apps.py b/authentication/apps.py
new file mode 100644
index 0000000..8bab8df
--- /dev/null
+++ b/authentication/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class AuthenticationConfig(AppConfig):
+ default_auto_field = 'django.db.models.BigAutoField'
+ name = 'authentication'
diff --git a/authentication/migrations/0001_initial.py b/authentication/migrations/0001_initial.py
new file mode 100644
index 0000000..e9c9202
--- /dev/null
+++ b/authentication/migrations/0001_initial.py
@@ -0,0 +1,73 @@
+# Generated by Django 4.2.19 on 2025-02-26 08:14
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import uuid
+
+
+class Migration(migrations.Migration):
+
+ initial = True
+
+ dependencies = [
+ migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='UserProfile',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('key', models.UUIDField(default=uuid.uuid4, null=True)),
+ ('fullname', models.CharField(default='', max_length=150, null=True)),
+ ('first_name', models.CharField(default='', max_length=200, null=True)),
+ ('last_name', models.CharField(default='', max_length=200, null=True)),
+ ('mobile', models.CharField(default='', max_length=11, null=True)),
+ ('password', models.CharField(max_length=100, null=True)),
+ ('base_order', models.BigIntegerField(null=True)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='users', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='Province',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('name', models.CharField(max_length=200, null=True)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ migrations.CreateModel(
+ name='City',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('key', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
+ ('create_date', models.DateTimeField(auto_now_add=True)),
+ ('modify_date', models.DateTimeField(auto_now=True)),
+ ('trash', models.BooleanField(default=False)),
+ ('name', models.CharField(max_length=200, null=True)),
+ ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_createdby', to=settings.AUTH_USER_MODEL)),
+ ('modified_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(class)s_modifiedby', to=settings.AUTH_USER_MODEL)),
+ ('province', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='city_province', to='authentication.province')),
+ ],
+ options={
+ 'abstract': False,
+ },
+ ),
+ ]
diff --git a/authentication/migrations/0002_city_lat_city_lng_province_lat_province_lng.py b/authentication/migrations/0002_city_lat_city_lng_province_lat_province_lng.py
new file mode 100644
index 0000000..e914770
--- /dev/null
+++ b/authentication/migrations/0002_city_lat_city_lng_province_lat_province_lng.py
@@ -0,0 +1,33 @@
+# Generated by Django 4.2.19 on 2025-08-09 08:37
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('authentication', '0001_initial'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='city',
+ name='Lat',
+ field=models.FloatField(default=0),
+ ),
+ migrations.AddField(
+ model_name='city',
+ name='Lng',
+ field=models.FloatField(default=0),
+ ),
+ migrations.AddField(
+ model_name='province',
+ name='Lat',
+ field=models.FloatField(default=0),
+ ),
+ migrations.AddField(
+ model_name='province',
+ name='Lng',
+ field=models.FloatField(default=0),
+ ),
+ ]
diff --git a/authentication/migrations/0003_auto_20250809_1248.py b/authentication/migrations/0003_auto_20250809_1248.py
new file mode 100644
index 0000000..e1a73de
--- /dev/null
+++ b/authentication/migrations/0003_auto_20250809_1248.py
@@ -0,0 +1,61 @@
+# Generated by Django 3.2.13 on 2025-08-09 09:18
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('authentication', '0002_city_lat_city_lng_province_lat_province_lng'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='city',
+ name='create_date',
+ ),
+ migrations.RemoveField(
+ model_name='city',
+ name='created_by',
+ ),
+ migrations.RemoveField(
+ model_name='city',
+ name='key',
+ ),
+ migrations.RemoveField(
+ model_name='city',
+ name='modified_by',
+ ),
+ migrations.RemoveField(
+ model_name='city',
+ name='modify_date',
+ ),
+ migrations.RemoveField(
+ model_name='city',
+ name='trash',
+ ),
+ migrations.RemoveField(
+ model_name='province',
+ name='create_date',
+ ),
+ migrations.RemoveField(
+ model_name='province',
+ name='created_by',
+ ),
+ migrations.RemoveField(
+ model_name='province',
+ name='key',
+ ),
+ migrations.RemoveField(
+ model_name='province',
+ name='modified_by',
+ ),
+ migrations.RemoveField(
+ model_name='province',
+ name='modify_date',
+ ),
+ migrations.RemoveField(
+ model_name='province',
+ name='trash',
+ ),
+ ]
diff --git a/authentication/migrations/0004_province_tel_prefix.py b/authentication/migrations/0004_province_tel_prefix.py
new file mode 100644
index 0000000..b86cc4d
--- /dev/null
+++ b/authentication/migrations/0004_province_tel_prefix.py
@@ -0,0 +1,18 @@
+# Generated by Django 3.2.13 on 2025-08-09 09:18
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('authentication', '0003_auto_20250809_1248'),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name='province',
+ name='tel_prefix',
+ field=models.CharField(max_length=200, null=True),
+ ),
+ ]
diff --git a/authentication/migrations/__init__.py b/authentication/migrations/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/authentication/migrations/__pycache__/0001_initial.cpython-310.pyc b/authentication/migrations/__pycache__/0001_initial.cpython-310.pyc
new file mode 100644
index 0000000..c13e797
Binary files /dev/null and b/authentication/migrations/__pycache__/0001_initial.cpython-310.pyc differ
diff --git a/authentication/migrations/__pycache__/0001_initial.cpython-311.pyc b/authentication/migrations/__pycache__/0001_initial.cpython-311.pyc
new file mode 100644
index 0000000..fd1db46
Binary files /dev/null and b/authentication/migrations/__pycache__/0001_initial.cpython-311.pyc differ
diff --git a/authentication/migrations/__pycache__/0001_initial.cpython-312.pyc b/authentication/migrations/__pycache__/0001_initial.cpython-312.pyc
new file mode 100644
index 0000000..98b6bfa
Binary files /dev/null and b/authentication/migrations/__pycache__/0001_initial.cpython-312.pyc differ
diff --git a/authentication/migrations/__pycache__/0001_initial.cpython-39.pyc b/authentication/migrations/__pycache__/0001_initial.cpython-39.pyc
new file mode 100644
index 0000000..eee51c0
Binary files /dev/null and b/authentication/migrations/__pycache__/0001_initial.cpython-39.pyc differ
diff --git a/authentication/migrations/__pycache__/0002_city_lat_city_lng_province_lat_province_lng.cpython-311.pyc b/authentication/migrations/__pycache__/0002_city_lat_city_lng_province_lat_province_lng.cpython-311.pyc
new file mode 100644
index 0000000..994914e
Binary files /dev/null and b/authentication/migrations/__pycache__/0002_city_lat_city_lng_province_lat_province_lng.cpython-311.pyc differ
diff --git a/authentication/migrations/__pycache__/0002_city_lat_city_lng_province_lat_province_lng.cpython-312.pyc b/authentication/migrations/__pycache__/0002_city_lat_city_lng_province_lat_province_lng.cpython-312.pyc
new file mode 100644
index 0000000..a43e432
Binary files /dev/null and b/authentication/migrations/__pycache__/0002_city_lat_city_lng_province_lat_province_lng.cpython-312.pyc differ
diff --git a/authentication/migrations/__pycache__/0002_city_lat_city_lng_province_lat_province_lng.cpython-39.pyc b/authentication/migrations/__pycache__/0002_city_lat_city_lng_province_lat_province_lng.cpython-39.pyc
new file mode 100644
index 0000000..8789660
Binary files /dev/null and b/authentication/migrations/__pycache__/0002_city_lat_city_lng_province_lat_province_lng.cpython-39.pyc differ
diff --git a/authentication/migrations/__pycache__/0003_auto_20250809_1248.cpython-312.pyc b/authentication/migrations/__pycache__/0003_auto_20250809_1248.cpython-312.pyc
new file mode 100644
index 0000000..c80288b
Binary files /dev/null and b/authentication/migrations/__pycache__/0003_auto_20250809_1248.cpython-312.pyc differ
diff --git a/authentication/migrations/__pycache__/0003_auto_20250809_1248.cpython-39.pyc b/authentication/migrations/__pycache__/0003_auto_20250809_1248.cpython-39.pyc
new file mode 100644
index 0000000..bafefbd
Binary files /dev/null and b/authentication/migrations/__pycache__/0003_auto_20250809_1248.cpython-39.pyc differ
diff --git a/authentication/migrations/__pycache__/0004_province_tel_prefix.cpython-312.pyc b/authentication/migrations/__pycache__/0004_province_tel_prefix.cpython-312.pyc
new file mode 100644
index 0000000..824ad9a
Binary files /dev/null and b/authentication/migrations/__pycache__/0004_province_tel_prefix.cpython-312.pyc differ
diff --git a/authentication/migrations/__pycache__/0004_province_tel_prefix.cpython-39.pyc b/authentication/migrations/__pycache__/0004_province_tel_prefix.cpython-39.pyc
new file mode 100644
index 0000000..2ee2cc6
Binary files /dev/null and b/authentication/migrations/__pycache__/0004_province_tel_prefix.cpython-39.pyc differ
diff --git a/authentication/migrations/__pycache__/__init__.cpython-310.pyc b/authentication/migrations/__pycache__/__init__.cpython-310.pyc
new file mode 100644
index 0000000..de4dd3a
Binary files /dev/null and b/authentication/migrations/__pycache__/__init__.cpython-310.pyc differ
diff --git a/authentication/migrations/__pycache__/__init__.cpython-311.pyc b/authentication/migrations/__pycache__/__init__.cpython-311.pyc
new file mode 100644
index 0000000..5763a06
Binary files /dev/null and b/authentication/migrations/__pycache__/__init__.cpython-311.pyc differ
diff --git a/authentication/migrations/__pycache__/__init__.cpython-312.pyc b/authentication/migrations/__pycache__/__init__.cpython-312.pyc
new file mode 100644
index 0000000..7149289
Binary files /dev/null and b/authentication/migrations/__pycache__/__init__.cpython-312.pyc differ
diff --git a/authentication/migrations/__pycache__/__init__.cpython-39.pyc b/authentication/migrations/__pycache__/__init__.cpython-39.pyc
new file mode 100644
index 0000000..d092132
Binary files /dev/null and b/authentication/migrations/__pycache__/__init__.cpython-39.pyc differ
diff --git a/authentication/models.py b/authentication/models.py
new file mode 100644
index 0000000..aeaa3c6
--- /dev/null
+++ b/authentication/models.py
@@ -0,0 +1,67 @@
+from django.db import models
+from django.conf import settings
+import uuid
+from django.contrib.auth.models import User
+
+
+class BaseModel(models.Model):
+ key = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
+ create_date = models.DateTimeField(auto_now_add=True)
+ modify_date = models.DateTimeField(auto_now=True)
+ created_by = models.ForeignKey(
+ settings.AUTH_USER_MODEL,
+ related_name="%(class)s_createdby",
+ on_delete=models.CASCADE,
+ null=True,
+ blank=True,
+ )
+ modified_by = models.ForeignKey(
+ settings.AUTH_USER_MODEL,
+ on_delete=models.CASCADE,
+ related_name="%(class)s_modifiedby",
+ null=True,
+ blank=True,
+ )
+ trash = models.BooleanField(default=False)
+
+ class Meta:
+ abstract = True
+
+
+class Province(models.Model):
+ name = models.CharField(max_length=200, null=True)
+ tel_prefix = models.CharField(max_length=200, null=True)
+ Lat = models.FloatField(default=0)
+ Lng = models.FloatField(default=0)
+
+ def save(self, *args, **kwargs):
+ super(Province, self).save(*args, **kwargs)
+
+
+class City(models.Model):
+ province = models.ForeignKey(
+ Province, on_delete=models.CASCADE, related_name="city_province", null=True
+ )
+ name = models.CharField(max_length=200, null=True)
+ Lat = models.FloatField(default=0)
+ Lng = models.FloatField(default=0)
+
+ def save(self, *args, **kwargs):
+ super(City, self).save(*args, **kwargs)
+
+
+class UserProfile(BaseModel):
+ key = models.UUIDField(default=uuid.uuid4, editable=True, null=True)
+ user = models.ForeignKey(
+ User, on_delete=models.CASCADE, related_name="users", null=True
+ )
+ fullname = models.CharField(max_length=150, null=True, default="")
+ first_name = models.CharField(max_length=200, null=True, default="")
+ last_name = models.CharField(max_length=200, null=True, default="")
+ mobile = models.CharField(max_length=11, null=True, default="")
+ password = models.CharField(max_length=100, null=True)
+ base_order = models.BigIntegerField(null=True)
+
+ def save(self, *args, **kwargs):
+ self.fullname = self.first_name + " " + self.last_name
+ super(UserProfile, self).save(*args, **kwargs)
diff --git a/authentication/serializers.py b/authentication/serializers.py
new file mode 100644
index 0000000..0db5ea2
--- /dev/null
+++ b/authentication/serializers.py
@@ -0,0 +1,21 @@
+from rest_framework import serializers
+
+from authentication.models import UserProfile, Province, City
+
+
+class ProvinceSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = Province
+ fields = ['key', 'name']
+
+
+class CitySerializer(serializers.ModelSerializer):
+ class Meta:
+ model = City
+ fields = ['key', 'name']
+
+
+class UserProfileSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = UserProfile
+ fields = '__all__'
diff --git a/authentication/tests.py b/authentication/tests.py
new file mode 100644
index 0000000..7ce503c
--- /dev/null
+++ b/authentication/tests.py
@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.
diff --git a/authentication/urls.py b/authentication/urls.py
new file mode 100644
index 0000000..8ee804f
--- /dev/null
+++ b/authentication/urls.py
@@ -0,0 +1,25 @@
+from django.urls import include, path
+from rest_framework.routers import DefaultRouter
+from authentication import views as auth_views
+
+router = DefaultRouter()
+
+router.register(
+ r'province',
+ auth_views.ProvinceViewSet,
+ basename="province"
+)
+router.register(
+ r'city',
+ auth_views.CityViewSet,
+ basename="city"
+)
+
+router.register(
+ r'profile',
+ auth_views.UserProfileViewSet,
+ basename="profile"
+)
+urlpatterns = [
+ path('', include(router.urls))
+]
diff --git a/authentication/views.py b/authentication/views.py
new file mode 100644
index 0000000..a2dd3a2
--- /dev/null
+++ b/authentication/views.py
@@ -0,0 +1,23 @@
+from rest_framework import viewsets
+from rest_framework.permissions import AllowAny
+
+from authentication.models import Province, City, UserProfile
+from authentication.serializers import ProvinceSerializer, CitySerializer, UserProfileSerializer
+
+
+class ProvinceViewSet(viewsets.ModelViewSet):
+ queryset = Province.objects.all()
+ serializer_class = ProvinceSerializer
+ permission_classes = [AllowAny]
+
+
+class UserProfileViewSet(viewsets.ModelViewSet):
+ queryset = UserProfile.objects.all()
+ serializer_class = UserProfileSerializer
+ permission_classes = [AllowAny]
+
+
+class CityViewSet(viewsets.ModelViewSet):
+ queryset = City.objects.all()
+ serializer_class = CitySerializer
+ permission_classes = [AllowAny]
diff --git a/cron_for_city.py b/cron_for_city.py
new file mode 100644
index 0000000..60338e6
--- /dev/null
+++ b/cron_for_city.py
@@ -0,0 +1,11 @@
+import os
+import django
+
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "RSI.settings")
+django.setup()
+
+from app.views import fix_province, fix_city
+
+fix_province()
+fix_city()
\ No newline at end of file
diff --git a/cron_for_code.py b/cron_for_code.py
new file mode 100644
index 0000000..74a79b2
--- /dev/null
+++ b/cron_for_code.py
@@ -0,0 +1,8 @@
+import os
+import django
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "RSI.settings")
+django.setup()
+
+from app.views import update_product_date_cron
+update_product_date_cron()
\ No newline at end of file
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..57fabf6
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,7 @@
+services:
+ web:
+ build: ./
+ volumes:
+ - .:/app
+ ports:
+ - "8000:8000"
\ No newline at end of file
diff --git a/helpers.py b/helpers.py
new file mode 100644
index 0000000..ba18a23
--- /dev/null
+++ b/helpers.py
@@ -0,0 +1,26 @@
+import jdatetime
+from rest_framework.pagination import PageNumberPagination
+from django.db.models import Q
+
+
+class CustomPagination(PageNumberPagination):
+ page_size = 10
+ page_size_query_param = 'page_size'
+ max_page_size = 100
+
+
+def convert_to_miladi(year=None, month=None, day=None):
+ date = jdatetime.datetime(year, month, day).togregorian()
+ return date
+
+
+def build_query(fields, value):
+ query = Q()
+ for field in fields:
+ query |= Q(**{f"{field}__icontains": value})
+ return query
+
+
+def build_calculation(queryset, column_name, aggregate_func):
+ result = queryset.aggregate(total=aggregate_func(column_name))
+ return result['total'] or 0
diff --git a/manage.py b/manage.py
new file mode 100644
index 0000000..ff16eae
--- /dev/null
+++ b/manage.py
@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+ """Run administrative tasks."""
+ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'RSI.settings')
+ try:
+ from django.core.management import execute_from_command_line
+ except ImportError as exc:
+ raise ImportError(
+ "Couldn't import Django. Are you sure it's installed and "
+ "available on your PYTHONPATH environment variable? Did you "
+ "forget to activate a virtual environment?"
+ ) from exc
+ execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..e850076
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,21 @@
+asgiref==3.8.1
+beautifulsoup4==4.12.3
+boto3==1.22.2
+cached-property==2.0.1
+Django==4.2.19
+django-cors-headers==4.7.0
+django-filter==25.1
+django-url-filter==0.3.15
+djangorestframework==3.15.2
+enum-compat==0.0.3
+fuzzywuzzy==0.18.0
+jalali-core==1.0.0
+jdatetime==5.2.0
+openpyxl==3.1.2
+psycopg2==2.9.10
+requests==2.27.1
+six==1.17.0
+sqlparse==0.5.3
+typing-extensions==4.12.2
+tzdata==2025.1
+python-dotenv