diff --git a/_base/settings.py b/_base/settings.py index e53a6f1..8261409 100644 --- a/_base/settings.py +++ b/_base/settings.py @@ -167,7 +167,7 @@ JAZZMIN_SETTINGS = { # Copyright on the footer "copyright": "سامانه شفافیت", # Logo to use for your site, must be present in static files, used for brand on top left - "site_logo": "../static/dist/img/iconlogo.png", + # "site_logo": "../static/dist/img/iconlogo.png", # Relative paths to custom CSS/JS scripts (must be present in static files) "custom_css": "../static/admin/css/custom_rtl.css", "custom_js": None, diff --git a/accounts/migrations/0001_initial.py b/accounts/migrations/0001_initial.py index de431d4..7b35fb7 100644 --- a/accounts/migrations/0001_initial.py +++ b/accounts/migrations/0001_initial.py @@ -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.db.models.deletion @@ -17,6 +17,27 @@ class Migration(migrations.Migration): ] 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( name='HistoricalProfile', fields=[ @@ -30,6 +51,7 @@ class Migration(migrations.Migration): ('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='شماره کارت')), ('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_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='تصویر')), @@ -84,6 +106,7 @@ class Migration(migrations.Migration): ('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='شماره کارت')), ('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_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='تصویر')), diff --git a/accounts/migrations/0002_company.py b/accounts/migrations/0002_company.py deleted file mode 100644 index c944cdf..0000000 --- a/accounts/migrations/0002_company.py +++ /dev/null @@ -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ها', - }, - ), - ] diff --git a/accounts/migrations/0003_historicalprofile_bank_name_profile_bank_name.py b/accounts/migrations/0003_historicalprofile_bank_name_profile_bank_name.py deleted file mode 100644 index 6becfec..0000000 --- a/accounts/migrations/0003_historicalprofile_bank_name_profile_bank_name.py +++ /dev/null @@ -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='نام بانک'), - ), - ] diff --git a/certificates/migrations/0001_initial.py b/certificates/migrations/0001_initial.py index 83533bd..b5753ec 100644 --- a/certificates/migrations/0001_initial.py +++ b/certificates/migrations/0001_initial.py @@ -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 from django.db import migrations, models @@ -9,6 +9,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ('accounts', '0001_initial'), ('processes', '0001_initial'), ] @@ -23,10 +24,8 @@ class Migration(migrations.Migration): ('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='تاریخ حذف')), ('title', models.CharField(max_length=200, verbose_name='عنوان')), ('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='فعال')), + ('company', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.company', verbose_name='شرکت صادر کننده')), ], options={ 'verbose_name': 'قالب گواهی', diff --git a/certificates/migrations/0002_remove_certificatetemplate_company_logo_and_more.py b/certificates/migrations/0002_remove_certificatetemplate_company_logo_and_more.py deleted file mode 100644 index e929c5a..0000000 --- a/certificates/migrations/0002_remove_certificatetemplate_company_logo_and_more.py +++ /dev/null @@ -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='شرکت صادر کننده'), - ), - ] diff --git a/certificates/templates/certificates/step.html b/certificates/templates/certificates/step.html index 3392f82..b8923c2 100644 --- a/certificates/templates/certificates/step.html +++ b/certificates/templates/certificates/step.html @@ -2,6 +2,7 @@ {% load static %} {% load processes_tags %} {% load humanize %} + {% load accounts_tags %} {% block sidebar %} {% include 'sidebars/admin.html' %} @@ -79,7 +80,11 @@ {% else %}{% endif %}
diff --git a/certificates/views.py b/certificates/views.py index 428c5ba..761ee83 100644 --- a/certificates/views.py +++ b/certificates/views.py @@ -9,6 +9,7 @@ from processes.models import ProcessInstance, StepInstance from invoices.models import Invoice from installations.models import InstallationReport from .models import CertificateTemplate, CertificateInstance +from common.consts import UserRoles from _helpers.jalali import Gregorian @@ -78,6 +79,14 @@ def certificate_step(request, instance_id, step_id): next_step = instance.process.steps.filter(order__gt=instance.current_step.order).first() if instance.current_step else None if request.method == 'POST': + # Only broker can approve and finish certificate step + try: + if not (hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.BROKER)): + messages.error(request, 'شما مجوز تایید این مرحله را ندارید') + return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id) + except Exception: + messages.error(request, 'شما مجوز تایید این مرحله را ندارید') + return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id) cert.approved = True cert.approved_at = timezone.now() cert.save() @@ -89,7 +98,10 @@ def certificate_step(request, instance_id, step_id): instance.current_step = next_step instance.save() return redirect('processes:step_detail', instance_id=instance.id, step_id=next_step.id) - return redirect('processes:request_list') + # Mark the whole process instance as completed on the last step + instance.status = 'completed' + instance.save() + return redirect('processes:instance_summary', instance_id=instance.id) return render(request, 'certificates/step.html', { 'instance': instance, diff --git a/contracts/migrations/0001_initial.py b/contracts/migrations/0001_initial.py index 8f9f4b7..25acea6 100644 --- a/contracts/migrations/0001_initial.py +++ b/contracts/migrations/0001_initial.py @@ -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 simple_history.models from django.conf import settings from django.db import migrations, models @@ -11,6 +10,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ('accounts', '0001_initial'), ('processes', '0001_initial'), 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='اسلاگ')), ('name', models.CharField(max_length=100, 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_signature', models.ImageField(blank=True, null=True, upload_to='contracts/signatures/%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='شرکت')), ], options={ 'verbose_name': 'قالب قرارداد', @@ -58,61 +57,4 @@ class Migration(migrations.Migration): '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), - ), ] diff --git a/contracts/migrations/0002_remove_historicalcontracttemplate_history_user_and_more.py b/contracts/migrations/0002_remove_historicalcontracttemplate_history_user_and_more.py deleted file mode 100644 index 60d434d..0000000 --- a/contracts/migrations/0002_remove_historicalcontracttemplate_history_user_and_more.py +++ /dev/null @@ -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', - ), - ] diff --git a/contracts/templates/contracts/contract_step.html b/contracts/templates/contracts/contract_step.html index 33ff326..df4fdfc 100644 --- a/contracts/templates/contracts/contract_step.html +++ b/contracts/templates/contracts/contract_step.html @@ -41,32 +41,36 @@تاریخ مراجعه: {{ report.visited_date|to_jalali|default:'-' }}
@@ -67,6 +86,9 @@