From 246a2c07592ae8153a9ffcba1abf7eeb13ee3a5b Mon Sep 17 00:00:00 2001 From: aminhashemi92 Date: Sun, 7 Sep 2025 18:43:14 +0330 Subject: [PATCH 01/11] fix print and preview quote and add broker to req and complete company model. --- accounts/admin.py | 4 +- accounts/migrations/0002_company_broker.py | 20 ++ ...count_number_company_bank_name_and_more.py | 34 +++ .../migrations/0004_company_branch_name.py | 18 ++ accounts/models.py | 81 +++++- db.sqlite3 | Bin 2375680 -> 2392064 bytes .../invoices/quote_preview_step.html | 267 +++++++++++------ invoices/templates/invoices/quote_print.html | 262 ++++++++--------- invoices/templates/invoices/quote_step.html | 16 +- invoices/views.py | 7 +- locations/admin.py | 2 +- locations/migrations/0002_broker_company.py | 20 ++ .../migrations/0003_remove_broker_company.py | 17 ++ processes/admin.py | 7 +- .../migrations/0002_processinstance_broker.py | 20 ++ processes/models.py | 27 +- .../includes/instance_info_modal.html | 275 ++++++++++++++++++ processes/templatetags/processes_tags.py | 54 ++++ processes/views.py | 1 + 19 files changed, 872 insertions(+), 260 deletions(-) create mode 100644 accounts/migrations/0002_company_broker.py create mode 100644 accounts/migrations/0003_company_account_number_company_bank_name_and_more.py create mode 100644 accounts/migrations/0004_company_branch_name.py create mode 100644 locations/migrations/0002_broker_company.py create mode 100644 locations/migrations/0003_remove_broker_company.py create mode 100644 processes/migrations/0002_processinstance_broker.py create mode 100644 processes/templates/processes/includes/instance_info_modal.html diff --git a/accounts/admin.py b/accounts/admin.py index 1d58e16..5530abc 100644 --- a/accounts/admin.py +++ b/accounts/admin.py @@ -33,9 +33,9 @@ class ProfileAdmin(admin.ModelAdmin): @admin.register(Company) class CompanyAdmin(admin.ModelAdmin): - list_display = ['name', 'logo', 'signature', 'address', 'phone'] + list_display = ['name', 'logo', 'signature', 'address', 'phone', 'broker'] prepopulated_fields = {'slug': ('name',)} search_fields = ['name', 'address', 'phone'] - list_filter = ['is_active'] + list_filter = ['is_active', 'broker'] date_hierarchy = 'created' ordering = ['-created'] \ No newline at end of file diff --git a/accounts/migrations/0002_company_broker.py b/accounts/migrations/0002_company_broker.py new file mode 100644 index 0000000..df12560 --- /dev/null +++ b/accounts/migrations/0002_company_broker.py @@ -0,0 +1,20 @@ +# Generated by Django 5.2.4 on 2025-09-07 13:43 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0001_initial'), + ('locations', '0003_remove_broker_company'), + ] + + operations = [ + migrations.AddField( + model_name='company', + name='broker', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='company', to='locations.broker', verbose_name='کارگزار'), + ), + ] diff --git a/accounts/migrations/0003_company_account_number_company_bank_name_and_more.py b/accounts/migrations/0003_company_account_number_company_bank_name_and_more.py new file mode 100644 index 0000000..6e692ff --- /dev/null +++ b/accounts/migrations/0003_company_account_number_company_bank_name_and_more.py @@ -0,0 +1,34 @@ +# Generated by Django 5.2.4 on 2025-09-07 14:11 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0002_company_broker'), + ] + + operations = [ + migrations.AddField( + model_name='company', + name='account_number', + field=models.CharField(blank=True, max_length=20, null=True, validators=[django.core.validators.RegexValidator(code='invalid_account_number', message='شماره حساب باید فقط شامل اعداد باشد.', regex='^\\d+$')], verbose_name='شماره حساب'), + ), + migrations.AddField( + model_name='company', + 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='company', + name='card_number', + field=models.CharField(blank=True, max_length=16, null=True, validators=[django.core.validators.RegexValidator(code='invalid_card_number', message='شماره کارت باید فقط شامل اعداد باشد.', regex='^\\d+$')], verbose_name='شماره کارت'), + ), + migrations.AddField( + model_name='company', + name='sheba_number', + field=models.CharField(blank=True, max_length=30, null=True, verbose_name='شماره شبا'), + ), + ] diff --git a/accounts/migrations/0004_company_branch_name.py b/accounts/migrations/0004_company_branch_name.py new file mode 100644 index 0000000..25114e1 --- /dev/null +++ b/accounts/migrations/0004_company_branch_name.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.4 on 2025-09-07 14:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0003_company_account_number_company_bank_name_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='company', + name='branch_name', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='شعبه بانک'), + ), + ] diff --git a/accounts/models.py b/accounts/models.py index a94311a..c1e78fd 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -181,11 +181,82 @@ class Profile(BaseModel): class Company(NameSlugModel): - logo = models.ImageField(upload_to='companies/logos', null=True, blank=True, verbose_name='لوگوی شرکت') - signature = models.ImageField(upload_to='companies/signatures', null=True, blank=True, verbose_name='امضای شرکت') - address = models.TextField(null=True, blank=True, verbose_name='آدرس') - phone = models.CharField(max_length=11, null=True, blank=True, verbose_name='شماره تماس') - + logo = models.ImageField( + upload_to='companies/logos', + null=True, + blank=True, + verbose_name='لوگوی شرکت' + ) + signature = models.ImageField( + upload_to='companies/signatures', + null=True, + blank=True, + verbose_name='امضای شرکت' + ) + address = models.TextField( + null=True, + blank=True, + verbose_name='آدرس' + ) + phone = models.CharField( + max_length=11, + null=True, + blank=True, + verbose_name='شماره تماس' + ) + broker = models.OneToOneField( + Broker, + on_delete=models.SET_NULL, + verbose_name="کارگزار", + null=True, + blank=True, + related_name='company' + ) + card_number = models.CharField( + max_length=16, + null=True, + verbose_name="شماره کارت", + blank=True, + validators=[ + RegexValidator( + regex=r'^\d+$', + message='شماره کارت باید فقط شامل اعداد باشد.', + code='invalid_card_number' + ) + ] + ) + account_number = models.CharField( + max_length=20, + null=True, + verbose_name="شماره حساب", + blank=True, + validators=[ + RegexValidator( + regex=r'^\d+$', + message='شماره حساب باید فقط شامل اعداد باشد.', + code='invalid_account_number' + ) + ] + ) + sheba_number = models.CharField( + max_length=30, + null=True, + verbose_name="شماره شبا", + blank=True, + ) + bank_name = models.CharField( + max_length=255, + choices=BANK_CHOICES, + null=True, + verbose_name="نام بانک", + blank=True + ) + branch_name = models.CharField( + max_length=255, + null=True, + verbose_name="شعبه بانک", + blank=True + ) class Meta: verbose_name = 'شرکت' verbose_name_plural = 'شرکت‌ها' diff --git a/db.sqlite3 b/db.sqlite3 index e02ec79f8053a52023164c01f0a8ef2f18a109be..94e4e201642885bb08c59d0da20a656c781a3a80 100644 GIT binary patch delta 36884 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 diff --git a/invoices/templates/invoices/quote_preview_step.html b/invoices/templates/invoices/quote_preview_step.html index e6e405a..509c138 100644 --- a/invoices/templates/invoices/quote_preview_step.html +++ b/invoices/templates/invoices/quote_preview_step.html @@ -24,6 +24,10 @@ {% block content %} {% include '_toasts.html' %} + + +{% instance_info_modal instance %} + {% csrf_token %}

@@ -32,15 +36,17 @@

{{ step.name }}: {{ instance.process.name }}

- اشتراک آب: {{ instance.well.water_subscription_number|default:"-" }} - | نماینده: {{ instance.representative.profile.national_code|default:"-" }} + {% instance_info instance %}
@@ -50,100 +56,116 @@
-
+
-
- - شرکت آب منطقه‌ای + +
+
+ + {% if instance.broker.company %} + لوگوی شرکت + {% else %} + + {% endif %} + +
+
+
+ {% if instance.broker.company %} + {{ instance.broker.company.name }} + {% else %} + شرکت آب منطقه‌ای + {% endif %} +
+ {% if instance.broker.company %} +
+ {% if instance.broker.company.address %} +
{{ instance.broker.company.address }}
+ {% endif %} + {% if instance.broker.affairs.county.city.name %} +
{{ instance.broker.affairs.county.city.name }}، ایران
+ {% endif %} + {% if instance.broker.company.phone %} +
{{ instance.broker.company.phone }}
+ {% endif %} +
+ {% endif %} +
-

دفتر مرکزی، خیابان اصلی

-

تهران، ایران

-

۰۲۱-۱۲۳۴۵۶۷۸

-
-

پیش‌فاکتور #{{ quote.name }}

-
- تاریخ صدور: - {{ quote.jcreated }} + +
+
+
#{{ quote.name }}
-
- معتبر تا: - {{ quote.valid_until|date:"Y/m/d" }} +
+
+ تاریخ صدور: + {{ quote.jcreated_date }} +

-
-
-
-
صادر شده برای:
-

{{ quote.customer.get_full_name }}

- {% if instance.representative.profile %} -

کد ملی: {{ instance.representative.profile.national_code }}

-

{{ instance.representative.profile.address|default:"آدرس نامشخص" }}

-

{{ instance.representative.profile.phone_number_1|default:"" }}

- {% endif %} +
+
+
+
+
+
+ اطلاعات مشترک +
+
+ نام: + {{ quote.customer.get_full_name }} +
+ {% if instance.representative.profile.national_code %} +
+ کد ملی: + {{ instance.representative.profile.national_code }} +
+ {% endif %} + {% if instance.representative.profile.phone_number_1 %} +
+ تلفن: + {{ instance.representative.profile.phone_number_1 }} +
+ {% endif %} + {% if instance.representative.profile.address %} +
+ آدرس: + {{ instance.representative.profile.address }} +
+ {% endif %} + +
+
-
-
اطلاعات چاه:
- - - - - - - - - - - - - - - - - - - - - - - -
شماره اشتراک آب:{{ instance.well.water_subscription_number }}
شماره اشتراک برق:{{ instance.well.electricity_subscription_number|default:"-" }}
سریال کنتور:{{ instance.well.water_meter_serial_number|default:"-" }}
قدرت چاه:{{ instance.well.well_power|default:"-" }}
کد درخواست:{{ instance.code }}
+
+
+
+
+ اطلاعات چاه +
+
+ شماره اشتراک آب: + {{ instance.well.water_subscription_number }} +
+
+ شماره اشتراک برق: + {{ instance.well.electricity_subscription_number|default:"-" }} +
+
+ سریال کنتور: + {{ instance.well.water_meter_serial_number|default:"-" }} +
+
+ قدرت چاه: + {{ instance.well.well_power|default:"-" }} +
+
+
@@ -170,11 +192,6 @@ {% endfor %} -

- صادر کننده: - {{ quote.created_by.get_full_name }} -

- با تشکر از انتخاب شما

جمع کل:

@@ -193,6 +210,72 @@ + +
+
+
+
شرایط و ضوابط:
+
    +
  • + + اعتبار پیش‌فاکتور صادر شده ۴۸ ساعت پس از تاریخ صدور می‌باشد +
  • +
  • + + مبلغ فوق به صورت علی‌الحساب دریافت می‌گردد +
  • +
  • + + این برگه صرفاً جهت اعلام قیمت بوده و ارزش قانونی دیگری ندارد +
  • + {% if instance.broker.company.signature %} +
  • + امضای شرکت +
  • + {% endif %} +
+
+ {% if instance.broker.company %} +
+
اطلاعات پرداخت:
+
+ {% if instance.broker.company.card_number %} +
+ شماره کارت: +
{{ instance.broker.company.card_number }}
+
+ {% endif %} + {% if instance.broker.company.account_number %} +
+ شماره حساب: +
{{ instance.broker.company.account_number }}
+
+ {% endif %} + {% if instance.broker.company.sheba_number %} +
+ شماره شبا: +
{{ instance.broker.company.sheba_number }}
+
+ {% endif %} + {% if instance.broker.company.bank_name %} +
+ بانک: +
{{ instance.broker.company.get_bank_name_display }}
+
+ {% endif %} + {% if instance.broker.company.branch_name %} +
+ شعبه: +
{{ instance.broker.company.branch_name }}
+
+ {% endif %} + +
+ +
+ {% endif %} +
+
{% if quote.notes %} diff --git a/invoices/templates/invoices/quote_print.html b/invoices/templates/invoices/quote_print.html index 7296d12..282ff44 100644 --- a/invoices/templates/invoices/quote_print.html +++ b/invoices/templates/invoices/quote_print.html @@ -5,8 +5,24 @@ پیش‌فاکتور {{ quote.name }} - {{ instance.code }} - - + {% load static %} + {% load humanize %} + + + + + + + + + + + + + + + + + {% endblock %} {% block content %} {% include '_toasts.html' %} + + +{% instance_info_modal instance %} + + {% csrf_token %}
@@ -35,19 +36,21 @@

{{ step.name }}: {{ instance.process.name }}

- اشتراک آب: {{ instance.well.water_subscription_number|default:"-" }} - | نماینده: {{ instance.representative.profile.national_code|default:"-" }} + {% instance_info instance %}
-
+
{% stepper_header instance step %}
@@ -60,7 +63,7 @@
- {% if can_manage_payments %} + {% if is_broker %}
@@ -104,7 +107,7 @@
{% endif %} -
+
وضعیت پیش‌فاکتور
@@ -171,7 +174,7 @@ {% endif %} - {% if can_manage_payments %} + {% if is_broker %} @@ -195,7 +198,7 @@
وضعیت تاییدها
{% if can_approve_reject %}
- +
{% endif %} diff --git a/invoices/templates/invoices/quote_preview_step.html b/invoices/templates/invoices/quote_preview_step.html index 509c138..5a2fbbd 100644 --- a/invoices/templates/invoices/quote_preview_step.html +++ b/invoices/templates/invoices/quote_preview_step.html @@ -41,7 +41,7 @@
- پرینت + پرینت diff --git a/invoices/urls.py b/invoices/urls.py index c338c4c..f959799 100644 --- a/invoices/urls.py +++ b/invoices/urls.py @@ -15,9 +15,7 @@ urlpatterns = [ # Quote payments step (step 3) path('instance//step//payments/', views.quote_payment_step, name='quote_payment_step'), path('instance//step//payments/add/', views.add_quote_payment, name='add_quote_payment'), - path('instance//step//payments//update/', views.update_quote_payment, name='update_quote_payment'), path('instance//step//payments//delete/', views.delete_quote_payment, name='delete_quote_payment'), - path('instance//step//payments/approve/', views.approve_payments, name='approve_payments'), # Quote print path('instance//quote/print/', views.quote_print, name='quote_print'), diff --git a/invoices/views.py b/invoices/views.py index e017518..cc1f925 100644 --- a/invoices/views.py +++ b/invoices/views.py @@ -107,7 +107,7 @@ def create_quote(request, instance_id, step_id): return JsonResponse({'success': False, 'message': 'هیچ آیتمی انتخاب نشده است'}) # Create or reuse quote - quote, _ = Quote.objects.get_or_create( + quote, created_q = Quote.objects.get_or_create( process_instance=instance, defaults={ 'name': f"پیش‌فاکتور {instance.code}", @@ -117,6 +117,15 @@ def create_quote(request, instance_id, step_id): } ) + # Track whether this step was already completed before this edit + step_instance_existing = instance.step_instances.filter(step=step).first() + was_already_completed = bool(step_instance_existing and step_instance_existing.status == 'completed') + + # Snapshot previous items before overwrite for change detection + previous_items_map = {} + if not created_q: + previous_items_map = {qi.item_id: int(qi.quantity) for qi in quote.items.filter(is_deleted=False).all()} + # Replace quote items with submitted ones quote.items.all().delete() for entry in items_payload: @@ -139,22 +148,62 @@ def create_quote(request, instance_id, step_id): ) quote.calculate_totals() + + # Detect changes versus previous state and mark audit fields if editing after completion + try: + new_items_map = {int(entry.get('id')): int(entry.get('qty') or 1) for entry in items_payload} + except Exception: + new_items_map = {} + + next_step = instance.process.steps.filter(order__gt=step.order).first() + + if was_already_completed and new_items_map != previous_items_map: + # StepInstance-level generic audit (for reuse across steps) + if step_instance_existing: + step_instance_existing.edited_after_completion = True + step_instance_existing.last_edited_at = timezone.now() + step_instance_existing.last_edited_by = request.user + step_instance_existing.edit_count = (step_instance_existing.edit_count or 0) + 1 + step_instance_existing.completed_at = timezone.now() + step_instance_existing.save(update_fields=['edited_after_completion', 'last_edited_at', 'last_edited_by', 'edit_count', 'completed_at']) + + + if quote.status != 'draft': + quote.status = 'draft' + quote.save(update_fields=['status']) + + if next_step: + next_step_instance = instance.step_instances.filter(step=next_step).first() + if next_step_instance and next_step_instance.status == 'completed': + next_step_instance.status = 'in_progress' + next_step_instance.completed_at = None + next_step_instance.save(update_fields=['status', 'completed_at']) + # Clear previous approvals if the step requires re-approval + try: + next_step_instance.approvals.all().delete() + except Exception: + pass + + instance.current_step = next_step + instance.save(update_fields=['current_step']) # تکمیل مرحله step_instance, created = StepInstance.objects.get_or_create( process_instance=instance, step=step ) - step_instance.status = 'completed' - step_instance.completed_at = timezone.now() - step_instance.save() + if not was_already_completed: + step_instance.status = 'completed' + step_instance.completed_at = timezone.now() + step_instance.save(update_fields=['status', 'completed_at']) # انتقال به مرحله بعدی - next_step = instance.process.steps.filter(order__gt=step.order).first() redirect_url = None if next_step: - instance.current_step = next_step - instance.save() + # Only advance current step if we are currently on this step to avoid regressions + if instance.current_step_id == step.id: + instance.current_step = next_step + instance.save(update_fields=['current_step']) # هدایت مستقیم به مرحله پیش‌نمایش پیش‌فاکتور redirect_url = reverse('invoices:quote_preview_step', args=[instance.id, next_step.id]) @@ -202,6 +251,7 @@ def quote_preview_step(request, instance_id, step_id): 'is_broker': is_broker, }) + @login_required def quote_print(request, instance_id): """صفحه پرینت پیش‌فاکتور""" @@ -213,6 +263,7 @@ def quote_print(request, instance_id): 'quote': quote, }) + @require_POST @login_required def approve_quote(request, instance_id, step_id): @@ -285,6 +336,7 @@ def quote_payment_step(request, instance_id, step_id): } step_instance, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step, defaults={'status': 'in_progress'}) + reqs = list(step.approver_requirements.select_related('role').all()) user_roles_qs = getattr(getattr(request.user, 'profile', None), 'roles', None) user_roles = list(user_roles_qs.all()) if user_roles_qs is not None else [] @@ -298,6 +350,7 @@ def quote_payment_step(request, instance_id, step_id): } for r in reqs ] + # dynamic permission: who can approve/reject this step (based on requirements) try: req_role_ids = {r.role_id for r in reqs} @@ -305,20 +358,7 @@ def quote_payment_step(request, instance_id, step_id): can_approve_reject = len(req_role_ids.intersection(user_role_ids)) > 0 except Exception: can_approve_reject = False - # approver status map for template - reqs = list(step.approver_requirements.select_related('role').all()) - user_roles_qs = getattr(getattr(request.user, 'profile', None), 'roles', None) - user_roles = list(user_roles_qs.all()) if user_roles_qs is not None else [] - approvals_list = list(step_instance.approvals.select_related('role').all()) - approvals_by_role = {a.role_id: a for a in approvals_list} - approver_statuses = [ - { - 'role': r.role, - 'status': (approvals_by_role.get(r.role_id).decision if approvals_by_role.get(r.role_id) else None), - 'reason': (approvals_by_role.get(r.role_id).reason if approvals_by_role.get(r.role_id) else ''), - } - for r in reqs - ] + # Accountant/Admin approval and rejection via POST (multi-role) if request.method == 'POST' and request.POST.get('action') in ['approve', 'reject']: @@ -362,6 +402,13 @@ def quote_payment_step(request, instance_id, step_id): defaults={'approved_by': request.user, 'decision': 'rejected', 'reason': reason} ) StepRejection.objects.create(step_instance=step_instance, rejected_by=request.user, reason=reason) + # If current step is ahead of this step, reset it back to this step + try: + if instance.current_step and instance.current_step.order > step.order: + instance.current_step = step + instance.save(update_fields=['current_step']) + except Exception: + pass messages.success(request, 'مرحله پرداخت‌ها رد شد و برای اصلاح بازگشت.') return redirect('invoices:quote_payment_step', instance_id=instance.id, step_id=step.id) @@ -388,8 +435,6 @@ def quote_payment_step(request, instance_id, step_id): 'approver_statuses': approver_statuses, 'is_broker': is_broker, 'is_accountant': is_accountant, - # dynamic permissions: any role required to approve can also manage payments - 'can_manage_payments': can_approve_reject, 'can_approve_reject': can_approve_reject, }) @@ -412,14 +457,16 @@ def add_quote_payment(request, instance_id, step_id): } ) - # dynamic permission: users whose roles are among required approvers can add payments + # who can add payments + profile = getattr(request.user, 'profile', None) + is_broker = False + is_accountant = False try: - req_role_ids = set(step.approver_requirements.values_list('role_id', flat=True)) - user_roles_qs = getattr(getattr(request.user, 'profile', None), 'roles', Role.objects.none()) - user_role_ids = set(user_roles_qs.values_list('id', flat=True)) - if len(req_role_ids.intersection(user_role_ids)) == 0: - return JsonResponse({'success': False, 'message': 'شما مجوز افزودن فیش را ندارید'}) + is_broker = bool(profile and profile.has_role(UserRoles.BROKER)) + is_accountant = bool(profile and profile.has_role(UserRoles.ACCOUNTANT)) except Exception: + is_broker = False + is_accountant = False return JsonResponse({'success': False, 'message': 'شما مجوز افزودن فیش را ندارید'}) logger = logging.getLogger(__name__) @@ -477,48 +524,11 @@ def add_quote_payment(request, instance_id, step_id): si.approvals.all().delete() except Exception: pass - redirect_url = reverse('invoices:quote_payment_step', args=[instance.id, step.id]) - return JsonResponse({'success': True, 'redirect': redirect_url}) - - -@require_POST -@login_required -def update_quote_payment(request, instance_id, step_id, payment_id): - instance = get_object_or_404(ProcessInstance, id=instance_id) - step = get_object_or_404(instance.process.steps, id=step_id) - quote = get_object_or_404(Quote, process_instance=instance) - invoice = Invoice.objects.filter(quote=quote).first() - if not invoice: - return JsonResponse({'success': False, 'message': 'فاکتور یافت نشد'}) - payment = get_object_or_404(Payment, id=payment_id, invoice=invoice) - + # If current step is ahead of this step, reset it back to this step try: - amount = request.POST.get('amount') - payment_date = request.POST.get('payment_date') or payment.payment_date - payment_method = request.POST.get('payment_method') or payment.payment_method - reference_number = request.POST.get('reference_number') or '' - notes = request.POST.get('notes') or '' - receipt_image = request.FILES.get('receipt_image') - if amount: - payment.amount = amount - payment.payment_date = payment_date - payment.payment_method = payment_method - payment.reference_number = reference_number - payment.notes = notes - # اگر نیاز به ذخیره عکس در Payment دارید، فیلد آن اضافه شده است - if receipt_image: - payment.receipt_image = receipt_image - payment.save() - except Exception: - return JsonResponse({'success': False, 'message': 'خطا در ویرایش فیش'}) - - # On update, return to awaiting approval - try: - si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step) - si.status = 'in_progress' - si.completed_at = None - si.save() - si.approvals.all().delete() + if instance.current_step and instance.current_step.order > step.order: + instance.current_step = step + instance.save(update_fields=['current_step']) except Exception: pass redirect_url = reverse('invoices:quote_payment_step', args=[instance.id, step.id]) @@ -535,15 +545,18 @@ def delete_quote_payment(request, instance_id, step_id, payment_id): if not invoice: return JsonResponse({'success': False, 'message': 'فاکتور یافت نشد'}) payment = get_object_or_404(Payment, id=payment_id, invoice=invoice) - # dynamic permission: users whose roles are among required approvers can delete payments + + # who can delete payments + profile = getattr(request.user, 'profile', None) + is_broker = False + is_accountant = False try: - req_role_ids = set(step.approver_requirements.values_list('role_id', flat=True)) - user_roles_qs = getattr(getattr(request.user, 'profile', None), 'roles', Role.objects.none()) - user_role_ids = set(user_roles_qs.values_list('id', flat=True)) - if len(req_role_ids.intersection(user_role_ids)) == 0: - return JsonResponse({'success': False, 'message': 'شما مجوز حذف فیش را ندارید'}) + is_broker = bool(profile and profile.has_role(UserRoles.BROKER)) + is_accountant = bool(profile and profile.has_role(UserRoles.ACCOUNTANT)) except Exception: - return JsonResponse({'success': False, 'message': 'شما مجوز حذف فیش را ندارید'}) + is_broker = False + is_accountant = False + return JsonResponse({'success': False, 'message': 'شما مجوز افزودن فیش را ندارید'}) try: # soft delete using project's BaseModel delete override @@ -559,43 +572,17 @@ def delete_quote_payment(request, instance_id, step_id, payment_id): si.approvals.all().delete() except Exception: pass + # If current step is ahead of this step, reset it back to this step + try: + if instance.current_step and instance.current_step.order > step.order: + instance.current_step = step + instance.save(update_fields=['current_step']) + except Exception: + pass redirect_url = reverse('invoices:quote_payment_step', args=[instance.id, step.id]) return JsonResponse({'success': True, 'redirect': redirect_url}) -@require_POST -@login_required -def approve_payments(request, instance_id, step_id): - """تایید مرحله پرداخت فیش‌ها و انتقال به مرحله بعد""" - instance = get_object_or_404(ProcessInstance, id=instance_id) - step = get_object_or_404(instance.process.steps, id=step_id) - quote = get_object_or_404(Quote, process_instance=instance) - - is_fully_paid = quote.get_remaining_amount() <= 0 - - # تکمیل مرحله - step_instance, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step) - step_instance.status = 'completed' - step_instance.completed_at = timezone.now() - step_instance.save() - - # حرکت به مرحله بعد - next_step = instance.process.steps.filter(order__gt=step.order).first() - redirect_url = reverse('processes:request_list') - if next_step: - instance.current_step = next_step - instance.save() - redirect_url = reverse('processes:step_detail', args=[instance.id, next_step.id]) - - msg = 'پرداخت‌ها تایید شد' - if is_fully_paid: - msg += ' - مبلغ پیش‌فاکتور به طور کامل پرداخت شده است.' - else: - msg += ' - توجه: مبلغ پیش‌فاکتور به طور کامل پرداخت نشده است.' - - return JsonResponse({'success': True, 'message': msg, 'redirect': redirect_url, 'is_fully_paid': is_fully_paid}) - - @login_required def final_invoice_step(request, instance_id, step_id): """تجمیع اقلام پیش‌فاکتور با تغییرات نصب و صدور فاکتور نهایی""" diff --git a/processes/admin.py b/processes/admin.py index 3eb169e..a23a341 100644 --- a/processes/admin.py +++ b/processes/admin.py @@ -50,6 +50,7 @@ class ProcessInstanceAdmin(SimpleHistoryAdmin): verbose_name_plural = "درخواست‌ها" list_display = [ 'code', + 'current_step', 'slug', 'well_display', 'representative', @@ -142,7 +143,7 @@ class ProcessInstanceAdmin(SimpleHistoryAdmin): @admin.register(StepInstance) class StepInstanceAdmin(SimpleHistoryAdmin): - list_display = ['process_instance', 'step', 'assigned_to', 'status_display', 'rejection_count', 'started_at', 'completed_at'] + list_display = ['process_instance', 'step', 'assigned_to', 'status_display', 'rejection_count', 'edit_count', 'started_at', 'completed_at'] list_filter = ['status', 'step__process', 'started_at'] search_fields = ['process_instance__name', 'step__name', 'assigned_to__username'] readonly_fields = ['started_at', 'completed_at'] diff --git a/processes/migrations/0003_historicalstepinstance_edit_count_and_more.py b/processes/migrations/0003_historicalstepinstance_edit_count_and_more.py new file mode 100644 index 0000000..6e983f6 --- /dev/null +++ b/processes/migrations/0003_historicalstepinstance_edit_count_and_more.py @@ -0,0 +1,56 @@ +# Generated by Django 5.2.4 on 2025-09-08 08:18 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('processes', '0002_processinstance_broker'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='historicalstepinstance', + name='edit_count', + field=models.PositiveIntegerField(default=0, verbose_name='تعداد ویرایش پس از تکمیل'), + ), + migrations.AddField( + model_name='historicalstepinstance', + name='edited_after_completion', + field=models.BooleanField(default=False, verbose_name='ویرایش پس از تکمیل'), + ), + migrations.AddField( + model_name='historicalstepinstance', + name='last_edited_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='آخرین زمان ویرایش پس از تکمیل'), + ), + migrations.AddField( + model_name='historicalstepinstance', + name='last_edited_by', + field=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='ویرایش توسط'), + ), + migrations.AddField( + model_name='stepinstance', + name='edit_count', + field=models.PositiveIntegerField(default=0, verbose_name='تعداد ویرایش پس از تکمیل'), + ), + migrations.AddField( + model_name='stepinstance', + name='edited_after_completion', + field=models.BooleanField(default=False, verbose_name='ویرایش پس از تکمیل'), + ), + migrations.AddField( + model_name='stepinstance', + name='last_edited_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='آخرین زمان ویرایش پس از تکمیل'), + ), + migrations.AddField( + model_name='stepinstance', + name='last_edited_by', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='step_instances_edited', to=settings.AUTH_USER_MODEL, verbose_name='ویرایش توسط'), + ), + ] diff --git a/processes/models.py b/processes/models.py index 0ff6484..414d475 100644 --- a/processes/models.py +++ b/processes/models.py @@ -327,6 +327,12 @@ class StepInstance(models.Model): notes = models.TextField(verbose_name="یادداشت‌ها", blank=True) started_at = models.DateTimeField(auto_now_add=True, verbose_name="تاریخ شروع") completed_at = models.DateTimeField(null=True, blank=True, verbose_name="تاریخ تکمیل") + # Generic edit-tracking for post-completion modifications + edited_after_completion = models.BooleanField(default=False, verbose_name="ویرایش پس از تکمیل") + last_edited_at = models.DateTimeField(null=True, blank=True, verbose_name="آخرین زمان ویرایش پس از تکمیل") + last_edited_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='step_instances_edited', verbose_name="ویرایش توسط") + edit_count = models.PositiveIntegerField(default=0, verbose_name="تعداد ویرایش پس از تکمیل") + history = HistoricalRecords() class Meta: From 204b0aa48e9686ab662385aa99d83cc5d8f494d5 Mon Sep 17 00:00:00 2001 From: aminhashemi92 Date: Mon, 8 Sep 2025 16:55:43 +0330 Subject: [PATCH 03/11] fix date in payment and imporove contract page. --- accounts/admin.py | 2 +- .../0005_company_registration_number.py | 18 ++++ .../0006_company_card_holder_name.py | 18 ++++ accounts/models.py | 12 +++ .../templates/contracts/contract_print.html | 68 ++++++++++----- .../templates/contracts/contract_step.html | 60 ++++++++----- contracts/views.py | 50 +++++++---- db.sqlite3 | Bin 2682880 -> 2859008 bytes invoices/models.py | 4 + .../invoices/final_settlement_step.html | 39 +++++++-- .../invoices/quote_payment_step.html | 15 +++- .../invoices/quote_preview_step.html | 2 +- invoices/templates/invoices/quote_step.html | 2 +- invoices/views.py | 79 ++++++++++++++++-- 14 files changed, 295 insertions(+), 74 deletions(-) create mode 100644 accounts/migrations/0005_company_registration_number.py create mode 100644 accounts/migrations/0006_company_card_holder_name.py diff --git a/accounts/admin.py b/accounts/admin.py index 5530abc..d741357 100644 --- a/accounts/admin.py +++ b/accounts/admin.py @@ -33,7 +33,7 @@ class ProfileAdmin(admin.ModelAdmin): @admin.register(Company) class CompanyAdmin(admin.ModelAdmin): - list_display = ['name', 'logo', 'signature', 'address', 'phone', 'broker'] + list_display = ['name', 'logo', 'signature', 'address', 'phone', 'broker', 'registration_number'] prepopulated_fields = {'slug': ('name',)} search_fields = ['name', 'address', 'phone'] list_filter = ['is_active', 'broker'] diff --git a/accounts/migrations/0005_company_registration_number.py b/accounts/migrations/0005_company_registration_number.py new file mode 100644 index 0000000..b38ab10 --- /dev/null +++ b/accounts/migrations/0005_company_registration_number.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.4 on 2025-09-08 10:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0004_company_branch_name'), + ] + + operations = [ + migrations.AddField( + model_name='company', + name='registration_number', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='شماره ثبت شرکت'), + ), + ] diff --git a/accounts/migrations/0006_company_card_holder_name.py b/accounts/migrations/0006_company_card_holder_name.py new file mode 100644 index 0000000..9228034 --- /dev/null +++ b/accounts/migrations/0006_company_card_holder_name.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.4 on 2025-09-08 10:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0005_company_registration_number'), + ] + + operations = [ + migrations.AddField( + model_name='company', + name='card_holder_name', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='نام دارنده کارت'), + ), + ] diff --git a/accounts/models.py b/accounts/models.py index c1e78fd..937c329 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -204,6 +204,12 @@ class Company(NameSlugModel): blank=True, verbose_name='شماره تماس' ) + registration_number = models.CharField( + max_length=255, + null=True, + blank=True, + verbose_name='شماره ثبت شرکت' + ) broker = models.OneToOneField( Broker, on_delete=models.SET_NULL, @@ -238,6 +244,12 @@ class Company(NameSlugModel): ) ] ) + card_holder_name = models.CharField( + max_length=255, + null=True, + verbose_name="نام دارنده کارت", + blank=True, + ) sheba_number = models.CharField( max_length=30, null=True, diff --git a/contracts/templates/contracts/contract_print.html b/contracts/templates/contracts/contract_print.html index bc47f05..d6a6b85 100644 --- a/contracts/templates/contracts/contract_print.html +++ b/contracts/templates/contracts/contract_print.html @@ -2,45 +2,71 @@ - + چاپ قرارداد {{ instance.code }} - + {% load static %} + + + + + + + + + + + +
-
-
-
{{ contract.template.company.name }}
-
{{ contract.template.name }}
-
کد درخواست: {{ instance.code }} | تاریخ: {{ contract.jcreated }}
-
- {% if contract.template.company.logo %} - - {% endif %} - + +
+
تاریخ: {{ contract.jcreated_date }} | کد درخواست: {{ instance.code }}
+
+ {% if instance.broker and instance.broker.company %} + {{ instance.broker.company.name }} + {% elif template.company %} + {{ template.company.name }} + {% else %} + شرکت آب منطقه‌ای + {% endif %} +
+

{{ contract.template.name }}

-
+ +
{{ contract.rendered_body|safe }}

+ +
امضای مشترک
-
+
امضای شرکت
-
- {% if contract.template.company.signature %} - امضای شرکت +
+ {% if instance.broker and instance.broker.company and instance.broker.company.signature %} + امضای شرکت + {% elif contract.template.company and contract.template.company.signature %} + امضای شرکت {% endif %}
diff --git a/contracts/templates/contracts/contract_step.html b/contracts/templates/contracts/contract_step.html index df4fdfc..700d44b 100644 --- a/contracts/templates/contracts/contract_step.html +++ b/contracts/templates/contracts/contract_step.html @@ -19,6 +19,10 @@ {% block content %} {% include '_toasts.html' %} + + +{% instance_info_modal instance %} +
@@ -41,29 +50,32 @@
+
تاریخ: {{ contract.jcreated_date }} | کد درخواست: {{ instance.code }}
+
+ {% if instance.broker and instance.broker.company %} + {{ instance.broker.company.name }} + {% elif template.company %} + {{ template.company.name }} + {% else %} + شرکت آب منطقه‌ای + {% endif %}
+

{{ contract.template.name }}

{% if can_view_contract_body %} - {% if template.company.logo %} -
- لوگوی شرکت -

{{ contract.template.company.name }}

-
{{ contract.template.name }}
-
- {% endif %} - -
تاریخ: {{ contract.jcreated }}

{{ contract.rendered_body|safe }}

امضای مشترک
-
+
امضای شرکت
-
- {% if template.company.signature %} - امضای شرکت +
+ {% if instance.broker and instance.broker.company and instance.broker.company.signature %} + امضای شرکت + {% elif template.company and template.company.signature %} + امضای شرکت {% endif %}
@@ -76,15 +88,23 @@
{% csrf_token %} {% if previous_step %} - قبلی + + + قبلی + {% else %} {% endif %} {% if next_step %} {% if is_broker %} - + {% else %} - بعدی + + بعدی + + {% endif %} {% else %} {% if is_broker %} diff --git a/contracts/views.py b/contracts/views.py index 1949665..7a1788b 100644 --- a/contracts/views.py +++ b/contracts/views.py @@ -2,29 +2,51 @@ from django.shortcuts import render, get_object_or_404, redirect from django.contrib.auth.decorators import login_required from django.urls import reverse from django.utils import timezone +from decimal import Decimal from django.template import Template, Context +from django.utils.safestring import mark_safe from processes.models import ProcessInstance, StepInstance from common.consts import UserRoles from .models import ContractTemplate, ContractInstance +from invoices.models import Invoice, Quote from _helpers.utils import jalali_converter2 +from django.http import JsonResponse def build_contract_context(instance: ProcessInstance) -> dict: representative = instance.representative profile = getattr(representative, 'profile', None) well = instance.well + # Compute prepayment from Quote-linked invoice payments + quote = Quote.objects.filter(process_instance=instance).first() + invoice = Invoice.objects.filter(quote=quote).first() if quote else None + payments_qs = invoice.payments.filter(is_deleted=False, direction='in').all() if invoice else [] + total_paid = sum((p.amount for p in payments_qs), Decimal('0')) + try: + latest_payment_date = max((p.payment_date for p in payments_qs)) if payments_qs else None + except Exception: + latest_payment_date = None + return { - 'customer_full_name': representative.get_full_name() if representative else '', - 'national_code': profile.national_code if profile else '', - 'address': profile.address if profile else '', - 'phone': profile.phone_number_1 if profile else '', - 'phone2': profile.phone_number_2 if profile else '', - 'water_subscription_number': well.water_subscription_number if well else '', - 'electricity_subscription_number': well.electricity_subscription_number if well else '', - 'water_meter_serial_number': well.water_meter_serial_number if well else '', - 'well_power': well.well_power if well else '', - 'request_code': instance.code, - 'today': jalali_converter2(timezone.now()), + 'customer_full_name': mark_safe(f"{representative.get_full_name() if representative else ''}"), + 'registration_number': mark_safe(f"{instance.broker.company.registration_number if instance.broker and instance.broker.company else ''}"), + 'national_code': mark_safe(f"{profile.national_code if profile else ''}"), + 'address': mark_safe(f"{profile.address if profile else ''}"), + 'phone': mark_safe(f"{profile.phone_number_1 if profile else ''}"), + 'phone2': mark_safe(f"{profile.phone_number_2 if profile else ''}"), + 'water_subscription_number': mark_safe(f"{well.water_subscription_number if well else ''}"), + 'electricity_subscription_number': mark_safe(f"{well.electricity_subscription_number if well else ''}"), + 'water_meter_serial_number': mark_safe(f"{well.water_meter_serial_number if well else ''}"), + 'well_power': mark_safe(f"{well.well_power if well else ''}"), + 'request_code': mark_safe(f"{instance.code}"), + 'today': mark_safe(f"{jalali_converter2(timezone.now())}"), + 'company_name': mark_safe(f"{instance.broker.company.name if instance.broker and instance.broker.company else ''}"), + 'city_name': mark_safe(f"{instance.broker.affairs.county.city.name if instance.broker and instance.broker.affairs and instance.broker.affairs.county and instance.broker.affairs.county.city else ''}"), + 'card_number': mark_safe(f"{instance.representative.profile.card_number if instance.representative else ''}"), + 'account_number': mark_safe(f"{instance.representative.profile.account_number if instance.representative else ''}"), + 'bank_name': mark_safe(f"{instance.representative.profile.get_bank_name_display() if instance.representative else ''}"), + 'prepayment_amount': mark_safe(f"{int(total_paid):,}"), + 'prepayment_date': mark_safe(f"{jalali_converter2(latest_payment_date)}") if latest_payment_date else '', } @@ -35,10 +57,7 @@ def contract_step(request, instance_id, step_id): step = get_object_or_404(instance.process.steps, id=step_id) previous_step = instance.process.steps.filter(order__lt=step.order).last() next_step = instance.process.steps.filter(order__gt=step.order).first() - # Access control: - # - INSTALLER: can open step but cannot view contract body (show inline message) - # - Others: can view - # - Only BROKER can submit/complete this step + profile = getattr(request.user, 'profile', None) is_broker = False can_view_contract_body = True @@ -72,7 +91,6 @@ def contract_step(request, instance_id, step_id): # If user submits to go next, only broker can complete and go to next if request.method == 'POST': if not is_broker: - from django.http import JsonResponse return JsonResponse({'success': False, 'message': 'شما مجوز تایید این مرحله را ندارید'}, status=403) StepInstance.objects.update_or_create( process_instance=instance, diff --git a/db.sqlite3 b/db.sqlite3 index 700863fed5d356ebf31e63ef899abdce104a6935..a04ea6caf5f679b74eb1fceea53bad861e7619c5 100644 GIT binary patch delta 127727 zcmeFa33wF6`ZqdVy-ZK9GYODk$TE{v1w@3fNF$;K6hsjXu0dSla*n7dxSfL@m-}{A0s#ikx!?Ui_ulV$z6{i_`>pEg z>guZQx8AKQn;vB<>o+mgu9SjHEf%zk{#MvR<1bVAwq zkwJejQs_?<`s40^ziTMiH4-igM&j{M_zFMN(zkUjk)gQzEJwSnY2$~DoP73yaI8OE z6b*%=k=V9BQcxO`Tf=l3!W^$D?NraGHJ;8{jH9M|=k;kXwh6I?%$NzcO&l|PNU*$Bc-zuMZ=OfGZ_1?e5wOBnq9LpEnkUEz&_JJZQt zW~(_|*rBF#QCH@lHKU5MnCUh1iY|xt9V*IX2G$%e`ke`E`?^bdF?YzK9fIC`NlTmU zXWCf0ceja__Wdiaz9`4$X)k!aw&o(M#XVu`3f8mY;u_|zKohoX^i zY-=Ug&_>Wdv%qb9Q2)37PyKuSxc;U78Jy*Hokhjdj4y{W87G~h{+?Op;nrpJ5B2w; z+-{vi_gDFh4~L>oey#&6cQB-CLqH}QJzVx!F=bTUQSq>QuZKj^>d|1z{D_BsYSN0hPqdM9U;A7|&V z_@@)d{y$j?u4$ZMZDMae>r^xIYFN&)Nf~Ka-E3zjpd3{8DK9F|D%+H&m37J*g5kpW=b8Y>Sr{v&#iI-EB%Y z!{43VLuacfARYc~^f^KdFChIO|wLS9lhV z7)a87_~?tQ&;BzM-^b`5!vNo>f36?I_ImRK_3Y!cG)qcDKVkK~e4q146(b`Gv(^22^TLr>Ek+Mn_~xk8>UPm*ty zhs%TIYvq1&v7C^Da%WxEdF>bFDkY-$LSa9JeH30cp_An$3VSK+p-^vv&GI7Mdx6696n0a1jzaD(iqBHmNx`77 z!-PW1Gse=RY&i#a>J&@^@rXG?oHjb1f4eG`=MCm0%i7yn^$*Mk+tSkywtv8I`2Y&< zg~!_Bs@_JE&gHr@RV!wdr?r1*`O+r+K_wYJ{+2#fU#s5>$6le!65F1km#5+(E4g;~o}t_fP1Q>oc$$8s@6tchOAUJ|*H2eu zwgc1L2Pz&nky|cJR~MVJ{9pPH*j_B9;kc8ucE1iZ(>M zPXAb5VZ2_=ZI!y1M+SnSFgcC6ar>FRA@!RyQop$|)i56B`kM96#nxIb2gw+Bs^vC{ z@<#bl`F?q>@m?+Wxa2o$HDC9nR&(R_R^3RdHHoBJb7QLU%u`(dHtclf4s0zIzOQlA zR^f`#f?5IWSgSz-195sL+nqTx=vrlm@4_VcrwM}9C8O+;=xKRXoY8jcjj6R}tz zFzwvz)L_?^d>Eq9sb)S7bnvw^iuQ8vsqA$1Nq8!AU*hkYkhE=b(;geV#7-h|-|^Gm zqH#w1kGXG{|LJlt1fOgff?5;$`kXTxPiL^#BPbNK=wHKQ=4W`dSYe#n`E73XDz&E? zSA+TqeXX`aTdLi!jnIbZEA?gSrRol~4#sU)1(#&-yfU47tg>h%_w zIlmc7ZOXSg{-ql)|F<-l*xN0@Fa8@Q^%wBkd|lrUQ~PVZ5vKNbeHWbk0iB#2@haXm zk-rKK*lC{ZGnmn@>#x9b&0_a+1==r`2i;E3gB^uJoA#&ni~g{Fw~i-`XM%9&M{A_#aCU{KGw#%% zwHpatc-hX*XD01%3tS015f~b4x$qpWEVkzwdrR_XFt~QApjWhFJ>j7nv24m>yQ9{_ z99Yw^xhRQ8qGtZn=jZz)U4y}*P%sqthbQ>Re(GPLU4t#qHr)4O2jRXC(n--`Tc=JC}MdmNj_X-xX66}`D2zxTjy7p z>pr-AC=e?0hx~zn-?FxKf#&>R*B~tK0x@`%S=J=a4;2NW10zvOZPI+dzbG7wMxlYL zTjj&rlQw`pjN-v)+;3Ucsz7soAksD9FNy?0LCAl^fT1(u-~Y|HWefClc-dHcvK~F% zxT@0n##tl#1Zt1_Ew=7p3$!ocy09!2U}Su-I=o5 z+MD5x7gt+*xNYpC%q(^#Ohl}e6j)$m-aE>Y>oZ~b^ILF%&;T&&6gmavR2;(>n5yo*EB8=m0h%k=R5Mdmrp=ZqM zr=sm9OhMaB7>l+VWA|H^+jyo}uvl>Pvvyl?Ls|M9No*7w)MILscmP*7SQ~GVrb~XQ z9N7QSl3Q|0c1e^9!_YqpiVqz-D-AQ|@4Kznu2HSa&sy#ILf z{$tJij}q*N);8lB6Dyc}R7(i?Xf;6}TGhP&NHeZ%raw%u6I#)X4-xE$mNz3Nn2(k< z<6MGSXb!gSnoTedRax06bagJOB!nDPK`r=!I#`Y7|l47n- zZn9we8czo9TrI`%&3>^Af84_s$@@@wPiatIQ(jhHP_`-?m0D$mvQ)VrL>{-n`g{ni z&ATc^xMaA~iC^%GnJ(osWvB9lvH&C^<*1vV-;u@&!5t6PXl!wXo3X(oV(&Z$@ zBwfa{6>z|Vr07yYsv+qEBwa$%YLYG{>HQ?VkEDx8dM`=uA?ZSrE+FZAlFlRPT$0Wq z>D?rqP0}hzeF#;O9eN{Xk?p%kI+LV#l5~bK=Mu3Jv0ZGV%*F0d^r)lOYK-bAt`gR9 zC1NGlMZCmJwu;rj8;dJdErl`t)4GRuk?4rY$6RP3bN;Ni<0 z$4Z^82fnJ7Nr3`SX<@W&{^h`dJUzb)q)*OwLb`4~XhG1*dACBkX5Q71J}~cMNSDmZ zgLJ_>4R?Fdwzs1=+T!4Lu=)sXkCv-6sCP(j%N3Ge@rzS!HtS()k?;`z6W^EH0Vi6- z4X1SJ z5r`I!7!iyN3yn(P0pn7$Doj#&*_aV$jz$IUs$Bc&vv)L4&a%nQ4l0^3oIMy1`{QSh zH<|b_G$2tFjrd`m({lD`801)m!-kEB1&77rS5mDDUjB+L%}cV%%O+19Q#Nkg9iWIA zGoqc_{?#WZ&(w-KXBj{3vrR>rwudbW+#K>(l_E7awBr?RIZ@<9JmY|^lN}g&Sgggn z20^M2ipD~C&o}M@d=h234B=JVD8@kxguB90AQlXVgF)OqkZ}>VKbPUe4X@e0X|4gf zH&_&l_(L&VmYv=UznYTa!iBHfx)VJ`2qbz?e<+a%U`LOf664V8wixSjXxCY^b^2`W zq|WNM=m)h$+AfgyJg7}FTn)CkhF3Y=YT;S;!r@{QEsChZvpdReX5sGpZS9OFKDG^E z*a_-XY;CgLRH3fYeo`OPzEd}7$J9FQGj)gdp}JdpPu;6EXkqO&^?>%W`iAy``i{0! z{XpBQHfbBRC)9svwb}~xxVBXN0R#^V)Kl7Q^$%^P#%Z@{l2)$Ws@Xvtakb%KJ z+uvxWt}Aaap~E0EO~2kuGp;k!%xldw>l!n4Uu~uyBGbm}zc*GfnA7w)+~z z-`QRs5a;ycmebM@y7%6Ian&km7b9}|F|)vC5E)4y>k=9=#{-YY zZ)XRMpEgVOQU&Q>oB4J6&-&MJ@P~RQbhY`C&p-x{2b!BH%owDvvGDpNvt%vEg&XvR zAPJfTw5hVhk?k$XuC>s_X5r{I+L&;R!J=c@U!{gPGwxoO8AR1{Fj7MSK z6~TteQ3cv#todDu!LR>b> zCs?5sp%|*deY?r+aq;tcoHZPYg+eIJnAAXY?R`i<%x>UgDt-e2zt&*W-#xBQ8EP<~tQ3KQ{DdB2t~&sN`8HpzYEo~o*h(++4= zTAI8_dtVy|om47Sm_qeLJRF(OvN7JurZO-$$2H?v8=Gbd^P`blPj8JOdH(2VlM*%m zZn3j@%+ak!w;z4x==LLXj#Q0mE*y@Iq_Tfd(y=l7-XK)WU<5HMC^J}K@hZ1!RyRZ0&lw={=>yw zYhEX`Wd@0LL zK$}5*&wXU1{AxeQoYqI2ez^z2aer{axhez5j)DORyqklOSa5uj?1-swNWgp!MaLz{ z2IeUMV%`u~DU3ZgyW%u+6$$&pkz3ErCtO)DQ51=U5<&l%^Rt_oqi`q^4vbEc4KmPR z5wLTyc;uEO*%dAMz_pA@lHZc;kNZbnFguWl1w$kLB0Ch03{Re20gVeJ{QmH;HnzRs2PMAQHd!!r38EuU+#O*}-7=>I-Ls zMj<#5vMbJ6gMh}s9IpWw6u|)(K0FW&gi0=)?T-c`{gY+~!GtNE2q)tGXma_=-5ZD6;7)ixPzlg(C-tN$P@GjPl; zEVs#U1KZuga)bVlFxq{9@itlQY+$_G0VCeRax;tI{nNr~ON7;y2&*j-R$C&hwu7+R z4#H~N39HQ!R+}TNwm?{Ifw0;FVYLOqYFi1bEfQ8+B&@bbSZy0&wQYpemI$jY5msBm zC%?_*dn$z0Rtc-E5>{IythP>AZJn^%I$^bS!fNY;)wUB>+e%n%JFwb+$S*GeR{NMm z{|-39U-h5#5&!)Ux@03|M%+@q$&5%oGj({))Skx!PsHnAnyl8dhEl?OtXr3jhn;%{ z;qNyqsgheSYc)=~LT&Av`K6Ir>}t-fcQ@6HZE;4ulFMG9cQ=abmEW7SiynwrDF65+Vnsv6K9ABRSLqC1F)m0bc< z5VP74b9jea{Xy4w4oJvlGPbN`{SLoL%V15BiqoVynED;XDp$?EAcJLJgPK{^TWzcQ zmA`{g+hC=;vRnRLJ}N&e&z5hHFIBwiBkELrxn8AD)34XN>FL^!+TXR^up}R@_0pW` z2kHj(66F`=2jDozDE+{C?E(3iykA}{kCJ=K9TZ8eg44`uP_sM()DCJ?`3B6{hACGl zdlgpxRDMxjAP<#$Dh2AJ>I}TALCvV>san)*kj9rQ*Mii2oqR$*BtIrkl?Tdx#i=e( zhw69gH*3FYS;|B5GC3O*=fm~QdOkcu)@YN|6Y5UYs+cV3Q*unn0$zF)sMr~;M)NAQ z@+0zixufDx2kE^vhcZWQqYqH-1?Bq~{TaPT|4KWot+KpJ}0=XrUiXkkAjL?Dw?nNfQwI&IAs9Yl47|n;@ZYNWrho9Tt6MZ1_mM5wR~w zKY^G;-Dj8ntxi&JRfnsC)oay$Ky7SNm*S0%yf#LUwcctgycwDp(jC`3Xm{9YcerVH zxM_E|X?M73ceoL;lyf7}A8y(oS+qa0&@i(pS+qm4phNn9qduKSJH$mh#Dz$QxX|Ct zqAo;w#DzXLJjc}_)1P*T1Cb7KAkrZY^n+Q}fk=-y5a|&+?GZce5j*V>JNm{fX-A|> z?9e4^zg35AWeemnu$t2@w#|{>l}{?NJjUo%t7XV|^;24EP9zo|5gipSFy&3A)Vf+IO^fp(=u|6F@l`(6K5uh1XSyXa%JoArI#Vwhc5>ep)r z^@Mh{;jPoMv^FM#Pgdmx<`xlWbe@0n3z~8B3;Mc%FomhL=uM_AMmqMM)Stms)p{Fz z!Y*#XO`m1yc%s$*2mV&Kr{X5v-T_yiu<}OJ4|=Z3nMdNflY0JxJ?+y$1|kXwDwX};non73Di(mJ)!|&ajzZ-X3=(QR*+?Rh1G7fR} zN`&9gQ~}p4%6}f0FV2_n2aEH)c)$s(+e}=>4ADNtiie!AszTTwOd#J5yPYfLo9)LPasi!8XGzwNi|alR236?@Z5xsBX%ZVp$@4d!}rVa^Q}J*U_&*amhtyPjRj zRr9wD_vb*jg%4>Jyc2i^AxCOM) zjZcBTdV~AUJKd8<-ZEy&)X8O2$4s0sbOP}pbKi9r-dvXHF=ozkjAXnQgKECT5@GVh z+p|qIeJj%NiQ@`QvxV+W;>~assCq}GgR_gQ{=K=vc?6Us%9px|7TIBCc1(ukOJPT? zgY!%Og@m4p+?yQ#LF9hIG59~!X1w{QBVs?>W@s8-*~9h*^zZ+ujSpDMoVM|S?JdMO zrH-Uv;{%jUBt3281C%p1KCpAz#s{|7k)ltK^l_4cjStZO!o~-3&$00V%71I)14aM; zZsU`YQVGr&N;`|b6+|QLv{$t|wLc?QV^?(CLHhO=_Pwd28|DRzOndg4dM zBA2o5nv~_DId}Fz+n-0&@}e}w&UO)pf^~LEUru~i#JHp+&4Z_vr+7H=4Y3+m+fy>| zHnRJQxDfBQ^Xd3xc}g}X){C=o%A}M_Iq_NXPFzB^wu)2n{gYBI=ESGO@%S~e^_Vyc z7iWv@an|IN98P>h917?ErP(0LMh&7wY7k{*h(VOV5Q8Y5AqG(#Lkyx=h8RR42Ep&p(2ncFw^f|DkoS}~eZCx3NZ2E#&?K%B^P!&(p%fZZf zpnjQtsh-fou;}ch=fT}O+d<%yl)i$Eae~hSUeZM@U~Kq>Pf{+=_}|U2ED-~}U5j

5SZ2xZ_%ox!}2O;ncYn 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 S^E>wzY_h-Sy}n{w!M_3Ds3=PS delta 15173 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 ZZnWuWFi#R0ve-rzW7`7+7M~AJ`Cn6(4K)A& diff --git a/invoices/models.py b/invoices/models.py index 53059d8..ddf1334 100644 --- a/invoices/models.py +++ b/invoices/models.py @@ -7,6 +7,7 @@ from decimal import Decimal from django.utils import timezone from django.core.validators import MinValueValidator from django.conf import settings +from _helpers.utils import jalali_converter2 User = get_user_model() @@ -372,3 +373,6 @@ class Payment(BaseModel): except Exception: pass return result + + def jpayment_date(self): + return jalali_converter2(self.payment_date) diff --git a/invoices/templates/invoices/final_settlement_step.html b/invoices/templates/invoices/final_settlement_step.html index ca11dec..2335298 100644 --- a/invoices/templates/invoices/final_settlement_step.html +++ b/invoices/templates/invoices/final_settlement_step.html @@ -150,7 +150,7 @@ {% if p.direction == 'in' %}دریافتی{% else %}پرداختی{% endif %} {{ p.amount|floatformat:0|intcomma:False }} تومان - {{ p.payment_date|date:'Y/m/d' }} + {{ p.jpayment_date }} {{ p.get_payment_method_display }} {{ p.reference_number|default:'-' }} @@ -316,11 +316,32 @@ (function initPersianDatePicker(){ if (window.$ && $.fn.persianDatepicker && $('#id_payment_date').length) { $('#id_payment_date').persianDatepicker({ - format: 'YYYY/MM/DD', initialValue: false, autoClose: true, persianDigit: false, observer: true, + format: 'YYYY/MM/DD', + initialValue: false, + autoClose: true, + persianDigit: false, + observer: true, calendar: { persian: { locale: 'fa', leapYearMode: 'astronomical' } }, onSelect: function(unix){ - const g = new window.persianDate(unix).toCalendar('gregorian').format('YYYY-MM-DD'); - $('#id_payment_date').attr('data-gregorian', g); + // تبدیل تاریخ شمسی به میلادی برای ارسال به سرور + const gregorianDate = new Date(unix); + const year = gregorianDate.getFullYear(); + const month = String(gregorianDate.getMonth() + 1).padStart(2, '0'); + const day = String(gregorianDate.getDate()).padStart(2, '0'); + const gregorianDateString = `${year}-${month}-${day}`; + + // نمایش تاریخ شمسی در فیلد + if (window.persianDate) { + const persianDate = new window.persianDate(unix); + const persianDateString = persianDate.format('YYYY/MM/DD'); + $('#id_payment_date').val(persianDateString); + } else { + // اگر persianDate در دسترس نبود، تاریخ میلادی را نمایش بده + $('#id_payment_date').val(gregorianDateString); + } + + // ذخیره تاریخ میلادی در فیلد مخفی برای ارسال به سرور + $('#id_payment_date').attr('data-gregorian', gregorianDateString); } }); } @@ -328,8 +349,14 @@ function buildForm(){ const fd = new FormData(document.getElementById('formFinalPayment')); - const g = document.getElementById('id_payment_date').getAttribute('data-gregorian'); - if (g) { fd.set('payment_date', g); } + + // تبدیل تاریخ شمسی به میلادی برای ارسال + const persianDateValue = $('#id_payment_date').val(); + const gregorianDateValue = $('#id_payment_date').attr('data-gregorian'); + if (persianDateValue && gregorianDateValue) { + fd.set('payment_date', gregorianDateValue); + } + return fd; } (function(){ diff --git a/invoices/templates/invoices/quote_payment_step.html b/invoices/templates/invoices/quote_payment_step.html index e73e0d3..551420b 100644 --- a/invoices/templates/invoices/quote_payment_step.html +++ b/invoices/templates/invoices/quote_payment_step.html @@ -164,7 +164,7 @@ {% for p in payments %} {{ p.amount|floatformat:0|intcomma:False }} تومان - {{ p.payment_date|date:'Y/m/d' }} + {{ p.jpayment_date }} {{ p.get_payment_method_display }} {{ p.reference_number|default:'-' }} @@ -359,6 +359,13 @@ } const form = document.getElementById('formAddPayment'); const fd = buildFormData(form); + + // تبدیل تاریخ شمسی به میلادی برای ارسال + const persianDateValue = $('#id_payment_date').val(); + const gregorianDateValue = $('#id_payment_date').attr('data-gregorian'); + if (persianDateValue && gregorianDateValue) { + fd.set('payment_date', gregorianDateValue); + } fetch('{% url "invoices:add_quote_payment" instance.id step.id %}', { method: 'POST', body: fd @@ -422,18 +429,24 @@ observer: true, calendar: { persian: { locale: 'fa', leapYearMode: 'astronomical' } }, onSelect: function(unix) { + // تبدیل تاریخ شمسی به میلادی برای ارسال به سرور const gregorianDate = new Date(unix); const year = gregorianDate.getFullYear(); const month = String(gregorianDate.getMonth() + 1).padStart(2, '0'); const day = String(gregorianDate.getDate()).padStart(2, '0'); const gregorianDateString = `${year}-${month}-${day}`; + + // نمایش تاریخ شمسی در فیلد if (window.persianDate) { const persianDate = new window.persianDate(unix); const persianDateString = persianDate.format('YYYY/MM/DD'); $('#id_payment_date').val(persianDateString); } else { + // اگر persianDate در دسترس نبود، تاریخ میلادی را نمایش بده $('#id_payment_date').val(gregorianDateString); } + + // ذخیره تاریخ میلادی در فیلد مخفی برای ارسال به سرور $('#id_payment_date').attr('data-gregorian', gregorianDateString); } }); diff --git a/invoices/templates/invoices/quote_preview_step.html b/invoices/templates/invoices/quote_preview_step.html index 5a2fbbd..7243f4b 100644 --- a/invoices/templates/invoices/quote_preview_step.html +++ b/invoices/templates/invoices/quote_preview_step.html @@ -323,7 +323,7 @@ {% else %} {% if next_step %} + class="btn btn-primary"> مرحله بعد diff --git a/invoices/templates/invoices/quote_step.html b/invoices/templates/invoices/quote_step.html index a12c9c0..404cf14 100644 --- a/invoices/templates/invoices/quote_step.html +++ b/invoices/templates/invoices/quote_step.html @@ -149,7 +149,7 @@ {% endif %} {% else %} {% if next_step %} - + مرحله بعد diff --git a/invoices/views.py b/invoices/views.py index cc1f925..b271a29 100644 --- a/invoices/views.py +++ b/invoices/views.py @@ -172,18 +172,24 @@ def create_quote(request, instance_id, step_id): quote.status = 'draft' quote.save(update_fields=['status']) - if next_step: - next_step_instance = instance.step_instances.filter(step=next_step).first() - if next_step_instance and next_step_instance.status == 'completed': - next_step_instance.status = 'in_progress' - next_step_instance.completed_at = None - next_step_instance.save(update_fields=['status', 'completed_at']) + # Reset ALL subsequent completed steps to in_progress + subsequent_steps = instance.process.steps.filter(order__gt=step.order) + for subsequent_step in subsequent_steps: + subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first() + if subsequent_step_instance and subsequent_step_instance.status == 'completed': + # Bypass validation by using update() instead of save() + instance.step_instances.filter(step=subsequent_step).update( + status='in_progress', + completed_at=None + ) # Clear previous approvals if the step requires re-approval try: - next_step_instance.approvals.all().delete() + subsequent_step_instance.approvals.all().delete() except Exception: pass + # Set current step to the next step + if next_step: instance.current_step = next_step instance.save(update_fields=['current_step']) @@ -524,6 +530,26 @@ def add_quote_payment(request, instance_id, step_id): si.approvals.all().delete() except Exception: pass + + # Reset ALL subsequent completed steps to in_progress + try: + subsequent_steps = instance.process.steps.filter(order__gt=step.order) + for subsequent_step in subsequent_steps: + subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first() + if subsequent_step_instance and subsequent_step_instance.status == 'completed': + # Bypass validation by using update() instead of save() + instance.step_instances.filter(step=subsequent_step).update( + status='in_progress', + completed_at=None + ) + # Clear previous approvals if the step requires re-approval + try: + subsequent_step_instance.approvals.all().delete() + except Exception: + pass + except Exception: + pass + # If current step is ahead of this step, reset it back to this step try: if instance.current_step and instance.current_step.order > step.order: @@ -572,6 +598,26 @@ def delete_quote_payment(request, instance_id, step_id, payment_id): si.approvals.all().delete() except Exception: pass + + # Reset ALL subsequent completed steps to in_progress + try: + subsequent_steps = instance.process.steps.filter(order__gt=step.order) + for subsequent_step in subsequent_steps: + subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first() + if subsequent_step_instance and subsequent_step_instance.status == 'completed': + # Bypass validation by using update() instead of save() + instance.step_instances.filter(step=subsequent_step).update( + status='in_progress', + completed_at=None + ) + # Clear previous approvals if the step requires re-approval + try: + subsequent_step_instance.approvals.all().delete() + except Exception: + pass + except Exception: + pass + # If current step is ahead of this step, reset it back to this step try: if instance.current_step and instance.current_step.order > step.order: @@ -1000,6 +1046,25 @@ def add_final_payment(request, instance_id, step_id): si.save() except Exception: pass + + # Reset ALL subsequent completed steps to in_progress + try: + subsequent_steps = instance.process.steps.filter(order__gt=step.order) + for subsequent_step in subsequent_steps: + subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first() + if subsequent_step_instance and subsequent_step_instance.status == 'completed': + # Bypass validation by using update() instead of save() + instance.step_instances.filter(step=subsequent_step).update( + status='in_progress', + completed_at=None + ) + # Clear previous approvals if the step requires re-approval + try: + subsequent_step_instance.approvals.all().delete() + except Exception: + pass + except Exception: + pass return JsonResponse({ 'success': True, 'redirect': reverse('invoices:final_settlement_step', args=[instance.id, step_id]), From 93db2fe7f52e04027fa7b517df9bb13213e16be1 Mon Sep 17 00:00:00 2001 From: aminhashemi92 Date: Tue, 9 Sep 2025 13:15:42 +0330 Subject: [PATCH 04/11] fix installation step and utm standarded. --- db.sqlite3 | Bin 2859008 -> 2940928 bytes installations/models.py | 4 +- .../installation_assign_step.html | 33 +++- .../installation_report_step.html | 95 ++++++----- installations/views.py | 155 ++++++++++++------ invoices/views.py | 2 +- locations/models.py | 2 +- .../templates/processes/request_list.html | 2 +- wells/forms.py | 2 - wells/models.py | 4 +- 10 files changed, 191 insertions(+), 108 deletions(-) diff --git a/db.sqlite3 b/db.sqlite3 index a04ea6caf5f679b74eb1fceea53bad861e7619c5..2f70da4f778ed3d7877c0fde5d64adb2f7aaebbc 100644 GIT binary patch delta 64195 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%0Dccx4h(vi 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 delta 17635 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 P{IQkZv;3CcbWZp`_%`uZ diff --git a/installations/models.py b/installations/models.py index 62f7ecc..98fc452 100644 --- a/installations/models.py +++ b/installations/models.py @@ -42,8 +42,8 @@ class InstallationReport(BaseModel): new_water_meter_serial = models.CharField(max_length=50, null=True, blank=True, verbose_name='سریال کنتور جدید') seal_number = models.CharField(max_length=50, null=True, blank=True, verbose_name='شماره پلمپ') is_meter_suspicious = models.BooleanField(default=False, verbose_name='کنتور مشکوک است؟') - utm_x = models.DecimalField(max_digits=10, decimal_places=6, null=True, blank=True, verbose_name='UTM X') - utm_y = models.DecimalField(max_digits=10, decimal_places=6, null=True, blank=True, verbose_name='UTM Y') + utm_x = models.DecimalField(max_digits=10, decimal_places=0, null=True, blank=True, verbose_name='UTM X') + utm_y = models.DecimalField(max_digits=10, decimal_places=0, null=True, blank=True, verbose_name='UTM Y') description = models.TextField(blank=True, verbose_name='توضیحات') created_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, verbose_name='ایجادکننده') approved = models.BooleanField(default=False, verbose_name='تایید شده') diff --git a/installations/templates/installations/installation_assign_step.html b/installations/templates/installations/installation_assign_step.html index 2bdbe4d..f78b3d8 100644 --- a/installations/templates/installations/installation_assign_step.html +++ b/installations/templates/installations/installation_assign_step.html @@ -22,7 +22,12 @@ {% endblock %} {% block content %} + {% include '_toasts.html' %} + + +{% instance_info_modal instance %} +

@@ -30,11 +35,13 @@

{{ step.name }}: {{ instance.process.name }}

- اشتراک آب: {{ instance.well.water_subscription_number|default:"-" }} - | نماینده: {{ instance.representative.profile.national_code|default:"-" }} + {% instance_info instance %}
- بازگشت + + + بازگشت +
@@ -64,17 +71,17 @@
{% if assignment.assigned_by or assignment.installer %} -
+
{% if assignment.assigned_by %}
-
تعیین‌کننده نصاب
+
تعیین‌کننده نصاب
{{ assignment.assigned_by.get_full_name|default:assignment.assigned_by.username }} ({{ assignment.assigned_by.username }})
{% endif %} {% if assignment.updated %}
-
تاریخ ثبت/ویرایش
+
تاریخ ثبت/ویرایش
{{ assignment.updated|to_jalali }}
{% endif %} @@ -83,14 +90,22 @@ {% endif %}
{% if previous_step %} - قبلی + + + قبلی + {% else %} {% endif %} {% if is_manager %} - + {% else %} - بعدی + + بعدی + + {% endif %}
diff --git a/installations/templates/installations/installation_report_step.html b/installations/templates/installations/installation_report_step.html index 6044f53..946c7b4 100644 --- a/installations/templates/installations/installation_report_step.html +++ b/installations/templates/installations/installation_report_step.html @@ -35,7 +35,12 @@ {% endblock %} {% block content %} + {% include '_toasts.html' %} + + +{% instance_info_modal instance %} +
@@ -43,11 +48,13 @@

{{ step.name }}: {{ instance.process.name }}

- اشتراک آب: {{ instance.well.water_subscription_number|default:"-" }} - | نماینده: {{ instance.representative.profile.national_code|default:"-" }} + {% instance_info instance %}
- بازگشت + + + بازگشت +
@@ -55,18 +62,15 @@
{% if report and not edit_mode %} -
-
-
- {% if request.user|is_installer %} - ویرایش گزارش نصب - {% else %} - - {% endif %} -
-
-
- {% if step_instance and step_instance.status == 'rejected' and step_instance.get_latest_rejection %} +
+ {% if user_is_installer %} + + + ویرایش گزارش نصب + + {% endif %} +
+ {% if step_instance and step_instance.status == 'rejected' and step_instance.get_latest_rejection %} {% endif %} +
+

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

@@ -151,7 +157,7 @@
وضعیت تاییدها
{% if user_can_approve %}
- +
{% endif %} @@ -184,18 +190,28 @@
{% if previous_step %} - قبلی + + + قبلی + {% else %} {% endif %} {% if next_step %} - بعدی + + بعدی + + {% endif %}
{% else %} - {% if not request.user|is_installer %} -
شما مجوز ثبت/ویرایش گزارش نصب را ندارید. اطلاعات به صورت فقط خواندنی نمایش داده می‌شود.
+ + {% if not user_is_installer %} +
شما مجوز ثبت/ویرایش گزارش نصب را ندارید.
{% endif %} + + {% if user_is_installer %} +
{% csrf_token %}
@@ -203,40 +219,40 @@
- +
- +
- +
- +
- +
- +
- +
- {% if request.user|is_installer %} + {% if user_is_installer %} {% endif %}
@@ -246,7 +262,7 @@
photo - {% if request.user|is_installer %} + {% if user_is_installer %} {% endif %} @@ -347,24 +363,28 @@
- +
- + {% endif %}
{% if previous_step %} - قبلی + + + قبلی + {% else %} {% endif %}
- {% if request.user|is_installer %} - - {% else %} - + {% if user_is_installer %} + {% endif %} {% if next_step %} - بعدی + + بعدی + + {% endif %}
@@ -499,7 +519,6 @@ try { if (sessionStorage.getItem('install_report_saved') === '1') { sessionStorage.removeItem('install_report_saved'); - showToast('گزارش نصب با موفقیت ثبت شد', 'success'); } } catch(_) {} })(); diff --git a/installations/views.py b/installations/views.py index 8c3dc7e..ac27db9 100644 --- a/installations/views.py +++ b/installations/views.py @@ -19,7 +19,7 @@ def installation_assign_step(request, instance_id, step_id): next_step = instance.process.steps.filter(order__gt=step.order).first() # Installers list (profiles that have installer role) - installers = Profile.objects.filter(roles__slug=UserRoles.INSTALLER.value).select_related('user').all() + installers = Profile.objects.filter(roles__slug=UserRoles.INSTALLER.value, county=instance.well.county).select_related('user').all() assignment, _ = InstallationAssignment.objects.get_or_create(process_instance=instance) # Role flags @@ -72,17 +72,56 @@ def installation_assign_step(request, instance_id, step_id): }) +def create_item_changes_for_report(report, remove_map, add_map, quote_price_map): + """Helper function to create item changes for a report""" + # Create remove changes + for item_id, qty in remove_map.items(): + up = quote_price_map.get(item_id) + total = (up * qty) if up is not None else None + InstallationItemChange.objects.create( + report=report, + item_id=item_id, + change_type='remove', + quantity=qty, + unit_price=up, + total_price=total, + ) + + # Create add changes + for item_id, data in add_map.items(): + unit_price = data.get('price') + qty = data.get('qty') or 1 + total = (unit_price * qty) if (unit_price is not None) else None + InstallationItemChange.objects.create( + report=report, + item_id=item_id, + change_type='add', + quantity=qty, + unit_price=unit_price, + total_price=total, + ) + + @login_required def installation_report_step(request, instance_id, step_id): instance = get_object_or_404(ProcessInstance, id=instance_id) step = get_object_or_404(instance.process.steps, id=step_id) + previous_step = instance.process.steps.filter(order__lt=step.order).last() next_step = instance.process.steps.filter(order__gt=step.order).first() + assignment = InstallationAssignment.objects.filter(process_instance=instance).first() existing_report = InstallationReport.objects.filter(assignment=assignment).order_by('-created').first() - # Only installers can enter edit mode - user_is_installer = hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.INSTALLER) + + # Only the assigned installer can create/edit the report + try: + has_installer_role = bool(getattr(request.user, 'profile', None) and request.user.profile.has_role(UserRoles.INSTALLER)) + except Exception: + has_installer_role = False + is_assigned_installer = bool(assignment and assignment.installer_id == request.user.id) + user_is_installer = bool(has_installer_role and is_assigned_installer) edit_mode = True if (request.GET.get('edit') == '1' and user_is_installer) else False + # current quote items baseline quote = Quote.objects.filter(process_instance=instance).first() quote_items = list(quote.items.select_related('item').all()) if quote else [] @@ -100,7 +139,14 @@ def installation_report_step(request, instance_id, step_id): reqs = list(step.approver_requirements.select_related('role').all()) user_roles_qs = getattr(getattr(request.user, 'profile', None), 'roles', None) user_roles = list(user_roles_qs.all()) if user_roles_qs is not None else [] - user_can_approve = any(r.role in user_roles for r in reqs) + # Align permission check with invoices flow (role id intersection) + try: + req_role_ids = {r.role_id for r in reqs} + user_role_ids = {ur.id for ur in user_roles} + can_approve_reject = len(req_role_ids.intersection(user_role_ids)) > 0 + except Exception: + can_approve_reject = False + user_can_approve = can_approve_reject approvals_list = list(step_instance.approvals.select_related('role').all()) approvals_by_role = {a.role_id: a for a in approvals_list} approver_statuses = [ @@ -160,6 +206,13 @@ def installation_report_step(request, instance_id, step_id): StepRejection.objects.create(step_instance=step_instance, rejected_by=request.user, reason=reason) existing_report.approved = False existing_report.save() + # If current step moved ahead of this step, reset it back for correction (align with invoices) + try: + if instance.current_step and instance.current_step.order > step.order: + instance.current_step = step + instance.save(update_fields=['current_step']) + except Exception: + pass messages.success(request, 'گزارش رد شد و برای اصلاح به نصاب بازگشت.') return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id) @@ -177,6 +230,21 @@ def installation_report_step(request, instance_id, step_id): is_suspicious = True if request.POST.get('is_meter_suspicious') == 'on' else False utm_x = request.POST.get('utm_x') or None utm_y = request.POST.get('utm_y') or None + # Normalize UTM to integer meters + if utm_x is not None and utm_x != '': + try: + utm_x = int(Decimal(str(utm_x))) + except InvalidOperation: + utm_x = None + else: + utm_x = None + if utm_y is not None and utm_y != '': + try: + utm_y = int(Decimal(str(utm_y))) + except InvalidOperation: + utm_y = None + else: + utm_y = None # Build maps from form fields: remove and add remove_map = {} @@ -221,8 +289,6 @@ def installation_report_step(request, instance_id, step_id): unit_price = item_obj.unit_price if item_obj else None add_map[item_id] = {'qty': qty, 'price': unit_price} - # اجازهٔ ثبت همزمان حذف و افزودن برای یک قلم (بدون محدودیت و ادغام) - if existing_report and edit_mode: report = existing_report report.description = description @@ -247,29 +313,7 @@ def installation_report_step(request, instance_id, step_id): InstallationPhoto.objects.create(report=report, image=f) # replace item changes with new submission report.item_changes.all().delete() - for item_id, qty in remove_map.items(): - up = quote_price_map.get(item_id) - total = (up * qty) if up is not None else None - InstallationItemChange.objects.create( - report=report, - item_id=item_id, - change_type='remove', - quantity=qty, - unit_price=up, - total_price=total, - ) - for item_id, data in add_map.items(): - unit_price = data.get('price') - qty = data.get('qty') or 1 - total = (unit_price * qty) if (unit_price is not None) else None - InstallationItemChange.objects.create( - report=report, - item_id=item_id, - change_type='add', - quantity=qty, - unit_price=unit_price, - total_price=total, - ) + create_item_changes_for_report(report, remove_map, add_map, quote_price_map) else: report = InstallationReport.objects.create( assignment=assignment, @@ -286,29 +330,7 @@ def installation_report_step(request, instance_id, step_id): for f in request.FILES.getlist('photos'): InstallationPhoto.objects.create(report=report, image=f) # item changes - for item_id, qty in remove_map.items(): - up = quote_price_map.get(item_id) - total = (up * qty) if up is not None else None - InstallationItemChange.objects.create( - report=report, - item_id=item_id, - change_type='remove', - quantity=qty, - unit_price=up, - total_price=total, - ) - for item_id, data in add_map.items(): - unit_price = data.get('price') - qty = data.get('qty') or 1 - total = (unit_price * qty) if (unit_price is not None) else None - InstallationItemChange.objects.create( - report=report, - item_id=item_id, - change_type='add', - quantity=qty, - unit_price=unit_price, - total_price=total, - ) + create_item_changes_for_report(report, remove_map, add_map, quote_price_map) # After installer submits/edits, set step back to in_progress and clear approvals step_instance.status = 'in_progress' @@ -319,6 +341,33 @@ def installation_report_step(request, instance_id, step_id): except Exception: pass + # If the report was edited, ensure downstream steps reopen like invoices flow + try: + subsequent_steps = instance.process.steps.filter(order__gt=step.order) + for subsequent_step in subsequent_steps: + subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first() + if subsequent_step_instance and subsequent_step_instance.status == 'completed': + # Reopen the step + instance.step_instances.filter(step=subsequent_step).update( + status='in_progress', + completed_at=None + ) + # Clear previous approvals if any + try: + subsequent_step_instance.approvals.all().delete() + except Exception: + pass + except Exception: + pass + + # If current step is ahead of this step, reset it back to this step + try: + if instance.current_step and instance.current_step.order > step.order: + instance.current_step = step + instance.save(update_fields=['current_step']) + except Exception: + pass + messages.success(request, 'گزارش ثبت شد و در انتظار تایید است.') return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id) @@ -340,6 +389,7 @@ def installation_report_step(request, instance_id, step_id): 'assignment': assignment, 'report': existing_report, 'edit_mode': edit_mode, + 'user_is_installer': user_is_installer, 'quote': quote, 'quote_items': quote_items, 'all_items': items, @@ -351,6 +401,7 @@ def installation_report_step(request, instance_id, step_id): 'step_instance': step_instance, 'approver_statuses': approver_statuses, 'user_can_approve': user_can_approve, + 'can_approve_reject': can_approve_reject, }) diff --git a/invoices/views.py b/invoices/views.py index b271a29..ea99eb7 100644 --- a/invoices/views.py +++ b/invoices/views.py @@ -408,7 +408,7 @@ def quote_payment_step(request, instance_id, step_id): defaults={'approved_by': request.user, 'decision': 'rejected', 'reason': reason} ) StepRejection.objects.create(step_instance=step_instance, rejected_by=request.user, reason=reason) - # If current step is ahead of this step, reset it back to this step + # If current step is ahead of this step, reset it back to this step try: if instance.current_step and instance.current_step.order > step.order: instance.current_step = step diff --git a/locations/models.py b/locations/models.py index d3de371..bca8971 100644 --- a/locations/models.py +++ b/locations/models.py @@ -11,7 +11,7 @@ class City(NameSlugModel): return self.name class County(NameSlugModel): - city = models.ForeignKey(City, on_delete=models.CASCADE, verbose_name="شهرستان") + city = models.ForeignKey(City, on_delete=models.CASCADE, verbose_name="استان") class Meta: verbose_name = "شهرستان" diff --git a/processes/templates/processes/request_list.html b/processes/templates/processes/request_list.html index dfcf4bc..feb1294 100644 --- a/processes/templates/processes/request_list.html +++ b/processes/templates/processes/request_list.html @@ -479,7 +479,7 @@ $('#requests-table').DataTable({ pageLength: 10, lengthMenu: [[10, 25, 50, -1], [10, 25, 50, "همه"]], - order: [[0, 'desc']], + order: [], responsive: true, }); let currentWellId = null; diff --git a/wells/forms.py b/wells/forms.py index 1649d01..5f39178 100644 --- a/wells/forms.py +++ b/wells/forms.py @@ -82,12 +82,10 @@ class WellForm(forms.ModelForm): 'utm_x': forms.NumberInput(attrs={ 'class': 'form-control', 'placeholder': 'X UTM', - 'step': '0.000001' }), 'utm_y': forms.NumberInput(attrs={ 'class': 'form-control', 'placeholder': 'Y UTM', - 'step': '0.000001' }), 'utm_zone': forms.NumberInput(attrs={ 'class': 'form-control', diff --git a/wells/models.py b/wells/models.py index c0663f9..89e9aad 100644 --- a/wells/models.py +++ b/wells/models.py @@ -78,14 +78,14 @@ class Well(SluggedModel): utm_x = models.DecimalField( max_digits=10, - decimal_places=6, + decimal_places=0, verbose_name="X UTM", null=True, blank=True ) utm_y = models.DecimalField( max_digits=10, - decimal_places=6, + decimal_places=0, verbose_name="Y UTM", null=True, blank=True From 9592c00565a89e53e8b8268e5fa91f1f8dd33b04 Mon Sep 17 00:00:00 2001 From: aminhashemi92 Date: Tue, 9 Sep 2025 15:59:41 +0330 Subject: [PATCH 05/11] fix final payment step. --- db.sqlite3 | Bin 2940928 -> 3088384 bytes .../invoices/final_invoice_print.html | 251 ++++++++++++++---- .../invoices/final_invoice_step.html | 31 ++- .../invoices/final_settlement_step.html | 30 ++- invoices/urls.py | 1 - invoices/views.py | 72 ++++- 6 files changed, 305 insertions(+), 80 deletions(-) diff --git a/db.sqlite3 b/db.sqlite3 index 2f70da4f778ed3d7877c0fde5d64adb2f7aaebbc..1bba24daf5d5ee25fb7162099b11379bebd678bd 100644 GIT binary patch delta 99666 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 delta 12752 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 + + + + + فاکتور نهایی {{ invoice.name }} - {{ instance.code }} + + {% load static %} + {% load humanize %} -{% block content %} -
-
-
-

فاکتور نهایی

- کد درخواست: {{ instance.code }} + + + + + + + + + + + + + + + + + + + +
+ +
+
+
+
+ {% if instance.broker.company and instance.broker.company.logo %} + لوگو + {% else %} + + {% endif %} +
+
+ {% if instance.broker.company %} + {{ instance.broker.company.name }} + {% endif %} + {% if instance.broker.company %} +
+ {% if instance.broker.company.address %} +
{{ instance.broker.company.address }}
+ {% endif %} + {% if instance.broker.affairs.county.city.name %} +
{{ instance.broker.affairs.county.city.name }}، ایران
+ {% endif %} + {% if instance.broker.company.phone %} +
تلفن: {{ instance.broker.company.phone }}
+ {% endif %} +
+ {% endif %} +
+
+
+
+
#فاکتور نهایی {{ instance.code }}
+
تاریخ صدور: {{ invoice.jcreated_date }}
+
+
+
-
- -
لوگو
+ + +
+
+
اطلاعات مشترک
+
نام: {{ invoice.customer.get_full_name|default:instance.representative.get_full_name }}
+ {% if instance.representative.profile and instance.representative.profile.national_code %} +
کد ملی: {{ instance.representative.profile.national_code }}
+ {% endif %} + {% if instance.representative.profile and instance.representative.profile.phone_number_1 %} +
تلفن: {{ instance.representative.profile.phone_number_1 }}
+ {% endif %} + {% if instance.representative.profile and instance.representative.profile.address %} +
آدرس: {{ instance.representative.profile.address }}
+ {% endif %} +
+
+
اطلاعات چاه
+
شماره اشتراک آب: {{ instance.well.water_subscription_number }}
+
شماره اشتراک برق: {{ instance.well.electricity_subscription_number|default:"-" }}
+
سریال کنتور: {{ instance.well.water_meter_serial_number|default:"-" }}
+
قدرت چاه: {{ instance.well.well_power|default:"-" }}
+
-
-
- - - - - - - - - - - {% for it in items %} - - - - - - - {% empty %} - - {% endfor %} - - - - - - - - -
آیتمتعدادقیمت واحدقیمت کل
{{ it.item.name }}{{ it.quantity }}{{ it.unit_price|floatformat:0|intcomma:False }}{{ it.total_price|floatformat:0|intcomma:False }}
آیتمی ندارد
مبلغ کل{{ invoice.total_amount|floatformat:0|intcomma:False }}
تخفیف{{ invoice.discount_amount|floatformat:0|intcomma:False }}
مبلغ نهایی{{ invoice.final_amount|floatformat:0|intcomma:False }}
پرداختی‌ها{{ invoice.paid_amount|floatformat:0|intcomma:False }}
مانده{{ invoice.remaining_amount|floatformat:0|intcomma:False }}
-
-
-
امضا مشتری
-
امضا شرکت
-
-
- -{% endblock %} + +
+ + + + + + + + + + + + + {% for it in items %} + + + + + + + + + {% empty %} + + {% endfor %} + + + + + + + {% if invoice.discount_amount > 0 %} + + + + + {% endif %} + + + + + + + + + + + + + +
ردیفشرح کالا/خدماتتوضیحاتتعدادقیمت واحد(تومان)قیمت کل(تومان)
{{ forloop.counter }}{{ it.item.name }}{{ it.item.description|default:"-" }}{{ it.quantity }}{{ it.unit_price|floatformat:0|intcomma:False }}{{ it.total_price|floatformat:0|intcomma:False }}
آیتمی ندارد
جمع کل(تومان):{{ invoice.total_amount|floatformat:0|intcomma:False }}
تخفیف(تومان):{{ invoice.discount_amount|floatformat:0|intcomma:False }}
مبلغ نهایی(تومان):{{ invoice.final_amount|floatformat:0|intcomma:False }}
پرداختی‌ها(تومان):{{ invoice.paid_amount|floatformat:0|intcomma:False }}
مانده(تومان):{{ invoice.remaining_amount|floatformat:0|intcomma:False }}
+
+ +
+
+
مهر و امضا:
+
    + {% if instance.broker.company and instance.broker.company.signature %} +
  • امضا
  • + {% endif %} +
+
+ {% if instance.broker.company %} +
+
اطلاعات پرداخت
+ {% if instance.broker.company.card_number %} +
شماره کارت: {{ instance.broker.company.card_number }}
+ {% endif %} + {% if instance.broker.company.account_number %} +
شماره حساب: {{ instance.broker.company.account_number }}
+ {% endif %} + {% if instance.broker.company.sheba_number %} +
شماره شبا: {{ instance.broker.company.sheba_number }}
+ {% endif %} + {% if instance.broker.company.bank_name %} +
بانک: {{ instance.broker.company.get_bank_name_display }}
+ {% endif %} +
+ {% endif %} +
+ +
+ + + + diff --git a/invoices/templates/invoices/final_invoice_step.html b/invoices/templates/invoices/final_invoice_step.html index dfee339..5fe05e4 100644 --- a/invoices/templates/invoices/final_invoice_step.html +++ b/invoices/templates/invoices/final_invoice_step.html @@ -24,6 +24,10 @@ {% block content %} {% include '_toasts.html' %} + + +{% instance_info_modal instance %} + {% csrf_token %}
@@ -32,14 +36,18 @@

{{ step.name }}: {{ instance.process.name }}

- اشتراک آب: {{ instance.well.water_subscription_number|default:"-" }} - | نماینده: {{ instance.representative.profile.national_code|default:"-" }} + {% instance_info instance %}
@@ -163,15 +171,24 @@
diff --git a/invoices/templates/invoices/final_settlement_step.html b/invoices/templates/invoices/final_settlement_step.html index 2335298..350d5b7 100644 --- a/invoices/templates/invoices/final_settlement_step.html +++ b/invoices/templates/invoices/final_settlement_step.html @@ -23,6 +23,10 @@ {% block content %} {% include '_toasts.html' %} + + +{% instance_info_modal instance %} + {% csrf_token %}
@@ -31,14 +35,18 @@

{{ step.name }}: {{ instance.process.name }}

- اشتراک آب: {{ instance.well.water_subscription_number|default:"-" }} - | نماینده: {{ instance.representative.profile.national_code|default:"-" }} + {% instance_info instance %}
@@ -88,7 +96,7 @@
- +
@@ -182,7 +190,7 @@
وضعیت تاییدها
{% if can_approve_reject %}
- +
{% endif %} @@ -214,13 +222,19 @@ {% endif %}
{% if previous_step %} - قبلی + + + قبلی + {% else %} {% endif %} {% if step_instance.status == 'completed' %} {% if next_step %} - بعدی + + بعدی + + {% else %} اتمام {% endif %} diff --git a/invoices/urls.py b/invoices/urls.py index f959799..f40df30 100644 --- a/invoices/urls.py +++ b/invoices/urls.py @@ -31,5 +31,4 @@ urlpatterns = [ path('instance//step//final-settlement/', views.final_settlement_step, name='final_settlement_step'), path('instance//step//final-settlement/add/', views.add_final_payment, name='add_final_payment'), path('instance//step//final-settlement//delete/', views.delete_final_payment, name='delete_final_payment'), - path('instance//step//final-settlement/approve/', views.approve_final_settlement, name='approve_final_settlement'), ] diff --git a/invoices/views.py b/invoices/views.py index ea99eb7..b8a1eb2 100644 --- a/invoices/views.py +++ b/invoices/views.py @@ -12,7 +12,7 @@ import json from processes.models import ProcessInstance, ProcessStep, StepInstance, StepRejection, StepApproval from accounts.models import Role from common.consts import UserRoles -from .models import Item, Quote, QuoteItem, Payment, Invoice +from .models import Item, Quote, QuoteItem, Payment, Invoice, InvoiceItem from installations.models import InstallationReport, InstallationItemChange @@ -792,14 +792,7 @@ def approve_final_invoice(request, instance_id, step_id): return JsonResponse({'success': False, 'message': 'شما مجوز تایید این مرحله را ندارید'}, status=403) except Exception: return JsonResponse({'success': False, 'message': 'شما مجوز تایید این مرحله را ندارید'}, status=403) - # Block approval when there is any remaining (positive or negative) - invoice.calculate_totals() - # if invoice.remaining_amount != 0: - # return JsonResponse({ - # 'success': False, - # 'message': f"تا زمانی که مانده فاکتور صفر نشده امکان تایید نیست (مانده فعلی: {invoice.remaining_amount})" - # }) - # mark step completed + step_instance, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step) step_instance.status = 'completed' step_instance.completed_at = timezone.now() @@ -826,7 +819,7 @@ def add_special_charge(request, instance_id, step_id): return JsonResponse({'success': False, 'message': 'شما مجوز افزودن هزینه ویژه را ندارید'}, status=403) except Exception: return JsonResponse({'success': False, 'message': 'شما مجوز افزودن هزینه ویژه را ندارید'}, status=403) - # charge_type was removed from UI; we no longer require it + item_id = request.POST.get('item_id') amount = (request.POST.get('amount') or '').strip() if not item_id: @@ -841,7 +834,7 @@ def add_special_charge(request, instance_id, step_id): # Fetch existing special item from DB special_item = get_object_or_404(Item, id=item_id, is_special=True) - from .models import InvoiceItem + InvoiceItem.objects.create( invoice=invoice, item=special_item, @@ -863,7 +856,6 @@ def delete_special_charge(request, instance_id, step_id, item_id): return JsonResponse({'success': False, 'message': 'شما مجوز حذف هزینه ویژه را ندارید'}, status=403) except Exception: return JsonResponse({'success': False, 'message': 'شما مجوز حذف هزینه ویژه را ندارید'}, status=403) - from .models import InvoiceItem inv_item = get_object_or_404(InvoiceItem, id=item_id, invoice=invoice) # allow deletion only for special items try: @@ -880,6 +872,7 @@ def delete_special_charge(request, instance_id, step_id, item_id): def final_settlement_step(request, instance_id, step_id): instance = get_object_or_404(ProcessInstance, id=instance_id) step = get_object_or_404(instance.process.steps, id=step_id) + if not instance.can_access_step(step): messages.error(request, 'شما به این مرحله دسترسی ندارید. ابتدا مراحل قبلی را تکمیل کنید.') return redirect('processes:request_list') @@ -890,6 +883,7 @@ def final_settlement_step(request, instance_id, step_id): # Ensure step instance exists step_instance, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step, defaults={'status': 'in_progress'}) + # Build approver statuses for template reqs = list(step.approver_requirements.select_related('role').all()) approvals_map = {a.role_id: a.decision for a in step_instance.approvals.select_related('role').all()} @@ -947,6 +941,13 @@ def final_settlement_step(request, instance_id, step_id): defaults={'approved_by': request.user, 'decision': 'rejected', 'reason': reason} ) StepRejection.objects.create(step_instance=step_instance, rejected_by=request.user, reason=reason) + # If current step is ahead of this step, reset it back to this step (align behavior with other steps) + try: + if instance.current_step and instance.current_step.order > step.order: + instance.current_step = step + instance.save(update_fields=['current_step']) + except Exception: + pass messages.success(request, 'مرحله تسویه نهایی رد شد و برای اصلاح بازگشت.') return redirect('invoices:final_settlement_step', instance_id=instance.id, step_id=step.id) @@ -984,6 +985,7 @@ def add_final_payment(request, instance_id, step_id): return JsonResponse({'success': False, 'message': 'شما مجوز افزودن تراکنش تسویه را ندارید'}, status=403) except Exception: return JsonResponse({'success': False, 'message': 'شما مجوز افزودن تراکنش تسویه را ندارید'}, status=403) + amount = (request.POST.get('amount') or '').strip() payment_date = (request.POST.get('payment_date') or '').strip() payment_method = (request.POST.get('payment_method') or '').strip() @@ -1038,12 +1040,14 @@ def add_final_payment(request, instance_id, step_id): ) # After creation, totals auto-updated by model save. Respond with redirect and new totals for UX. invoice.refresh_from_db() - # After payment change, set step back to in_progress + + # On delete, return to awaiting approval try: si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step) si.status = 'in_progress' si.completed_at = None si.save() + si.approvals.all().delete() except Exception: pass @@ -1065,6 +1069,16 @@ def add_final_payment(request, instance_id, step_id): pass except Exception: pass + + # If current step is ahead of this step, reset it back to this step + try: + if instance.current_step and instance.current_step.order > step.order: + instance.current_step = step + instance.save(update_fields=['current_step']) + except Exception: + pass + + return JsonResponse({ 'success': True, 'redirect': reverse('invoices:final_settlement_step', args=[instance.id, step_id]), @@ -1091,14 +1105,44 @@ def delete_final_payment(request, instance_id, step_id, payment_id): return JsonResponse({'success': False, 'message': 'شما مجوز حذف تراکنش تسویه را ندارید'}, status=403) payment.delete() invoice.refresh_from_db() - # After payment change, set step back to in_progress + + # On delete, return to awaiting approval try: si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step) si.status = 'in_progress' si.completed_at = None si.save() + si.approvals.all().delete() except Exception: pass + + # Reset ALL subsequent completed steps to in_progress + try: + subsequent_steps = instance.process.steps.filter(order__gt=step.order) + for subsequent_step in subsequent_steps: + subsequent_step_instance = instance.step_instances.filter(step=subsequent_step).first() + if subsequent_step_instance and subsequent_step_instance.status == 'completed': + # Bypass validation by using update() instead of save() + instance.step_instances.filter(step=subsequent_step).update( + status='in_progress', + completed_at=None + ) + # Clear previous approvals if the step requires re-approval + try: + subsequent_step_instance.approvals.all().delete() + except Exception: + pass + except Exception: + pass + + # If current step is ahead of this step, reset it back to this step + try: + if instance.current_step and instance.current_step.order > step.order: + instance.current_step = step + instance.save(update_fields=['current_step']) + except Exception: + pass + return JsonResponse({'success': True, 'redirect': reverse('invoices:final_settlement_step', args=[instance.id, step_id]), 'totals': { 'final_amount': str(invoice.final_amount), 'paid_amount': str(invoice.paid_amount), From 394546dc674680b757213868c69400297de26ee9 Mon Sep 17 00:00:00 2001 From: aminhashemi92 Date: Tue, 9 Sep 2025 16:42:22 +0330 Subject: [PATCH 06/11] fix final step --- certificates/models.py | 4 + .../templates/certificates/print.html | 89 +++++++++++++----- certificates/templates/certificates/step.html | 46 +++++---- certificates/views.py | 17 +++- db.sqlite3 | Bin 3088384 -> 3133440 bytes .../invoices/final_settlement_step.html | 4 +- invoices/views.py | 29 +----- processes/admin.py | 4 +- .../templates/processes/instance_summary.html | 26 +++-- 9 files changed, 137 insertions(+), 82 deletions(-) diff --git a/certificates/models.py b/certificates/models.py index 64d53f9..1b3dcaf 100644 --- a/certificates/models.py +++ b/certificates/models.py @@ -1,6 +1,7 @@ from django.db import models from django.contrib.auth import get_user_model from common.models import BaseModel +from _helpers.utils import jalali_converter2 User = get_user_model() @@ -35,4 +36,7 @@ class CertificateInstance(BaseModel): def __str__(self): return f"گواهی {self.process_instance.code}" + def jissued_at(self): + return jalali_converter2(self.issued_at) + diff --git a/certificates/templates/certificates/print.html b/certificates/templates/certificates/print.html index 5bd26c2..d5ef11f 100644 --- a/certificates/templates/certificates/print.html +++ b/certificates/templates/certificates/print.html @@ -1,28 +1,71 @@ -{% extends '_base.html' %} + + + + + + تاییدیه - {{ instance.code }} + {% load static %} -{% block content %} -
-
- {% if template.company and template.company.logo %} - logo - {% endif %} -

{{ cert.rendered_title }}

- {% if template.company %}
{{ template.company.name }}
{% endif %} -
-
- {{ cert.rendered_body|safe }} -
-
-
تاریخ: {{ cert.issued_at }}
-
- {% if template.company and template.company.signature %} - seal + + + + + + + + + + + + + + +
+ +
+
+
شماره درخواست: {{ instance.code }}
+
تاریخ: {{ cert.jissued_at }}
+
+
+ + +
+ {% if template.company and template.company.logo %} + logo {% endif %} -
مهر و امضای شرکت
+

{{ cert.rendered_title }}

+ {% if template.company %} +
{{ template.company.name }}
+ {% endif %} +
+ + +
+ {{ cert.rendered_body|safe }} +
+ + +
+
+
مهر و امضای تایید کننده
+
{{ template.company.name }}
+ {% if template.company and template.company.signature %} + seal + {% endif %} +
-
- -{% endblock %} - + + + diff --git a/certificates/templates/certificates/step.html b/certificates/templates/certificates/step.html index b8923c2..0027d18 100644 --- a/certificates/templates/certificates/step.html +++ b/certificates/templates/certificates/step.html @@ -18,40 +18,49 @@ - {% endblock %} {% block content %} {% include '_toasts.html' %} + + + {% instance_info_modal instance %} + {% csrf_token %}
-
+

{{ step.name }}: {{ instance.process.name }}

- اشتراک آب: {{ instance.well.water_subscription_number|default:"-" }} - | نماینده: {{ instance.representative.profile.national_code|default:"-" }} + {% instance_info instance %}
-
+
{% stepper_header instance step %}
+
+
+
شماره درخواست: {{ instance.code }}
+
تاریخ: {{ cert.jissued_at }}
+
+
{% if template.company and template.company.logo %} logo @@ -62,21 +71,22 @@
{{ cert.rendered_body|safe }}
-
-
-
تاریخ صدور: {{ cert.issued_at }}
-
+
+
مهر و امضای تایید کننده
+
{{ template.company.name }}
{% if template.company and template.company.signature %} - seal + seal {% endif %} -
مهر و امضای شرکت
+ {% if access_denied %} + + {% endif %} + +
+
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + + + حذف فیلتر + +
+ +
+
@@ -178,7 +266,7 @@ - + {% empty %} - + + + + + + + + + + + {% endfor %} diff --git a/processes/templatetags/processes_tags.py b/processes/templatetags/processes_tags.py index 0922e3c..5ee7b2f 100644 --- a/processes/templatetags/processes_tags.py +++ b/processes/templatetags/processes_tags.py @@ -1,6 +1,7 @@ from django import template from django.utils.safestring import mark_safe from ..models import ProcessInstance, StepInstance +from ..utils import count_incomplete_instances register = template.Library() @@ -104,3 +105,8 @@ def instance_info(instance, modal_id=None): title="اطلاعات کامل چاه و نماینده"> ''' return mark_safe(html) + + +@register.simple_tag +def incomplete_requests_count(user): + return count_incomplete_instances(user) diff --git a/processes/utils.py b/processes/utils.py new file mode 100644 index 0000000..951398e --- /dev/null +++ b/processes/utils.py @@ -0,0 +1,118 @@ +from django.shortcuts import get_object_or_404 +from .models import ProcessInstance +from common.consts import UserRoles + + +def scope_instances_queryset(user, queryset=None): + """Return a queryset of ProcessInstance scoped by the user's role. + + If no profile/role, returns an empty queryset. + """ + qs = queryset if queryset is not None else ProcessInstance.objects.all() + profile = getattr(user, 'profile', None) + if not profile: + return qs.none() + try: + if profile.has_role(UserRoles.INSTALLER): + # Only instances assigned to this installer + from installations.models import InstallationAssignment + assign_ids = InstallationAssignment.objects.filter(installer=user).values_list('process_instance', flat=True) + return qs.filter(id__in=assign_ids) + if profile.has_role(UserRoles.BROKER): + return qs.filter(broker=profile.broker) + if profile.has_role(UserRoles.ACCOUNTANT) or profile.has_role(UserRoles.MANAGER): + return qs.filter(broker__affairs__county=profile.county) + if profile.has_role(UserRoles.ADMIN): + return qs + # if profile.has_role(UserRoles.WATER_RESOURCE_MANAGER) or profile.has_role(UserRoles.HEADQUARTER): + # return qs.filter(well__county=profile.county) + # Fallback: no special scope + # return qs + except Exception: + return qs.none() + + +def count_incomplete_instances(user): + """Count non-completed, non-deleted requests within the user's scope.""" + base = ProcessInstance.objects.select_related('well').filter(is_deleted=False).exclude(status='completed') + return scope_instances_queryset(user, base).count() + + +def user_can_access_instance(user, instance: ProcessInstance) -> bool: + """Check if user can access a specific instance based on scoping rules.""" + try: + scoped = scope_instances_queryset(user, ProcessInstance.objects.filter(id=instance.id)) + return scoped.exists() + except Exception: + return False + + +def get_scoped_instance_or_404(request, instance_id: int) -> ProcessInstance: + """Return instance only if it's within the user's scope; otherwise 404. + + Use this in any view receiving instance_id from URL to prevent URL tampering. + """ + base = ProcessInstance.objects.filter(is_deleted=False) + qs = scope_instances_queryset(request.user, base) + return get_object_or_404(qs, id=instance_id) + + +def scope_wells_queryset(user, queryset=None): + """Return a queryset of Well scoped by the user's role (parity with instances).""" + try: + from wells.models import Well + qs = queryset if queryset is not None else Well.objects.all() + profile = getattr(user, 'profile', None) + if not profile: + return qs.none() + if profile.has_role(UserRoles.ADMIN): + return qs + if profile.has_role(UserRoles.BROKER): + return qs.filter(broker=profile.broker) + if profile.has_role(UserRoles.ACCOUNTANT) or profile.has_role(UserRoles.MANAGER): + return qs.filter(broker__affairs__county=profile.county) + if profile.has_role(UserRoles.INSTALLER): + # Wells that have instances assigned to this installer + from installations.models import InstallationAssignment + assign_ids = InstallationAssignment.objects.filter(installer=user).values_list('process_instance', flat=True) + inst_qs = ProcessInstance.objects.filter(id__in=assign_ids) + return qs.filter(process_instances__in=inst_qs).distinct() + # Fallback + return qs.none() + except Exception: + return qs.none() if 'qs' in locals() else [] + + +def scope_customers_queryset(user, queryset=None): + """Return a queryset of customer Profiles scoped by user's role. + + Assumes queryset is Profiles already filtered to customers, otherwise we filter here. + """ + try: + from accounts.models import Profile + qs = queryset if queryset is not None else Profile.objects.all() + # Ensure we're only looking at customer profiles + from common.consts import UserRoles as UR + qs = qs.filter(roles__slug=UR.CUSTOMER.value, is_deleted=False) + + profile = getattr(user, 'profile', None) + if not profile: + return qs.none() + if profile.has_role(UserRoles.ADMIN): + return qs + if profile.has_role(UserRoles.BROKER): + return qs.filter(broker=profile.broker) + if profile.has_role(UserRoles.ACCOUNTANT) or profile.has_role(UserRoles.MANAGER): + return qs.filter(county=profile.county) + if profile.has_role(UserRoles.INSTALLER): + # Customers that are representatives of instances assigned to this installer + from installations.models import InstallationAssignment + assign_ids = InstallationAssignment.objects.filter(installer=user).values_list('process_instance', flat=True) + rep_ids = ProcessInstance.objects.filter(id__in=assign_ids).values_list('representative', flat=True) + return qs.filter(user_id__in=rep_ids) + # Fallback + return qs.none() + except Exception: + return qs.none() if 'qs' in locals() else [] + + diff --git a/processes/views.py b/processes/views.py index f84e826..38ad946 100644 --- a/processes/views.py +++ b/processes/views.py @@ -7,19 +7,62 @@ from django.http import JsonResponse from django.views.decorators.http import require_POST, require_GET from django.db import transaction from django.contrib.auth import get_user_model -from .models import Process, ProcessInstance, StepInstance +from .models import Process, ProcessInstance, StepInstance, ProcessStep +from .utils import scope_instances_queryset, get_scoped_instance_or_404 +from installations.models import InstallationAssignment from wells.models import Well -from accounts.models import Profile +from accounts.models import Profile, Broker +from locations.models import Affairs from accounts.forms import CustomerForm from wells.forms import WellForm from wells.models import WaterMeterManufacturer +from common.consts import UserRoles @login_required def request_list(request): """نمایش لیست درخواست‌ها با جدول و مدال ایجاد""" - instances = ProcessInstance.objects.select_related('well', 'representative', 'requester').prefetch_related('step_instances__step').filter(is_deleted=False).order_by('-created') + instances = ProcessInstance.objects.select_related('well', 'representative', 'requester', 'broker', 'current_step', 'process').prefetch_related('step_instances__step').filter(is_deleted=False).order_by('-created') + access_denied = False + + # filter by roles (scoped queryset) + try: + instances = scope_instances_queryset(request.user, instances) + if not instances.exists() and not getattr(request.user, 'profile', None): + access_denied = True + instances = instances.none() + except Exception: + access_denied = True + instances = instances.none() + + # Filters + status_q = (request.GET.get('status') or '').strip() + affairs_q = (request.GET.get('affairs') or '').strip() + broker_q = (request.GET.get('broker') or '').strip() + step_q = (request.GET.get('step') or '').strip() + + if status_q: + instances = instances.filter(status=status_q) + if affairs_q: + try: + instances = instances.filter(well__affairs_id=int(affairs_q)) + except Exception: + pass + if broker_q: + try: + instances = instances.filter(broker_id=int(broker_q)) + except Exception: + pass + if step_q: + try: + instances = instances.filter(current_step_id=int(step_q)) + except Exception: + pass processes = Process.objects.filter(is_active=True) + status_choices = list(ProcessInstance.STATUS_CHOICES) + affairs_list = Affairs.objects.all().order_by('name') + brokers_list = Broker.objects.all().order_by('name') + steps_list = ProcessStep.objects.select_related('process').all().order_by('process__name', 'order') manufacturers = WaterMeterManufacturer.objects.all().order_by('name') # Calculate progress for each instance @@ -52,6 +95,16 @@ def request_list(request): 'completed_count': completed_count, 'in_progress_count': in_progress_count, 'pending_count': pending_count, + # filter context + 'status_choices': status_choices, + 'affairs_list': affairs_list, + 'brokers_list': brokers_list, + 'steps_list': steps_list, + 'filter_status': status_q, + 'filter_affairs': affairs_q, + 'filter_broker': broker_q, + 'filter_step': step_q, + 'access_denied': access_denied, }) @@ -125,6 +178,13 @@ def lookup_representative_by_national_code(request): def create_request_with_entities(request): """ایجاد/به‌روزرسانی چاه و نماینده و سپس ایجاد درخواست""" User = get_user_model() + # Only BROKER can create requests + try: + if not (hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.BROKER)): + return JsonResponse({'ok': False, 'error': 'فقط کارگزار مجاز به ایجاد درخواست است'}, status=403) + except Exception: + return JsonResponse({'ok': False, 'error': 'فقط کارگزار مجاز به ایجاد درخواست است'}, status=403) + process_id = request.POST.get('process') process = Process.objects.get(id=process_id) description = request.POST.get('description', '') @@ -230,6 +290,14 @@ def create_request_with_entities(request): well.broker = current_profile.broker well.save() + # Ensure no active (non-deleted, non-completed) request exists for this well + try: + active_exists = ProcessInstance.objects.filter(well=well, is_deleted=False).exclude(status='completed').exists() + if active_exists: + return JsonResponse({'ok': False, 'error': 'برای این چاه یک درخواست جاری وجود دارد. ابتدا آن را تکمیل یا حذف کنید.'}, status=400) + except Exception: + return JsonResponse({'ok': False, 'error': 'خطا در بررسی وضعیت درخواست‌های قبلی این چاه'}, status=400) + # Create request instance instance = ProcessInstance.objects.create( process=process, @@ -261,7 +329,17 @@ def create_request_with_entities(request): @login_required def delete_request(request, instance_id): """حذف درخواست""" - instance = get_object_or_404(ProcessInstance, id=instance_id) + instance = get_scoped_instance_or_404(request, instance_id) + # Only BROKER can delete requests and only within their scope + try: + profile = getattr(request.user, 'profile', None) + if not (profile and profile.has_role(UserRoles.BROKER)): + return JsonResponse({'success': False, 'message': 'فقط کارگزار مجاز به حذف درخواست است'}, status=403) + # Enforce ownership by broker (prevent deleting others' requests) + if instance.broker_id and profile.broker and instance.broker_id != profile.broker.id: + return JsonResponse({'success': False, 'message': 'شما مجاز به حذف این درخواست نیستید'}, status=403) + except Exception: + return JsonResponse({'success': False, 'message': 'فقط کارگزار مجاز به حذف درخواست است'}, status=403) code = instance.code if instance.status == 'completed': return JsonResponse({ @@ -278,10 +356,10 @@ def delete_request(request, instance_id): @login_required def step_detail(request, instance_id, step_id): """نمایش جزئیات مرحله خاص""" - instance = get_object_or_404( - ProcessInstance.objects.select_related('process', 'well', 'requester', 'representative', 'representative__profile'), - id=instance_id - ) + # Enforce scoped access to prevent URL tampering + instance = get_scoped_instance_or_404(request, instance_id) + # Prefetch for performance + instance = ProcessInstance.objects.select_related('process', 'well', 'requester', 'representative', 'representative__profile').get(id=instance.id) step = get_object_or_404(instance.process.steps, id=step_id) # If the request is already completed, redirect to read-only summary page if instance.status == 'completed': @@ -339,7 +417,8 @@ def step_detail(request, instance_id, step_id): @login_required def instance_steps(request, instance_id): """هدایت به مرحله فعلی instance""" - instance = get_object_or_404(ProcessInstance, id=instance_id) + # Enforce scoped access to prevent URL tampering + instance = get_scoped_instance_or_404(request, instance_id) if not instance.current_step: # اگر مرحله فعلی تعریف نشده، به اولین مرحله برو @@ -361,6 +440,9 @@ def instance_steps(request, instance_id): @login_required def instance_summary(request, instance_id): """نمای خلاصهٔ فقط‌خواندنی برای درخواست‌های تکمیل‌شده.""" + # Enforce scoped access to prevent URL tampering + instance = get_scoped_instance_or_404(request, instance_id) + instance = get_object_or_404(ProcessInstance.objects.select_related('well', 'representative'), id=instance_id) # Only show for completed requests; otherwise route to steps if instance.status != 'completed': diff --git a/templates/sidebars/admin.html b/templates/sidebars/admin.html index aacdf24..066c177 100644 --- a/templates/sidebars/admin.html +++ b/templates/sidebars/admin.html @@ -1,4 +1,5 @@ {% load static %} +{% load accounts_tags %} - + + + + + + + {% endfor %} diff --git a/accounts/templates/accounts/login.html b/accounts/templates/accounts/login.html index e5791f3..e43800d 100644 --- a/accounts/templates/accounts/login.html +++ b/accounts/templates/accounts/login.html @@ -16,6 +16,9 @@ layout-wide customizer-hide {% endblock style %} {% block content %} + +{% include '_toasts.html' %} +
@@ -69,7 +72,7 @@ layout-wide customizer-hide {% csrf_token %}
- +
diff --git a/accounts/urls.py b/accounts/urls.py index ac5119d..7c79207 100644 --- a/accounts/urls.py +++ b/accounts/urls.py @@ -4,7 +4,7 @@ from accounts.views import login_view, dashboard, customer_list, add_customer_aj app_name = "accounts" urlpatterns = [ - path('login/', login_view, name='login'), + path('', login_view, name='login'), path('logout/', logout_view, name='logout'), path('dashboard/', dashboard, name='dashboard'), path('customers/', customer_list, name='customer_list'), diff --git a/db.sqlite3 b/db.sqlite3 index 9ffff659c87b574c93db71d451bda94cebeeb062..d80d44cb1bd70d4a407fc4c217ab6a9bc3f2c1a7 100644 GIT binary patch delta 788 zcmajcJ4_P+90u^7J!pHhcTfb%!&08&W5G994||dDqdp%J zt}h%dAkT<4%{!F=y-*SJ`lJTKU!WI?u@(7_YU?~^P}OgF<(Fo}EPw4nd_6Qpvn1sv zZFURg_RuyDjfIq5*o5w)&cB@Z-=uhn%6F2C(ThLk5JgwYs(rzj6Y zS>3dawK4mr{Rp@O)Bpudpc!a@7U%#5T7X_T?Kcd5GGVB!!ap0{=pVO~u_@h{7H$60 zbd1V}ZH;^A&iz5GDe@rhrSnpb&9h6Tin~-4XsMjfuGUJ6rL`inM%OA%YbIQs$=zO$ zZsokOiOl>;FfR#$FI^2M?NL8xw~Jf+WPXz?`$L&f+*c5@3BHyr2!0`L&uy%Wu0&z0 zQWgU>a+yj{63- z7=EC_5013>9zHUxjv)VA4O3p>IFEE;_(v6SO1vkY6MICK2oht24nM<{li2vV=Frmk z&@AtKYt^^^Ta4|WnQ=gXR?r5FpdDNW9iS6*0Tbv3J)jr#fh)ia`hf)ufI%<>hQSCJ Y1y{isxCX9+8{j4&fmJy(k9Yp~1vDW95C8xG delta 389 zcmWO0J5Rz;0EXcnXell2Ie-?qSqfFb8y0UhEDQ`fIN;_cle7Q8G7wgcCS4pf8WU$B z$3#p>`~yu~9SoDvI5^1cH$2Nbz25bRcyN{@jq`kyG&=1;o7}L3k`Z(%5ZIA+D68~d ze&NYD`4%s%(Hjp8@f~S81{-Cmq*vcIvfS-EpfM z?v}gm^u`4OAqayA5=20TC@7#p3^YD2=%v6=q@h6H^(XBzcB1Ypm8cl`ka*Dx-%5P= zr|+qvLi@r%rCo}@GVfE`A}NJ_32e+>*&XY$W46mG4Cyg33aEej8P|FflLCV{Bp?Y> vFbxKzUR0m6{>t {% empty %}
- + + + + + + + {% endfor %} From 855ad3912c61e5094cadd23958d0bf104744d4d9 Mon Sep 17 00:00:00 2001 From: aminhashemi92 Date: Sat, 13 Sep 2025 13:11:12 +0330 Subject: [PATCH 11/11] fix complete profile for new user --- accounts/forms.py | 37 +++++++++++++++++++++++++++++++++++++ accounts/views.py | 3 +++ db.sqlite3 | Bin 3149824 -> 3149824 bytes 3 files changed, 40 insertions(+) diff --git a/accounts/forms.py b/accounts/forms.py index 7654a13..76beb31 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -90,6 +90,19 @@ class CustomerForm(forms.ModelForm): return national_code def save(self, commit=True): + def _compute_completed(cleaned): + try: + first_ok = bool((cleaned.get('first_name') or '').strip()) + last_ok = bool((cleaned.get('last_name') or '').strip()) + nc_ok = bool((cleaned.get('national_code') or '').strip()) + phone_ok = bool((cleaned.get('phone_number_1') or '').strip() or (cleaned.get('phone_number_2') or '').strip()) + addr_ok = bool((cleaned.get('address') or '').strip()) + bank_ok = bool(cleaned.get('bank_name')) + card_ok = bool((cleaned.get('card_number') or '').strip()) + acc_ok = bool((cleaned.get('account_number') or '').strip()) + return all([first_ok, last_ok, nc_ok, phone_ok, addr_ok, bank_ok, card_ok, acc_ok]) + except Exception: + return False # Check if this is an update (instance exists) if self.instance and self.instance.pk: # Update existing profile @@ -108,6 +121,18 @@ class CustomerForm(forms.ModelForm): profile.affairs = current_user_profile.affairs profile.county = current_user_profile.county profile.broker = current_user_profile.broker + # Set completion flag based on provided form data + profile.is_completed = _compute_completed({ + 'first_name': user.first_name, + 'last_name': user.last_name, + 'national_code': self.cleaned_data.get('national_code'), + 'phone_number_1': self.cleaned_data.get('phone_number_1'), + 'phone_number_2': self.cleaned_data.get('phone_number_2'), + 'address': self.cleaned_data.get('address'), + 'bank_name': self.cleaned_data.get('bank_name'), + 'card_number': self.cleaned_data.get('card_number'), + 'account_number': self.cleaned_data.get('account_number'), + }) if commit: profile.save() @@ -142,6 +167,18 @@ class CustomerForm(forms.ModelForm): profile.affairs = current_user_profile.affairs profile.county = current_user_profile.county profile.broker = current_user_profile.broker + # Set completion flag based on provided form data + profile.is_completed = _compute_completed({ + 'first_name': user.first_name, + 'last_name': user.last_name, + 'national_code': self.cleaned_data.get('national_code'), + 'phone_number_1': self.cleaned_data.get('phone_number_1'), + 'phone_number_2': self.cleaned_data.get('phone_number_2'), + 'address': self.cleaned_data.get('address'), + 'bank_name': self.cleaned_data.get('bank_name'), + 'card_number': self.cleaned_data.get('card_number'), + 'account_number': self.cleaned_data.get('account_number'), + }) if commit: profile.save() diff --git a/accounts/views.py b/accounts/views.py index beb49ff..c5bec75 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -19,6 +19,9 @@ def login_view(request): renders login page and authenticating user POST requests to log user in """ + # If already authenticated, go straight to request list + if request.user.is_authenticated: + return redirect("processes:request_list") if request.method == "POST": username = request.POST.get("username") password = request.POST.get("password") diff --git a/db.sqlite3 b/db.sqlite3 index d80d44cb1bd70d4a407fc4c217ab6a9bc3f2c1a7..b3c2c854148917eb4018ad6bb49f6fd507365a4c 100644 GIT binary patch delta 7114 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 delta 3143 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 jCA-)
{{ item.instance.get_status_display_with_color|safe }}{{ item.instance.jcreated }}{{ item.instance.jcreated_date }}
@@ -196,19 +284,31 @@ {% endif %} + {% if request.user|is_broker %}
  • حذف
  • + {% endif %}
    موردی ثبت نشده استموردی ثبت نشده است
    +
    هیچ کاربری یافت نشد
    +

    چاهی یافت نشد