diff --git a/accounts/forms.py b/accounts/forms.py index a5d493b..e5f3b90 100644 --- a/accounts/forms.py +++ b/accounts/forms.py @@ -39,7 +39,8 @@ class CustomerForm(forms.ModelForm): }), 'phone_number_1': forms.TextInput(attrs={ 'class': 'form-control', - 'placeholder': '09123456789' + 'placeholder': '09123456789', + 'required': True }), 'phone_number_2': forms.TextInput(attrs={ 'class': 'form-control', diff --git a/accounts/migrations/0008_alter_historicalprofile_phone_number_1_and_more.py b/accounts/migrations/0008_alter_historicalprofile_phone_number_1_and_more.py new file mode 100644 index 0000000..a005ff8 --- /dev/null +++ b/accounts/migrations/0008_alter_historicalprofile_phone_number_1_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 5.2.4 on 2025-10-02 09:32 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0007_historicalprofile_company_name_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='historicalprofile', + name='phone_number_1', + field=models.CharField(default=1, max_length=11, verbose_name='شماره تماس ۱'), + preserve_default=False, + ), + migrations.AlterField( + model_name='profile', + name='phone_number_1', + field=models.CharField(default=1, max_length=11, verbose_name='شماره تماس ۱'), + preserve_default=False, + ), + ] diff --git a/accounts/models.py b/accounts/models.py index 348304e..f6ccf3d 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -78,9 +78,7 @@ class Profile(BaseModel): ) phone_number_1 = models.CharField( max_length=11, - null=True, verbose_name="شماره تماس ۱", - blank=True ) phone_number_2 = models.CharField( max_length=11, diff --git a/certificates/views.py b/certificates/views.py index 9a8f1de..5bdcade 100644 --- a/certificates/views.py +++ b/certificates/views.py @@ -4,6 +4,8 @@ from django.contrib import messages from django.http import JsonResponse from django.urls import reverse from django.utils import timezone +from django.template import Template, Context +from django.utils.safestring import mark_safe from processes.models import ProcessInstance, StepInstance from invoices.models import Invoice @@ -28,20 +30,33 @@ def _render_template(template: CertificateTemplate, instance: ProcessInstance): well = instance.well rep = instance.representative latest_report = InstallationReport.objects.filter(assignment__process_instance=instance).order_by('-created').first() + individual = True if rep.profile and rep.profile.user_type == 'individual' else False + customer_company_name = rep.profile.company_name if rep.profile and rep.profile.user_type == 'legal' else None + city = template.company.broker.affairs.county.city.name if template.company and template.company.broker and template.company.broker.affairs and template.company.broker.affairs.county and template.company.broker.affairs.county.city else None + county = template.company.broker.affairs.county.name if template.company and template.company.broker and template.company.broker.affairs and template.company.broker.affairs.county else None + ctx = { - 'today_jalali': _to_jalali(timezone.now().date()), - 'request_code': instance.code, - 'company_name': (template.company.name if template.company else '') or '', - 'customer_full_name': rep.get_full_name() if rep else '', - 'water_subscription_number': getattr(well, 'water_subscription_number', '') or '', - 'address': getattr(well, 'county', '') or '', - 'visit_date_jalali': _to_jalali(getattr(latest_report, 'visited_date', None)) if latest_report else '', + 'today_jalali': mark_safe(f"{_to_jalali(timezone.now().date())}"), + 'request_code': mark_safe(f"{instance.code}"), + 'company_name': mark_safe(f"{(template.company.name if template.company else '') or ''}"), + 'customer_full_name': mark_safe(f"{rep.get_full_name() if rep else ''}"), + 'water_subscription_number': mark_safe(f"{getattr(well, 'water_subscription_number', '') or ''}"), + 'address': mark_safe(f"{getattr(well, 'county', '') or ''}"), + 'visit_date_jalali': mark_safe(f"{_to_jalali(getattr(latest_report, 'visited_date', None)) if latest_report else ''}"), + 'city': mark_safe(f"{city or ''}"), + 'county': mark_safe(f"{county or ''}"), + 'customer_company_name': mark_safe(f"{customer_company_name or ''}"), + 'individual': individual, } - title = (template.title or '').format(**ctx) - body = (template.body or '') - # Render body placeholders with bold values - for k, v in ctx.items(): - body = body.replace(f"{{{{ {k} }}}}", f"{str(v)}") + + # Render title using Django template engine + title_template = Template(template.title or '') + title = title_template.render(Context(ctx)) + + # Render body using Django template engine + body_template = Template(template.body or '') + body = body_template.render(Context(ctx)) + return title, body diff --git a/db.sqlite3 b/db.sqlite3 index 194dde7..d2eb7a1 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ diff --git a/installations/templates/installations/installation_report_step.html b/installations/templates/installations/installation_report_step.html index df12a29..9ff6c20 100644 --- a/installations/templates/installations/installation_report_step.html +++ b/installations/templates/installations/installation_report_step.html @@ -99,9 +99,9 @@

UTM Y: {{ report.utm_y|default:'-' }}

نوع مصرف: {{ report.get_usage_type_display|default:'-' }}

شماره پروانه بهره‌برداری: {{ report.exploitation_license_number|default:'-' }}

-

(کیلووات ساعت)قدرت موتور: {{ report.motor_power|default:'-' }}

-

(لیتر/ثانیه) دبی قبل کالیبراسیون: {{ report.pre_calibration_flow_rate|default:'-' }}

-

(لیتر/ثانیه) دبی بعد کالیبراسیون: {{ report.post_calibration_flow_rate|default:'-' }}

+

قدرت موتور(کیلووات ساعت): {{ report.motor_power|default:'-' }}

+

دبی قبل کالیبراسیون(لیتر/ثانیه): {{ report.pre_calibration_flow_rate|default:'-' }}

+

دبی بعد کالیبراسیون(لیتر/ثانیه): {{ report.post_calibration_flow_rate|default:'-' }}

@@ -157,7 +157,7 @@ تغییری ثبت نشده است {% endfor %} - + nvoices/instance/69/step/3/payments/ diff --git a/installations/views.py b/installations/views.py index 177ccc3..367edac 100644 --- a/installations/views.py +++ b/installations/views.py @@ -320,15 +320,29 @@ def installation_report_step(request, instance_id, step_id): can_approve_reject = False user_can_approve = can_approve_reject approvals_list = list(step_instance.approvals.select_related('role', 'approved_by').filter(is_deleted=False)) + rejections_list = list(step_instance.rejections.select_related('role', 'rejected_by').filter(is_deleted=False)) approvals_by_role = {a.role_id: a for a in approvals_list} - approver_statuses = [ - { + rejections_by_role = {r.role_id: r for r in rejections_list} + approver_statuses = [] + for r in reqs: + appr = approvals_by_role.get(r.role_id) + rejection = rejections_by_role.get(r.role_id) + + if appr: + status = 'approved' + reason = appr.reason + elif rejection: + status = 'rejected' + reason = rejection.reason + else: + status = None + reason = '' + + approver_statuses.append({ '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 - ] + 'status': status, + 'reason': reason, + }) # Determine if current user has already approved/rejected (to disable buttons) current_user_has_decided = False @@ -356,10 +370,11 @@ def installation_report_step(request, instance_id, step_id): if action == 'approve': # Record this user's approval for their role - StepApproval.objects.update_or_create( + StepApproval.objects.create( step_instance=step_instance, role=matching_role, - defaults={'approved_by': request.user, 'decision': 'approved', 'reason': ''} + approved_by=request.user, + reason='' ) # Only mark report approved when ALL required roles have approved if step_instance.is_fully_approved(): @@ -386,12 +401,8 @@ def installation_report_step(request, instance_id, step_id): if not reason: messages.error(request, 'لطفاً علت رد شدن را وارد کنید.') return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id) - StepApproval.objects.update_or_create( - step_instance=step_instance, - role=matching_role, - defaults={'approved_by': request.user, 'decision': 'rejected', 'reason': reason} - ) - StepRejection.objects.create(step_instance=step_instance, rejected_by=request.user, reason=reason) + # Only create StepRejection for rejections, not StepApproval + StepRejection.objects.create(step_instance=step_instance, role=matching_role, 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) diff --git a/invoices/templates/invoices/quote_print.html b/invoices/templates/invoices/quote_print.html index bbf7a97..fc445a6 100644 --- a/invoices/templates/invoices/quote_print.html +++ b/invoices/templates/invoices/quote_print.html @@ -145,7 +145,17 @@
-
اطلاعات مشترک
+
+ {% if instance.representative.profile.user_type == 'legal' %} + اطلاعات مشترک (حقوقی) + {% else %} + اطلاعات مشترک (حقیقی) + {% endif %} +
+ {% if instance.representative.profile.user_type == 'legal' %} +
نام شرکت: {{ instance.representative.profile.company_name|default:"-" }}
+
شناسه ملی: {{ instance.representative.profile.company_national_id|default:"-" }}
+ {% endif %}
نام: {{ quote.customer.get_full_name }}
{% if instance.representative.profile and instance.representative.profile.national_code %}
کد ملی: {{ instance.representative.profile.national_code }}
diff --git a/invoices/templates/invoices/quote_step.html b/invoices/templates/invoices/quote_step.html index 404cf14..853f250 100644 --- a/invoices/templates/invoices/quote_step.html +++ b/invoices/templates/invoices/quote_step.html @@ -57,7 +57,7 @@
پیش‌فاکتور موجود
{{ existing_quote.name }} | - مبلغ کل: {{ existing_quote.final_amount|floatformat:0|intcomma:False }} تومان | + مبلغ کل (با احتساب مالیات): {{ existing_quote.final_amount|floatformat:0|intcomma:False }} تومان | وضعیت: {{ existing_quote.get_status_display_with_color|safe }}
diff --git a/invoices/views.py b/invoices/views.py index 6e467b9..0df8191 100644 --- a/invoices/views.py +++ b/invoices/views.py @@ -358,14 +358,28 @@ def quote_payment_step(request, instance_id, step_id): 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', 'approved_by').filter(is_deleted=False)) + rejections_list = list(step_instance.rejections.select_related('role', 'rejected_by').filter(is_deleted=False)) approvals_by_role = {a.role_id: a for a in approvals_list} + rejections_by_role = {r.role_id: r for r in rejections_list} approver_statuses = [] for r in reqs: appr = approvals_by_role.get(r.role_id) + rejection = rejections_by_role.get(r.role_id) + + if appr: + status = 'approved' + reason = appr.reason + elif rejection: + status = 'rejected' + reason = rejection.reason + else: + status = None + reason = '' + approver_statuses.append({ 'role': r.role, - 'status': (appr.decision if appr else None), - 'reason': (appr.reason if appr else ''), + 'status': status, + 'reason': reason, }) # dynamic permission: who can approve/reject this step (based on requirements) @@ -398,10 +412,11 @@ def quote_payment_step(request, instance_id, step_id): action = request.POST.get('action') if action == 'approve': - StepApproval.objects.update_or_create( + StepApproval.objects.create( step_instance=step_instance, role=matching_role, - defaults={'approved_by': request.user, 'decision': 'approved', 'reason': ''} + approved_by=request.user, + reason='' ) if step_instance.is_fully_approved(): step_instance.status = 'completed' @@ -422,12 +437,12 @@ def quote_payment_step(request, instance_id, step_id): if not reason: messages.error(request, 'علت رد شدن را وارد کنید') return redirect('invoices:quote_payment_step', instance_id=instance.id, step_id=step.id) - StepApproval.objects.update_or_create( + StepRejection.objects.create( step_instance=step_instance, role=matching_role, - defaults={'approved_by': request.user, 'decision': 'rejected', 'reason': reason} - ) - StepRejection.objects.create(step_instance=step_instance, rejected_by=request.user, reason=reason) + 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: @@ -928,15 +943,29 @@ def final_settlement_step(request, instance_id, step_id): # Build approver statuses for template (include reason to display in UI) reqs = list(step.approver_requirements.select_related('role').all()) approvals = list(step_instance.approvals.select_related('role').all()) + rejections = list(step_instance.rejections.select_related('role').all()) approvals_by_role = {a.role_id: a for a in approvals} - approver_statuses = [ - { + rejections_by_role = {r.role_id: r for r in rejections} + approver_statuses = [] + for r in reqs: + appr = approvals_by_role.get(r.role_id) + rejection = rejections_by_role.get(r.role_id) + + if appr: + status = 'approved' + reason = appr.reason + elif rejection: + status = 'rejected' + reason = rejection.reason + else: + status = None + reason = '' + + approver_statuses.append({ '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 - ] + 'status': status, + 'reason': reason, + }) # dynamic permission to control approve/reject UI try: user_roles_qs = getattr(getattr(request.user, 'profile', None), 'roles', Role.objects.none()) @@ -971,10 +1000,11 @@ def final_settlement_step(request, instance_id, step_id): if invoice.remaining_amount != 0: messages.error(request, f"تا زمانی که مانده فاکتور صفر نشده امکان تایید نیست (مانده فعلی: {invoice.remaining_amount})") return redirect('invoices:final_settlement_step', instance_id=instance.id, step_id=step.id) - StepApproval.objects.update_or_create( + StepApproval.objects.create( step_instance=step_instance, role=matching_role, - defaults={'approved_by': request.user, 'decision': 'approved', 'reason': ''} + approved_by=request.user, + reason='' ) if step_instance.is_fully_approved(): step_instance.status = 'completed' @@ -993,12 +1023,12 @@ def final_settlement_step(request, instance_id, step_id): if not reason: messages.error(request, 'علت رد شدن را وارد کنید') return redirect('invoices:final_settlement_step', instance_id=instance.id, step_id=step.id) - StepApproval.objects.update_or_create( + StepRejection.objects.create( step_instance=step_instance, role=matching_role, - defaults={'approved_by': request.user, 'decision': 'rejected', 'reason': reason} - ) - StepRejection.objects.create(step_instance=step_instance, rejected_by=request.user, reason=reason) + 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: diff --git a/processes/admin.py b/processes/admin.py index 4739742..42d4bca 100644 --- a/processes/admin.py +++ b/processes/admin.py @@ -162,9 +162,9 @@ class StepInstanceAdmin(SimpleHistoryAdmin): @admin.register(StepRejection) class StepRejectionAdmin(SimpleHistoryAdmin): - list_display = ['step_instance', 'rejected_by', 'reason_short', 'created_at', 'is_deleted'] - list_filter = ['rejected_by', 'created_at', 'step_instance__step__process'] - search_fields = ['step_instance__step__name', 'rejected_by__username', 'reason'] + list_display = ['step_instance', 'role', 'rejected_by', 'reason_short', 'created_at', 'is_deleted'] + list_filter = ['role', 'rejected_by', 'created_at', 'step_instance__step__process'] + search_fields = ['step_instance__step__name', 'rejected_by__username', 'reason', 'role__name'] readonly_fields = ['created_at'] ordering = ['-created_at'] @@ -182,6 +182,6 @@ class StepApproverRequirementAdmin(admin.ModelAdmin): @admin.register(StepApproval) class StepApprovalAdmin(admin.ModelAdmin): - list_display = ("step_instance", "role", "decision", "approved_by", "created_at", "is_deleted") - list_filter = ("decision", "role", "step_instance__step__process") + list_display = ("step_instance", "role", "approved_by", "created_at", "is_deleted") + list_filter = ("role", "step_instance__step__process") search_fields = ("step_instance__process_instance__code", "role__name", "approved_by__username") diff --git a/processes/migrations/0006_alter_stepapproval_unique_together_and_more.py b/processes/migrations/0006_alter_stepapproval_unique_together_and_more.py new file mode 100644 index 0000000..f4cb896 --- /dev/null +++ b/processes/migrations/0006_alter_stepapproval_unique_together_and_more.py @@ -0,0 +1,34 @@ +# Generated by Django 5.2.4 on 2025-10-02 09:32 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0008_alter_historicalprofile_phone_number_1_and_more'), + ('processes', '0005_alter_historicalstepinstance_status_and_more'), + ] + + operations = [ + migrations.AlterUniqueTogether( + name='stepapproval', + unique_together=set(), + ), + migrations.AddField( + model_name='historicalsteprejection', + name='role', + field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='accounts.role', verbose_name='نقش'), + ), + migrations.AddField( + model_name='steprejection', + name='role', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.role', verbose_name='نقش'), + ), + migrations.AlterField( + model_name='stepapproval', + name='role', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='accounts.role', verbose_name='نقش'), + ), + ] diff --git a/processes/migrations/0007_remove_stepapproval_decision_and_more.py b/processes/migrations/0007_remove_stepapproval_decision_and_more.py new file mode 100644 index 0000000..d97d16b --- /dev/null +++ b/processes/migrations/0007_remove_stepapproval_decision_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 5.2.4 on 2025-10-02 09:50 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('processes', '0006_alter_stepapproval_unique_together_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='stepapproval', + name='decision', + ), + migrations.AlterField( + model_name='stepapproval', + name='reason', + field=models.TextField(blank=True, verbose_name='توضیحات'), + ), + ] diff --git a/processes/models.py b/processes/models.py index 391fd26..19b612d 100644 --- a/processes/models.py +++ b/processes/models.py @@ -387,7 +387,7 @@ class StepInstance(models.Model): def approvals_by_role(self): decisions = {} for a in self.approvals.select_related('role').order_by('created_at'): - decisions[a.role_id] = a.decision + decisions[a.role_id] = 'approved' return decisions def is_fully_approved(self) -> bool: @@ -409,6 +409,7 @@ class StepRejection(models.Model): related_name='rejections', verbose_name="نمونه مرحله" ) + role = models.ForeignKey(Role, on_delete=models.SET_NULL, blank=True, null=True, verbose_name="نقش") rejected_by = models.ForeignKey( User, on_delete=models.CASCADE, @@ -431,7 +432,7 @@ class StepRejection(models.Model): ordering = ['-created_at'] def __str__(self): - return f"رد شدن {self.step_instance} توسط {self.rejected_by.get_full_name()}" + return f"رد شدن {self.step_instance} توسط {self.rejected_by.get_full_name()} ({self.role.name})" def save(self, *args, **kwargs): """ذخیره با تغییر وضعیت مرحله""" @@ -447,7 +448,6 @@ class StepRejection(models.Model): self.save() - class StepApproverRequirement(models.Model): """Required approver roles for a step.""" step = models.ForeignKey(ProcessStep, on_delete=models.CASCADE, related_name='approver_requirements', verbose_name="مرحله") @@ -466,15 +466,13 @@ class StepApproverRequirement(models.Model): class StepApproval(models.Model): """Approvals per role for a concrete step instance.""" step_instance = models.ForeignKey(StepInstance, on_delete=models.CASCADE, related_name='approvals', verbose_name="نمونه مرحله") - role = models.ForeignKey(Role, on_delete=models.CASCADE, verbose_name="نقش") + role = models.ForeignKey(Role, on_delete=models.SET_NULL, blank=True, null=True, verbose_name="نقش") approved_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, verbose_name="تاییدکننده") - decision = models.CharField(max_length=8, choices=[('approved', 'تایید'), ('rejected', 'رد')], verbose_name='نتیجه') - reason = models.TextField(blank=True, verbose_name='علت (برای رد)') + reason = models.TextField(blank=True, verbose_name='توضیحات') created_at = models.DateTimeField(auto_now_add=True, verbose_name='تاریخ') is_deleted = models.BooleanField(default=False, verbose_name='حذف شده') class Meta: - unique_together = ('step_instance', 'role') verbose_name = 'تایید مرحله' verbose_name_plural = 'تاییدهای مرحله' @@ -487,4 +485,4 @@ class StepApproval(models.Model): def __str__(self): - return f"{self.step_instance} - {self.role} - {self.decision}" + return f"{self.step_instance} - {self.role} - تایید شده" diff --git a/processes/templates/processes/instance_summary.html b/processes/templates/processes/instance_summary.html index ad84b59..a8529c6 100644 --- a/processes/templates/processes/instance_summary.html +++ b/processes/templates/processes/instance_summary.html @@ -95,32 +95,113 @@
گزارش نصب
- {% if latest_report and latest_report.assignment and latest_report.assignment.installer %} - نصاب: {{ latest_report.assignment.installer.get_full_name|default:latest_report.assignment.installer.username }} - {% endif %} +
+ {% if installation_delay_days > 0 %} + + {{ installation_delay_days }} روز تاخیر + + {% elif installation_assignment and latest_report %} + + به موقع + + {% endif %} + {% if latest_report and latest_report.assignment and latest_report.assignment.installer %} + نصاب: {{ latest_report.assignment.installer.get_full_name|default:latest_report.assignment.installer.username }} + {% endif %} +
{% if latest_report %} -
-
-

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

+ +
+
+

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

+
+ {% if installation_assignment.scheduled_date %} +
+

تاریخ برنامه‌ریزی: {{ installation_assignment.scheduled_date|to_jalali }}

+
+ {% endif %} +

سریال کنتور جدید: {{ latest_report.new_water_meter_serial|default:'-' }}

+
+

شماره پلمپ: {{ latest_report.seal_number|default:'-' }}

-
+

کنتور مشکوک: {{ latest_report.is_meter_suspicious|yesno:'بله,خیر' }}

+
+ {% if latest_report.sim_number %} +
+

شماره سیمکارت: {{ latest_report.sim_number }}

+
+ {% endif %} + {% if latest_report.meter_type %} +
+

نوع کنتور: {{ latest_report.get_meter_type_display }}

+
+ {% endif %} + {% if latest_report.meter_size %} +
+

سایز کنتور: {{ latest_report.meter_size }}

+
+ {% endif %} + {% if latest_report.water_meter_manufacturer %} +
+

سازنده: {{ latest_report.water_meter_manufacturer.name }}

+
+ {% endif %} + {% if latest_report.discharge_pipe_diameter %} +
+

قطر لوله آبده: {{ latest_report.discharge_pipe_diameter }} اینچ

+
+ {% endif %} + {% if latest_report.usage_type %} +
+

نوع مصرف: {{ latest_report.get_usage_type_display }}

+
+ {% endif %} + {% if latest_report.driving_force %} +
+

نیرو محرکه: {{ latest_report.driving_force }}

+
+ {% endif %} + {% if latest_report.motor_power %} +
+

قدرت موتور: {{ latest_report.motor_power }} کیلووات ساعت

+
+ {% endif %} + {% if latest_report.exploitation_license_number %} +
+

شماره پروانه: {{ latest_report.exploitation_license_number }}

+
+ {% endif %} + {% if latest_report.pre_calibration_flow_rate %} +
+

دبی قبل از کالیبراسیون: {{ latest_report.pre_calibration_flow_rate }} لیتر/ثانیه

+
+ {% endif %} + {% if latest_report.post_calibration_flow_rate %} +
+

دبی بعد از کالیبراسیون: {{ latest_report.post_calibration_flow_rate }} لیتر/ثانیه

+
+ {% endif %} +

UTM X: {{ latest_report.utm_x|default:'-' }}

+
+

UTM Y: {{ latest_report.utm_y|default:'-' }}

+ {% if latest_report.description %} -
-

توضیحات:

+
+
توضیحات
{{ latest_report.description }}
{% endif %} -
-
عکس‌ها
+ +
عکس‌ها
{% for p in latest_report.photos.all %}
photo
diff --git a/processes/templates/processes/request_list.html b/processes/templates/processes/request_list.html index 5640425..9ec7e92 100644 --- a/processes/templates/processes/request_list.html +++ b/processes/templates/processes/request_list.html @@ -37,12 +37,14 @@
+ {% if not request.user|is_installer %} + {% endif %} {% if request.user|is_broker %}