first commit
This commit is contained in:
commit
b71ea45681
898 changed files with 138202 additions and 0 deletions
0
wells/__init__.py
Normal file
0
wells/__init__.py
Normal file
26
wells/admin.py
Normal file
26
wells/admin.py
Normal file
|
@ -0,0 +1,26 @@
|
|||
from django.contrib import admin
|
||||
from simple_history.admin import SimpleHistoryAdmin
|
||||
from .models import Well, WaterMeterManufacturer
|
||||
|
||||
# Register your models here.
|
||||
|
||||
@admin.register(WaterMeterManufacturer)
|
||||
class WaterMeterManufacturerAdmin(admin.ModelAdmin):
|
||||
list_display = ['name']
|
||||
search_fields = ['name']
|
||||
ordering = ['name']
|
||||
|
||||
@admin.register(Well)
|
||||
class WellAdmin(SimpleHistoryAdmin):
|
||||
list_display = ['water_subscription_number', 'representative', 'electricity_subscription_number', 'water_meter_manufacturer', 'county']
|
||||
list_filter = ['representative', 'water_meter_manufacturer', 'county', 'broker']
|
||||
search_fields = [
|
||||
'water_subscription_number',
|
||||
'electricity_subscription_number',
|
||||
'representative__username',
|
||||
'representative__first_name',
|
||||
'representative__last_name'
|
||||
]
|
||||
autocomplete_fields = ['representative', 'water_meter_manufacturer', 'affairs', 'county', 'broker']
|
||||
|
||||
history_list_display = ['water_subscription_number', 'representative']
|
7
wells/apps.py
Normal file
7
wells/apps.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class WellsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'wells'
|
||||
verbose_name='چاهها'
|
155
wells/forms.py
Normal file
155
wells/forms.py
Normal file
|
@ -0,0 +1,155 @@
|
|||
from django import forms
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.exceptions import ValidationError
|
||||
from .models import Well, WaterMeterManufacturer
|
||||
from locations.models import Affairs, County, Broker
|
||||
|
||||
|
||||
class WaterMeterManufacturerForm(forms.ModelForm):
|
||||
"""فرم برای افزودن شرکت سازنده کنتور آب جدید"""
|
||||
|
||||
class Meta:
|
||||
model = WaterMeterManufacturer
|
||||
fields = ['name']
|
||||
widgets = {
|
||||
'name': forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'نام شرکت سازنده'
|
||||
}),
|
||||
}
|
||||
|
||||
|
||||
class WellForm(forms.ModelForm):
|
||||
"""فرم برای ایجاد و ویرایش چاه"""
|
||||
|
||||
# فیلد جدید برای افزودن شرکت سازنده جدید
|
||||
new_manufacturer = forms.CharField(
|
||||
required=False,
|
||||
widget=forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'شرکت سازنده جدید',
|
||||
'style': 'display: none;' # پیشفرض مخفی
|
||||
}),
|
||||
label='شرکت سازنده جدید'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Well
|
||||
fields = [
|
||||
'representative',
|
||||
'water_subscription_number',
|
||||
'electricity_subscription_number',
|
||||
'water_meter_serial_number',
|
||||
'water_meter_old_serial_number',
|
||||
'water_meter_manufacturer',
|
||||
'utm_x',
|
||||
'utm_y',
|
||||
'utm_zone',
|
||||
'utm_hemisphere',
|
||||
'well_power',
|
||||
'reference_letter_number',
|
||||
'reference_letter_date',
|
||||
'representative_letter_file',
|
||||
# affairs, county, broker will be auto-filled from user profile
|
||||
]
|
||||
|
||||
widgets = {
|
||||
'representative': forms.Select(attrs={
|
||||
'class': 'form-select',
|
||||
}),
|
||||
'water_subscription_number': forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'شماره اشتراک آب',
|
||||
'required': True
|
||||
}),
|
||||
'electricity_subscription_number': forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'شماره اشتراک برق'
|
||||
}),
|
||||
'water_meter_serial_number': forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'سریال کنتور آب'
|
||||
}),
|
||||
'water_meter_old_serial_number': forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'سریال کنتور قدیمی آب'
|
||||
}),
|
||||
'water_meter_manufacturer': forms.Select(attrs={
|
||||
'class': 'form-select',
|
||||
'id': 'id_water_meter_manufacturer'
|
||||
}),
|
||||
'utm_x': forms.NumberInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'X UTM',
|
||||
'step': '0.000001'
|
||||
}),
|
||||
'utm_y': forms.NumberInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'Y UTM',
|
||||
'step': '0.000001'
|
||||
}),
|
||||
'utm_zone': forms.NumberInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'زون UTM (پیشفرض: 40)',
|
||||
'min': '1',
|
||||
'max': '60'
|
||||
}),
|
||||
'utm_hemisphere': forms.Select(attrs={
|
||||
'class': 'form-select'
|
||||
}),
|
||||
'well_power': forms.NumberInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'قدرت چاه',
|
||||
'min': '0'
|
||||
}),
|
||||
'reference_letter_number': forms.TextInput(attrs={
|
||||
'class': 'form-control',
|
||||
'placeholder': 'شماره معرفی نامه'
|
||||
}),
|
||||
'reference_letter_date': forms.DateInput(attrs={
|
||||
'class': 'form-control',
|
||||
'type': 'date'
|
||||
}),
|
||||
'representative_letter_file': forms.FileInput(attrs={
|
||||
'class': 'form-control',
|
||||
'accept': '.pdf,.doc,.docx,.jpg,.jpeg,.png'
|
||||
}),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# تنظیم querysetها
|
||||
self.fields['representative'].queryset = get_user_model().objects.all()
|
||||
self.fields['water_meter_manufacturer'].queryset = WaterMeterManufacturer.objects.all()
|
||||
|
||||
# اضافه کردن گزینه خالی
|
||||
self.fields['representative'].empty_label = "انتخاب نماینده"
|
||||
self.fields['water_meter_manufacturer'].empty_label = "انتخاب شرکت سازنده"
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
new_manufacturer = cleaned_data.get('new_manufacturer')
|
||||
manufacturer = cleaned_data.get('water_meter_manufacturer')
|
||||
|
||||
# اگر شرکت سازنده جدید وارد شده، آن را ایجاد کن
|
||||
if new_manufacturer and new_manufacturer.strip():
|
||||
try:
|
||||
manufacturer_obj, created = WaterMeterManufacturer.objects.get_or_create(
|
||||
name=new_manufacturer.strip()
|
||||
)
|
||||
cleaned_data['water_meter_manufacturer'] = manufacturer_obj
|
||||
except Exception as e:
|
||||
raise forms.ValidationError(f'خطا در ایجاد شرکت سازنده: {str(e)}')
|
||||
|
||||
return cleaned_data
|
||||
|
||||
def clean_water_subscription_number(self):
|
||||
water_subscription_number = self.cleaned_data.get('water_subscription_number', '')
|
||||
|
||||
# چک کردن تکراری نبودن شماره اشتراک آب (فقط برای ایجاد)
|
||||
if water_subscription_number and not self.instance.pk:
|
||||
if Well.objects.filter(water_subscription_number=water_subscription_number).exists():
|
||||
raise forms.ValidationError('شماره اشتراک آب قبلاً ثبت شده است.')
|
||||
|
||||
return water_subscription_number
|
60
wells/migrations/0001_initial.py
Normal file
60
wells/migrations/0001_initial.py
Normal file
|
@ -0,0 +1,60 @@
|
|||
# Generated by Django 5.2.4 on 2025-08-07 09:08
|
||||
|
||||
import django.db.models.deletion
|
||||
import simple_history.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='HistoricalWell',
|
||||
fields=[
|
||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
||||
('created', models.DateTimeField(blank=True, editable=False, verbose_name='تاریخ ایجاد')),
|
||||
('updated', models.DateTimeField(blank=True, editable=False, verbose_name='تاریخ بروزرسانی')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='فعال')),
|
||||
('is_deleted', models.BooleanField(default=False, verbose_name='حذف شده')),
|
||||
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
||||
('slug', models.SlugField(max_length=100, verbose_name='اسلاگ')),
|
||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||
('history_date', models.DateTimeField(db_index=True)),
|
||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
|
||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
||||
('representative', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL, verbose_name='نماینده')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'historical چاه',
|
||||
'verbose_name_plural': 'historical چاه\u200cها',
|
||||
'ordering': ('-history_date', '-history_id'),
|
||||
'get_latest_by': ('history_date', 'history_id'),
|
||||
},
|
||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Well',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ ایجاد')),
|
||||
('updated', models.DateTimeField(auto_now=True, verbose_name='تاریخ بروزرسانی')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='فعال')),
|
||||
('is_deleted', models.BooleanField(default=False, verbose_name='حذف شده')),
|
||||
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
||||
('slug', models.SlugField(max_length=100, unique=True, verbose_name='اسلاگ')),
|
||||
('representative', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='wells', to=settings.AUTH_USER_MODEL, verbose_name='نماینده')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'چاه',
|
||||
'verbose_name_plural': 'چاه\u200cها',
|
||||
},
|
||||
),
|
||||
]
|
|
@ -0,0 +1,202 @@
|
|||
# Generated by Django 5.2.4 on 2025-08-07 14:29
|
||||
|
||||
import datetime
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('locations', '0001_initial'),
|
||||
('wells', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='WaterMeterManufacturer',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('created', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ ایجاد')),
|
||||
('updated', models.DateTimeField(auto_now=True, verbose_name='تاریخ بروزرسانی')),
|
||||
('is_active', models.BooleanField(default=True, verbose_name='فعال')),
|
||||
('is_deleted', models.BooleanField(default=False, verbose_name='حذف شده')),
|
||||
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
||||
('name', models.CharField(blank=True, max_length=20, null=True, verbose_name='نام شرکت سازنده کنتور آب')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'شرکت سازنده کنتور آب',
|
||||
'verbose_name_plural': 'شرکت\u200cهای سازنده کنتور آب',
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='affairs',
|
||||
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='locations.affairs', verbose_name='امور'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='broker',
|
||||
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='locations.broker', verbose_name='کارگزار'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='county',
|
||||
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='locations.county', verbose_name='شهرستان'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='electricity_subscription_number',
|
||||
field=models.CharField(blank=True, db_index=True, max_length=20, null=True, verbose_name='شماره اشتراک برق'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='reference_letter_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='تاریخ معرفی نامه'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='reference_letter_number',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='شماره معرفی نامه'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='representative_letter_file',
|
||||
field=models.TextField(blank=True, max_length=100, null=True, verbose_name='نامه نمایندگی'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='utm_hemisphere',
|
||||
field=models.CharField(blank=True, choices=[('N', 'شمال'), ('S', 'جنوب')], default='N', max_length=1, null=True, verbose_name='نیمکره UTM'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='utm_x',
|
||||
field=models.DecimalField(blank=True, decimal_places=6, max_digits=10, null=True, verbose_name='X UTM'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='utm_y',
|
||||
field=models.DecimalField(blank=True, decimal_places=6, max_digits=10, null=True, verbose_name='Y UTM'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='utm_zone',
|
||||
field=models.PositiveIntegerField(blank=True, default=40, null=True, verbose_name='زون UTM'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='water_meter_old_serial_number',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='سریال کنتور قدیمی آب'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='water_meter_serial_number',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='سریال کنتور آب'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='water_subscription_number',
|
||||
field=models.CharField(db_index=True, default=datetime.datetime(2025, 8, 7, 14, 29, 15, 340093, tzinfo=datetime.timezone.utc), max_length=20, verbose_name='شماره اشتراک آب'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='well_power',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='قدرت چاه'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='affairs',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='locations.affairs', verbose_name='امور'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='broker',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='locations.broker', verbose_name='کارگزار'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='county',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='locations.county', verbose_name='شهرستان'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='electricity_subscription_number',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, unique=True, verbose_name='شماره اشتراک برق'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='reference_letter_date',
|
||||
field=models.DateField(blank=True, null=True, verbose_name='تاریخ معرفی نامه'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='reference_letter_number',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='شماره معرفی نامه'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='representative_letter_file',
|
||||
field=models.FileField(blank=True, null=True, upload_to='representative_letters/', verbose_name='نامه نمایندگی'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='utm_hemisphere',
|
||||
field=models.CharField(blank=True, choices=[('N', 'شمال'), ('S', 'جنوب')], default='N', max_length=1, null=True, verbose_name='نیمکره UTM'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='utm_x',
|
||||
field=models.DecimalField(blank=True, decimal_places=6, max_digits=10, null=True, verbose_name='X UTM'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='utm_y',
|
||||
field=models.DecimalField(blank=True, decimal_places=6, max_digits=10, null=True, verbose_name='Y UTM'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='utm_zone',
|
||||
field=models.PositiveIntegerField(blank=True, default=40, null=True, verbose_name='زون UTM'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='water_meter_old_serial_number',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='سریال کنتور قدیمی آب'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='water_meter_serial_number',
|
||||
field=models.CharField(blank=True, max_length=20, null=True, verbose_name='سریال کنتور آب'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='water_subscription_number',
|
||||
field=models.CharField(default=django.utils.timezone.now, max_length=20, unique=True, verbose_name='شماره اشتراک آب'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='well_power',
|
||||
field=models.PositiveIntegerField(blank=True, null=True, verbose_name='قدرت چاه'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='well',
|
||||
name='representative',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='wells', to=settings.AUTH_USER_MODEL, verbose_name='نماینده'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='historicalwell',
|
||||
name='water_meter_manufacturer',
|
||||
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='wells.watermetermanufacturer', verbose_name='شرکت سازنده کنتور آب'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='well',
|
||||
name='water_meter_manufacturer',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='wells.watermetermanufacturer', verbose_name='شرکت سازنده کنتور آب'),
|
||||
),
|
||||
]
|
0
wells/migrations/__init__.py
Normal file
0
wells/migrations/__init__.py
Normal file
171
wells/models.py
Normal file
171
wells/models.py
Normal file
|
@ -0,0 +1,171 @@
|
|||
from django.db import models
|
||||
from common.models import SluggedModel, BaseModel
|
||||
from django.contrib.auth import get_user_model
|
||||
from simple_history.models import HistoricalRecords
|
||||
from locations.models import Affairs, Broker, County
|
||||
import utm
|
||||
from uuid import uuid4
|
||||
import os
|
||||
|
||||
class WaterMeterManufacturer(BaseModel):
|
||||
name = models.CharField(
|
||||
max_length=20,
|
||||
verbose_name="نام شرکت سازنده کنتور آب",
|
||||
null=True,
|
||||
blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class Meta:
|
||||
verbose_name = "شرکت سازنده کنتور آب"
|
||||
verbose_name_plural = "شرکتهای سازنده کنتور آب"
|
||||
|
||||
|
||||
class Well(SluggedModel):
|
||||
def path_and_rename(self, filename):
|
||||
upload_to = "representative_letters"
|
||||
ext = filename.split(".")[-1]
|
||||
# create unique filename
|
||||
filename = f"{uuid4().hex}.{ext}"
|
||||
# return the whole path to the file
|
||||
return os.path.join(upload_to, filename)
|
||||
|
||||
Hemisphere_Choices = [
|
||||
('N', 'شمال'),
|
||||
('S', 'جنوب'),
|
||||
]
|
||||
|
||||
representative = models.ForeignKey(
|
||||
get_user_model(),
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name="نماینده",
|
||||
related_name="wells",
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
|
||||
water_subscription_number = models.CharField(
|
||||
max_length=20,
|
||||
verbose_name="شماره اشتراک آب",
|
||||
unique=True
|
||||
)
|
||||
electricity_subscription_number = models.CharField(
|
||||
max_length=20,
|
||||
verbose_name="شماره اشتراک برق",
|
||||
null=True,
|
||||
blank=True,
|
||||
unique=True
|
||||
)
|
||||
|
||||
water_meter_serial_number = models.CharField(
|
||||
max_length=20,
|
||||
verbose_name="سریال کنتور آب",
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
water_meter_old_serial_number = models.CharField(
|
||||
max_length=20,
|
||||
verbose_name="سریال کنتور قدیمی آب",
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
water_meter_manufacturer = models.ForeignKey(
|
||||
WaterMeterManufacturer,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name="شرکت سازنده کنتور آب",
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
|
||||
utm_x = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=6,
|
||||
verbose_name="X UTM",
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
utm_y = models.DecimalField(
|
||||
max_digits=10,
|
||||
decimal_places=6,
|
||||
verbose_name="Y UTM",
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
utm_zone = models.PositiveIntegerField(
|
||||
verbose_name="زون UTM",
|
||||
null=True,
|
||||
blank=True,
|
||||
default=40
|
||||
)
|
||||
utm_hemisphere = models.CharField(
|
||||
max_length=1,
|
||||
verbose_name="نیمکره UTM",
|
||||
null=True,
|
||||
blank=True,
|
||||
choices=Hemisphere_Choices,
|
||||
default='N'
|
||||
)
|
||||
|
||||
well_power = models.PositiveIntegerField(
|
||||
verbose_name="قدرت چاه",
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
|
||||
reference_letter_number = models.CharField(
|
||||
max_length=20,
|
||||
verbose_name="شماره معرفی نامه",
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
reference_letter_date = models.DateField(
|
||||
verbose_name="تاریخ معرفی نامه",
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
representative_letter_file = models.FileField(
|
||||
upload_to=path_and_rename,
|
||||
verbose_name="نامه نمایندگی",
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
|
||||
|
||||
affairs = models.ForeignKey(
|
||||
Affairs,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name="امور",
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
county = models.ForeignKey(
|
||||
County,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name="شهرستان",
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
broker = models.ForeignKey(
|
||||
Broker,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name="کارگزار",
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
|
||||
|
||||
history = HistoricalRecords()
|
||||
|
||||
class Meta:
|
||||
verbose_name = "چاه"
|
||||
verbose_name_plural = "چاهها"
|
||||
|
||||
def __str__(self):
|
||||
return self.water_subscription_number
|
||||
|
||||
def lat_long(self):
|
||||
if self.utm_x and self.utm_y and self.utm_zone and self.utm_hemisphere:
|
||||
northern = False if self.utm_hemisphere == 'S' else True
|
||||
return utm.to_latlon(self.utm_x, self.utm_y, self.utm_zone, northern=northern)
|
||||
return None
|
707
wells/templates/wells/well_list.html
Normal file
707
wells/templates/wells/well_list.html
Normal file
|
@ -0,0 +1,707 @@
|
|||
{% extends '_base.html' %}
|
||||
{% load static %}
|
||||
|
||||
{% block sidebar %}
|
||||
{% include 'sidebars/admin.html' %}
|
||||
{% endblock sidebar %}
|
||||
|
||||
{% block navbar %}
|
||||
{% include 'navbars/admin.html' %}
|
||||
{% endblock navbar %}
|
||||
|
||||
{% block title %}مدیریت چاهها{% endblock %}
|
||||
|
||||
{% block style %}
|
||||
<!-- DataTables CSS -->
|
||||
<link rel="stylesheet" href="{% static 'assets/vendor/libs/datatables-bs5/datatables.bootstrap5.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'assets/vendor/libs/datatables-responsive-bs5/responsive.bootstrap5.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'assets/vendor/libs/datatables-buttons-bs5/buttons.bootstrap5.css' %}">
|
||||
|
||||
<!-- Persian Date Picker CSS -->
|
||||
<link rel="stylesheet" href="https://unpkg.com/persian-datepicker@latest/dist/css/persian-datepicker.min.css">
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<!-- Include Toasts -->
|
||||
{% include '_toasts.html' %}
|
||||
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
<div class="row py-3 mb-4 card-header flex-column flex-md-row pb-0">
|
||||
<div class="d-md-flex justify-content-between align-items-center dt-layout-start col-md-auto me-auto mt-0">
|
||||
<h5 class="card-title mb-0 text-md-start text-center fw-bold">لیست چاهها</h5>
|
||||
</div>
|
||||
<div class="d-md-flex justify-content-between align-items-center dt-layout-end col-md-auto ms-auto mt-0">
|
||||
<div class="dt-buttons btn-group flex-wrap mb-0">
|
||||
<div class="btn-group">
|
||||
<button class="btn buttons-collection btn-label-primary dropdown-toggle me-4" tabindex="0" aria-controls="DataTables_Table_0" type="button" aria-haspopup="dialog" aria-expanded="false">
|
||||
<span>
|
||||
<span class="d-flex align-items-center gap-2">
|
||||
<i class="icon-base bx bx-export me-sm-1"></i>
|
||||
<span class="d-none d-sm-inline-block">خروجی</span>
|
||||
</span>
|
||||
</span>
|
||||
</button>
|
||||
<button class="btn btn-primary" onclick="prepareAddForm()">
|
||||
<i class="bx bx-plus me-1"></i>
|
||||
افزودن چاه جدید
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-datatable table-responsive">
|
||||
<table class="datatables-basic table border-top" id="wells-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ردیف</th>
|
||||
<th>شماره اشتراک آب</th>
|
||||
<th>نماینده</th>
|
||||
<th>شماره اشتراک برق</th>
|
||||
<th>شرکت سازنده کنتور</th>
|
||||
<th>مختصات UTM</th>
|
||||
<th>نامه نمایندگی</th>
|
||||
<th>عملیات</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for well in wells %}
|
||||
<tr>
|
||||
<td>{{ forloop.counter }}</td>
|
||||
<td>
|
||||
<div class="d-flex justify-content-start align-items-center well-name">
|
||||
<div class="d-flex flex-column">
|
||||
<h6 class="mb-0">{{ well.water_subscription_number }}</h6>
|
||||
{% if well.water_meter_serial_number %}
|
||||
<small class="text-muted">سریال: {{ well.water_meter_serial_number }}</small>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{% if well.representative %}
|
||||
<div class="d-flex justify-content-start align-items-center">
|
||||
<div class="avatar-wrapper">
|
||||
<div class="avatar me-2">
|
||||
<span class="avatar-initial rounded-circle bg-label-primary">
|
||||
{% if well.representative.profile.pic and well.representative.profile.pic.url %}
|
||||
<img src="{{ well.representative.profile.pic.url }}" alt="Avatar" class="rounded-circle" style="width: 40px; height: 40px; object-fit: cover;">
|
||||
{% else %}
|
||||
{% if well.representative.first_name and well.representative.last_name %}
|
||||
{{ well.representative.first_name|first|upper }}{{ well.representative.last_name|first|upper }}
|
||||
{% elif well.representative.first_name %}
|
||||
{{ well.representative.first_name|first|upper }}
|
||||
{% elif well.representative.last_name %}
|
||||
{{ well.representative.last_name|first|upper }}
|
||||
{% else %}
|
||||
{{ well.representative.username|first|upper }}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="emp_name text-truncate text-heading">{{ well.representative.get_full_name|default:well.representative.username }}</span>
|
||||
<small class="emp_post text-truncate">{{ well.representative.username }}</small>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted">-</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ well.electricity_subscription_number|default:"-" }}</td>
|
||||
<td>{{ well.water_meter_manufacturer|default:"-" }}</td>
|
||||
<td>
|
||||
{% if well.utm_x and well.utm_y and well.utm_zone and well.utm_hemisphere %}
|
||||
<div class="d-flex flex-column">
|
||||
<small class="text-muted">X: {{ well.utm_x }}</small>
|
||||
<small class="text-muted">Y: {{ well.utm_y }}</small>
|
||||
<small class="text-muted">زون: {{ well.utm_zone }} {{ well.utm_hemisphere }}</small>
|
||||
</div>
|
||||
{% else %}
|
||||
<span class="text-muted">-</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if well.representative_letter_file %}
|
||||
<a href="{{ well.representative_letter_file.url }}" target="_blank" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bx bx-file me-1"></i>مشاهده
|
||||
</a>
|
||||
{% else %}
|
||||
<span class="text-muted">-</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-inline-block">
|
||||
<a href="javascript:;" class="btn btn-icon dropdown-toggle hide-arrow" data-bs-toggle="dropdown">
|
||||
<i class="icon-base bx bx-dots-vertical-rounded"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end m-0">
|
||||
<li>
|
||||
<a href="#" class="dropdown-item" data-well-id="{{ well.id }}" onclick="viewWell(this.getAttribute('data-well-id'))">
|
||||
<i class="bx bx-show me-1"></i>مشاهده جزئیات
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" class="dropdown-item" data-well-id="{{ well.id }}" onclick="editWell(this.getAttribute('data-well-id'))">
|
||||
<i class="bx bx-edit me-1"></i>ویرایش
|
||||
</a>
|
||||
</li>
|
||||
<div class="dropdown-divider"></div>
|
||||
<li>
|
||||
<a href="#" class="dropdown-item text-danger" data-well-id="{{ well.id }}" data-well-name="{{ well.water_subscription_number }}" onclick="deleteWell(this.getAttribute('data-well-id'), this.getAttribute('data-well-name'))">
|
||||
<i class="bx bx-trash me-1"></i>حذف
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<a href="#" class="btn btn-icon item-edit" data-well-id="{{ well.id }}" onclick="editWell(this.getAttribute('data-well-id'))">
|
||||
<i class="icon-base bx bx-edit icon-sm"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% empty %}
|
||||
<tr>
|
||||
<td colspan="7" class="text-center py-4">
|
||||
<div class="text-muted">
|
||||
<i class="ti ti-database-off ti-lg mb-2"></i>
|
||||
<p>چاهی یافت نشد</p>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- فرم افزودن/ویرایش چاه -->
|
||||
<div class="offcanvas offcanvas-end" tabindex="-1" id="add-new-record" aria-labelledby="add-new-record">
|
||||
<div class="offcanvas-header border-bottom">
|
||||
<h5 id="exampleModalLabel" class="offcanvas-title">افزودن چاه جدید</h5>
|
||||
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="offcanvas-body flex-grow-1">
|
||||
<form class="add-new-record pt-0 row g-3" id="form-add-new-record" method="post" enctype="multipart/form-data">
|
||||
{% csrf_token %}
|
||||
|
||||
<!-- فیلد مخفی برای ID -->
|
||||
<input type="hidden" id="well-id" name="well_id" value="">
|
||||
|
||||
<!-- نماینده -->
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label fw-bold" for="{{ form.representative.id_for_label }}">{{ form.representative.label }}</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<span class="input-group-text"><i class="bx bx-user"></i></span>
|
||||
{{ form.representative }}
|
||||
</div>
|
||||
{% if form.representative.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.representative.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- معرفی نامه -->
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label fw-bold" for="id_reference_letter_number">شماره معرفی نامه</label>
|
||||
{{ form.reference_letter_number }}
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label fw-bold" for="id_reference_letter_date">تاریخ معرفی نامه</label>
|
||||
<input type="text" class="form-control" id="id_reference_letter_date" name="reference_letter_date" placeholder="انتخاب تاریخ" readonly>
|
||||
</div>
|
||||
|
||||
<!-- فایل نامه نمایندگی -->
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label fw-bold" for="id_representative_letter_file">نامه نمایندگی</label>
|
||||
{{ form.representative_letter_file }}
|
||||
<!-- نمایش فایل موجود -->
|
||||
<div id="current-file-display" style="display: none; margin-top: 10px;">
|
||||
<div class="alert alert-info d-flex align-items-center justify-content-between">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="bx bx-file me-2"></i>
|
||||
<span id="current-file-name" class="text-truncate" style="max-width: 200px;" title=""></span>
|
||||
</div>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger" onclick="removeCurrentFile()">
|
||||
<i class="bx bx-trash me-1"></i>حذف
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" id="remove-file" name="remove_file" value="false">
|
||||
</div>
|
||||
|
||||
<!-- شماره اشتراک آب -->
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label fw-bold" for="{{ form.water_subscription_number.id_for_label }}">{{ form.water_subscription_number.label }}</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<span class="input-group-text"><i class="bx bx-droplet"></i></span>
|
||||
{{ form.water_subscription_number }}
|
||||
</div>
|
||||
{% if form.water_subscription_number.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.water_subscription_number.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- شماره اشتراک برق -->
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label fw-bold" for="{{ form.electricity_subscription_number.id_for_label }}">{{ form.electricity_subscription_number.label }}</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<span class="input-group-text"><i class='bx bx-bolt-circle'></i></span>
|
||||
{{ form.electricity_subscription_number }}
|
||||
</div>
|
||||
{% if form.electricity_subscription_number.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.electricity_subscription_number.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- سریال کنتور آب -->
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label fw-bold" for="{{ form.water_meter_serial_number.id_for_label }}">{{ form.water_meter_serial_number.label }}</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<span class="input-group-text"><i class="bx bx-barcode"></i></span>
|
||||
{{ form.water_meter_serial_number }}
|
||||
</div>
|
||||
{% if form.water_meter_serial_number.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.water_meter_serial_number.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- سریال کنتور قدیمی آب -->
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label fw-bold" for="{{ form.water_meter_old_serial_number.id_for_label }}">{{ form.water_meter_old_serial_number.label }}</label>
|
||||
<div class="input-group input-group-merge">
|
||||
<span class="input-group-text"><i class="bx bx-barcode"></i></span>
|
||||
{{ form.water_meter_old_serial_number }}
|
||||
</div>
|
||||
{% if form.water_meter_old_serial_number.errors %}
|
||||
<div class="invalid-feedback d-block">{{ form.water_meter_old_serial_number.errors.0 }}</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<!-- شرکت سازنده کنتور آب با دکمه افزودن -->
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label fw-bold" for="id_water_meter_manufacturer">شرکت سازنده کنتور آب</label>
|
||||
<div class="input-group">
|
||||
{{ form.water_meter_manufacturer }}
|
||||
<button class="btn btn-outline-primary" type="button" onclick="toggleNewManufacturer()">
|
||||
<i class="icon-base bx bx-plus"></i>
|
||||
</button>
|
||||
</div>
|
||||
<!-- فیلد افزودن شرکت جدید -->
|
||||
<div id="new-manufacturer-field" style="display: none; margin-top: 10px;">
|
||||
{{ form.new_manufacturer }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- مختصات UTM -->
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label fw-bold" for="id_utm_x">X UTM</label>
|
||||
{{ form.utm_x }}
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label fw-bold" for="id_utm_y">Y UTM</label>
|
||||
{{ form.utm_y }}
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label fw-bold" for="id_utm_zone">زون UTM</label>
|
||||
{{ form.utm_zone }}
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<label class="form-label fw-bold" for="id_utm_hemisphere">نیمکره UTM</label>
|
||||
{{ form.utm_hemisphere }}
|
||||
</div>
|
||||
|
||||
<!-- قدرت چاه -->
|
||||
<div class="col-sm-12">
|
||||
<label class="form-label fw-bold" for="id_well_power">قدرت چاه</label>
|
||||
{{ form.well_power }}
|
||||
</div>
|
||||
|
||||
<!-- دکمههای عملیات -->
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" class="btn btn-primary data-submit me-sm-4 me-1">ذخیره</button>
|
||||
<button type="reset" class="btn btn-outline-secondary" data-bs-dismiss="offcanvas">انصراف</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Confirmation Modal -->
|
||||
<div class="modal fade" id="deleteConfirmModal" tabindex="-1" aria-labelledby="deleteConfirmModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="deleteConfirmModalLabel">تایید حذف</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p id="deleteConfirmText">آیا از حذف این چاه اطمینان دارید؟</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">انصراف</button>
|
||||
<button type="button" class="btn btn-danger" id="confirmDeleteBtn">حذف</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block script %}
|
||||
<!-- DataTables JS -->
|
||||
<script src="{% static 'assets/vendor/libs/datatables-bs5/datatables-bootstrap5.js' %}"></script>
|
||||
<!-- Persian DataTable Language -->
|
||||
<script src="{% static 'assets/js/persian-datatable.js' %}"></script>
|
||||
|
||||
<!-- Persian Date Picker JS -->
|
||||
<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>
|
||||
// متغیرهای جهانی
|
||||
let currentWellId = null;
|
||||
let isEditMode = false;
|
||||
|
||||
$(document).ready(function() {
|
||||
// Initialize DataTable
|
||||
$('#wells-table').DataTable({
|
||||
pageLength: 10,
|
||||
lengthMenu: [[10, 25, 50, -1], [10, 25, 50, "همه"]],
|
||||
order: [[0, 'asc']],
|
||||
responsive: true,
|
||||
});
|
||||
|
||||
// Initialize Persian Date Picker with delay to ensure libraries are loaded
|
||||
setTimeout(function() {
|
||||
if ($.fn.persianDatepicker) {
|
||||
$('#id_reference_letter_date').persianDatepicker({
|
||||
format: 'YYYY/MM/DD',
|
||||
initialValue: false,
|
||||
autoClose: true,
|
||||
persianDigit: false,
|
||||
observer: true,
|
||||
calendar: {
|
||||
persian: {
|
||||
locale: 'fa'
|
||||
}
|
||||
},
|
||||
onSelect: function(unix) {
|
||||
// تبدیل تاریخ شمسی به میلادی برای ارسال به سرور
|
||||
const gregorianDate = new Date(unix);
|
||||
const year = gregorianDate.getFullYear();
|
||||
const month = String(gregorianDate.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(gregorianDate.getDate()).padStart(2, '0');
|
||||
const gregorianDateString = `${year}-${month}-${day}`;
|
||||
|
||||
// نمایش تاریخ شمسی در فیلد
|
||||
if (window.persianDate) {
|
||||
const persianDate = new window.persianDate(unix);
|
||||
const persianDateString = persianDate.format('YYYY/MM/DD');
|
||||
$('#id_reference_letter_date').val(persianDateString);
|
||||
} else {
|
||||
// اگر persianDate در دسترس نبود، تاریخ میلادی را نمایش بده
|
||||
$('#id_reference_letter_date').val(gregorianDateString);
|
||||
}
|
||||
|
||||
// ذخیره تاریخ میلادی در فیلد مخفی برای ارسال به سرور
|
||||
$('#id_reference_letter_date').attr('data-gregorian', gregorianDateString);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 100);
|
||||
|
||||
// Handle form submission
|
||||
$('#form-add-new-record').on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const form = this;
|
||||
const formData = new FormData(form);
|
||||
const wellId = $('#well-id').val();
|
||||
|
||||
// تبدیل تاریخ شمسی به میلادی برای ارسال
|
||||
const persianDateValue = $('#id_reference_letter_date').val();
|
||||
const gregorianDateValue = $('#id_reference_letter_date').attr('data-gregorian');
|
||||
if (persianDateValue && gregorianDateValue) {
|
||||
formData.set('reference_letter_date', gregorianDateValue);
|
||||
}
|
||||
|
||||
// Determine URL based on whether we're editing or adding
|
||||
const url = wellId ? '{% url "wells:edit_well_ajax" 0 %}'.replace('0', wellId) : '{% url "wells:add_well_ajax" %}';
|
||||
|
||||
// Show loading state
|
||||
const submitBtn = $(form).find('button[type="submit"]');
|
||||
const originalText = submitBtn.text();
|
||||
submitBtn.prop('disabled', true).text('در حال ذخیره...');
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: 'POST',
|
||||
data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
// Show success message
|
||||
showToast(response.message, 'success');
|
||||
|
||||
// Close offcanvas and reset form
|
||||
$('#add-new-record').offcanvas('hide');
|
||||
form.reset();
|
||||
|
||||
// Reload page to show new well
|
||||
setTimeout(function() {
|
||||
location.reload();
|
||||
}, 1500);
|
||||
} else {
|
||||
// Show error message
|
||||
showToast(response.message, 'danger');
|
||||
|
||||
// Show form errors if any
|
||||
if (response.errors) {
|
||||
Object.keys(response.errors).forEach(function(field) {
|
||||
const errorMsg = response.errors[field][0];
|
||||
const fieldElement = $('[name="' + field + '"]');
|
||||
fieldElement.addClass('is-invalid');
|
||||
fieldElement.siblings('.invalid-feedback').remove();
|
||||
fieldElement.after('<div class="invalid-feedback d-block">' + errorMsg + '</div>');
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
showToast('خطا در ارتباط با سرور', 'danger');
|
||||
},
|
||||
complete: function() {
|
||||
// Reset button state
|
||||
submitBtn.prop('disabled', false).text(originalText);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Reset form when offcanvas is hidden
|
||||
$('#add-new-record').on('hidden.bs.offcanvas', function() {
|
||||
const form = $('#form-add-new-record')[0];
|
||||
form.reset();
|
||||
|
||||
// Reset form for adding new well
|
||||
currentWellId = null;
|
||||
isEditMode = false;
|
||||
$('#well-id').val('');
|
||||
$('#exampleModalLabel').text('افزودن چاه جدید');
|
||||
$('.data-submit').text('ذخیره');
|
||||
|
||||
// Hide new manufacturer field
|
||||
document.getElementById('new-manufacturer-field').style.display = 'none';
|
||||
document.getElementById('id_new_manufacturer').style.display = 'none';
|
||||
|
||||
// Reset file display
|
||||
document.getElementById('current-file-display').style.display = 'none';
|
||||
document.getElementById('id_representative_letter_file').style.display = 'block';
|
||||
document.getElementById('remove-file').value = 'false';
|
||||
|
||||
// Reset date picker
|
||||
$('#id_reference_letter_date').val('');
|
||||
$('#id_reference_letter_date').removeAttr('data-gregorian');
|
||||
|
||||
// Clear validation errors
|
||||
$('.is-invalid').removeClass('is-invalid');
|
||||
$('.invalid-feedback').remove();
|
||||
});
|
||||
|
||||
// Clear validation errors when user starts typing
|
||||
$('input, textarea, select').on('input change', function() {
|
||||
$(this).removeClass('is-invalid');
|
||||
$(this).siblings('.invalid-feedback').remove();
|
||||
});
|
||||
|
||||
// مدیریت انتخاب فایل جدید
|
||||
$('#id_representative_letter_file').on('change', function() {
|
||||
if (this.files && this.files.length > 0) {
|
||||
// اگر فایل جدید انتخاب شده، حذف فایل قبلی را لغو کن
|
||||
document.getElementById('remove-file').value = 'false';
|
||||
// مخفی کردن نمایش فایل موجود
|
||||
document.getElementById('current-file-display').style.display = 'none';
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// toggle برای فیلد شرکت سازنده جدید
|
||||
function toggleNewManufacturer() {
|
||||
const newField = document.getElementById('new-manufacturer-field');
|
||||
const newInput = document.getElementById('id_new_manufacturer');
|
||||
|
||||
if (newField.style.display === 'none') {
|
||||
newField.style.display = 'block';
|
||||
newInput.style.display = 'block';
|
||||
newInput.focus();
|
||||
} else {
|
||||
newField.style.display = 'none';
|
||||
newInput.style.display = 'none';
|
||||
newInput.value = '';
|
||||
}
|
||||
}
|
||||
|
||||
// Well functions
|
||||
function viewWell(id) {
|
||||
// Implement view functionality
|
||||
showToast('قابلیت مشاهده جزئیات به زودی اضافه خواهد شد', 'info');
|
||||
}
|
||||
|
||||
// حذف فایل موجود
|
||||
function removeCurrentFile() {
|
||||
document.getElementById('current-file-display').style.display = 'none';
|
||||
document.getElementById('remove-file').value = 'true';
|
||||
document.getElementById('id_representative_letter_file').style.display = 'block';
|
||||
// Reset فیلد فایل
|
||||
document.getElementById('id_representative_letter_file').value = '';
|
||||
}
|
||||
|
||||
function editWell(id) {
|
||||
// Load well data and open edit form
|
||||
$.ajax({
|
||||
url: '{% url "wells:get_well_data" 0 %}'.replace('0', id),
|
||||
type: 'GET',
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
const well = response.well;
|
||||
|
||||
// Fill form with well data
|
||||
const fieldsMap = {
|
||||
'well-id': well.id,
|
||||
'id_representative': well.representative,
|
||||
'id_water_subscription_number': well.water_subscription_number,
|
||||
'id_electricity_subscription_number': well.electricity_subscription_number,
|
||||
'id_water_meter_serial_number': well.water_meter_serial_number,
|
||||
'id_water_meter_old_serial_number': well.water_meter_old_serial_number,
|
||||
'id_water_meter_manufacturer': well.water_meter_manufacturer,
|
||||
'id_utm_x': well.utm_x,
|
||||
'id_utm_y': well.utm_y,
|
||||
'id_utm_zone': well.utm_zone,
|
||||
'id_utm_hemisphere': well.utm_hemisphere,
|
||||
'id_well_power': well.well_power,
|
||||
'id_reference_letter_number': well.reference_letter_number,
|
||||
'id_reference_letter_date': well.reference_letter_date
|
||||
};
|
||||
|
||||
// Loop through fields for easier maintenance
|
||||
Object.keys(fieldsMap).forEach(function(fieldId) {
|
||||
$('#' + fieldId).val(fieldsMap[fieldId] || '');
|
||||
});
|
||||
|
||||
// تبدیل تاریخ میلادی به شمسی برای نمایش
|
||||
if (well.reference_letter_date) {
|
||||
try {
|
||||
if (window.persianDate) {
|
||||
const gregorianDate = new Date(well.reference_letter_date);
|
||||
const persianDateObj = new window.persianDate(gregorianDate);
|
||||
const persianDateString = persianDateObj.format('YYYY/MM/DD');
|
||||
$('#id_reference_letter_date').val(persianDateString);
|
||||
} else {
|
||||
// اگر persianDate در دسترس نبود، تاریخ میلادی را نمایش بده
|
||||
$('#id_reference_letter_date').val(well.reference_letter_date);
|
||||
}
|
||||
// ذخیره تاریخ میلادی در data attribute
|
||||
$('#id_reference_letter_date').attr('data-gregorian', well.reference_letter_date);
|
||||
} catch (error) {
|
||||
// اگر تبدیل نشد، تاریخ میلادی را نمایش بده
|
||||
$('#id_reference_letter_date').val(well.reference_letter_date);
|
||||
}
|
||||
}
|
||||
|
||||
// نمایش فایل موجود اگر وجود داشته باشد
|
||||
if (well.representative_letter_file_url) {
|
||||
document.getElementById('current-file-display').style.display = 'block';
|
||||
const fileName = well.representative_letter_file_name || 'فایل موجود';
|
||||
document.getElementById('current-file-name').textContent = fileName;
|
||||
document.getElementById('current-file-name').title = fileName; // برای tooltip
|
||||
document.getElementById('id_representative_letter_file').style.display = 'none';
|
||||
document.getElementById('remove-file').value = 'false';
|
||||
} else {
|
||||
document.getElementById('current-file-display').style.display = 'none';
|
||||
document.getElementById('id_representative_letter_file').style.display = 'block';
|
||||
document.getElementById('remove-file').value = 'false';
|
||||
}
|
||||
|
||||
// Update modal title and button
|
||||
$('#exampleModalLabel').text('ویرایش چاه');
|
||||
$('.data-submit').text('ویرایش');
|
||||
|
||||
// Open modal
|
||||
$('#add-new-record').offcanvas('show');
|
||||
} else {
|
||||
showToast('خطا در بارگذاری اطلاعات چاه', 'danger');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showToast('خطا در ارتباط با سرور', 'danger');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteWell(id, waterSubscriptionNumber) {
|
||||
// Set modal content
|
||||
document.getElementById('deleteConfirmText').textContent = `آیا از حذف چاه ${waterSubscriptionNumber} اطمینان دارید؟`;
|
||||
|
||||
// Show modal
|
||||
const modal = new bootstrap.Modal(document.getElementById('deleteConfirmModal'));
|
||||
modal.show();
|
||||
|
||||
// Handle confirm button click
|
||||
document.getElementById('confirmDeleteBtn').onclick = function() {
|
||||
$.ajax({
|
||||
url: '{% url "wells:delete_well" 0 %}'.replace('0', id),
|
||||
type: 'POST',
|
||||
data: {
|
||||
'csrfmiddlewaretoken': $('[name=csrfmiddlewaretoken]').val()
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
showToast(response.message, 'success');
|
||||
modal.hide();
|
||||
setTimeout(() => {
|
||||
window.location.reload();
|
||||
}, 1500);
|
||||
} else {
|
||||
showToast(response.message, 'danger');
|
||||
}
|
||||
},
|
||||
error: function() {
|
||||
showToast('خطا در ارتباط با سرور', 'danger');
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function prepareAddForm() {
|
||||
// Reset form for adding new well
|
||||
const form = $('#form-add-new-record')[0];
|
||||
form.reset();
|
||||
currentWellId = null;
|
||||
isEditMode = false;
|
||||
$('#well-id').val('');
|
||||
$('#exampleModalLabel').text('افزودن چاه جدید');
|
||||
$('.data-submit').text('ذخیره');
|
||||
|
||||
// Hide new manufacturer field
|
||||
document.getElementById('new-manufacturer-field').style.display = 'none';
|
||||
document.getElementById('id_new_manufacturer').style.display = 'none';
|
||||
|
||||
// Reset file display
|
||||
document.getElementById('current-file-display').style.display = 'none';
|
||||
document.getElementById('id_representative_letter_file').style.display = 'block';
|
||||
document.getElementById('remove-file').value = 'false';
|
||||
|
||||
// Clear validation errors
|
||||
$('.is-invalid').removeClass('is-invalid');
|
||||
$('.invalid-feedback').remove();
|
||||
|
||||
// Open modal
|
||||
$('#add-new-record').offcanvas('show');
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
3
wells/tests.py
Normal file
3
wells/tests.py
Normal file
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
16
wells/urls.py
Normal file
16
wells/urls.py
Normal file
|
@ -0,0 +1,16 @@
|
|||
from django.urls import path
|
||||
from . import views
|
||||
|
||||
app_name = 'wells'
|
||||
|
||||
urlpatterns = [
|
||||
# چاهها
|
||||
path('', views.well_list, name='well_list'),
|
||||
path('add/', views.add_well_ajax, name='add_well_ajax'),
|
||||
path('<int:well_id>/edit/', views.edit_well_ajax, name='edit_well_ajax'),
|
||||
path('<int:well_id>/delete/', views.delete_well, name='delete_well'),
|
||||
path('<int:well_id>/data/', views.get_well_data, name='get_well_data'),
|
||||
|
||||
# شرکتهای سازنده کنتور آب
|
||||
path('manufacturer/create/', views.create_water_meter_manufacturer, name='create_water_meter_manufacturer'),
|
||||
]
|
207
wells/views.py
Normal file
207
wells/views.py
Normal file
|
@ -0,0 +1,207 @@
|
|||
from django.shortcuts import render, get_object_or_404
|
||||
from django.http import JsonResponse
|
||||
from django.views.decorators.http import require_http_methods, require_GET, require_POST
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Q
|
||||
from django.contrib import messages
|
||||
from django import forms
|
||||
from .models import Well, WaterMeterManufacturer
|
||||
from .forms import WellForm, WaterMeterManufacturerForm
|
||||
|
||||
|
||||
def well_list(request):
|
||||
"""نمایش لیست چاهها"""
|
||||
wells = Well.objects.select_related(
|
||||
'representative',
|
||||
'water_meter_manufacturer',
|
||||
'affairs',
|
||||
'county',
|
||||
'broker'
|
||||
).filter(is_deleted=False)
|
||||
|
||||
# فرم برای افزودن چاه جدید
|
||||
form = WellForm()
|
||||
|
||||
context = {
|
||||
'wells': wells,
|
||||
'form': form,
|
||||
}
|
||||
|
||||
return render(request, 'wells/well_list.html', context)
|
||||
|
||||
|
||||
@require_POST
|
||||
def add_well_ajax(request):
|
||||
"""AJAX endpoint for adding wells"""
|
||||
try:
|
||||
print(f"POST data: {request.POST}") # Debug log
|
||||
print(f"FILES data: {request.FILES}") # Debug log
|
||||
|
||||
form = WellForm(request.POST, request.FILES)
|
||||
print(f"Form is valid: {form.is_valid()}") # Debug log
|
||||
|
||||
if form.is_valid():
|
||||
well = form.save(commit=False)
|
||||
|
||||
# تنظیم فیلدهای affairs, county, broker از profile کاربر
|
||||
if hasattr(request.user, 'profile'):
|
||||
well.affairs = request.user.profile.affairs
|
||||
well.county = request.user.profile.county
|
||||
well.broker = request.user.profile.broker
|
||||
|
||||
well.save()
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'message': 'چاه با موفقیت ایجاد شد',
|
||||
'well': {
|
||||
'id': well.id,
|
||||
'water_subscription_number': well.water_subscription_number,
|
||||
'representative': str(well.representative) if well.representative else '',
|
||||
'electricity_subscription_number': well.electricity_subscription_number or '',
|
||||
'water_meter_manufacturer': str(well.water_meter_manufacturer) if well.water_meter_manufacturer else '',
|
||||
'affairs': str(well.affairs) if well.affairs else '',
|
||||
'county': str(well.county) if well.county else '',
|
||||
'broker': str(well.broker) if well.broker else '',
|
||||
}
|
||||
})
|
||||
else:
|
||||
print(f"Form errors: {form.errors}") # Debug log
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'message': 'خطا در اعتبارسنجی فرم',
|
||||
'errors': form.errors
|
||||
})
|
||||
except forms.ValidationError as e:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'message': str(e)
|
||||
})
|
||||
except Exception as e:
|
||||
print(f"Exception in add_well_ajax: {str(e)}") # Debug log
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'message': f'خطا در ذخیره چاه: {str(e)}',
|
||||
'errors': {}
|
||||
})
|
||||
|
||||
|
||||
@require_POST
|
||||
def edit_well_ajax(request, well_id):
|
||||
"""AJAX endpoint for editing wells"""
|
||||
well = get_object_or_404(Well, id=well_id)
|
||||
form = WellForm(request.POST, request.FILES, instance=well)
|
||||
|
||||
if form.is_valid():
|
||||
try:
|
||||
well = form.save(commit=False)
|
||||
|
||||
# تنظیم فیلدهای affairs, county, broker از profile کاربر (در ویرایش هم میتواند بهروزرسانی شود)
|
||||
if hasattr(request.user, 'profile'):
|
||||
well.affairs = request.user.profile.affairs
|
||||
well.county = request.user.profile.county
|
||||
well.broker = request.user.profile.broker
|
||||
|
||||
# حذف فایل اگر درخواست شده باشد
|
||||
if request.POST.get('remove_file') == 'true':
|
||||
well.representative_letter_file.delete(save=False)
|
||||
|
||||
well.save()
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'message': 'چاه با موفقیت ویرایش شد',
|
||||
'well': {
|
||||
'id': well.id,
|
||||
'water_subscription_number': well.water_subscription_number,
|
||||
'representative': str(well.representative) if well.representative else '',
|
||||
'electricity_subscription_number': well.electricity_subscription_number or '',
|
||||
'water_meter_manufacturer': str(well.water_meter_manufacturer) if well.water_meter_manufacturer else '',
|
||||
'affairs': str(well.affairs) if well.affairs else '',
|
||||
'county': str(well.county) if well.county else '',
|
||||
'broker': str(well.broker) if well.broker else '',
|
||||
}
|
||||
})
|
||||
except forms.ValidationError as e:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'message': str(e)
|
||||
})
|
||||
except Exception as e:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'message': f'خطا در ویرایش چاه: {str(e)}'
|
||||
})
|
||||
else:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'message': 'خطا در اعتبارسنجی فرم',
|
||||
'errors': form.errors
|
||||
})
|
||||
|
||||
|
||||
@require_POST
|
||||
def delete_well(request, well_id):
|
||||
"""حذف چاه"""
|
||||
well = get_object_or_404(Well, id=well_id)
|
||||
water_subscription_number = well.water_subscription_number
|
||||
well.delete()
|
||||
print(f"Well deleted: {well_id}")
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'message': f'چاه {water_subscription_number} با موفقیت حذف شد'
|
||||
})
|
||||
|
||||
|
||||
@require_GET
|
||||
def get_well_data(request, well_id):
|
||||
"""دریافت اطلاعات چاه برای ویرایش"""
|
||||
well = get_object_or_404(Well, id=well_id)
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'well': {
|
||||
'id': well.id,
|
||||
'representative': well.representative.id if well.representative else '',
|
||||
'water_subscription_number': well.water_subscription_number,
|
||||
'electricity_subscription_number': well.electricity_subscription_number or '',
|
||||
'water_meter_serial_number': well.water_meter_serial_number or '',
|
||||
'water_meter_old_serial_number': well.water_meter_old_serial_number or '',
|
||||
'water_meter_manufacturer': well.water_meter_manufacturer.id if well.water_meter_manufacturer else '',
|
||||
'utm_x': str(well.utm_x) if well.utm_x else '',
|
||||
'utm_y': str(well.utm_y) if well.utm_y else '',
|
||||
'utm_zone': well.utm_zone or '',
|
||||
'utm_hemisphere': well.utm_hemisphere or '',
|
||||
'well_power': well.well_power or '',
|
||||
'reference_letter_number': well.reference_letter_number or '',
|
||||
'reference_letter_date': well.reference_letter_date.strftime('%Y-%m-%d') if well.reference_letter_date else '',
|
||||
'representative_letter_file_url': well.representative_letter_file.url if well.representative_letter_file else '',
|
||||
'representative_letter_file_name': well.representative_letter_file.name.split('/')[-1] if well.representative_letter_file else '',
|
||||
# affairs, county, broker are auto-filled from user profile, so not needed in edit form
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@require_POST
|
||||
def create_water_meter_manufacturer(request):
|
||||
"""ایجاد شرکت سازنده کنتور آب جدید"""
|
||||
form = WaterMeterManufacturerForm(request.POST)
|
||||
|
||||
if form.is_valid():
|
||||
manufacturer = form.save()
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'message': 'شرکت سازنده با موفقیت ایجاد شد',
|
||||
'manufacturer': {
|
||||
'id': manufacturer.id,
|
||||
'name': manufacturer.name
|
||||
}
|
||||
})
|
||||
else:
|
||||
return JsonResponse({
|
||||
'success': False,
|
||||
'message': 'خطا در اعتبارسنجی فرم',
|
||||
'errors': form.errors
|
||||
})
|
||||
|
||||
# Create your views here.
|
Loading…
Add table
Add a link
Reference in a new issue