diff --git a/.gitignore b/.gitignore index 38eb404..f719955 100644 --- a/.gitignore +++ b/.gitignore @@ -1,142 +1,12 @@ -.idea - -# Created by https://www.toptal.com/developers/gitignore/api/django,python,virtualenv -# Edit at https://www.toptal.com/developers/gitignore?templates=django,python,virtualenv - -### Django ### -*.log -*.pot -*.pyc -__pycache__/ -local_settings.py -#*.sqlite3 -#db.sqlite3 -db.sqlite3-journal -media -#static -staticfiles -profile_images - -# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ -# in your Git repository. Update and uncomment the following line accordingly. -# /staticfiles/ - -# db.sqlite3 -### Django.Python Stack ### # Byte-compiled / optimized / DLL files +__pycache__/ *.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -build/ -develop-eggs/ -#dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -wheels/ -share/python-wheels/ -*.egg-info/ -.installed.cfg -*.egg -MANIFEST - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ -cover/ - -# Translations -*.mo # Django stuff: - -# Flask stuff: -instance/ -.webassets-cache - -# Scrapy stuff: -.scrapy - -# Sphinx documentation -docs/_build/ - -# PyBuilder -.pybuilder/ -target/ - -# Jupyter Notebook -.ipynb_checkpoints - -# IPython -profile_default/ -ipython_config.py - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -#pdm.lock -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide -.pdm.toml - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm -__pypackages__/ - -# Celery stuff -celerybeat-schedule -celerybeat.pid - -# SageMath parsed files -*.sage.py +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal # Environments .env @@ -147,141 +17,29 @@ ENV/ env.bak/ venv.bak/ -# Spyder project settings -.spyderproject -.spyproject +# Media & Static Files +/media/* +/staticfiles/* -# Rope project settings -.ropeproject -# mkdocs documentation -/site +# Cache & Temporary Files +*.swp +*.swo +*.swn +*.bak +*.tmp -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json +# Editor-specific files +*.kate-swp +*.backup +.vscode/ +.idea/ +.DS_Store +._* -# Pyre type checker -.pyre/ +# migrations +# */migrations/0*.py +!*/migrations/__init__.py -# pytype static type analyzer -.pytype/ - -# Cython debug symbols -cython_debug/ - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. -#.idea/ - -### Python ### -# Byte-compiled / optimized / DLL files - -# C extensions - -# Distribution / packaging - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. - -# Installer logs - -# Unit test / coverage reports - -# Translations - -# Django stuff: - -# Flask stuff: - -# Scrapy stuff: - -# Sphinx documentation - -# PyBuilder - -# Jupyter Notebook - -# IPython - -# pyenv -# For a library or package, you might want to ignore these files since the code is -# intended to run in multiple environments; otherwise, check them in: -# .python-version - -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. - -# poetry -# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. -# This is especially recommended for binary packages to ensure reproducibility, and is more -# commonly ignored for libraries. -# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control - -# pdm -# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. -# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it -# in version control. -# https://pdm.fming.dev/#use-with-ide - -# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm - -# Celery stuff - -# SageMath parsed files - -# Environments - -# Spyder project settings - -# Rope project settings - -# mkdocs documentation - -# mypy - -# Pyre type checker - -# pytype static type analyzer - -# Cython debug symbols - -# PyCharm -# JetBrains specific template is maintained in a separate JetBrains.gitignore that can -# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore -# and can be added to the global gitignore or merged into this file. For a more nuclear -# option (not recommended) you can uncomment the following to ignore the entire idea folder. - -### Python Patch ### -# Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration -poetry.toml - -# ruff -.ruff_cache/ - -# LSP config files -pyrightconfig.json - -### VirtualEnv ### -# Virtualenv -# http://iamzed.com/2009/05/07/a-primer-on-virtualenv/ -[Bb]in -[Ii]nclude -[Ll]ib -[Ll]ib64 -[Ll]ocal -[Ss]cripts -pyvenv.cfg -pip-selfcheck.json - -# End of https://www.toptal.com/developers/gitignore/api/django,python,virtualenv - -.cursor +# cursor +.cursor/* diff --git a/_base/settings.py b/_base/settings.py index 6283ccf..3710f84 100644 --- a/_base/settings.py +++ b/_base/settings.py @@ -11,6 +11,8 @@ https://docs.djangoproject.com/en/5.2/ref/settings/ """ import os from pathlib import Path +from decouple import config +import dj_database_url # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -20,16 +22,28 @@ BASE_DIR = Path(__file__).resolve().parent.parent # See https://docs.djangoproject.com/en/5.2/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-h!2hx$h=f6ktgdks!g2_*pg_s1nnuyk+j2yd*_x8r+3+3iyfy*' +SECRET_KEY = config('DJANGO_SECRET_KEY', default="unsecure-secretkey-kjhsgfjsfgjsfgjsg") # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True +DEBUG = config('DEBUG', cast=bool, default=True) -ALLOWED_HOSTS = [] +# Allowed hosts +ALLOWED_HOSTS = [host for host in config("DJANGO_ALLOWED_HOSTS", default="").split() if host] +# URL scheme (http or https) +URL_SCHEME = config("URL_SCHEME", default="http") + + +# CSRF trusted origins (add both with and without port if you use a non-standard port) +CSRF_TRUSTED_ORIGINS = [] +for host in ALLOWED_HOSTS: + if host not in ("localhost", "127.0.0.1"): + CSRF_TRUSTED_ORIGINS.append(f"{URL_SCHEME}://{host}") + +# Generate base URL for absolute URLs (use first allowed host) +BASE_URL = f"{URL_SCHEME}://{ALLOWED_HOSTS[0]}" if ALLOWED_HOSTS else "http://localhost" # Application definition - INSTALLED_APPS = [ # ------ theme ------ # 'jazzmin', @@ -45,6 +59,7 @@ INSTALLED_APPS = [ # ------- third party apps ------- # 'simple_history', + 'django_extensions', # -------------------------------- # # ------- my apps ------- # @@ -96,13 +111,20 @@ WSGI_APPLICATION = '_base.wsgi.application' # Database # https://docs.djangoproject.com/en/5.2/ref/settings/#databases -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', - } -} +DB_TYPE = config('DB_TYPE', default='sqlite').lower() +if DB_TYPE == 'postgres': + DATABASES = { + 'default': dj_database_url.config(default=config('DATABASE_URL')) + } + +else: + DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR/"db.sqlite3", + } + } # Password validation # https://docs.djangoproject.com/en/5.2/ref/settings/#auth-password-validators @@ -139,15 +161,15 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/5.2/howto/static-files/ -STATIC_ROOT = 'ss' -STATIC_URL = 'static/' +STATIC_URL = '/static/' +STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') STATICFILES_DIRS = [ - os.path.join(BASE_DIR, 'static') + os.path.join(BASE_DIR, 'static'), ] +# Media files MEDIA_URL = '/media/' -MEDIA_ROOT = BASE_DIR / 'media' +MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # Default primary key field type # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field diff --git a/accounts/management/commands/create_roles.py b/accounts/management/commands/create_roles.py index 92f57c0..cb1305a 100644 --- a/accounts/management/commands/create_roles.py +++ b/accounts/management/commands/create_roles.py @@ -45,6 +45,10 @@ class Command(BaseCommand): "name": "ستاد آب‌منطقه‌ای", "slug": UserRoles.HEADQUARTER, }, + { + "name": "مدیر پیمانکار", + "slug": UserRoles.CONTRACTOR_MANAGER, + }, ] for role in roles: diff --git a/accounts/templatetags/accounts_tags.py b/accounts/templatetags/accounts_tags.py index e2aec6b..7b3831e 100644 --- a/accounts/templatetags/accounts_tags.py +++ b/accounts/templatetags/accounts_tags.py @@ -58,3 +58,7 @@ def is_headquarter(user): def is_customer(user): return _has_profile(user) and user.profile.has_role(UserRoles.CUSTOMER) +@register.filter +def is_contractor_manager(user): + return _has_profile(user) and user.profile.has_role(UserRoles.CONTRACTOR_MANAGER) + diff --git a/certificates/templates/certificates/print.html b/certificates/templates/certificates/print.html index 39b028a..c37382a 100644 --- a/certificates/templates/certificates/print.html +++ b/certificates/templates/certificates/print.html @@ -5,6 +5,7 @@ تاییدیه - {{ instance.code }} {% load static %} + {% load common_tags %} @@ -52,21 +53,22 @@
مشخصات چاه و کنتور هوشمند
-
موقعیت مکانی (UTM): {{ latest_report.utm_x|default:'-' }} , {{ latest_report.utm_y|default:'-' }}
-
نیرو محرکه چاه: {{ latest_report.driving_force|default:'-' }}
-
نوع کنتور: {{ latest_report.get_meter_type_display|default:'-' }}
+
موقعیت مکانی (UTM): X: {{ latest_report.utm_x|default:'-' }} , Y: {{ latest_report.utm_y|default:'-' }}
+
نوع پمپ: {{ latest_report.get_pump_type_display|default:'-' }}
+
نوع کنتور: {{ latest_report.get_meter_type_display|default:'-' }}
+
نام شرکت کنتورساز: {{ latest_report.water_meter_manufacturer.name|default:'-' }}
قطر لوله آبده (اینچ): {{ latest_report.discharge_pipe_diameter|default:'-' }}
نوع مصرف: {{ latest_report.get_usage_type_display|default:'-' }}
-
شماره سیم‌کارت: {{ latest_report.sim_number|default:'-' }}
+
توان مصرفی (کیلووات): {{ latest_report.motor_power|default:'-' }}
-
سایز کنتور: {{ latest_report.meter_size|default:'-' }}
+
دبی بعد از کالیبراسیون (لیتر بر ثانیه): {{ latest_report.flow_rate|default:'-' }}
+
ساعت کارکرد پروانه: {{ latest_report.licence_working_hour|default:'-' }}
+
شماره پلمپ: {{ latest_report.seal_number|default:'-' }}
شماره پروانه بهره‌برداری چاه: {{ latest_report.exploitation_license_number|default:'-' }}
-
قدرت موتور: {{ latest_report.motor_power|default:'-' }}
-
دبی قبل از کالیبراسیون: {{ latest_report.pre_calibration_flow_rate|default:'-' }}
-
دبی بعد از کالیبراسیون: {{ latest_report.post_calibration_flow_rate|default:'-' }}
-
نام شرکت کنتورساز: {{ latest_report.water_meter_manufacturer.name|default:'-' }}
-
شماره سریال کنتور: {{ instance.well.water_meter_serial_number|default:'-' }}
+
پایان اعتبار شارژ: {{ latest_report.meter_charge_expiration_date|to_jalali|default:'-' }}
+
سریال کنتور: {{ instance.well.water_meter_serial_number|default:'-' }}
+
تابلو قطع: {{ latest_report.is_panel_cut|yesno:'دارد,ندارد' }}
diff --git a/certificates/templates/certificates/step.html b/certificates/templates/certificates/step.html index f8249cb..917183a 100644 --- a/certificates/templates/certificates/step.html +++ b/certificates/templates/certificates/step.html @@ -2,6 +2,7 @@ {% load static %} {% load processes_tags %} {% load humanize %} + {% load common_tags %} {% load accounts_tags %} {% block sidebar %} @@ -73,21 +74,24 @@
مشخصات چاه و کنتور هوشمند
-
موقعیت مکانی (UTM):{{ latest_report.utm_x|default:'-' }} , {{ latest_report.utm_y|default:'-' }}
-
نیرو محرکه چاه:{{ latest_report.driving_force|default:'-' }}
+
موقعیت مکانی (UTM):X: {{ latest_report.utm_x|default:'-' }} , Y: {{ latest_report.utm_y|default:'-' }}
+
نوع پمپ:{{ latest_report.get_pump_type_display|default:'-' }}
نوع کنتور:{{ latest_report.get_meter_type_display|default:'-' }}
+
نام شرکت کنتورساز:{{ latest_report.water_meter_manufacturer.name|default:'-' }}
قطر لوله آبده (اینچ):{{ latest_report.discharge_pipe_diameter|default:'-' }}
نوع مصرف:{{ latest_report.get_usage_type_display|default:'-' }}
-
شماره سیم‌کارت:{{ latest_report.sim_number|default:'-' }}
+
توان مصرفی (کیلووات):{{ latest_report.motor_power|default:'-' }}
+
-
سایز کنتور:{{ latest_report.meter_size|default:'-' }}
+
دبی بعد از کالیبراسیون (لیتر بر ثانیه):{{ latest_report.flow_rate|default:'-' }}
+
ساعت کارکرد پروانه:{{ latest_report.licence_working_hour|default:'-' }}
+
شماره پلمپ:{{ latest_report.seal_number|default:'-' }}
شماره پروانه بهره‌برداری چاه:{{ latest_report.exploitation_license_number|default:'-' }}
-
قدرت موتور:{{ latest_report.motor_power|default:'-' }}
-
دبی قبل از کالیبراسیون:{{ latest_report.pre_calibration_flow_rate|default:'-' }}
-
دبی بعد از کالیبراسیون:{{ latest_report.post_calibration_flow_rate|default:'-' }}
-
نام شرکت کنتورساز:{{ latest_report.water_meter_manufacturer.name|default:'-' }}
-
شماره سریال کنتور:{{ instance.well.water_meter_serial_number|default:'-' }}
+
پایان اعتبار شارژ:{{ latest_report.meter_charge_expiration_date|to_jalali|default:'-' }}
+
سریال کنتور:{{ instance.well.water_meter_serial_number|default:'-' }}
+
تابلو قطع:{{ latest_report.is_panel_cut|yesno:'دارد,ندارد' }}
+
diff --git a/common/consts.py b/common/consts.py index c367ad3..37b4258 100644 --- a/common/consts.py +++ b/common/consts.py @@ -11,6 +11,7 @@ class UserRoles(Enum): REGIONAL_WATER_AUTHORITY = "rwa" # کارشناس امور WATER_RESOURCE_MANAGER = "wrm" # مدیر منابع آب HEADQUARTER = "hdq" # ستاد آب منطقه‌ای + CONTRACTOR_MANAGER = "cmn" # مدیر پیمانکار USER_TYPE_CHOICES = [ diff --git a/db.sqlite3 b/db.sqlite3 index b4e650c..54bffa7 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ diff --git a/installations/admin.py b/installations/admin.py index fc78615..b32240b 100644 --- a/installations/admin.py +++ b/installations/admin.py @@ -24,11 +24,13 @@ class InstallationReportAdmin(admin.ModelAdmin): list_display = ( 'assignment', 'visited_date', 'meter_type', 'meter_size', 'water_meter_manufacturer', 'discharge_pipe_diameter', 'usage_type', 'exploitation_license_number', - 'motor_power', 'pre_calibration_flow_rate', 'post_calibration_flow_rate', + 'motor_power', 'flow_rate', 'meter_reading', 'meter_charge', 'meter_charge_expiration_date', + 'pump_type', 'licence_working_hour', 'new_water_meter_serial', 'seal_number', 'sim_number', + 'is_panel_sealed', 'is_panel_cut', 'is_disconnection_done', 'is_meter_suspicious', 'approved', 'created' ) - list_filter = ('is_meter_suspicious', 'approved', 'visited_date', 'meter_type', 'usage_type', 'water_meter_manufacturer') + list_filter = ('is_meter_suspicious', 'is_panel_sealed', 'is_panel_cut', 'is_disconnection_done', 'approved', 'visited_date', 'meter_type', 'pump_type', 'usage_type', 'water_meter_manufacturer') search_fields = ( 'assignment__process_instance__code', 'new_water_meter_serial', 'seal_number', 'exploitation_license_number', 'sim_number' ) @@ -43,13 +45,16 @@ class InstallationReportAdmin(admin.ModelAdmin): ) }), ('مشخصات هیدرولیکی', { - 'fields': ('discharge_pipe_diameter', 'pre_calibration_flow_rate', 'post_calibration_flow_rate') + 'fields': ('discharge_pipe_diameter', 'flow_rate', 'meter_reading', 'meter_charge', 'meter_charge_expiration_date') }), ('کاربری و مجوز', { 'fields': ('usage_type', 'exploitation_license_number') }), ('توان و محرکه', { - 'fields': ('driving_force', 'motor_power') + 'fields': ('driving_force', 'motor_power', 'pump_type', 'licence_working_hour') + }), + ('وضعیت تابلو/قطع', { + 'fields': ('is_panel_sealed', 'is_panel_cut', 'is_disconnection_done') }), ('توضیحات', { 'fields': ('description',) diff --git a/installations/forms.py b/installations/forms.py index b8be2b0..d2483e7 100644 --- a/installations/forms.py +++ b/installations/forms.py @@ -20,10 +20,12 @@ class InstallationReportForm(forms.ModelForm): model = InstallationReport fields = [ 'visited_date', 'new_water_meter_serial', 'seal_number', - 'utm_x', 'utm_y', 'meter_type', 'meter_size', 'meter_model', + 'utm_x', 'utm_y', 'meter_type', 'discharge_pipe_diameter', 'usage_type', 'exploitation_license_number', - 'motor_power', 'pre_calibration_flow_rate', 'post_calibration_flow_rate', - 'water_meter_manufacturer', 'sim_number', 'driving_force', + 'motor_power', 'flow_rate', 'meter_charge_expiration_date', + 'pump_type', 'licence_working_hour', + 'is_panel_cut', + 'water_meter_manufacturer', 'is_meter_suspicious', 'description' ] @@ -59,16 +61,7 @@ class InstallationReportForm(forms.ModelForm): ('smart', 'هوشمند (آب و برق)'), ('volumetric', 'حجمی') ]), - 'meter_size': forms.TextInput(attrs={ - 'class': 'form-control' - }), - 'meter_model': forms.Select(attrs={ - 'class': 'form-select' - }, choices=[ - ('', 'انتخاب کنید'), - ('direct', 'مستقیم'), - ('indirect', 'غیرمستقیم') - ]), + # meter_size and meter_model removed from form UI 'discharge_pipe_diameter': forms.NumberInput(attrs={ 'class': 'form-control', 'required': True @@ -90,35 +83,41 @@ class InstallationReportForm(forms.ModelForm): 'class': 'form-control', 'required': True }), - 'pre_calibration_flow_rate': forms.NumberInput(attrs={ + 'flow_rate': forms.NumberInput(attrs={ 'class': 'form-control', 'min': '0', 'step': '0.0001', - 'required': True }), - 'post_calibration_flow_rate': forms.NumberInput(attrs={ + # meter_reading and meter_charge removed from form UI + 'meter_charge_expiration_date': forms.DateInput(attrs={ + 'type': 'date', 'class': 'form-control', - 'min': '0', - 'step': '0.0001', - 'required': True }), 'water_meter_manufacturer': forms.Select(attrs={ 'class': 'form-select', 'id': 'id_water_meter_manufacturer', 'required': True }), - 'sim_number': forms.TextInput(attrs={ - 'class': 'form-control', + # sim_number and driving_force removed from form UI + 'pump_type': forms.Select(attrs={ + 'class': 'form-select', 'required': True }), - 'driving_force': forms.TextInput(attrs={ + 'licence_working_hour': forms.NumberInput(attrs={ 'class': 'form-control', + 'min': '0', + 'step': '1', 'required': True }), 'is_meter_suspicious': forms.CheckboxInput(attrs={ 'class': 'form-check-input', 'id': 'id_is_meter_suspicious', }), + # is_panel_sealed removed from form UI + 'is_panel_cut': forms.CheckboxInput(attrs={ + 'class': 'form-check-input', + }), + # is_disconnection_done removed from form UI 'description': forms.Textarea(attrs={ 'class': 'form-control', 'rows': 3 @@ -127,22 +126,22 @@ class InstallationReportForm(forms.ModelForm): labels = { 'visited_date': 'تاریخ مراجعه', - 'new_water_meter_serial': 'سریال کنتور جدید', + 'new_water_meter_serial': 'سریال کنتور', 'seal_number': 'شماره پلمپ', 'utm_x': 'UTM X', 'utm_y': 'UTM Y', 'meter_type': 'نوع کنتور', - 'meter_size': 'سایز کنتور', 'discharge_pipe_diameter': 'قطر لوله آبده (اینچ)', 'usage_type': 'نوع مصرف', 'exploitation_license_number': 'شماره پروانه بهره‌برداری', - 'motor_power': 'قدرت موتور (کیلووات ساعت)', - 'pre_calibration_flow_rate': 'دبی قبل از کالیبراسیون (لیتر بر ثانیه)', - 'post_calibration_flow_rate': 'دبی بعد از کالیبراسیون (لیتر بر ثانیه)', + 'motor_power': 'توان مصرفی (کیلووات)', + 'flow_rate': 'دبی بعد از کالیبراسیون (لیتر بر ثانیه)', + 'meter_charge_expiration_date': 'تاریخ پایان اعتبار شارژ', + 'pump_type': 'نوع پمپ', + 'licence_working_hour': 'ساعت کارکرد پروانه', 'water_meter_manufacturer': 'شرکت سازنده کنتور', - 'sim_number': 'شماره سیمکارت', - 'driving_force': 'نیرو محرکه چاه', 'is_meter_suspicious': 'کنتور مشکوک است', + 'is_panel_cut': 'تابلو قطع', 'description': 'توضیحات' } diff --git a/installations/migrations/0012_installationreport_flow_rate_and_more.py b/installations/migrations/0012_installationreport_flow_rate_and_more.py new file mode 100644 index 0000000..1f3f29b --- /dev/null +++ b/installations/migrations/0012_installationreport_flow_rate_and_more.py @@ -0,0 +1,50 @@ +# Generated by Django 5.2.4 on 2025-10-27 03:42 + +import django.core.validators +from decimal import Decimal +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('installations', '0011_alter_installationreport_discharge_pipe_diameter'), + ] + + operations = [ + migrations.AddField( + model_name='installationreport', + name='flow_rate', + field=models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True, verbose_name='(لیتر بر ثانیه)دبی'), + ), + migrations.AddField( + model_name='installationreport', + name='is_disconnection_done', + field=models.BooleanField(default=False, verbose_name='انجام عملیات قطع'), + ), + migrations.AddField( + model_name='installationreport', + name='is_panel_cut', + field=models.BooleanField(default=False, verbose_name='تابلو قطع'), + ), + migrations.AddField( + model_name='installationreport', + name='is_panel_sealed', + field=models.BooleanField(default=False, verbose_name='پلمپ تابلو'), + ), + migrations.AddField( + model_name='installationreport', + name='meter_charge', + field=models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True, validators=[django.core.validators.MinValueValidator(Decimal('0'))], verbose_name='شارژ کنتور (متر مکعب)'), + ), + migrations.AddField( + model_name='installationreport', + name='meter_charge_expiration_date', + field=models.DateField(blank=True, null=True, verbose_name='تاریخ پایان اعتبار شارژ'), + ), + migrations.AddField( + model_name='installationreport', + name='meter_reading', + field=models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True, validators=[django.core.validators.MinValueValidator(Decimal('0'))], verbose_name='عدد کنتور (متر مکعب)'), + ), + ] diff --git a/installations/migrations/0013_remove_installationreport_post_calibration_flow_rate_and_more.py b/installations/migrations/0013_remove_installationreport_post_calibration_flow_rate_and_more.py new file mode 100644 index 0000000..31f653d --- /dev/null +++ b/installations/migrations/0013_remove_installationreport_post_calibration_flow_rate_and_more.py @@ -0,0 +1,21 @@ +# Generated by Django 5.2.4 on 2025-10-27 03:47 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('installations', '0012_installationreport_flow_rate_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='installationreport', + name='post_calibration_flow_rate', + ), + migrations.RemoveField( + model_name='installationreport', + name='pre_calibration_flow_rate', + ), + ] diff --git a/installations/migrations/0014_installationreport_licence_working_hour_and_more.py b/installations/migrations/0014_installationreport_licence_working_hour_and_more.py new file mode 100644 index 0000000..f77fb6a --- /dev/null +++ b/installations/migrations/0014_installationreport_licence_working_hour_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 5.2.4 on 2025-11-23 08:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('installations', '0013_remove_installationreport_post_calibration_flow_rate_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='installationreport', + name='licence_working_hour', + field=models.PositiveIntegerField(blank=True, null=True, verbose_name='ساعت کارکرد پروانه'), + ), + migrations.AddField( + model_name='installationreport', + name='pump_type', + field=models.CharField(blank=True, choices=[('shaft_sleeve', 'شفت و غلاف'), ('submersible', 'شناور')], max_length=20, null=True, verbose_name='نوع پمپ'), + ), + migrations.AlterField( + model_name='installationreport', + name='flow_rate', + field=models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True, verbose_name='(لیتر بر ثانیه)دبی بعد از کالیبراسیون'), + ), + migrations.AlterField( + model_name='installationreport', + name='motor_power', + field=models.DecimalField(blank=True, decimal_places=4, max_digits=10, null=True, verbose_name='(کیلووات)توان مصرفی'), + ), + migrations.AlterField( + model_name='installationreport', + name='new_water_meter_serial', + field=models.CharField(blank=True, max_length=50, null=True, verbose_name='سریال کنتور'), + ), + ] diff --git a/installations/models.py b/installations/models.py index c7d7e31..9761ae3 100644 --- a/installations/models.py +++ b/installations/models.py @@ -41,7 +41,7 @@ class InstallationReport(BaseModel): related_name='reports', verbose_name='اختصاص' ) visited_date = models.DateField(null=True, blank=True, verbose_name='تاریخ مراجعه') - new_water_meter_serial = models.CharField(max_length=50, null=True, blank=True, verbose_name='سریال کنتور جدید') + new_water_meter_serial = models.CharField(max_length=50, null=True, blank=True, verbose_name='سریال کنتور') seal_number = models.CharField(max_length=50, null=True, blank=True, verbose_name='شماره پلمپ') is_meter_suspicious = models.BooleanField(default=False, verbose_name='کنتور مشکوک است؟') METER_TYPE_CHOICES = [ @@ -55,6 +55,18 @@ class InstallationReport(BaseModel): ] meter_model = models.CharField(max_length=20, choices=METER_MODEL_CHOICES, null=True, blank=True, verbose_name='مدل کنتور') meter_size = models.CharField(max_length=50, null=True, blank=True, verbose_name='سایز کنتور') + meter_reading = models.DecimalField(max_digits=10, decimal_places=4, null=True, blank=True, validators=[MinValueValidator(Decimal('0'))], verbose_name='عدد کنتور (متر مکعب)') + meter_charge = models.DecimalField(max_digits=10, decimal_places=4, null=True, blank=True, validators=[MinValueValidator(Decimal('0'))], verbose_name='شارژ کنتور (متر مکعب)') + meter_charge_expiration_date = models.DateField(null=True, blank=True, verbose_name='تاریخ پایان اعتبار شارژ') + is_panel_sealed = models.BooleanField(default=False, verbose_name='پلمپ تابلو') + is_panel_cut = models.BooleanField(default=False, verbose_name='تابلو قطع') + is_disconnection_done = models.BooleanField(default=False, verbose_name='انجام عملیات قطع') + PUMP_TYPE_CHOICES = [ + ('shaft_sleeve', 'شفت و غلاف'), + ('submersible', 'شناور'), + ] + pump_type = models.CharField(max_length=20, choices=PUMP_TYPE_CHOICES, null=True, blank=True, verbose_name='نوع پمپ') + licence_working_hour = models.PositiveIntegerField(null=True, blank=True, verbose_name='ساعت کارکرد پروانه') discharge_pipe_diameter = models.DecimalField(max_digits=10, decimal_places=4, null=True, blank=True, verbose_name='قطر لوله آبده (اینچ)') USAGE_TYPE_CHOICES = [ ('domestic', 'شرب و خدمات'), @@ -63,9 +75,8 @@ class InstallationReport(BaseModel): ] usage_type = models.CharField(max_length=20, choices=USAGE_TYPE_CHOICES, null=True, verbose_name='نوع مصرف') exploitation_license_number = models.CharField(max_length=50, verbose_name='شماره پروانه بهره‌برداری چاه') - motor_power = models.DecimalField(max_digits=10, decimal_places=4, null=True, blank=True, verbose_name='(کیلووات ساعت) قدرت موتور') - pre_calibration_flow_rate = models.DecimalField(max_digits=10, decimal_places=4, null=True, blank=True, verbose_name='(لیتر بر ثانیه)دبی قبل از کالیبراسیون') - post_calibration_flow_rate = models.DecimalField(max_digits=10, decimal_places=4, null=True, blank=True, verbose_name='(لیتر بر ثانیه)دبی بعد از کالیبراسیون') + motor_power = models.DecimalField(max_digits=10, decimal_places=4, null=True, blank=True, verbose_name='(کیلووات)توان مصرفی') + flow_rate = models.DecimalField(max_digits=10, decimal_places=4, null=True, blank=True, verbose_name='(لیتر بر ثانیه)دبی بعد از کالیبراسیون') water_meter_manufacturer = models.ForeignKey('wells.WaterMeterManufacturer', on_delete=models.SET_NULL, null=True, blank=True, verbose_name='شرکت سازنده کنتور آب') sim_number = models.CharField(max_length=20, null=True, blank=True, verbose_name='شماره سیمکارت') driving_force = models.CharField(max_length=50, null=True, blank=True, verbose_name='نیرو محرکه چاه') diff --git a/installations/templates/installations/installation_report_step.html b/installations/templates/installations/installation_report_step.html index 3246dea..9aa11c3 100644 --- a/installations/templates/installations/installation_report_step.html +++ b/installations/templates/installations/installation_report_step.html @@ -146,28 +146,23 @@

تاریخ مراجعه: {{ report.visited_date|to_jalali|default:'-' }}

-

سریال جدید: {{ report.new_water_meter_serial|default:'-' }}

+

سریال کنتور: {{ report.new_water_meter_serial|default:'-' }}

شماره پلمپ: {{ report.seal_number|default:'-' }}

نوع کنتور: {{ report.get_meter_type_display|default:'-' }}

- {% if report.meter_type == 'smart' %} -

مدل کنتور: {{ report.get_meter_model_display|default:'-' }}

- {% else %} -

سایز کنتور: {{ report.meter_size|default:'-' }}

- {% endif %}

قطر لوله آبده (اینچ): {{ report.discharge_pipe_diameter|default:'-' }}

-

سازنده کنتور: {{ report.water_meter_manufacturer|default:'-' }}

-

شماره سیمکارت: {{ report.sim_number|default:'-' }}

-

نیرو محرکه چاه: {{ report.driving_force|default:'-' }}

+

شرکت سازنده کنتور: {{ report.water_meter_manufacturer.name|default:'-' }}

+

پایان اعتبار شارژ: {{ report.meter_charge_expiration_date|to_jalali|default:'-' }}

+

کنتور مشکوک: {{ report.is_meter_suspicious|yesno:'بله,خیر' }}

-

کنتور مشکوک: {{ report.is_meter_suspicious|yesno:'بله,خیر' }}

-

UTM X: {{ report.utm_x|default:'-' }}

-

UTM Y: {{ report.utm_y|default:'-' }}

+

UTM X: {{ report.utm_x|default:'-' }} Y: {{ report.utm_y|default:'-' }}

نوع مصرف: {{ report.get_usage_type_display|default:'-' }}

شماره پروانه بهره‌برداری: {{ report.exploitation_license_number|default:'-' }}

-

قدرت موتور(کیلووات ساعت): {{ report.motor_power|default:'-' }}

-

دبی قبل کالیبراسیون(لیتر/ثانیه): {{ report.pre_calibration_flow_rate|default:'-' }}

-

دبی بعد کالیبراسیون(لیتر/ثانیه): {{ report.post_calibration_flow_rate|default:'-' }}

+

توان مصرفی (کیلووات): {{ report.motor_power|default:'-' }}

+

دبی بعد از کالیبراسیون (لیتر بر ثانیه): {{ report.flow_rate|default:'-' }}

+

نوع پمپ: {{ report.get_pump_type_display|default:'-' }}

+

ساعت کارکرد پروانه: {{ report.licence_working_hour|default:'-' }}

+

تابلو قطع: {{ report.is_panel_cut|yesno:'بله,خیر' }}

@@ -346,20 +341,7 @@
{{ form.meter_type.errors.0 }}
{% endif %}
-
- {{ form.meter_size.label_tag }} - {{ form.meter_size }} - {% if form.meter_size.errors %} -
{{ form.meter_size.errors.0 }}
- {% endif %} -
-
- {{ form.meter_model.label_tag }} - {{ form.meter_model }} - {% if form.meter_model.errors %} -
{{ form.meter_size.errors.0 }}
- {% endif %} -
+
{{ form.discharge_pipe_diameter.label_tag }} {{ form.discharge_pipe_diameter }} @@ -389,21 +371,23 @@ {% endif %}
- {{ form.pre_calibration_flow_rate.label_tag }} - {{ form.pre_calibration_flow_rate }} - {% if form.pre_calibration_flow_rate.errors %} -
{{ form.pre_calibration_flow_rate.errors.0 }}
+ {{ form.flow_rate.label_tag }} + {{ form.flow_rate }} + {% if form.flow_rate.errors %} +
{{ form.flow_rate.errors.0 }}
+ {% endif %} +
+ +
+ {{ form.meter_charge_expiration_date.label_tag }} + + + {% if form.meter_charge_expiration_date.errors %} +
{{ form.meter_charge_expiration_date.errors.0 }}
{% endif %}
- {{ form.post_calibration_flow_rate.label_tag }} - {{ form.post_calibration_flow_rate }} - {% if form.post_calibration_flow_rate.errors %} -
{{ form.post_calibration_flow_rate.errors.0 }}
- {% endif %} -
-
- {{ form.water_meter_manufacturer.label_tag }}حجمی + {{ form.water_meter_manufacturer.label_tag }}
{{ form.water_meter_manufacturer }} {{ form.new_manufacturer }} @@ -418,20 +402,22 @@
{{ form.new_manufacturer.errors.0 }}
{% endif %}
+
- {{ form.sim_number.label_tag }} - {{ form.sim_number }} - {% if form.sim_number.errors %} -
{{ form.sim_number.errors.0 }}
+ {{ form.pump_type.label_tag }} + {{ form.pump_type }} + {% if form.pump_type.errors %} +
{{ form.pump_type.errors.0 }}
{% endif %}
- {{ form.driving_force.label_tag }} - {{ form.driving_force }} - {% if form.driving_force.errors %} -
{{ form.driving_force.errors.0 }}
+ {{ form.licence_working_hour.label_tag }} + {{ form.licence_working_hour }} + {% if form.licence_working_hour.errors %} +
{{ form.licence_working_hour.errors.0 }}
{% endif %}
+
{{ form.is_meter_suspicious }} @@ -440,6 +426,18 @@ {% if form.is_meter_suspicious.errors %}
{{ form.is_meter_suspicious.errors.0 }}
{% endif %} +
+ +
+
+ {{ form.is_panel_cut }} + {{ form.is_panel_cut.label_tag }} +
+ {% if form.is_panel_cut.errors %} +
{{ form.is_panel_cut.errors.0 }}
+ {% endif %} +
+
@@ -653,7 +651,7 @@ {% endblock %} diff --git a/installations/views.py b/installations/views.py index 367edac..02325ba 100644 --- a/installations/views.py +++ b/installations/views.py @@ -23,14 +23,14 @@ def installation_assign_step(request, instance_id, step_id): next_step = instance.process.steps.filter(order__gt=step.order).first() # Installers list (profiles that have installer role) - installers = Profile.objects.filter(roles__slug=UserRoles.INSTALLER.value, county=instance.well.county).select_related('user').all() + installers = Profile.objects.filter(roles__slug=UserRoles.INSTALLER.value, county__city=instance.well.county.city).select_related('user').all() assignment, _ = InstallationAssignment.objects.get_or_create(process_instance=instance) # Role flags profile = getattr(request.user, 'profile', None) is_manager = False try: - is_manager = bool(profile and profile.has_role(UserRoles.MANAGER)) + is_manager = bool(profile and profile.has_role(UserRoles.MANAGER)) or bool(profile and profile.has_role(UserRoles.CONTRACTOR_MANAGER)) except Exception: is_manager = False diff --git a/invoices/models.py b/invoices/models.py index b61d487..4bf617a 100644 --- a/invoices/models.py +++ b/invoices/models.py @@ -147,8 +147,8 @@ class Quote(NameSlugModel): """مبلغ باقی‌مانده بر اساس پرداخت‌ها""" paid = self.get_paid_amount() remaining = self.final_amount - paid - if remaining < 0: - remaining = Decimal('0') + # if remaining < 0: + # remaining = Decimal('0') return remaining def get_vat_amount(self) -> Decimal: diff --git a/invoices/views.py b/invoices/views.py index 0313143..01b8c48 100644 --- a/invoices/views.py +++ b/invoices/views.py @@ -36,7 +36,7 @@ def quote_step(request, instance_id, step_id): return redirect('processes:request_list') # دریافت آیتم‌ها - items = Item.objects.filter(is_active=True, is_special=False, is_deleted=False).order_by('name') + items = Item.objects.filter(is_active=True, is_special=False, is_deleted=False).order_by('-is_default_in_quotes') existing_quote = Quote.objects.filter(process_instance=instance).first() existing_quote_items = {} if existing_quote: @@ -556,8 +556,8 @@ def add_quote_payment(request, instance_id, step_id): except InvalidOperation: return JsonResponse({'success': False, 'message': 'مبلغ نامعتبر است'}) remaining = quote.get_remaining_amount() - if amount_dec > remaining: - return JsonResponse({'success': False, 'message': 'مبلغ فیش بیشتر از مانده پیش‌فاکتور است'}) + # if amount_dec > remaining: + # return JsonResponse({'success': False, 'message': 'مبلغ فیش بیشتر از مانده پیش‌فاکتور است'}) Payment.objects.create( invoice=invoice, diff --git a/liara.json b/liara.json new file mode 100644 index 0000000..d421180 --- /dev/null +++ b/liara.json @@ -0,0 +1,14 @@ +{ + "app": "meterplus", + "port": 80, + "team-id": "68822f40f04e5bc3027fc2b7", + "build": { + "location": "iran" + }, + "disks": [ + { + "name": "media", + "mountTo": "/usr/src/app/media" + } + ] +} diff --git a/processes/templates/processes/instance_summary.html b/processes/templates/processes/instance_summary.html index 59f910f..82335c3 100644 --- a/processes/templates/processes/instance_summary.html +++ b/processes/templates/processes/instance_summary.html @@ -122,11 +122,11 @@
{% if installation_assignment.scheduled_date %}
-

تاریخ برنامه‌ریزی: {{ installation_assignment.scheduled_date|to_jalali }}

+

تاریخ تعیین شده: {{ installation_assignment.scheduled_date|to_jalali }}

{% endif %}
-

سریال کنتور جدید: {{ latest_report.new_water_meter_serial|default:'-' }}

+

سریال کنتور: {{ latest_report.new_water_meter_serial|default:'-' }}

شماره پلمپ: {{ latest_report.seal_number|default:'-' }}

@@ -134,24 +134,16 @@

کنتور مشکوک: {{ latest_report.is_meter_suspicious|yesno:'بله,خیر' }}

- {% if latest_report.sim_number %} -
-

شماره سیمکارت: {{ latest_report.sim_number }}

-
- {% endif %} + {# sim_number removed from summary UI #} {% if latest_report.meter_type %}

نوع کنتور: {{ latest_report.get_meter_type_display }}

- {% endif %} - {% if latest_report.meter_size %} -
-

سایز کنتور: {{ latest_report.meter_size }}

-
+ {# meter model/size removed from summary UI #} {% endif %} {% if latest_report.water_meter_manufacturer %}
-

سازنده: {{ latest_report.water_meter_manufacturer.name }}

+

شرکت سازنده کنتور: {{ latest_report.water_meter_manufacturer.name }}

{% endif %} {% if latest_report.discharge_pipe_diameter %} @@ -164,14 +156,10 @@

نوع مصرف: {{ latest_report.get_usage_type_display }}

{% endif %} - {% if latest_report.driving_force %} -
-

نیرو محرکه: {{ latest_report.driving_force }}

-
- {% endif %} + {# driving_force removed from summary UI #} {% if latest_report.motor_power %}
-

قدرت موتور: {{ latest_report.motor_power }} کیلووات ساعت

+

توان مصرفی (کیلووات): {{ latest_report.motor_power }}

{% endif %} {% if latest_report.exploitation_license_number %} @@ -179,14 +167,30 @@

شماره پروانه: {{ latest_report.exploitation_license_number }}

{% endif %} - {% if latest_report.pre_calibration_flow_rate %} + {% if latest_report.flow_rate %}
-

دبی قبل از کالیبراسیون: {{ latest_report.pre_calibration_flow_rate }} لیتر/ثانیه

+

دبی بعد از کالیبراسیون (لیتر بر ثانیه): {{ latest_report.flow_rate }}

{% endif %} - {% if latest_report.post_calibration_flow_rate %} + {# meter_reading and meter_charge removed from summary UI #} + {% if latest_report.meter_charge_expiration_date %}
-

دبی بعد از کالیبراسیون: {{ latest_report.post_calibration_flow_rate }} لیتر/ثانیه

+

پایان اعتبار شارژ: {{ latest_report.meter_charge_expiration_date|to_jalali }}

+
+ {% endif %} + {# is_panel_sealed removed from summary UI #} +
+

تابلو قطع: {{ latest_report.is_panel_cut|yesno:'دارد,ندارد' }}

+
+ {# is_disconnection_done removed from summary UI #} + {% if latest_report.pump_type %} +
+

نوع پمپ: {{ latest_report.get_pump_type_display }}

+
+ {% endif %} + {% if latest_report.licence_working_hour %} +
+

ساعت کارکرد پروانه: {{ latest_report.licence_working_hour }}

{% endif %}
diff --git a/processes/templates/processes/request_list.html b/processes/templates/processes/request_list.html index 1ae210a..5549f07 100644 --- a/processes/templates/processes/request_list.html +++ b/processes/templates/processes/request_list.html @@ -155,7 +155,7 @@ {% endfor %}
- {% if request.user|is_admin or request.user|is_manager or request.user|is_accountant %} + {% if request.user|is_admin or request.user|is_manager or request.user|is_accountant or request.user|is_contractor_manager %}
{% endif %} - {% if request.user|is_admin or request.user|is_manager or request.user|is_accountant %} + {% if request.user|is_admin or request.user|is_manager or request.user|is_accountant or request.user|is_contractor_manager %}