Compare commits

..

34 commits

Author SHA1 Message Date
7720926b2b Merge remote-tracking branch 'origin/fix_installer' into production 2025-10-28 14:40:14 +03:30
18f37ab513 fix installer list. 2025-10-28 14:25:56 +03:30
3752438630 Merge remote-tracking branch 'origin/fix_city' into production 2025-10-28 13:06:56 +03:30
c58ccb3977 fix city in queryset. 2025-10-28 12:42:19 +03:30
1b37aa1522 Merge remote-tracking branch 'origin/Add_field' into production 2025-10-27 09:16:12 +03:30
c90e19daaa Add field to installation report 2025-10-27 09:12:05 +03:30
71ca2e58e3 add defualt filter for nassab 2025-10-15 15:13:28 +03:30
d3857d37c5 Merge branch 'main' of ssh://git.poraab.com/aminhashemi92/shafafiyat into production 2025-10-10 11:58:34 +03:30
bb1b1a8ebf initially make meterplus.ir ready for production 2025-10-08 14:04:58 +03:30
672e26c89d remove sqlite 2025-10-07 15:33:35 +03:30
21e76f8f55 update gitignore for db separated version 2025-10-07 15:30:14 +03:30
9815aa4c73 Merge remote-tracking branch 'origin' into shafafiyat/production 2025-10-07 14:30:50 +03:30
2a59c1acb5 Merge remote-tracking branch 'origin' into shafafiyat/production 2025-10-07 10:21:43 +03:30
92ad9ee301 add migration files 2025-10-07 10:20:57 +03:30
916c66a281 Merge remote-tracking branch 'origin' into shafafiyat/production 2025-10-04 14:23:27 +03:30
241f56f550 Merge remote-tracking branch 'origin' into shafafiyat/production 2025-10-04 13:13:38 +03:30
42971c5e58 add openpyxl to requirements 2025-09-30 13:21:52 +03:30
494b7743e9 Merge remote-tracking branch 'origin' into shafafiyat/production 2025-09-30 13:19:05 +03:30
37a5953134 Merge remote-tracking branch 'origin' into shafafiyat/production 2025-09-14 16:49:00 +03:30
896d66e801 Merge remote-tracking branch 'origin' into shafafiyat/production 2025-09-14 11:57:22 +03:30
af3b043a46 Merge remote-tracking branch 'origin/main' into shafafiyat/production 2025-09-13 21:33:32 +03:30
20c5f13ea0 remove db for upcoming db 2025-09-13 21:30:20 +03:30
976be64028 Merge remote-tracking branch 'origin/shafafiyat/production' into shafafiyat/production 2025-09-13 13:40:21 +03:30
20f00b786e Merge remote-tracking branch 'origin/main' into shafafiyat/production 2025-09-13 13:38:21 +03:30
8083b6d32e delete db for upcoming db 2025-09-13 13:34:34 +03:30
ce8584f86b Bring back db.sqlite3 from main 2025-09-08 20:24:32 +03:30
80fcdca876 Merge remote-tracking branch 'origin/shafafiyat/production' into shafafiyat/production 2025-09-08 20:17:59 +03:30
9124e5d52c delete db because the main has a good one 2025-09-08 20:14:32 +03:30
6cecb7fa80 add db and requirements.txt file 2025-09-08 20:14:02 +03:30
3acbeb7770 add liara config for deployment 2025-09-08 20:12:20 +03:30
bf4047714c Merge remote-tracking branch 'origin/main' into shafafiyat/production 2025-09-07 13:24:10 +03:30
0201779fb5 delete db because the main has a good one 2025-09-07 13:18:47 +03:30
aa09d1e190 add db and requirements.txt file 2025-08-30 10:33:51 +03:30
2941c7a7fc add liara config for deployment 2025-08-27 12:57:45 +03:30
17 changed files with 359 additions and 349 deletions

294
.gitignore vendored
View file

@ -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.
# <django-project-name>/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/*

View file

@ -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

View file

@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>تاییدیه - {{ instance.code }}</title>
{% load static %}
{% load common_tags %}
<!-- Fonts (match project) -->
<link rel="preconnect" href="https://fonts.googleapis.com">
@ -52,21 +53,31 @@
<h6 class="my-2">مشخصات چاه و کنتور هوشمند</h6>
<div class="row" style="font-size: 14px;">
<div class="col-4">
<div>موقعیت مکانی (UTM): {{ latest_report.utm_x|default:'-' }} , {{ latest_report.utm_y|default:'-' }}</div>
<div>موقعیت مکانی (UTM): X: {{ latest_report.utm_x|default:'-' }} , Y: {{ latest_report.utm_y|default:'-' }}</div>
<div>نیرو محرکه چاه: {{ latest_report.driving_force|default:'-' }}</div>
<div>نوع کنتور: {{ latest_report.get_meter_type_display|default:'-' }}</div>
<div>قطر لوله آبده (اینچ): {{ latest_report.discharge_pipe_diameter|default:'-' }}</div>
{% if latest_report.meter_type == 'smart' %}
<div>مدل کنتور: {{ latest_report.get_meter_model_display|default:'-' }}</div>
{% else %}
<div>سایز کنتور: {{ latest_report.meter_size|default:'-' }}</div>
{% endif %}
<div>نوع مصرف: {{ latest_report.get_usage_type_display|default:'-' }}</div>
<div>شماره سیم‌کارت: {{ latest_report.sim_number|default:'-' }}</div>
<div>شارژ کنتور (متر مکعب): {{ latest_report.meter_charge|default:'-' }}</div>
<div>پایان اعتبار شارژ: {{ latest_report.meter_charge_expiration_date|to_jalali|default:'-' }}</div>
<div>شماره سریال کنتور: {{ instance.well.water_meter_serial_number|default:'-' }}</div>
</div>
<div class="col-4">
<div>سایز کنتور: {{ latest_report.meter_size|default:'-' }}</div>
<div>قطر لوله آبده (اینچ): {{ latest_report.discharge_pipe_diameter|default:'-' }}</div>
<div>شماره پروانه بهره‌برداری چاه: {{ latest_report.exploitation_license_number|default:'-' }}</div>
<div>قدرت موتور: {{ latest_report.motor_power|default:'-' }}</div>
<div>دبی قبل از کالیبراسیون: {{ latest_report.pre_calibration_flow_rate|default:'-' }}</div>
<div>دبی بعد از کالیبراسیون: {{ latest_report.post_calibration_flow_rate|default:'-' }}</div>
<div>قدرت موتور (کیلووات ساعت): {{ latest_report.motor_power|default:'-' }}</div>
<div>دبی (لیتر بر ثانیه): {{ latest_report.flow_rate|default:'-' }}</div>
<div>عدد کنتور (متر مکعب): {{ latest_report.meter_reading|default:'-' }}</div>
<div>پلمپ تابلو: {{ latest_report.is_panel_sealed|yesno:'دارد,ندارد' }}</div>
<div>تابلو قطع: {{ latest_report.is_panel_cut|yesno:'دارد,ندارد' }}</div>
<div>انجام عملیات قطع: {{ latest_report.is_disconnection_done|yesno:'دارد,ندارد' }}</div>
<div>نام شرکت کنتورساز: {{ latest_report.water_meter_manufacturer.name|default:'-' }}</div>
<div>شماره سریال کنتور: {{ instance.well.water_meter_serial_number|default:'-' }}</div>
</div>
<div class="col-4">
<!-- Signature -->

View file

@ -2,6 +2,7 @@
{% load static %}
{% load processes_tags %}
{% load humanize %}
{% load common_tags %}
{% load accounts_tags %}
{% block sidebar %}
@ -73,21 +74,30 @@
<h6 class="mb-2">مشخصات چاه و کنتور هوشمند</h6>
<div class="row g-2 small">
<div class="col-12 col-md-6">
<div class="d-flex gap-2"><span class="text-muted">موقعیت مکانی (UTM):</span><span class="fw-medium">{{ latest_report.utm_x|default:'-' }} , {{ latest_report.utm_y|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">موقعیت مکانی (UTM):</span><span class="fw-medium">X: {{ latest_report.utm_x|default:'-' }} , Y: {{ latest_report.utm_y|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">نیرو محرکه چاه:</span><span class="fw-medium">{{ latest_report.driving_force|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">نوع کنتور:</span><span class="fw-medium">{{ latest_report.get_meter_type_display|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">قطر لوله آبده (اینچ):</span><span class="fw-medium">{{ latest_report.discharge_pipe_diameter|default:'-' }}</span></div>
{% if latest_report.meter_type == 'smart' %}
<div class="d-flex gap-2"><span class="text-muted">مدل کنتور:</span><span class="fw-medium">{{ latest_report.get_meter_model_display|default:'-' }}</span></div>
{% else %}
<div class="d-flex gap-2"><span class="text-muted">سایز کنتور:</span><span class="fw-medium">{{ latest_report.meter_size|default:'-' }}</span></div>
{% endif %}
<div class="d-flex gap-2"><span class="text-muted">نوع مصرف:</span><span class="fw-medium">{{ latest_report.get_usage_type_display|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">شماره سیم‌کارت:</span><span class="fw-medium">{{ latest_report.sim_number|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">شارژ کنتور (متر مکعب):</span><span class="fw-medium">{{ latest_report.meter_charge|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">پایان اعتبار شارژ:</span><span class="fw-medium">{{ latest_report.meter_charge_expiration_date|to_jalali|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">شماره سریال کنتور:</span><span class="fw-medium">{{ instance.well.water_meter_serial_number|default:'-' }}</span></div>
</div>
<div class="col-12 col-md-6">
<div class="d-flex gap-2"><span class="text-muted">سایز کنتور:</span><span class="fw-medium">{{ latest_report.meter_size|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">قطر لوله آبده (اینچ):</span><span class="fw-medium">{{ latest_report.discharge_pipe_diameter|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">شماره پروانه بهره‌برداری چاه:</span><span class="fw-medium">{{ latest_report.exploitation_license_number|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">قدرت موتور:</span><span class="fw-medium">{{ latest_report.motor_power|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">دبی قبل از کالیبراسیون:</span><span class="fw-medium">{{ latest_report.pre_calibration_flow_rate|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">دبی بعد از کالیبراسیون:</span><span class="fw-medium">{{ latest_report.post_calibration_flow_rate|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">قدرت موتور (کیلووات ساعت):</span><span class="fw-medium">{{ latest_report.motor_power|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">دبی (لیتر بر ثانیه):</span><span class="fw-medium">{{ latest_report.flow_rate|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">عدد کنتور (متر مکعب):</span><span class="fw-medium">{{ latest_report.meter_reading|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">پلمپ تابلو:</span><span class="fw-medium">{{ latest_report.is_panel_sealed|yesno:'دارد,ندارد' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">تابلو قطع:</span><span class="fw-medium">{{ latest_report.is_panel_cut|yesno:'دارد,ندارد' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">انجام عملیات قطع:</span><span class="fw-medium">{{ latest_report.is_disconnection_done|yesno:'دارد,ندارد' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">نام شرکت کنتورساز:</span><span class="fw-medium">{{ latest_report.water_meter_manufacturer.name|default:'-' }}</span></div>
<div class="d-flex gap-2"><span class="text-muted">شماره سریال کنتور:</span><span class="fw-medium">{{ instance.well.water_meter_serial_number|default:'-' }}</span></div>
</div>
</div>
<div class="signature-section d-flex justify-content-end">

Binary file not shown.

View file

@ -24,11 +24,12 @@ 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',
'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', 'usage_type', 'water_meter_manufacturer')
search_fields = (
'assignment__process_instance__code', 'new_water_meter_serial', 'seal_number', 'exploitation_license_number', 'sim_number'
)
@ -43,7 +44,7 @@ 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')
@ -51,6 +52,9 @@ class InstallationReportAdmin(admin.ModelAdmin):
('توان و محرکه', {
'fields': ('driving_force', 'motor_power')
}),
('وضعیت تابلو/قطع', {
'fields': ('is_panel_sealed', 'is_panel_cut', 'is_disconnection_done')
}),
('توضیحات', {
'fields': ('description',)
}),

View file

@ -22,7 +22,8 @@ class InstallationReportForm(forms.ModelForm):
'visited_date', 'new_water_meter_serial', 'seal_number',
'utm_x', 'utm_y', 'meter_type', 'meter_size', 'meter_model',
'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',
'is_panel_sealed', 'is_panel_cut', 'is_disconnection_done',
'water_meter_manufacturer', 'sim_number', 'driving_force',
'is_meter_suspicious', 'description'
]
@ -90,17 +91,24 @@ 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': forms.NumberInput(attrs={
'class': 'form-control',
'min': '0',
'step': '0.0001',
'required': True
}),
'meter_charge': forms.NumberInput(attrs={
'class': 'form-control',
'min': '0',
'step': '0.0001',
}),
'meter_charge_expiration_date': forms.DateInput(attrs={
'type': 'date',
'class': 'form-control',
}),
'water_meter_manufacturer': forms.Select(attrs={
'class': 'form-select',
@ -119,6 +127,15 @@ class InstallationReportForm(forms.ModelForm):
'class': 'form-check-input',
'id': 'id_is_meter_suspicious',
}),
'is_panel_sealed': forms.CheckboxInput(attrs={
'class': 'form-check-input',
}),
'is_panel_cut': forms.CheckboxInput(attrs={
'class': 'form-check-input',
}),
'is_disconnection_done': forms.CheckboxInput(attrs={
'class': 'form-check-input',
}),
'description': forms.Textarea(attrs={
'class': 'form-control',
'rows': 3
@ -137,12 +154,17 @@ class InstallationReportForm(forms.ModelForm):
'usage_type': 'نوع مصرف',
'exploitation_license_number': 'شماره پروانه بهره‌برداری',
'motor_power': 'قدرت موتور (کیلووات ساعت)',
'pre_calibration_flow_rate': 'دبی قبل از کالیبراسیون (لیتر بر ثانیه)',
'post_calibration_flow_rate': 'دبی بعد از کالیبراسیون (لیتر بر ثانیه)',
'flow_rate': 'دبی (لیتر بر ثانیه)',
'meter_reading': 'عدد کنتور (متر مکعب)',
'meter_charge': 'شارژ کنتور (متر مکعب)',
'meter_charge_expiration_date': 'تاریخ پایان اعتبار شارژ',
'water_meter_manufacturer': 'شرکت سازنده کنتور',
'sim_number': 'شماره سیمکارت',
'driving_force': 'نیرو محرکه چاه',
'is_meter_suspicious': 'کنتور مشکوک است',
'is_panel_sealed': 'پلمپ تابلو',
'is_panel_cut': 'تابلو قطع',
'is_disconnection_done': 'انجام عملیات قطع',
'description': 'توضیحات'
}

View file

@ -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='عدد کنتور (متر مکعب)'),
),
]

View file

@ -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',
),
]

View file

@ -55,6 +55,12 @@ 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='انجام عملیات قطع')
discharge_pipe_diameter = models.DecimalField(max_digits=10, decimal_places=4, null=True, blank=True, verbose_name='قطر لوله آبده (اینچ)')
USAGE_TYPE_CHOICES = [
('domestic', 'شرب و خدمات'),
@ -64,8 +70,7 @@ 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='(لیتر بر ثانیه)دبی بعد از کالیبراسیون')
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='نیرو محرکه چاه')

View file

@ -156,8 +156,10 @@
{% endif %}
<p class="text-nowrap mb-2"><i class="bx bx-tachometer bx-sm me-2"></i>قطر لوله آبده (اینچ): {{ report.discharge_pipe_diameter|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-building bx-sm me-2"></i>سازنده کنتور: {{ report.water_meter_manufacturer|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-sim-card bx-sm me-2"></i>شماره سیمکارت: {{ report.sim_number|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-mobile bx-sm me-2"></i>شماره سیمکارت: {{ report.sim_number|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-cog bx-sm me-2"></i>نیرو محرکه چاه: {{ report.driving_force|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-battery bx-sm me-2"></i>شارژ کنتور (متر مکعب): {{ report.meter_charge|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-calendar-exclamation bx-sm me-2"></i>پایان اعتبار شارژ: {{ report.meter_charge_expiration_date|to_jalali|default:'-' }}</p>
</div>
<div class="col-md-6">
<p class="text-nowrap mb-2"><i class="bx bx-help-circle bx-sm me-2"></i>کنتور مشکوک: {{ report.is_meter_suspicious|yesno:'بله,خیر' }}</p>
@ -165,9 +167,12 @@
<p class="text-nowrap mb-2"><i class="bx bx-map-pin bx-sm me-2"></i>UTM Y: {{ report.utm_y|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-category bx-sm me-2"></i>نوع مصرف: {{ report.get_usage_type_display|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-id-card bx-sm me-2"></i>شماره پروانه بهره‌برداری: {{ report.exploitation_license_number|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-bolt-circle bx-sm me-2"></i>قدرت موتور(کیلووات ساعت): {{ report.motor_power|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-water bx-sm me-2"></i>دبی قبل کالیبراسیون(لیتر/ثانیه): {{ report.pre_calibration_flow_rate|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-water bx-sm me-2"></i>دبی بعد کالیبراسیون(لیتر/ثانیه): {{ report.post_calibration_flow_rate|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-bolt-circle bx-sm me-2"></i>قدرت موتور (کیلووات ساعت): {{ report.motor_power|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-water bx-sm me-2"></i>دبی (لیتر/ثانیه): {{ report.flow_rate|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-pen bx-sm me-2"></i>عدد کنتور (متر مکعب): {{ report.meter_reading|default:'-' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-lock-open-alt bx-sm me-2"></i>پلمپ تابلو: {{ report.is_panel_sealed|yesno:'بله,خیر' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-power-off bx-sm me-2"></i>تابلو قطع: {{ report.is_panel_cut|yesno:'بله,خیر' }}</p>
<p class="text-nowrap mb-2"><i class="bx bx-cut bx-sm me-2"></i>انجام عملیات قطع: {{ report.is_disconnection_done|yesno:'بله,خیر' }}</p>
</div>
</div>
@ -389,17 +394,32 @@
{% endif %}
</div>
<div class="col-md-3">
{{ form.pre_calibration_flow_rate.label_tag }}
{{ form.pre_calibration_flow_rate }}
{% if form.pre_calibration_flow_rate.errors %}
<div class="invalid-feedback">{{ form.pre_calibration_flow_rate.errors.0 }}</div>
{{ form.flow_rate.label_tag }}
{{ form.flow_rate }}
{% if form.flow_rate.errors %}
<div class="invalid-feedback">{{ form.flow_rate.errors.0 }}</div>
{% endif %}
</div>
<div class="col-md-3">
{{ form.post_calibration_flow_rate.label_tag }}
{{ form.post_calibration_flow_rate }}
{% if form.post_calibration_flow_rate.errors %}
<div class="invalid-feedback">{{ form.post_calibration_flow_rate.errors.0 }}</div>
{{ form.meter_reading.label_tag }}
{{ form.meter_reading }}
{% if form.meter_reading.errors %}
<div class="invalid-feedback">{{ form.meter_reading.errors.0 }}</div>
{% endif %}
</div>
<div class="col-md-3">
{{ form.meter_charge.label_tag }}
{{ form.meter_charge }}
{% if form.meter_charge.errors %}
<div class="invalid-feedback">{{ form.meter_charge.errors.0 }}</div>
{% endif %}
</div>
<div class="col-md-3">
{{ form.meter_charge_expiration_date.label_tag }}
<input type="text" id="id_meter_charge_expiration_date_display" class="form-control{% if form.meter_charge_expiration_date.errors %} is-invalid{% endif %}" placeholder="انتخاب تاریخ" {% if not user_is_installer %}disabled{% endif %} readonly value="{% if report and edit_mode and report.meter_charge_expiration_date %}{{ report.meter_charge_expiration_date|date:'Y/m/d' }}{% elif form.meter_charge_expiration_date.value %}{{ form.meter_charge_expiration_date.value|date:'Y/m/d' }}{% endif %}">
<input type="hidden" id="id_meter_charge_expiration_date" name="meter_charge_expiration_date" value="{% if report and edit_mode and report.meter_charge_expiration_date %}{{ report.meter_charge_expiration_date|date:'Y-m-d' }}{% elif form.meter_charge_expiration_date.value %}{{ form.meter_charge_expiration_date.value }}{% endif %}">
{% if form.meter_charge_expiration_date.errors %}
<div class="invalid-feedback">{{ form.meter_charge_expiration_date.errors.0 }}</div>
{% endif %}
</div>
<div class="col-md-3">
@ -432,14 +452,43 @@
<div class="invalid-feedback">{{ form.driving_force.errors.0 }}</div>
{% endif %}
</div>
<div class="col-md-3 d-flex align-items-end">
<div class="form-check">
{{ form.is_meter_suspicious }}
{{ form.is_meter_suspicious.label_tag }}
<div class="row mt-3">
<div class="col-md-3 d-flex align-items-end">
<div class="form-check">
{{ form.is_meter_suspicious }}
{{ form.is_meter_suspicious.label_tag }}
</div>
{% if form.is_meter_suspicious.errors %}
<div class="invalid-feedback">{{ form.is_meter_suspicious.errors.0 }}</div>
{% endif %}
</div>
<div class="col-md-3 d-flex align-items-end">
<div class="form-check">
{{ form.is_panel_sealed }}
{{ form.is_panel_sealed.label_tag }}
</div>
{% if form.is_panel_sealed.errors %}
<div class="invalid-feedback">{{ form.is_panel_sealed.errors.0 }}</div>
{% endif %}
</div>
<div class="col-md-3 d-flex align-items-end">
<div class="form-check">
{{ form.is_panel_cut }}
{{ form.is_panel_cut.label_tag }}
</div>
{% if form.is_panel_cut.errors %}
<div class="invalid-feedback">{{ form.is_panel_cut.errors.0 }}</div>
{% endif %}
</div>
<div class="col-md-3 d-flex align-items-end">
<div class="form-check">
{{ form.is_disconnection_done }}
{{ form.is_disconnection_done.label_tag }}
</div>
{% if form.is_disconnection_done.errors %}
<div class="invalid-feedback">{{ form.is_disconnection_done.errors.0 }}</div>
{% endif %}
</div>
{% if form.is_meter_suspicious.errors %}
<div class="invalid-feedback">{{ form.is_meter_suspicious.errors.0 }}</div>
{% endif %}
</div>
</div>
<div class="my-3">
@ -653,7 +702,7 @@
<script src="https://unpkg.com/persian-date@latest/dist/persian-date.min.js"></script>
<script src="https://unpkg.com/persian-datepicker@latest/dist/js/persian-datepicker.min.js"></script>
<script>
// Persian datepicker for visited_date (exact pattern like sample: display + altField)
// Persian datepicker helper for date fields (DRY)
(function(){
function convertPersianToEnglishNumbers(str) {
const persianNumbers = '۰۱۲۳۴۵۶۷۸۹';
@ -664,12 +713,12 @@
}).join('');
}
if (window.$ && $.fn.persianDatepicker && $('#id_visited_date_display').length) {
function initPersianDateField(displaySelector, hiddenSelector){
if (!(window.$ && $.fn.persianDatepicker)) return;
var $display = $(displaySelector);
var $hidden = $(hiddenSelector);
if (!$display.length || !$hidden.length) return;
try {
var $display = $('#id_visited_date_display');
var $hidden = $('#id_visited_date');
// Prefill from hidden Gregorian to visible Jalali
var initialGregorian = $hidden.val();
if (initialGregorian) {
try {
@ -678,10 +727,9 @@
} catch (e) {}
}
// Initialize datepicker with altField exactly like the sample
var picker = $display.persianDatepicker({
$display.persianDatepicker({
calendarType: 'persian',
altField: '#id_visited_date',
altField: hiddenSelector,
format: 'YYYY/MM/DD',
altFormat: 'YYYY-MM-DD',
observer: true,
@ -694,8 +742,12 @@
$hidden.val(g);
}
});
} catch (e) { console.error('Error initializing Persian Date Picker:', e); }
} catch (e) { console.error('Error initializing Persian Date Picker:', displaySelector, e); }
}
// Initialize both fields
initPersianDateField('#id_visited_date_display', '#id_visited_date');
initPersianDateField('#id_meter_charge_expiration_date_display', '#id_meter_charge_expiration_date');
})();
// Require date and show success toast on submit (persist across redirect)

View file

@ -23,7 +23,7 @@ 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

14
liara.json Normal file
View file

@ -0,0 +1,14 @@
{
"app": "meterplus",
"port": 80,
"team-id": "68822f40f04e5bc3027fc2b7",
"build": {
"location": "iran"
},
"disks": [
{
"name": "media",
"mountTo": "/usr/src/app/media"
}
]
}

View file

@ -143,12 +143,16 @@
<div class="col-12 col-md-4">
<p class="text-nowrap mb-2"><i class="bx bx-category bx-sm me-2"></i>نوع کنتور: {{ latest_report.get_meter_type_display }}</p>
</div>
{% endif %}
{% if latest_report.meter_size %}
{% if latest_report.meter_type == 'smart' %}
<div class="col-12 col-md-4">
<p class="text-nowrap mb-2"><i class="bx bx-chip bx-sm me-2"></i>مدل کنتور: {{ latest_report.get_meter_model_display|default:'-' }}</p>
</div>
{% else %}
<div class="col-12 col-md-4">
<p class="text-nowrap mb-2"><i class="bx bx-ruler bx-sm me-2"></i>سایز کنتور: {{ latest_report.meter_size }}</p>
</div>
{% endif %}
{% endif %}
{% if latest_report.water_meter_manufacturer %}
<div class="col-12 col-md-4">
<p class="text-nowrap mb-2"><i class="bx bx-buildings bx-sm me-2"></i>سازنده: {{ latest_report.water_meter_manufacturer.name }}</p>
@ -179,16 +183,35 @@
<p class="text-nowrap mb-2"><i class="bx bx-id-card bx-sm me-2"></i>شماره پروانه: {{ latest_report.exploitation_license_number }}</p>
</div>
{% endif %}
{% if latest_report.pre_calibration_flow_rate %}
{% if latest_report.flow_rate %}
<div class="col-12 col-md-4">
<p class="text-nowrap mb-2"><i class="bx bx-water bx-sm me-2"></i>دبی قبل از کالیبراسیون: {{ latest_report.pre_calibration_flow_rate }} لیتر/ثانیه</p>
<p class="text-nowrap mb-2"><i class="bx bx-water bx-sm me-2"></i>دبی: {{ latest_report.flow_rate }} لیتر/ثانیه</p>
</div>
{% endif %}
{% if latest_report.post_calibration_flow_rate %}
{% if latest_report.meter_reading %}
<div class="col-12 col-md-4">
<p class="text-nowrap mb-2"><i class="bx bx-tachometer bx-sm me-2"></i>دبی بعد از کالیبراسیون: {{ latest_report.post_calibration_flow_rate }} لیتر/ثانیه</p>
<p class="text-nowrap mb-2"><i class="bx bx-pen bx-sm me-2"></i>عدد کنتور: {{ latest_report.meter_reading }} متر مکعب</p>
</div>
{% endif %}
{% if latest_report.meter_charge %}
<div class="col-12 col-md-4">
<p class="text-nowrap mb-2"><i class="bx bx-battery bx-sm me-2"></i>شارژ کنتور: {{ latest_report.meter_charge }} متر مکعب</p>
</div>
{% endif %}
{% if latest_report.meter_charge_expiration_date %}
<div class="col-12 col-md-4">
<p class="text-nowrap mb-2"><i class="bx bx-calendar-exclamation bx-sm me-2"></i>پایان اعتبار شارژ: {{ latest_report.meter_charge_expiration_date|to_jalali }}</p>
</div>
{% endif %}
<div class="col-12 col-md-4">
<p class="text-nowrap mb-2"><i class="bx bx-lock-open-alt bx-sm me-2"></i>پلمپ تابلو: {{ latest_report.is_panel_sealed|yesno:'دارد,ندارد' }}</p>
</div>
<div class="col-12 col-md-4">
<p class="text-nowrap mb-2"><i class="bx bx-power-off bx-sm me-2"></i>تابلو قطع: {{ latest_report.is_panel_cut|yesno:'دارد,ندارد' }}</p>
</div>
<div class="col-12 col-md-4">
<p class="text-nowrap mb-2"><i class="bx bx-cut bx-sm me-2"></i>انجام عملیات قطع: {{ latest_report.is_disconnection_done|yesno:'دارد,ندارد' }}</p>
</div>
<div class="col-12 col-md-4">
<p class="text-nowrap mb-2"><i class="bx bx-map bx-sm me-2"></i>UTM X: {{ latest_report.utm_x|default:'-' }}</p>
</div>

View file

@ -21,7 +21,7 @@ def scope_instances_queryset(user, queryset=None):
if profile.has_role(UserRoles.BROKER):
return qs.filter(broker=profile.broker)
if profile.has_role(UserRoles.ACCOUNTANT) or profile.has_role(UserRoles.MANAGER) or profile.has_role(UserRoles.WATER_RESOURCE_MANAGER):
return qs.filter(broker__affairs__county=profile.county)
return qs.filter(broker__affairs__county__city=profile.county.city)
if profile.has_role(UserRoles.ADMIN):
return qs
# if profile.has_role(UserRoles.WATER_RESOURCE_MANAGER) or profile.has_role(UserRoles.HEADQUARTER):
@ -70,7 +70,7 @@ def scope_wells_queryset(user, queryset=None):
if profile.has_role(UserRoles.BROKER):
return qs.filter(broker=profile.broker)
if profile.has_role(UserRoles.ACCOUNTANT) or profile.has_role(UserRoles.MANAGER) or profile.has_role(UserRoles.WATER_RESOURCE_MANAGER):
return qs.filter(broker__affairs__county=profile.county)
return qs.filter(broker__affairs__county__city=profile.county.city)
if profile.has_role(UserRoles.INSTALLER):
# Wells that have instances assigned to this installer
from installations.models import InstallationAssignment
@ -103,7 +103,7 @@ def scope_customers_queryset(user, queryset=None):
if profile.has_role(UserRoles.BROKER):
return qs.filter(broker=profile.broker)
if profile.has_role(UserRoles.ACCOUNTANT) or profile.has_role(UserRoles.MANAGER) or profile.has_role(UserRoles.WATER_RESOURCE_MANAGER):
return qs.filter(county=profile.county)
return qs.filter(county__city=profile.county.city)
if profile.has_role(UserRoles.INSTALLER):
# Customers that are representatives of instances assigned to this installer
from installations.models import InstallationAssignment

View file

@ -45,7 +45,10 @@ def request_list(request):
status_q = (request.GET.get('status') or '').strip()
affairs_q = (request.GET.get('affairs') or '').strip()
broker_q = (request.GET.get('broker') or '').strip()
step_q = (request.GET.get('step') or '').strip()
if request.user.profile.roles.filter(slug='inst'):
step_q = (request.GET.get('step') or '6').strip()
else:
step_q = (request.GET.get('step') or '').strip()
if status_q:
instances = instances.filter(status=status_q)

15
requirements.txt Normal file
View file

@ -0,0 +1,15 @@
asgiref==3.9.1
dj-database-url==3.0.1
Django==5.2.5
django-extensions==4.1
django-jazzmin==3.0.1
django-simple-history==3.10.1
et_xmlfile==2.0.0
openpyxl==3.1.5
pillow==11.3.0
psycopg2==2.9.10
psycopg2-binary==2.9.10
python-decouple==3.8
sqlparse==0.5.3
typing_extensions==4.15.0
utm==0.8.1