clean up proccess and req_list app.
This commit is contained in:
parent
35799b7754
commit
6f3ce51ab9
26 changed files with 287 additions and 744 deletions
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-14 09:02
|
# Generated by Django 5.2.4 on 2025-09-07 07:35
|
||||||
|
|
||||||
import django.core.validators
|
import django.core.validators
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
|
@ -17,6 +17,27 @@ class Migration(migrations.Migration):
|
||||||
]
|
]
|
||||||
|
|
||||||
operations = [
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='Company',
|
||||||
|
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='اسلاگ')),
|
||||||
|
('name', models.CharField(max_length=100, verbose_name='نام')),
|
||||||
|
('logo', models.ImageField(blank=True, null=True, upload_to='companies/logos', verbose_name='لوگوی شرکت')),
|
||||||
|
('signature', models.ImageField(blank=True, null=True, upload_to='companies/signatures', verbose_name='امضای شرکت')),
|
||||||
|
('address', models.TextField(blank=True, null=True, verbose_name='آدرس')),
|
||||||
|
('phone', models.CharField(blank=True, max_length=11, null=True, verbose_name='شماره تماس')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'شرکت',
|
||||||
|
'verbose_name_plural': 'شرکت\u200cها',
|
||||||
|
},
|
||||||
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='HistoricalProfile',
|
name='HistoricalProfile',
|
||||||
fields=[
|
fields=[
|
||||||
|
@ -30,6 +51,7 @@ class Migration(migrations.Migration):
|
||||||
('address', models.TextField(blank=True, null=True, verbose_name='آدرس')),
|
('address', models.TextField(blank=True, null=True, verbose_name='آدرس')),
|
||||||
('card_number', models.CharField(blank=True, max_length=16, null=True, validators=[django.core.validators.RegexValidator(code='invalid_card_number', message='شماره کارت باید فقط شامل اعداد باشد.', regex='^\\d+$')], verbose_name='شماره کارت')),
|
('card_number', models.CharField(blank=True, max_length=16, null=True, validators=[django.core.validators.RegexValidator(code='invalid_card_number', message='شماره کارت باید فقط شامل اعداد باشد.', regex='^\\d+$')], verbose_name='شماره کارت')),
|
||||||
('account_number', models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.RegexValidator(code='invalid_account_number', message='شماره حساب باید فقط شامل اعداد باشد.', regex='^\\d+$')], verbose_name='شماره حساب')),
|
('account_number', models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.RegexValidator(code='invalid_account_number', message='شماره حساب باید فقط شامل اعداد باشد.', regex='^\\d+$')], verbose_name='شماره حساب')),
|
||||||
|
('bank_name', models.CharField(blank=True, choices=[('mellat', 'بانک ملت'), ('saman', 'بانک سامان'), ('parsian', 'بانک پارسیان'), ('sina', 'بانک سینا'), ('tejarat', 'بانک تجارت'), ('tosee', 'بانک توسعه'), ('iran_zamin', 'بانک ایران زمین'), ('meli', 'بانک ملی'), ('saderat', 'بانک توسعه صادرات'), ('iran_zamin', 'بانک ایران زمین'), ('refah', 'بانک رفاه'), ('eghtesad_novin', 'بانک اقتصاد نوین'), ('pasargad', 'بانک پاسارگاد'), ('other', 'سایر')], max_length=255, null=True, verbose_name='نام بانک')),
|
||||||
('phone_number_1', models.CharField(blank=True, max_length=11, null=True, verbose_name='شماره تماس ۱')),
|
('phone_number_1', models.CharField(blank=True, max_length=11, null=True, verbose_name='شماره تماس ۱')),
|
||||||
('phone_number_2', models.CharField(blank=True, max_length=11, null=True, verbose_name='شماره تماس ۲')),
|
('phone_number_2', models.CharField(blank=True, max_length=11, null=True, verbose_name='شماره تماس ۲')),
|
||||||
('pic', models.TextField(default='../static/sample_images/profile.jpg', max_length=100, verbose_name='تصویر')),
|
('pic', models.TextField(default='../static/sample_images/profile.jpg', max_length=100, verbose_name='تصویر')),
|
||||||
|
@ -84,6 +106,7 @@ class Migration(migrations.Migration):
|
||||||
('address', models.TextField(blank=True, null=True, verbose_name='آدرس')),
|
('address', models.TextField(blank=True, null=True, verbose_name='آدرس')),
|
||||||
('card_number', models.CharField(blank=True, max_length=16, null=True, validators=[django.core.validators.RegexValidator(code='invalid_card_number', message='شماره کارت باید فقط شامل اعداد باشد.', regex='^\\d+$')], verbose_name='شماره کارت')),
|
('card_number', models.CharField(blank=True, max_length=16, null=True, validators=[django.core.validators.RegexValidator(code='invalid_card_number', message='شماره کارت باید فقط شامل اعداد باشد.', regex='^\\d+$')], verbose_name='شماره کارت')),
|
||||||
('account_number', models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.RegexValidator(code='invalid_account_number', message='شماره حساب باید فقط شامل اعداد باشد.', regex='^\\d+$')], verbose_name='شماره حساب')),
|
('account_number', models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.RegexValidator(code='invalid_account_number', message='شماره حساب باید فقط شامل اعداد باشد.', regex='^\\d+$')], verbose_name='شماره حساب')),
|
||||||
|
('bank_name', models.CharField(blank=True, choices=[('mellat', 'بانک ملت'), ('saman', 'بانک سامان'), ('parsian', 'بانک پارسیان'), ('sina', 'بانک سینا'), ('tejarat', 'بانک تجارت'), ('tosee', 'بانک توسعه'), ('iran_zamin', 'بانک ایران زمین'), ('meli', 'بانک ملی'), ('saderat', 'بانک توسعه صادرات'), ('iran_zamin', 'بانک ایران زمین'), ('refah', 'بانک رفاه'), ('eghtesad_novin', 'بانک اقتصاد نوین'), ('pasargad', 'بانک پاسارگاد'), ('other', 'سایر')], max_length=255, null=True, verbose_name='نام بانک')),
|
||||||
('phone_number_1', models.CharField(blank=True, max_length=11, null=True, verbose_name='شماره تماس ۱')),
|
('phone_number_1', models.CharField(blank=True, max_length=11, null=True, verbose_name='شماره تماس ۱')),
|
||||||
('phone_number_2', models.CharField(blank=True, max_length=11, null=True, verbose_name='شماره تماس ۲')),
|
('phone_number_2', models.CharField(blank=True, max_length=11, null=True, verbose_name='شماره تماس ۲')),
|
||||||
('pic', models.ImageField(default='../static/sample_images/profile.jpg', upload_to='profile_images', verbose_name='تصویر')),
|
('pic', models.ImageField(default='../static/sample_images/profile.jpg', upload_to='profile_images', verbose_name='تصویر')),
|
||||||
|
|
|
@ -1,34 +0,0 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-21 06:33
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('accounts', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Company',
|
|
||||||
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='اسلاگ')),
|
|
||||||
('name', models.CharField(max_length=100, verbose_name='نام')),
|
|
||||||
('logo', models.ImageField(blank=True, null=True, upload_to='companies/logos', verbose_name='لوگوی شرکت')),
|
|
||||||
('signature', models.ImageField(blank=True, null=True, upload_to='companies/signatures', verbose_name='امضای شرکت')),
|
|
||||||
('address', models.TextField(blank=True, null=True, verbose_name='آدرس')),
|
|
||||||
('phone', models.CharField(blank=True, max_length=11, null=True, verbose_name='شماره تماس')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'شرکت',
|
|
||||||
'verbose_name_plural': 'شرکت\u200cها',
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,23 +0,0 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-21 07:06
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('accounts', '0002_company'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='historicalprofile',
|
|
||||||
name='bank_name',
|
|
||||||
field=models.CharField(blank=True, choices=[('mellat', 'بانک ملت'), ('saman', 'بانک سامان'), ('parsian', 'بانک پارسیان'), ('sina', 'بانک سینا'), ('tejarat', 'بانک تجارت'), ('tosee', 'بانک توسعه'), ('iran_zamin', 'بانک ایران زمین'), ('meli', 'بانک ملی'), ('saderat', 'بانک توسعه صادرات'), ('iran_zamin', 'بانک ایران زمین'), ('refah', 'بانک رفاه'), ('eghtesad_novin', 'بانک اقتصاد نوین'), ('pasargad', 'بانک پاسارگاد'), ('other', 'سایر')], max_length=255, null=True, verbose_name='نام بانک'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='profile',
|
|
||||||
name='bank_name',
|
|
||||||
field=models.CharField(blank=True, choices=[('mellat', 'بانک ملت'), ('saman', 'بانک سامان'), ('parsian', 'بانک پارسیان'), ('sina', 'بانک سینا'), ('tejarat', 'بانک تجارت'), ('tosee', 'بانک توسعه'), ('iran_zamin', 'بانک ایران زمین'), ('meli', 'بانک ملی'), ('saderat', 'بانک توسعه صادرات'), ('iran_zamin', 'بانک ایران زمین'), ('refah', 'بانک رفاه'), ('eghtesad_novin', 'بانک اقتصاد نوین'), ('pasargad', 'بانک پاسارگاد'), ('other', 'سایر')], max_length=255, null=True, verbose_name='نام بانک'),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-22 09:58
|
# Generated by Django 5.2.4 on 2025-09-07 07:35
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
@ -9,6 +9,7 @@ class Migration(migrations.Migration):
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
('accounts', '0001_initial'),
|
||||||
('processes', '0001_initial'),
|
('processes', '0001_initial'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -23,10 +24,8 @@ class Migration(migrations.Migration):
|
||||||
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
||||||
('title', models.CharField(max_length=200, verbose_name='عنوان')),
|
('title', models.CharField(max_length=200, verbose_name='عنوان')),
|
||||||
('body', models.TextField(verbose_name='متن قالب (با جایگزین\u200cها)')),
|
('body', models.TextField(verbose_name='متن قالب (با جایگزین\u200cها)')),
|
||||||
('company_logo', models.ImageField(blank=True, null=True, upload_to='certificates/logos/%Y/%m/%d/', verbose_name='لوگو')),
|
|
||||||
('company_name', models.CharField(blank=True, max_length=200, verbose_name='نام شرکت')),
|
|
||||||
('company_seal_signature', models.ImageField(blank=True, null=True, upload_to='certificates/seals/%Y/%m/%d/', verbose_name='مهر و امضا')),
|
|
||||||
('is_active', models.BooleanField(default=True, verbose_name='فعال')),
|
('is_active', models.BooleanField(default=True, verbose_name='فعال')),
|
||||||
|
('company', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.company', verbose_name='شرکت صادر کننده')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'قالب گواهی',
|
'verbose_name': 'قالب گواهی',
|
||||||
|
|
|
@ -1,32 +0,0 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-22 10:05
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('accounts', '0003_historicalprofile_bank_name_profile_bank_name'),
|
|
||||||
('certificates', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='certificatetemplate',
|
|
||||||
name='company_logo',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='certificatetemplate',
|
|
||||||
name='company_name',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='certificatetemplate',
|
|
||||||
name='company_seal_signature',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='certificatetemplate',
|
|
||||||
name='company',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.company', verbose_name='شرکت صادر کننده'),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,7 +1,6 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-21 06:00
|
# Generated by Django 5.2.4 on 2025-09-07 07:35
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import simple_history.models
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
@ -11,6 +10,7 @@ class Migration(migrations.Migration):
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
('accounts', '0001_initial'),
|
||||||
('processes', '0001_initial'),
|
('processes', '0001_initial'),
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
]
|
]
|
||||||
|
@ -28,8 +28,7 @@ class Migration(migrations.Migration):
|
||||||
('slug', models.SlugField(max_length=100, unique=True, verbose_name='اسلاگ')),
|
('slug', models.SlugField(max_length=100, unique=True, verbose_name='اسلاگ')),
|
||||||
('name', models.CharField(max_length=100, verbose_name='نام')),
|
('name', models.CharField(max_length=100, verbose_name='نام')),
|
||||||
('body', models.TextField(verbose_name='متن قرارداد')),
|
('body', models.TextField(verbose_name='متن قرارداد')),
|
||||||
('company_logo', models.ImageField(blank=True, null=True, upload_to='contracts/logos/%Y/%m/%d/', verbose_name='لوگوی شرکت')),
|
('company', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.company', verbose_name='شرکت')),
|
||||||
('company_signature', models.ImageField(blank=True, null=True, upload_to='contracts/signatures/%Y/%m/%d/', verbose_name='امضای شرکت')),
|
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'قالب قرارداد',
|
'verbose_name': 'قالب قرارداد',
|
||||||
|
@ -58,61 +57,4 @@ class Migration(migrations.Migration):
|
||||||
'ordering': ['-created'],
|
'ordering': ['-created'],
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
|
||||||
name='HistoricalContractInstance',
|
|
||||||
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='تاریخ حذف')),
|
|
||||||
('rendered_body', models.TextField(verbose_name='متن نهایی قرارداد')),
|
|
||||||
('approved', models.BooleanField(default=False, verbose_name='تایید شده')),
|
|
||||||
('approved_at', models.DateTimeField(blank=True, null=True, 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)),
|
|
||||||
('created_by', 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='ایجاد کننده')),
|
|
||||||
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
|
|
||||||
('process_instance', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='processes.processinstance', verbose_name='نمونه فرآیند')),
|
|
||||||
('template', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='contracts.contracttemplate', verbose_name='قالب مورد استفاده')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'historical قرارداد',
|
|
||||||
'verbose_name_plural': 'historical قراردادها',
|
|
||||||
'ordering': ('-history_date', '-history_id'),
|
|
||||||
'get_latest_by': ('history_date', 'history_id'),
|
|
||||||
},
|
|
||||||
bases=(simple_history.models.HistoricalChanges, models.Model),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='HistoricalContractTemplate',
|
|
||||||
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='اسلاگ')),
|
|
||||||
('name', models.CharField(max_length=100, verbose_name='نام')),
|
|
||||||
('body', models.TextField(verbose_name='متن قرارداد')),
|
|
||||||
('company_logo', models.TextField(blank=True, max_length=100, null=True, verbose_name='لوگوی شرکت')),
|
|
||||||
('company_signature', models.TextField(blank=True, max_length=100, null=True, 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)),
|
|
||||||
],
|
|
||||||
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),
|
|
||||||
),
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,38 +0,0 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-21 06:33
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('accounts', '0002_company'),
|
|
||||||
('contracts', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='historicalcontracttemplate',
|
|
||||||
name='history_user',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='contracttemplate',
|
|
||||||
name='company_logo',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='contracttemplate',
|
|
||||||
name='company_signature',
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='contracttemplate',
|
|
||||||
name='company',
|
|
||||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.company', verbose_name='شرکت'),
|
|
||||||
),
|
|
||||||
migrations.DeleteModel(
|
|
||||||
name='HistoricalContractInstance',
|
|
||||||
),
|
|
||||||
migrations.DeleteModel(
|
|
||||||
name='HistoricalContractTemplate',
|
|
||||||
),
|
|
||||||
]
|
|
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-21 08:25
|
# Generated by Django 5.2.4 on 2025-09-07 07:35
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -10,7 +10,7 @@ class Migration(migrations.Migration):
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
('invoices', '0002_historicalpayment_receipt_image_and_more'),
|
('invoices', '0001_initial'),
|
||||||
('processes', '0001_initial'),
|
('processes', '0001_initial'),
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
]
|
]
|
||||||
|
@ -53,6 +53,8 @@ class Migration(migrations.Migration):
|
||||||
('utm_x', models.DecimalField(blank=True, decimal_places=6, max_digits=10, null=True, verbose_name='UTM X')),
|
('utm_x', models.DecimalField(blank=True, decimal_places=6, max_digits=10, null=True, verbose_name='UTM X')),
|
||||||
('utm_y', models.DecimalField(blank=True, decimal_places=6, max_digits=10, null=True, verbose_name='UTM Y')),
|
('utm_y', models.DecimalField(blank=True, decimal_places=6, max_digits=10, null=True, verbose_name='UTM Y')),
|
||||||
('description', models.TextField(blank=True, verbose_name='توضیحات')),
|
('description', models.TextField(blank=True, verbose_name='توضیحات')),
|
||||||
|
('approved', models.BooleanField(default=False, verbose_name='تایید شده')),
|
||||||
|
('approved_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ تایید')),
|
||||||
('assignment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reports', to='installations.installationassignment', verbose_name='اختصاص')),
|
('assignment', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reports', to='installations.installationassignment', verbose_name='اختصاص')),
|
||||||
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='ایجادکننده')),
|
('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='ایجادکننده')),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-21 09:04
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('installations', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='installationreport',
|
|
||||||
name='approved',
|
|
||||||
field=models.BooleanField(default=False, verbose_name='تایید شده'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='installationreport',
|
|
||||||
name='approved_at',
|
|
||||||
field=models.DateTimeField(blank=True, null=True, verbose_name='تاریخ تایید'),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-14 09:02
|
# Generated by Django 5.2.4 on 2025-09-07 07:35
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import simple_history.models
|
import simple_history.models
|
||||||
|
@ -29,6 +29,7 @@ class Migration(migrations.Migration):
|
||||||
('name', models.CharField(max_length=100, verbose_name='نام')),
|
('name', models.CharField(max_length=100, verbose_name='نام')),
|
||||||
('description', models.TextField(blank=True, verbose_name='توضیحات')),
|
('description', models.TextField(blank=True, verbose_name='توضیحات')),
|
||||||
('unit_price', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='قیمت واحد')),
|
('unit_price', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='قیمت واحد')),
|
||||||
|
('is_special', models.BooleanField(default=False, verbose_name='ویژه برای فاکتور نهایی')),
|
||||||
('default_quantity', models.PositiveIntegerField(default=1, verbose_name='تعداد پیش\u200cفرض')),
|
('default_quantity', models.PositiveIntegerField(default=1, verbose_name='تعداد پیش\u200cفرض')),
|
||||||
('is_default_in_quotes', models.BooleanField(default=False, help_text='این آیتم به صورت پیش\u200cفرض در همه پیش\u200cفاکتورها قرار می\u200cگیرد', verbose_name='پیش\u200cفرض در پیش\u200cفاکتورها')),
|
('is_default_in_quotes', models.BooleanField(default=False, help_text='این آیتم به صورت پیش\u200cفرض در همه پیش\u200cفاکتورها قرار می\u200cگیرد', verbose_name='پیش\u200cفرض در پیش\u200cفاکتورها')),
|
||||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||||
|
@ -121,10 +122,12 @@ class Migration(migrations.Migration):
|
||||||
('is_deleted', models.BooleanField(default=False, verbose_name='حذف شده')),
|
('is_deleted', models.BooleanField(default=False, verbose_name='حذف شده')),
|
||||||
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
||||||
('amount', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='مبلغ پرداخت')),
|
('amount', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='مبلغ پرداخت')),
|
||||||
|
('direction', models.CharField(choices=[('in', 'دریافتی'), ('out', 'پرداختی')], default='in', max_length=3, verbose_name='نوع تراکنش')),
|
||||||
('payment_method', models.CharField(choices=[('cash', 'نقدی'), ('bank_transfer', 'انتقال بانکی'), ('check', 'چک'), ('card', 'کارت بانکی'), ('other', 'سایر')], default='cash', max_length=20, verbose_name='روش پرداخت')),
|
('payment_method', models.CharField(choices=[('cash', 'نقدی'), ('bank_transfer', 'انتقال بانکی'), ('check', 'چک'), ('card', 'کارت بانکی'), ('other', 'سایر')], default='cash', max_length=20, verbose_name='روش پرداخت')),
|
||||||
('reference_number', models.CharField(blank=True, max_length=100, verbose_name='شماره مرجع')),
|
('reference_number', models.CharField(blank=True, db_index=True, max_length=100, verbose_name='شماره مرجع')),
|
||||||
('payment_date', models.DateField(verbose_name='تاریخ پرداخت')),
|
('payment_date', models.DateField(verbose_name='تاریخ پرداخت')),
|
||||||
('notes', models.TextField(blank=True, verbose_name='یادداشت\u200cها')),
|
('notes', models.TextField(blank=True, verbose_name='یادداشت\u200cها')),
|
||||||
|
('receipt_image', models.TextField(blank=True, max_length=100, null=True, verbose_name='تصویر فیش')),
|
||||||
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
('history_id', models.AutoField(primary_key=True, serialize=False)),
|
||||||
('history_date', models.DateTimeField(db_index=True)),
|
('history_date', models.DateTimeField(db_index=True)),
|
||||||
('history_change_reason', models.CharField(max_length=100, null=True)),
|
('history_change_reason', models.CharField(max_length=100, null=True)),
|
||||||
|
@ -154,6 +157,7 @@ class Migration(migrations.Migration):
|
||||||
('name', models.CharField(max_length=100, verbose_name='نام')),
|
('name', models.CharField(max_length=100, verbose_name='نام')),
|
||||||
('description', models.TextField(blank=True, verbose_name='توضیحات')),
|
('description', models.TextField(blank=True, verbose_name='توضیحات')),
|
||||||
('unit_price', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='قیمت واحد')),
|
('unit_price', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='قیمت واحد')),
|
||||||
|
('is_special', models.BooleanField(default=False, verbose_name='ویژه برای فاکتور نهایی')),
|
||||||
('default_quantity', models.PositiveIntegerField(default=1, verbose_name='تعداد پیش\u200cفرض')),
|
('default_quantity', models.PositiveIntegerField(default=1, verbose_name='تعداد پیش\u200cفرض')),
|
||||||
('is_default_in_quotes', models.BooleanField(default=False, help_text='این آیتم به صورت پیش\u200cفرض در همه پیش\u200cفاکتورها قرار می\u200cگیرد', verbose_name='پیش\u200cفرض در پیش\u200cفاکتورها')),
|
('is_default_in_quotes', models.BooleanField(default=False, help_text='این آیتم به صورت پیش\u200cفرض در همه پیش\u200cفاکتورها قرار می\u200cگیرد', verbose_name='پیش\u200cفرض در پیش\u200cفاکتورها')),
|
||||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='ایجاد کننده')),
|
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='ایجاد کننده')),
|
||||||
|
@ -225,10 +229,12 @@ class Migration(migrations.Migration):
|
||||||
('is_deleted', models.BooleanField(default=False, verbose_name='حذف شده')),
|
('is_deleted', models.BooleanField(default=False, verbose_name='حذف شده')),
|
||||||
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')),
|
||||||
('amount', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='مبلغ پرداخت')),
|
('amount', models.DecimalField(decimal_places=2, max_digits=15, verbose_name='مبلغ پرداخت')),
|
||||||
|
('direction', models.CharField(choices=[('in', 'دریافتی'), ('out', 'پرداختی')], default='in', max_length=3, verbose_name='نوع تراکنش')),
|
||||||
('payment_method', models.CharField(choices=[('cash', 'نقدی'), ('bank_transfer', 'انتقال بانکی'), ('check', 'چک'), ('card', 'کارت بانکی'), ('other', 'سایر')], default='cash', max_length=20, verbose_name='روش پرداخت')),
|
('payment_method', models.CharField(choices=[('cash', 'نقدی'), ('bank_transfer', 'انتقال بانکی'), ('check', 'چک'), ('card', 'کارت بانکی'), ('other', 'سایر')], default='cash', max_length=20, verbose_name='روش پرداخت')),
|
||||||
('reference_number', models.CharField(blank=True, max_length=100, verbose_name='شماره مرجع')),
|
('reference_number', models.CharField(blank=True, max_length=100, unique=True, verbose_name='شماره مرجع')),
|
||||||
('payment_date', models.DateField(verbose_name='تاریخ پرداخت')),
|
('payment_date', models.DateField(verbose_name='تاریخ پرداخت')),
|
||||||
('notes', models.TextField(blank=True, verbose_name='یادداشت\u200cها')),
|
('notes', models.TextField(blank=True, verbose_name='یادداشت\u200cها')),
|
||||||
|
('receipt_image', models.ImageField(blank=True, null=True, upload_to='payments/%Y/%m/%d/', verbose_name='تصویر فیش')),
|
||||||
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='ثبت کننده')),
|
('created_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='ثبت کننده')),
|
||||||
('invoice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='invoices.invoice', verbose_name='فاکتور')),
|
('invoice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='payments', to='invoices.invoice', verbose_name='فاکتور')),
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-16 04:18
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('invoices', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='historicalpayment',
|
|
||||||
name='receipt_image',
|
|
||||||
field=models.TextField(blank=True, max_length=100, null=True, verbose_name='تصویر فیش'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='payment',
|
|
||||||
name='receipt_image',
|
|
||||||
field=models.ImageField(blank=True, null=True, upload_to='payments/%Y/%m/%d/', verbose_name='تصویر فیش'),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,23 +0,0 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-21 18:03
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('invoices', '0002_historicalpayment_receipt_image_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='historicalpayment',
|
|
||||||
name='reference_number',
|
|
||||||
field=models.CharField(blank=True, db_index=True, max_length=100, verbose_name='شماره مرجع'),
|
|
||||||
),
|
|
||||||
migrations.AlterField(
|
|
||||||
model_name='payment',
|
|
||||||
name='reference_number',
|
|
||||||
field=models.CharField(blank=True, max_length=100, unique=True, verbose_name='شماره مرجع'),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,23 +0,0 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-22 08:18
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('invoices', '0003_alter_historicalpayment_reference_number_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='historicalpayment',
|
|
||||||
name='direction',
|
|
||||||
field=models.CharField(choices=[('in', 'دریافتی'), ('out', 'پرداختی')], default='in', max_length=3, verbose_name='نوع تراکنش'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='payment',
|
|
||||||
name='direction',
|
|
||||||
field=models.CharField(choices=[('in', 'دریافتی'), ('out', 'پرداختی')], default='in', max_length=3, verbose_name='نوع تراکنش'),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,33 +0,0 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-22 08:54
|
|
||||||
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('invoices', '0004_historicalpayment_direction_payment_direction'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='historicalitem',
|
|
||||||
name='is_special',
|
|
||||||
field=models.BooleanField(default=False, verbose_name='ویژه برای فاکتور نهایی'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='historicalitem',
|
|
||||||
name='special_kind',
|
|
||||||
field=models.CharField(blank=True, choices=[('repair', 'تعمیر'), ('replace', 'تعویض')], max_length=10, verbose_name='نوع ویژه'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='item',
|
|
||||||
name='is_special',
|
|
||||||
field=models.BooleanField(default=False, verbose_name='ویژه برای فاکتور نهایی'),
|
|
||||||
),
|
|
||||||
migrations.AddField(
|
|
||||||
model_name='item',
|
|
||||||
name='special_kind',
|
|
||||||
field=models.CharField(blank=True, choices=[('repair', 'تعمیر'), ('replace', 'تعویض')], max_length=10, verbose_name='نوع ویژه'),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,21 +0,0 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-22 08:59
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('invoices', '0005_historicalitem_is_special_and_more'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='historicalitem',
|
|
||||||
name='special_kind',
|
|
||||||
),
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='item',
|
|
||||||
name='special_kind',
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-14 09:02
|
# Generated by Django 5.2.4 on 2025-09-07 07:35
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
|
|
|
@ -2,7 +2,7 @@ from django.contrib import admin
|
||||||
from simple_history.admin import SimpleHistoryAdmin
|
from simple_history.admin import SimpleHistoryAdmin
|
||||||
from django.utils.html import format_html
|
from django.utils.html import format_html
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from .models import Process, ProcessStep, ProcessInstance, StepInstance, StepDependency, StepRejection, StepRevision, StepApproverRequirement, StepApproval
|
from .models import Process, ProcessStep, ProcessInstance, StepInstance, StepDependency, StepRejection, StepApproverRequirement, StepApproval
|
||||||
|
|
||||||
@admin.register(Process)
|
@admin.register(Process)
|
||||||
class ProcessAdmin(SimpleHistoryAdmin):
|
class ProcessAdmin(SimpleHistoryAdmin):
|
||||||
|
@ -168,18 +168,6 @@ class StepRejectionAdmin(SimpleHistoryAdmin):
|
||||||
return obj.reason[:50] + "..." if len(obj.reason) > 50 else obj.reason
|
return obj.reason[:50] + "..." if len(obj.reason) > 50 else obj.reason
|
||||||
reason_short.short_description = "دلیل رد شدن"
|
reason_short.short_description = "دلیل رد شدن"
|
||||||
|
|
||||||
@admin.register(StepRevision)
|
|
||||||
class StepRevisionAdmin(SimpleHistoryAdmin):
|
|
||||||
list_display = ['step_instance', 'rejection', 'revised_by', 'changes_short', 'created_at']
|
|
||||||
list_filter = ['revised_by', 'created_at', 'step_instance__step__process']
|
|
||||||
search_fields = ['step_instance__step__name', 'revised_by__username', 'changes_description']
|
|
||||||
readonly_fields = ['created_at']
|
|
||||||
ordering = ['-created_at']
|
|
||||||
|
|
||||||
def changes_short(self, obj):
|
|
||||||
return obj.changes_description[:50] + "..." if len(obj.changes_description) > 50 else obj.changes_description
|
|
||||||
changes_short.short_description = "تغییرات"
|
|
||||||
|
|
||||||
|
|
||||||
@admin.register(StepApproverRequirement)
|
@admin.register(StepApproverRequirement)
|
||||||
class StepApproverRequirementAdmin(admin.ModelAdmin):
|
class StepApproverRequirementAdmin(admin.ModelAdmin):
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
from django import forms
|
|
||||||
from .models import ProcessInstance, StepInstance
|
|
||||||
|
|
||||||
class ProcessInstanceForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = ProcessInstance
|
|
||||||
fields = ['description', 'process', 'well', 'representative', 'requester', 'priority', 'status', 'current_step']
|
|
||||||
widgets = {
|
|
||||||
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
||||||
'process': forms.Select(attrs={'class': 'form-control'}),
|
|
||||||
'well': forms.Select(attrs={'class': 'form-control'}),
|
|
||||||
'representative': forms.Select(attrs={'class': 'form-control'}),
|
|
||||||
'requester': forms.Select(attrs={'class': 'form-control'}),
|
|
||||||
'priority': forms.Select(attrs={'class': 'form-control'}),
|
|
||||||
'status': forms.Select(attrs={'class': 'form-control'}),
|
|
||||||
'current_step': forms.Select(attrs={'class': 'form-control'}),
|
|
||||||
}
|
|
||||||
|
|
||||||
class StepInstanceForm(forms.ModelForm):
|
|
||||||
class Meta:
|
|
||||||
model = StepInstance
|
|
||||||
fields = ['status', 'notes']
|
|
||||||
widgets = {
|
|
||||||
'status': forms.Select(attrs={'class': 'form-control'}),
|
|
||||||
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3})
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-14 09:02
|
# Generated by Django 5.2.4 on 2025-09-07 07:35
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import simple_history.models
|
import simple_history.models
|
||||||
|
@ -11,6 +11,7 @@ class Migration(migrations.Migration):
|
||||||
initial = True
|
initial = True
|
||||||
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
('accounts', '0001_initial'),
|
||||||
('wells', '0001_initial'),
|
('wells', '0001_initial'),
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||||
]
|
]
|
||||||
|
@ -231,42 +232,17 @@ class Migration(migrations.Migration):
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='HistoricalStepRevision',
|
name='StepApproverRequirement',
|
||||||
fields=[
|
|
||||||
('id', models.BigIntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
|
|
||||||
('changes_description', models.TextField(help_text='توضیح تغییراتی که برای اصلاح انجام شده', verbose_name='توضیح تغییرات')),
|
|
||||||
('created_at', models.DateTimeField(blank=True, editable=False, 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)),
|
|
||||||
('revised_by', 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='اصلاح کننده')),
|
|
||||||
('step_instance', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='processes.stepinstance', verbose_name='نمونه مرحله')),
|
|
||||||
('rejection', models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='processes.steprejection', 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='StepRevision',
|
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('changes_description', models.TextField(help_text='توضیح تغییراتی که برای اصلاح انجام شده', verbose_name='توضیح تغییرات')),
|
('required_count', models.PositiveIntegerField(default=1, verbose_name='تعداد موردنیاز')),
|
||||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ اصلاح')),
|
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='accounts.role', verbose_name='نقش تاییدکننده')),
|
||||||
('rejection', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='revisions', to='processes.steprejection', verbose_name='رد شدن مربوطه')),
|
('step', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='approver_requirements', to='processes.processstep', verbose_name='مرحله')),
|
||||||
('revised_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='step_revisions', to=settings.AUTH_USER_MODEL, verbose_name='اصلاح کننده')),
|
|
||||||
('step_instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='revisions', to='processes.stepinstance', verbose_name='نمونه مرحله')),
|
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'بازبینی مرحله',
|
'verbose_name': 'نیازمندی تایید نقش',
|
||||||
'verbose_name_plural': 'بازبینی\u200cهای مراحل',
|
'verbose_name_plural': 'نیازمندی\u200cهای تایید نقش',
|
||||||
'ordering': ['-created_at'],
|
'unique_together': {('step', 'role')},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
|
@ -284,4 +260,21 @@ class Migration(migrations.Migration):
|
||||||
'unique_together': {('dependent_step', 'dependency_step')},
|
'unique_together': {('dependent_step', 'dependency_step')},
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='StepApproval',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('decision', models.CharField(choices=[('approved', 'تایید'), ('rejected', 'رد')], max_length=8, verbose_name='نتیجه')),
|
||||||
|
('reason', models.TextField(blank=True, verbose_name='علت (برای رد)')),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ')),
|
||||||
|
('approved_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='تاییدکننده')),
|
||||||
|
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='accounts.role', verbose_name='نقش')),
|
||||||
|
('step_instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='approvals', to='processes.stepinstance', verbose_name='نمونه مرحله')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'تایید مرحله',
|
||||||
|
'verbose_name_plural': 'تاییدهای مرحله',
|
||||||
|
'unique_together': {('step_instance', 'role')},
|
||||||
|
},
|
||||||
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
# Generated by Django 5.2.4 on 2025-09-01 10:33
|
|
||||||
|
|
||||||
import django.db.models.deletion
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('accounts', '0003_historicalprofile_bank_name_profile_bank_name'),
|
|
||||||
('processes', '0001_initial'),
|
|
||||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='StepApproval',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('decision', models.CharField(choices=[('approved', 'تایید'), ('rejected', 'رد')], max_length=8, verbose_name='نتیجه')),
|
|
||||||
('reason', models.TextField(blank=True, verbose_name='علت (برای رد)')),
|
|
||||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ')),
|
|
||||||
('approved_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='تاییدکننده')),
|
|
||||||
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='accounts.role', verbose_name='نقش')),
|
|
||||||
('step_instance', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='approvals', to='processes.stepinstance', verbose_name='نمونه مرحله')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'تایید مرحله',
|
|
||||||
'verbose_name_plural': 'تاییدهای مرحله',
|
|
||||||
'unique_together': {('step_instance', 'role')},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='StepApproverRequirement',
|
|
||||||
fields=[
|
|
||||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('required_count', models.PositiveIntegerField(default=1, verbose_name='تعداد موردنیاز')),
|
|
||||||
('role', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='accounts.role', verbose_name='نقش تاییدکننده')),
|
|
||||||
('step', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='approver_requirements', to='processes.processstep', verbose_name='مرحله')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'نیازمندی تایید نقش',
|
|
||||||
'verbose_name_plural': 'نیازمندی\u200cهای تایید نقش',
|
|
||||||
'unique_together': {('step', 'role')},
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -68,6 +68,7 @@ class ProcessStep(NameSlugModel):
|
||||||
"""دریافت مراحلی که به این مرحله وابسته هستند"""
|
"""دریافت مراحلی که به این مرحله وابسته هستند"""
|
||||||
return StepDependency.objects.filter(dependency_step=self).values_list('dependent_step', flat=True)
|
return StepDependency.objects.filter(dependency_step=self).values_list('dependent_step', flat=True)
|
||||||
|
|
||||||
|
|
||||||
class StepDependency(models.Model):
|
class StepDependency(models.Model):
|
||||||
"""مدل وابستگی بین مراحل"""
|
"""مدل وابستگی بین مراحل"""
|
||||||
dependent_step = models.ForeignKey(
|
dependent_step = models.ForeignKey(
|
||||||
|
@ -295,6 +296,7 @@ class ProcessInstance(SluggedModel):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class StepInstance(models.Model):
|
class StepInstance(models.Model):
|
||||||
"""مدل نمونه مرحله (برای هر مرحله در هر درخواست)"""
|
"""مدل نمونه مرحله (برای هر مرحله در هر درخواست)"""
|
||||||
process_instance = models.ForeignKey(ProcessInstance, on_delete=models.CASCADE, related_name='step_instances', verbose_name="نمونه فرآیند")
|
process_instance = models.ForeignKey(ProcessInstance, on_delete=models.CASCADE, related_name='step_instances', verbose_name="نمونه فرآیند")
|
||||||
|
@ -378,6 +380,7 @@ class StepInstance(models.Model):
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
class StepRejection(models.Model):
|
class StepRejection(models.Model):
|
||||||
"""مدل رد شدن مرحله"""
|
"""مدل رد شدن مرحله"""
|
||||||
step_instance = models.ForeignKey(
|
step_instance = models.ForeignKey(
|
||||||
|
@ -415,41 +418,6 @@ class StepRejection(models.Model):
|
||||||
self.step_instance.save()
|
self.step_instance.save()
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
class StepRevision(models.Model):
|
|
||||||
"""مدل بازبینی و اصلاح مرحله"""
|
|
||||||
step_instance = models.ForeignKey(
|
|
||||||
StepInstance,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='revisions',
|
|
||||||
verbose_name="نمونه مرحله"
|
|
||||||
)
|
|
||||||
rejection = models.ForeignKey(
|
|
||||||
StepRejection,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
related_name='revisions',
|
|
||||||
verbose_name="رد شدن مربوطه"
|
|
||||||
)
|
|
||||||
revised_by = models.ForeignKey(
|
|
||||||
User,
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
verbose_name="اصلاح کننده",
|
|
||||||
related_name='step_revisions'
|
|
||||||
)
|
|
||||||
changes_description = models.TextField(
|
|
||||||
verbose_name="توضیح تغییرات",
|
|
||||||
help_text="توضیح تغییراتی که برای اصلاح انجام شده"
|
|
||||||
)
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ اصلاح")
|
|
||||||
history = HistoricalRecords()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
verbose_name = "بازبینی مرحله"
|
|
||||||
verbose_name_plural = "بازبینیهای مراحل"
|
|
||||||
ordering = ['-created_at']
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return f"بازبینی {self.step_instance} توسط {self.revised_by.get_full_name()}"
|
|
||||||
|
|
||||||
|
|
||||||
class StepApproverRequirement(models.Model):
|
class StepApproverRequirement(models.Model):
|
||||||
"""Required approver roles for a step."""
|
"""Required approver roles for a step."""
|
||||||
|
|
|
@ -28,17 +28,113 @@
|
||||||
|
|
||||||
<div class="container-xxl flex-grow-1 container-p-y">
|
<div class="container-xxl flex-grow-1 container-p-y">
|
||||||
|
|
||||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
<div class="row py-3 mb-4 card-header flex-column flex-md-row pb-0">
|
||||||
<h4 class="mb-0">درخواستها</h4>
|
<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 d-none" type="button">
|
||||||
|
<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" data-bs-toggle="modal" data-bs-target="#requestModal">
|
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#requestModal">
|
||||||
<i class="bx bx-plus"></i>
|
<i class="bx bx-plus me-1"></i>
|
||||||
درخواست جدید
|
درخواست جدید
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Summary Cards -->
|
||||||
|
<div class="row g-4 mb-4">
|
||||||
|
<div class="col-sm-6 col-xl-3">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex align-items-start justify-content-between">
|
||||||
|
<div class="content-left">
|
||||||
|
<span>کل درخواستها</span>
|
||||||
|
<div class="d-flex align-items-end mt-2">
|
||||||
|
<h4 class="mb-0 me-2">{{ total_count }}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="avatar">
|
||||||
|
<span class="avatar-initial rounded bg-label-primary">
|
||||||
|
<i class="bx bx-list-ul bx-sm"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 col-xl-3">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex align-items-start justify-content-between">
|
||||||
|
<div class="content-left">
|
||||||
|
<span>تکمیلشده</span>
|
||||||
|
<div class="d-flex align-items-end mt-2">
|
||||||
|
<h4 class="mb-0 me-2">{{ completed_count }}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="avatar">
|
||||||
|
<span class="avatar-initial rounded bg-label-success">
|
||||||
|
<i class="bx bx-badge-check bx-sm"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 col-xl-3">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex align-items-start justify-content-between">
|
||||||
|
<div class="content-left">
|
||||||
|
<span>در حال انجام</span>
|
||||||
|
<div class="d-flex align-items-end mt-2">
|
||||||
|
<h4 class="mb-0 me-2">{{ in_progress_count }}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="avatar">
|
||||||
|
<span class="avatar-initial rounded bg-label-info">
|
||||||
|
<i class="bx bx-loader-circle bx-sm"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6 col-xl-3">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="d-flex align-items-start justify-content-between">
|
||||||
|
<div class="content-left">
|
||||||
|
<span>در انتظار</span>
|
||||||
|
<div class="d-flex align-items-end mt-2">
|
||||||
|
<h4 class="mb-0 me-2">{{ pending_count }}</h4>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="avatar">
|
||||||
|
<span class="avatar-initial rounded bg-label-warning">
|
||||||
|
<i class="bx bx-time bx-sm"></i>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="table-responsive">
|
<div class="card-datatable table-responsive">
|
||||||
<table id="requestsTable" class="table table-striped">
|
<table id="requests-table" class="datatables-basic table border-top">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>شناسه</th>
|
<th>شناسه</th>
|
||||||
|
@ -46,8 +142,8 @@
|
||||||
<th>مرحله فعلی</th>
|
<th>مرحله فعلی</th>
|
||||||
<th>شماره اشتراک آب</th>
|
<th>شماره اشتراک آب</th>
|
||||||
<th>نماینده</th>
|
<th>نماینده</th>
|
||||||
<th>درخواستکننده</th>
|
<th>استان</th>
|
||||||
<th>اولویت</th>
|
<th>امور</th>
|
||||||
<th>وضعیت</th>
|
<th>وضعیت</th>
|
||||||
<th>تاریخ ایجاد</th>
|
<th>تاریخ ایجاد</th>
|
||||||
<th>عملیات</th>
|
<th>عملیات</th>
|
||||||
|
@ -61,9 +157,9 @@
|
||||||
<td class="text-primary">{{ inst.current_step.name|default:"--" }}</td>
|
<td class="text-primary">{{ inst.current_step.name|default:"--" }}</td>
|
||||||
<td>{{ inst.well.water_subscription_number }}</td>
|
<td>{{ inst.well.water_subscription_number }}</td>
|
||||||
<td>{% if inst.representative %}{{ inst.representative.get_full_name }}{% else %}-{% endif %}</td>
|
<td>{% if inst.representative %}{{ inst.representative.get_full_name }}{% else %}-{% endif %}</td>
|
||||||
<td>{% if inst.requester %}{{ inst.requester.get_full_name }}{% else %}-{% endif %}</td>
|
<td>{% if inst.well and inst.well.county %}{{ inst.well.county }}{% else %}-{% endif %}</td>
|
||||||
<td>{{ inst.get_priority_display }}</td>
|
<td>{% if inst.well and inst.well.affairs %}{{ inst.well.affairs }}{% else %}-{% endif %}</td>
|
||||||
<td>{{ inst.get_status_display }}</td>
|
<td>{{ inst.get_status_display_with_color|safe }}</td>
|
||||||
<td>{{ inst.jcreated }}</td>
|
<td>{{ inst.jcreated }}</td>
|
||||||
<td>
|
<td>
|
||||||
<div class="d-inline-block">
|
<div class="d-inline-block">
|
||||||
|
@ -126,7 +222,7 @@
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<label class="form-label">شماره اشتراک آب</label>
|
<label class="form-label">شماره اشتراک آب</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="form-control" id="req_water_sub" placeholder="مثال: 12345" required>
|
<input type="text" class="form-control" id="req_water_sub" name="water_subscription_number" data-field="water_subscription_number" placeholder="مثال: 12345" required>
|
||||||
<button class="btn btn-outline-secondary" type="button" id="btnLookupWell">
|
<button class="btn btn-outline-secondary" type="button" id="btnLookupWell">
|
||||||
بررسی/افزودن چاه
|
بررسی/افزودن چاه
|
||||||
</button>
|
</button>
|
||||||
|
@ -217,7 +313,7 @@
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<label class="form-label">کد ملی نماینده</label>
|
<label class="form-label">کد ملی نماینده</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input type="text" class="form-control" id="rep_national_code" placeholder="مثال: 0012345678">
|
<input type="text" class="form-control" id="rep_national_code" data-field="national_code" placeholder="مثال: 0012345678" maxlength="10" inputmode="numeric" pattern="\d*">
|
||||||
<button class="btn btn-outline-secondary" type="button" id="btnLookupRep">
|
<button class="btn btn-outline-secondary" type="button" id="btnLookupRep">
|
||||||
بررسی/افزودن نماینده
|
بررسی/افزودن نماینده
|
||||||
</button>
|
</button>
|
||||||
|
@ -268,7 +364,7 @@
|
||||||
<hr class="mt-3 border border-dashed">
|
<hr class="mt-3 border border-dashed">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
<label class="form-label">توضیحات</label>
|
<label class="form-label">توضیحات</label>
|
||||||
<textarea class="form-control" rows="3" id="req_description"></textarea>
|
<textarea class="form-control" rows="3" id="req_description" name="description"></textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
@ -361,19 +457,13 @@
|
||||||
|
|
||||||
|
|
||||||
$(function() {
|
$(function() {
|
||||||
// if ($.fn.DataTable) {
|
// Initialize DataTable similar to customer_list
|
||||||
// try {
|
$('#requests-table').DataTable({
|
||||||
// $('#requestsTable').DataTable({
|
pageLength: 10,
|
||||||
// pageLength: 10,
|
lengthMenu: [[10, 25, 50, -1], [10, 25, 50, "همه"]],
|
||||||
// order: [[0, 'desc']]
|
order: [[0, 'desc']],
|
||||||
// });
|
responsive: true,
|
||||||
// } catch (e) {
|
});
|
||||||
// console.error('DataTable init failed', e);
|
|
||||||
// }
|
|
||||||
// } else {
|
|
||||||
// console.warn('DataTables library not loaded');
|
|
||||||
// }
|
|
||||||
|
|
||||||
let currentWellId = null;
|
let currentWellId = null;
|
||||||
let currentRepId = null;
|
let currentRepId = null;
|
||||||
let wellChecked = false;
|
let wellChecked = false;
|
||||||
|
@ -399,7 +489,7 @@
|
||||||
if (!$el.length) return false;
|
if (!$el.length) return false;
|
||||||
$el.addClass('is-invalid');
|
$el.addClass('is-invalid');
|
||||||
const $feedback = $('<div class="invalid-feedback inline-error"></div>').text(message);
|
const $feedback = $('<div class="invalid-feedback inline-error"></div>').text(message);
|
||||||
const $grp = $el.closest('.input-group');
|
const $grp = $el.closest('.input-group, .form-group, .mb-3');
|
||||||
if ($grp.length) {
|
if ($grp.length) {
|
||||||
$feedback.insertAfter($grp);
|
$feedback.insertAfter($grp);
|
||||||
} else {
|
} else {
|
||||||
|
@ -408,60 +498,49 @@
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapWellFieldToSelector(field) {
|
// Generic field resolution with small exception map
|
||||||
switch (field) {
|
const exceptionMap = {
|
||||||
case 'water_subscription_number': return '#req_water_sub';
|
water_subscription_number: '#req_water_sub',
|
||||||
case 'electricity_subscription_number': return '#id_electricity_subscription_number';
|
national_code: '#rep_national_code',
|
||||||
case 'water_meter_serial_number': return '#id_water_meter_serial_number';
|
representative: '#rep_national_code'
|
||||||
case 'water_meter_old_serial_number': return '#id_water_meter_old_serial_number';
|
};
|
||||||
case 'water_meter_manufacturer': return '#id_water_meter_manufacturer';
|
|
||||||
case 'new_manufacturer': return '#id_new_manufacturer';
|
|
||||||
case 'utm_x': return '#id_utm_x';
|
|
||||||
case 'utm_y': return '#id_utm_y';
|
|
||||||
case 'utm_zone': return '#id_utm_zone';
|
|
||||||
case 'utm_hemisphere': return '#id_utm_hemisphere';
|
|
||||||
case 'well_power': return '#id_well_power';
|
|
||||||
case 'reference_letter_number': return '#id_reference_letter_number';
|
|
||||||
case 'reference_letter_date': return '#id_reference_letter_date';
|
|
||||||
case 'representative_letter_file': return '#id_representative_letter_file';
|
|
||||||
case 'representative': return '#rep_national_code';
|
|
||||||
default: return '#id_' + field;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapCustomerFieldToSelector(field) {
|
function findFieldSelector(field, context) {
|
||||||
switch (field) {
|
const $ctx = context ? $(context) : $('#requestModal');
|
||||||
case 'national_code': return $('#id_national_code').length ? '#id_national_code' : '#rep_national_code';
|
let $el = $ctx.find(`#id_${field}`).first();
|
||||||
case 'first_name': return '#id_first_name';
|
if ($el.length) return $el;
|
||||||
case 'last_name': return '#id_last_name';
|
$el = $ctx.find(`[name="${field}"]`).first();
|
||||||
case 'phone_number_1': return '#id_phone_number_1';
|
if ($el.length) return $el;
|
||||||
case 'phone_number_2': return '#id_phone_number_2';
|
$el = $ctx.find(`[data-field="${field}"]`).first();
|
||||||
case 'card_number': return '#id_card_number';
|
if ($el.length) return $el;
|
||||||
case 'account_number': return '#id_account_number';
|
const ex = exceptionMap[field];
|
||||||
case 'bank_name': return '#id_bank_name';
|
return ex ? $(ex) : $();
|
||||||
case 'address': return '#id_address';
|
|
||||||
default: return '#id_' + field;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showInlineErrors(errors) {
|
function showInlineErrors(errors) {
|
||||||
if (!errors) return;
|
if (!errors) return;
|
||||||
let nonFieldWell = '';
|
let nonFieldWell = '';
|
||||||
let nonFieldCustomer = '';
|
let nonFieldCustomer = '';
|
||||||
|
// Request-level errors (e.g., process)
|
||||||
|
if (errors.request) {
|
||||||
|
for (const key in errors.request) {
|
||||||
|
const msgs = Array.isArray(errors.request[key]) ? errors.request[key] : [errors.request[key]];
|
||||||
|
if (key === '__all__' || key === 'non_field_errors') { continue; }
|
||||||
|
applyErrorTo(findFieldSelector(key, '#requestForm'), msgs[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (errors.well) {
|
if (errors.well) {
|
||||||
for (const key in errors.well) {
|
for (const key in errors.well) {
|
||||||
const msgs = Array.isArray(errors.well[key]) ? errors.well[key] : [errors.well[key]];
|
const msgs = Array.isArray(errors.well[key]) ? errors.well[key] : [errors.well[key]];
|
||||||
if (key === '__all__' || key === 'non_field_errors') { nonFieldWell = msgs.join('، '); continue; }
|
if (key === '__all__' || key === 'non_field_errors') { nonFieldWell = msgs.join('، '); continue; }
|
||||||
const sel = mapWellFieldToSelector(key);
|
applyErrorTo(findFieldSelector(key, '#wellFormBlock'), msgs[0]);
|
||||||
applyErrorTo(sel, msgs[0]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (errors.customer) {
|
if (errors.customer) {
|
||||||
for (const key in errors.customer) {
|
for (const key in errors.customer) {
|
||||||
const msgs = Array.isArray(errors.customer[key]) ? errors.customer[key] : [errors.customer[key]];
|
const msgs = Array.isArray(errors.customer[key]) ? errors.customer[key] : [errors.customer[key]];
|
||||||
if (key === '__all__' || key === 'non_field_errors') { nonFieldCustomer = msgs.join('، '); continue; }
|
if (key === '__all__' || key === 'non_field_errors') { nonFieldCustomer = msgs.join('، '); continue; }
|
||||||
const sel = mapCustomerFieldToSelector(key);
|
applyErrorTo(findFieldSelector(key, '#repNewFields'), msgs[0]);
|
||||||
applyErrorTo(sel, msgs[0]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (nonFieldWell) setStatus('#wellStatus', nonFieldWell, 'danger');
|
if (nonFieldWell) setStatus('#wellStatus', nonFieldWell, 'danger');
|
||||||
|
@ -536,7 +615,7 @@
|
||||||
$('#remove-file').val('false');
|
$('#remove-file').val('false');
|
||||||
// Initialize Persian Date Picker after well form is shown
|
// Initialize Persian Date Picker after well form is shown
|
||||||
setTimeout(initPersianDatePicker, 100);
|
setTimeout(initPersianDatePicker, 100);
|
||||||
setStatus('#wellStatus', 'چاه یافت نشد. با ذخیره، ایجاد خواهد شد.', 'danger');
|
setStatus('#wellStatus', 'چاه یافت نشد. اطلاعات چاه را وارد کنید.', 'danger');
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fail(function(){ setStatus('#wellStatus', 'خطا در بررسی چاه', 'danger'); });
|
.fail(function(){ setStatus('#wellStatus', 'خطا در بررسی چاه', 'danger'); });
|
||||||
|
@ -594,46 +673,26 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
$('#btnSaveRequest').on('click', function(){
|
$('#btnSaveRequest').on('click', function(){
|
||||||
const formData = new FormData();
|
clearInlineErrors();
|
||||||
formData.append('csrfmiddlewaretoken', $('input[name=csrfmiddlewaretoken]').val());
|
// Use form's native FormData - much cleaner!
|
||||||
formData.append('process', $('#req_process').val());
|
const formData = new FormData(document.getElementById('requestForm'));
|
||||||
formData.append('description', $('#req_description').val());
|
|
||||||
formData.append('water_subscription_number', $('#req_water_sub').val().trim());
|
// Add custom fields that aren't in the form
|
||||||
if (currentWellId) formData.append('well_id', currentWellId);
|
if (currentWellId) formData.append('well_id', currentWellId);
|
||||||
if (currentRepId) formData.append('representative_id', currentRepId);
|
if (currentRepId) formData.append('representative_id', currentRepId);
|
||||||
// Send fields using CustomerForm names if visible
|
|
||||||
const ncField = $('#id_national_code').length ? $('#id_national_code').val() : '';
|
// Handle special national_code logic (prefer visible field)
|
||||||
formData.append('national_code', (ncField || $('#rep_national_code').val().trim()));
|
const ncField = $('#id_national_code').val();
|
||||||
formData.append('first_name', $('#id_first_name').val() || '');
|
if (ncField) {
|
||||||
formData.append('last_name', $('#id_last_name').val() || '');
|
formData.set('national_code', ncField);
|
||||||
formData.append('phone_number_1', $('#id_phone_number_1').val() || '');
|
} else {
|
||||||
formData.append('phone_number_2', $('#id_phone_number_2').val() || '');
|
formData.set('national_code', $('#rep_national_code').val().trim());
|
||||||
formData.append('card_number', $('#id_card_number').val() || '');
|
|
||||||
formData.append('account_number', $('#id_account_number').val() || '');
|
|
||||||
formData.append('address', $('#id_address').val() || '');
|
|
||||||
formData.append('bank_name', $('#id_bank_name').val() || '');
|
|
||||||
// Include WellForm fields so edits are saved
|
|
||||||
if ($('#wellFormBlock').is(':visible')) {
|
|
||||||
formData.append('electricity_subscription_number', $('#id_electricity_subscription_number').val() || '');
|
|
||||||
formData.append('water_meter_serial_number', $('#id_water_meter_serial_number').val() || '');
|
|
||||||
formData.append('water_meter_old_serial_number', $('#id_water_meter_old_serial_number').val() || '');
|
|
||||||
formData.append('water_meter_manufacturer', $('#id_water_meter_manufacturer').is(':visible') ? ($('#id_water_meter_manufacturer').val() || '') : '');
|
|
||||||
formData.append('new_manufacturer', $('#id_new_manufacturer').is(':visible') ? ($('#id_new_manufacturer').val() || '') : '');
|
|
||||||
formData.append('utm_x', $('#id_utm_x').val() || '');
|
|
||||||
formData.append('utm_y', $('#id_utm_y').val() || '');
|
|
||||||
formData.append('utm_zone', $('#id_utm_zone').val() || '');
|
|
||||||
formData.append('utm_hemisphere', $('#id_utm_hemisphere').val() || '');
|
|
||||||
formData.append('well_power', $('#id_well_power').val() || '');
|
|
||||||
formData.append('reference_letter_number', $('#id_reference_letter_number').val() || '');
|
|
||||||
// Use gregorian date if available, otherwise use the field value
|
|
||||||
const gregorianDate = $('#id_reference_letter_date').attr('data-gregorian');
|
|
||||||
formData.append('reference_letter_date', gregorianDate || $('#id_reference_letter_date').val() || '');
|
|
||||||
// Remove flag
|
|
||||||
formData.append('remove_file', $('#remove-file').val() || 'false');
|
|
||||||
const repFile = document.getElementById('id_representative_letter_file');
|
|
||||||
if (repFile && repFile.files && repFile.files[0]) {
|
|
||||||
formData.append('representative_letter_file', repFile.files[0]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle Persian date conversion
|
||||||
|
const gregorianDate = $('#id_reference_letter_date').attr('data-gregorian');
|
||||||
|
if (gregorianDate) {
|
||||||
|
formData.set('reference_letter_date', gregorianDate);
|
||||||
}
|
}
|
||||||
|
|
||||||
const $btn = $(this).prop('disabled', true).text('در حال ذخیره...');
|
const $btn = $(this).prop('disabled', true).text('در حال ذخیره...');
|
||||||
|
@ -652,6 +711,10 @@
|
||||||
setTimeout(function(){ location.reload(); }, 1200);
|
setTimeout(function(){ location.reload(); }, 1200);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
clearInlineErrors();
|
||||||
|
if (resp.errors) {
|
||||||
|
showInlineErrors(resp.errors);
|
||||||
|
}
|
||||||
const msg = buildErrorMessage(resp);
|
const msg = buildErrorMessage(resp);
|
||||||
showToast(msg, 'danger');
|
showToast(msg, 'danger');
|
||||||
}
|
}
|
||||||
|
@ -659,6 +722,10 @@
|
||||||
let msg = 'خطا در ذخیره';
|
let msg = 'خطا در ذخیره';
|
||||||
try {
|
try {
|
||||||
const resp = JSON.parse(xhr.responseText);
|
const resp = JSON.parse(xhr.responseText);
|
||||||
|
clearInlineErrors();
|
||||||
|
if (resp && resp.errors) {
|
||||||
|
showInlineErrors(resp.errors);
|
||||||
|
}
|
||||||
msg = buildErrorMessage(resp) || msg;
|
msg = buildErrorMessage(resp) || msg;
|
||||||
} catch(e) {}
|
} catch(e) {}
|
||||||
showToast(msg, 'danger');
|
showToast(msg, 'danger');
|
||||||
|
@ -715,6 +782,14 @@
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Enforce digit-only and max length for national code input
|
||||||
|
$('#rep_national_code').on('input', function() {
|
||||||
|
const cleaned = (this.value || '').replace(/\D/g, '').slice(0, 10);
|
||||||
|
if (this.value !== cleaned) {
|
||||||
|
this.value = cleaned;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$('#requestModal').on('hidden.bs.modal', function(){
|
$('#requestModal').on('hidden.bs.modal', function(){
|
||||||
$('#requestForm')[0].reset();
|
$('#requestForm')[0].reset();
|
||||||
$('#wellFormBlock').hide();
|
$('#wellFormBlock').hide();
|
||||||
|
|
|
@ -16,10 +16,4 @@ urlpatterns = [
|
||||||
path('instance/<int:instance_id>/step/<int:step_id>/', views.step_detail, name='step_detail'),
|
path('instance/<int:instance_id>/step/<int:step_id>/', views.step_detail, name='step_detail'),
|
||||||
path('instance/<int:instance_id>/summary/', views.instance_summary, name='instance_summary'),
|
path('instance/<int:instance_id>/summary/', views.instance_summary, name='instance_summary'),
|
||||||
|
|
||||||
# Legacy process views
|
|
||||||
path('', views.process_list, name='process_list'),
|
|
||||||
path('<int:process_id>/', views.process_detail, name='process_detail'),
|
|
||||||
path('<int:process_id>/start/', views.start_process, name='start_process'),
|
|
||||||
path('instance/<int:instance_id>/', views.instance_detail, name='instance_detail'),
|
|
||||||
path('my-processes/', views.my_processes, name='my_processes'),
|
|
||||||
]
|
]
|
|
@ -1,7 +1,6 @@
|
||||||
from django.shortcuts import render, get_object_or_404, redirect
|
from django.shortcuts import render, get_object_or_404, redirect
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils import timezone
|
|
||||||
import json
|
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.http import JsonResponse
|
from django.http import JsonResponse
|
||||||
|
@ -11,41 +10,32 @@ from django.contrib.auth import get_user_model
|
||||||
from .models import Process, ProcessInstance, StepInstance
|
from .models import Process, ProcessInstance, StepInstance
|
||||||
from wells.models import Well
|
from wells.models import Well
|
||||||
from accounts.models import Profile
|
from accounts.models import Profile
|
||||||
from .forms import ProcessInstanceForm
|
|
||||||
from accounts.forms import CustomerForm
|
from accounts.forms import CustomerForm
|
||||||
from wells.forms import WellForm
|
from wells.forms import WellForm
|
||||||
from wells.models import WaterMeterManufacturer
|
from wells.models import WaterMeterManufacturer
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def process_list(request):
|
|
||||||
"""نمایش لیست فرآیندهای فعال"""
|
|
||||||
processes = Process.objects.filter(is_active=True)
|
|
||||||
return render(request, 'processes/process_list.html', {
|
|
||||||
'processes': processes
|
|
||||||
})
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def process_detail(request, process_id):
|
|
||||||
"""نمایش جزئیات فرآیند"""
|
|
||||||
process = get_object_or_404(Process, id=process_id, is_active=True)
|
|
||||||
return render(request, 'processes/process_detail.html', {
|
|
||||||
'process': process
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def request_list(request):
|
def request_list(request):
|
||||||
"""نمایش لیست درخواستها با جدول و مدال ایجاد"""
|
"""نمایش لیست درخواستها با جدول و مدال ایجاد"""
|
||||||
instances = ProcessInstance.objects.select_related('well', 'representative', 'requester').filter(is_deleted=False).order_by('-created')
|
instances = ProcessInstance.objects.select_related('well', 'representative', 'requester').filter(is_deleted=False).order_by('-created')
|
||||||
processes = Process.objects.filter(is_active=True)
|
processes = Process.objects.filter(is_active=True)
|
||||||
manufacturers = WaterMeterManufacturer.objects.all().order_by('name')
|
manufacturers = WaterMeterManufacturer.objects.all().order_by('name')
|
||||||
|
# Summary stats for header cards
|
||||||
|
total_count = instances.count()
|
||||||
|
completed_count = instances.filter(status='completed').count()
|
||||||
|
in_progress_count = instances.filter(status='in_progress').count()
|
||||||
|
pending_count = instances.filter(status='pending').count()
|
||||||
return render(request, 'processes/request_list.html', {
|
return render(request, 'processes/request_list.html', {
|
||||||
'instances': instances,
|
'instances': instances,
|
||||||
'customer_form': CustomerForm(),
|
'customer_form': CustomerForm(),
|
||||||
'well_form': WellForm(),
|
'well_form': WellForm(),
|
||||||
'processes': processes,
|
'processes': processes,
|
||||||
'manufacturers': manufacturers
|
'manufacturers': manufacturers,
|
||||||
|
'total_count': total_count,
|
||||||
|
'completed_count': completed_count,
|
||||||
|
'in_progress_count': in_progress_count,
|
||||||
|
'pending_count': pending_count,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
@ -127,23 +117,12 @@ def create_request_with_entities(request):
|
||||||
well_id = request.POST.get('well_id') # optional if existing
|
well_id = request.POST.get('well_id') # optional if existing
|
||||||
# Representative fields
|
# Representative fields
|
||||||
representative_id = request.POST.get('representative_id')
|
representative_id = request.POST.get('representative_id')
|
||||||
# Prefer plain CustomerForm keys; fallback to representative_* keys
|
|
||||||
representative_national_code = request.POST.get('national_code') or request.POST.get('representative_national_code')
|
|
||||||
representative_first_name = request.POST.get('first_name') or request.POST.get('representative_first_name')
|
|
||||||
representative_last_name = request.POST.get('last_name') or request.POST.get('representative_last_name')
|
|
||||||
representative_username = request.POST.get('username') or request.POST.get('representative_username')
|
|
||||||
representative_phone_number_1 = request.POST.get('phone_number_1') or request.POST.get('representative_phone_number_1')
|
|
||||||
representative_phone_number_2 = request.POST.get('phone_number_2') or request.POST.get('representative_phone_number_2')
|
|
||||||
representative_card_number = request.POST.get('card_number') or request.POST.get('representative_card_number')
|
|
||||||
representative_account_number = request.POST.get('account_number') or request.POST.get('representative_account_number')
|
|
||||||
representative_bank_name = request.POST.get('bank_name') or request.POST.get('representative_bank_name')
|
|
||||||
representative_address = request.POST.get('address') or request.POST.get('representative_address')
|
|
||||||
|
|
||||||
if not process_id:
|
if not process_id:
|
||||||
return JsonResponse({'ok': False, 'errors': {'request': {'process': ['فرآیند الزامی است']}}}, status=400)
|
return JsonResponse({'ok': False, 'errors': {'request': {'process': ['فرآیند الزامی است']}}}, status=400)
|
||||||
if not water_subscription_number:
|
if not water_subscription_number:
|
||||||
return JsonResponse({'ok': False, 'errors': {'well': {'water_subscription_number': ['شماره اشتراک آب الزامی است']}}}, status=400)
|
return JsonResponse({'ok': False, 'errors': {'well': {'water_subscription_number': ['شماره اشتراک آب الزامی است']}}}, status=400)
|
||||||
if not representative_id and not representative_national_code:
|
if not representative_id and not request.POST.get('national_code'):
|
||||||
return JsonResponse({'ok': False, 'errors': {'customer': {'national_code': ['کد ملی نماینده را وارد کنید یا دکمه بررسی/افزودن نماینده را بزنید']}}}, status=400)
|
return JsonResponse({'ok': False, 'errors': {'customer': {'national_code': ['کد ملی نماینده را وارد کنید یا دکمه بررسی/افزودن نماینده را بزنید']}}}, status=400)
|
||||||
|
|
||||||
representative_user = None
|
representative_user = None
|
||||||
|
@ -152,52 +131,20 @@ def create_request_with_entities(request):
|
||||||
representative_profile = Profile.objects.select_related('user').filter(user_id=representative_id).first()
|
representative_profile = Profile.objects.select_related('user').filter(user_id=representative_id).first()
|
||||||
if not representative_profile:
|
if not representative_profile:
|
||||||
return JsonResponse({'ok': False, 'errors': {'customer': {'__all__': ['نماینده انتخابشده یافت نشد']}}}, status=400)
|
return JsonResponse({'ok': False, 'errors': {'customer': {'__all__': ['نماینده انتخابشده یافت نشد']}}}, status=400)
|
||||||
|
# Use CustomerForm with request.POST data, merging with existing values
|
||||||
|
customer_form = CustomerForm(request.POST, instance=representative_profile)
|
||||||
|
customer_form.request = request
|
||||||
|
if not customer_form.is_valid():
|
||||||
|
return JsonResponse({'ok': False, 'errors': {'customer': customer_form.errors}}, status=400)
|
||||||
|
representative_profile = customer_form.save()
|
||||||
representative_user = representative_profile.user
|
representative_user = representative_profile.user
|
||||||
# Optionally update if fields provided
|
|
||||||
changed = False
|
|
||||||
if representative_first_name:
|
|
||||||
representative_user.first_name = representative_first_name
|
|
||||||
changed = True
|
|
||||||
if representative_last_name:
|
|
||||||
representative_user.last_name = representative_last_name
|
|
||||||
changed = True
|
|
||||||
if representative_username:
|
|
||||||
representative_user.username = representative_username
|
|
||||||
changed = True
|
|
||||||
if changed:
|
|
||||||
representative_user.save()
|
|
||||||
if representative_national_code:
|
|
||||||
representative_profile.national_code = representative_national_code
|
|
||||||
if representative_phone_number_1 is not None:
|
|
||||||
representative_profile.phone_number_1 = representative_phone_number_1
|
|
||||||
if representative_phone_number_2 is not None:
|
|
||||||
representative_profile.phone_number_2 = representative_phone_number_2
|
|
||||||
if representative_card_number is not None:
|
|
||||||
representative_profile.card_number = representative_card_number
|
|
||||||
if representative_account_number is not None:
|
|
||||||
representative_profile.account_number = representative_account_number
|
|
||||||
if representative_bank_name is not None:
|
|
||||||
representative_profile.bank_name = representative_bank_name
|
|
||||||
if representative_address is not None:
|
|
||||||
representative_profile.address = representative_address
|
|
||||||
representative_profile.save()
|
|
||||||
else:
|
else:
|
||||||
# Use CustomerForm to validate/create/update representative profile by national code
|
# Use CustomerForm to validate/create/update representative profile by national code
|
||||||
profile_instance = None
|
profile_instance = None
|
||||||
if representative_national_code:
|
national_code = request.POST.get('national_code')
|
||||||
profile_instance = Profile.objects.filter(national_code=representative_national_code).first()
|
if national_code:
|
||||||
customer_data = {
|
profile_instance = Profile.objects.filter(national_code=national_code).first()
|
||||||
'first_name': representative_first_name or '',
|
customer_form = CustomerForm(request.POST, instance=profile_instance)
|
||||||
'last_name': representative_last_name or '',
|
|
||||||
'phone_number_1': representative_phone_number_1 or '',
|
|
||||||
'phone_number_2': representative_phone_number_2 or '',
|
|
||||||
'national_code': representative_national_code or '',
|
|
||||||
'address': representative_address or '',
|
|
||||||
'card_number': representative_card_number or '',
|
|
||||||
'account_number': representative_account_number or '',
|
|
||||||
'bank_name': representative_bank_name or '',
|
|
||||||
}
|
|
||||||
customer_form = CustomerForm(customer_data, instance=profile_instance)
|
|
||||||
customer_form.request = request
|
customer_form.request = request
|
||||||
if not customer_form.is_valid():
|
if not customer_form.is_valid():
|
||||||
return JsonResponse({'ok': False, 'errors': {'customer': customer_form.errors}}, status=400)
|
return JsonResponse({'ok': False, 'errors': {'customer': customer_form.errors}}, status=400)
|
||||||
|
@ -292,62 +239,24 @@ def create_request_with_entities(request):
|
||||||
redirect_url = reverse('processes:instance_steps', args=[instance.id])
|
redirect_url = reverse('processes:instance_steps', args=[instance.id])
|
||||||
return JsonResponse({'ok': True, 'instance_id': instance.id, 'redirect': redirect_url})
|
return JsonResponse({'ok': True, 'instance_id': instance.id, 'redirect': redirect_url})
|
||||||
|
|
||||||
|
|
||||||
@require_POST
|
@require_POST
|
||||||
@login_required
|
@login_required
|
||||||
def delete_request(request, instance_id):
|
def delete_request(request, instance_id):
|
||||||
"""حذف درخواست"""
|
"""حذف درخواست"""
|
||||||
instance = get_object_or_404(ProcessInstance, id=instance_id)
|
instance = get_object_or_404(ProcessInstance, id=instance_id)
|
||||||
code = instance.code
|
code = instance.code
|
||||||
|
if instance.status == 'completed':
|
||||||
|
return JsonResponse({
|
||||||
|
'success': False,
|
||||||
|
'message': 'درخواست تکمیل شده نمیتواند حذف شود'
|
||||||
|
})
|
||||||
instance.delete()
|
instance.delete()
|
||||||
return JsonResponse({
|
return JsonResponse({
|
||||||
'success': True,
|
'success': True,
|
||||||
'message': f'درخواست {code} با موفقیت حذف شد'
|
'message': f'درخواست {code} با موفقیت حذف شد'
|
||||||
})
|
})
|
||||||
|
|
||||||
@login_required
|
|
||||||
def start_process(request, process_id):
|
|
||||||
"""شروع فرآیند جدید"""
|
|
||||||
process = get_object_or_404(Process, id=process_id, is_active=True)
|
|
||||||
|
|
||||||
if request.method == 'POST':
|
|
||||||
form = ProcessInstanceForm(request.POST)
|
|
||||||
if form.is_valid():
|
|
||||||
instance = form.save(commit=False)
|
|
||||||
instance.process = process
|
|
||||||
instance.requester = request.user
|
|
||||||
instance.save()
|
|
||||||
|
|
||||||
# ایجاد نمونههای مرحله
|
|
||||||
for step in process.steps.all():
|
|
||||||
StepInstance.objects.create(
|
|
||||||
process_instance=instance,
|
|
||||||
step=step
|
|
||||||
)
|
|
||||||
|
|
||||||
# تنظیم مرحله اول به عنوان مرحله فعلی
|
|
||||||
first_step = process.steps.first()
|
|
||||||
if first_step:
|
|
||||||
instance.current_step = first_step
|
|
||||||
instance.status = 'in_progress'
|
|
||||||
instance.save()
|
|
||||||
|
|
||||||
messages.success(request, f'فرآیند {process.name} با موفقیت شروع شد.')
|
|
||||||
return redirect('processes:instance_detail', instance_id=instance.id)
|
|
||||||
else:
|
|
||||||
form = ProcessInstanceForm()
|
|
||||||
|
|
||||||
return render(request, 'processes/start_process.html', {
|
|
||||||
'process': process,
|
|
||||||
'form': form
|
|
||||||
})
|
|
||||||
|
|
||||||
@login_required
|
|
||||||
def instance_detail(request, instance_id):
|
|
||||||
"""نمایش جزئیات نمونه فرآیند"""
|
|
||||||
instance = get_object_or_404(ProcessInstance, id=instance_id)
|
|
||||||
return render(request, 'processes/instance_detail.html', {
|
|
||||||
'instance': instance
|
|
||||||
})
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def step_detail(request, instance_id, step_id):
|
def step_detail(request, instance_id, step_id):
|
||||||
|
@ -409,6 +318,7 @@ def step_detail(request, instance_id, step_id):
|
||||||
'next_step': next_step,
|
'next_step': next_step,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def instance_steps(request, instance_id):
|
def instance_steps(request, instance_id):
|
||||||
"""هدایت به مرحله فعلی instance"""
|
"""هدایت به مرحله فعلی instance"""
|
||||||
|
@ -430,6 +340,7 @@ def instance_steps(request, instance_id):
|
||||||
return redirect('processes:instance_summary', instance_id=instance.id)
|
return redirect('processes:instance_summary', instance_id=instance.id)
|
||||||
return redirect('processes:step_detail', instance_id=instance.id, step_id=instance.current_step.id)
|
return redirect('processes:step_detail', instance_id=instance.id, step_id=instance.current_step.id)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def instance_summary(request, instance_id):
|
def instance_summary(request, instance_id):
|
||||||
"""نمای خلاصهٔ فقطخواندنی برای درخواستهای تکمیلشده."""
|
"""نمای خلاصهٔ فقطخواندنی برای درخواستهای تکمیلشده."""
|
||||||
|
@ -462,14 +373,3 @@ def instance_summary(request, instance_id):
|
||||||
'certificate': certificate,
|
'certificate': certificate,
|
||||||
})
|
})
|
||||||
|
|
||||||
@login_required
|
|
||||||
def my_processes(request):
|
|
||||||
"""نمایش فرآیندهای کاربر"""
|
|
||||||
my_instances = ProcessInstance.objects.filter(requester=request.user)
|
|
||||||
assigned_steps = StepInstance.objects.filter(assigned_to=request.user, status='in_progress')
|
|
||||||
|
|
||||||
return render(request, 'processes/my_processes.html', {
|
|
||||||
'my_instances': my_instances,
|
|
||||||
'assigned_steps': assigned_steps
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 5.2.4 on 2025-08-14 09:02
|
# Generated by Django 5.2.4 on 2025-09-07 07:35
|
||||||
|
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import simple_history.models
|
import simple_history.models
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue