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