fix final step
This commit is contained in:
parent
9592c00565
commit
394546dc67
9 changed files with 137 additions and 82 deletions
|
@ -1,6 +1,7 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
from common.models import BaseModel
|
from common.models import BaseModel
|
||||||
|
from _helpers.utils import jalali_converter2
|
||||||
|
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
|
@ -35,4 +36,7 @@ class CertificateInstance(BaseModel):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"گواهی {self.process_instance.code}"
|
return f"گواهی {self.process_instance.code}"
|
||||||
|
|
||||||
|
def jissued_at(self):
|
||||||
|
return jalali_converter2(self.issued_at)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,28 +1,71 @@
|
||||||
{% extends '_base.html' %}
|
<!DOCTYPE html>
|
||||||
|
<html lang="fa" dir="rtl">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>تاییدیه - {{ instance.code }}</title>
|
||||||
|
{% load static %}
|
||||||
|
|
||||||
{% block content %}
|
<!-- Fonts (match project) -->
|
||||||
<div class="container py-4">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<div class="text-center mb-4">
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
{% if template.company and template.company.logo %}
|
<link href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap" rel="stylesheet">
|
||||||
<img src="{{ template.company.logo.url }}" alt="logo" style="max-height:90px">
|
|
||||||
{% endif %}
|
<!-- Core CSS (same as other prints) -->
|
||||||
<h4 class="mt-2">{{ cert.rendered_title }}</h4>
|
<link rel="stylesheet" href="{% static 'assets/vendor/css/rtl/core.css' %}">
|
||||||
{% if template.company %}<div class="text-muted">{{ template.company.name }}</div>{% endif %}
|
<link rel="stylesheet" href="{% static 'assets/vendor/css/rtl/theme-default.css' %}">
|
||||||
</div>
|
<link rel="stylesheet" href="{% static 'assets/css/demo.css' %}">
|
||||||
<div style="white-space:pre-line; line-height:1.9;">
|
<link rel="stylesheet" href="{% static 'assets/css/persian-fonts.css' %}">
|
||||||
{{ cert.rendered_body|safe }}
|
|
||||||
</div>
|
<style>
|
||||||
<div class="mt-5 d-flex justify-content-between">
|
@page { size: A4; margin: 1cm; }
|
||||||
<div>تاریخ: {{ cert.issued_at }}</div>
|
@media print { body { print-color-adjust: exact; } .no-print { display: none !important; } }
|
||||||
<div class="text-center">
|
.header { border-bottom: 1px solid #dee2e6; padding-bottom: 16px; margin-bottom: 24px; }
|
||||||
{% if template.company and template.company.signature %}
|
.company-name { font-weight: 600; }
|
||||||
<img src="{{ template.company.signature.url }}" alt="seal" style="max-height:120px">
|
.body-text { white-space: pre-line; line-height: 1.9; }
|
||||||
|
.signature-section { margin-top: 40px; border-top: 1px solid #dee2e6; padding-top: 24px; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container-fluid py-3">
|
||||||
|
<!-- Top-left request info -->
|
||||||
|
<div class="d-flex mb-2">
|
||||||
|
<div class="ms-auto text-end">
|
||||||
|
<div class="">شماره درخواست: {{ instance.code }}</div>
|
||||||
|
<div class="">تاریخ: {{ cert.jissued_at }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Header with logo and company -->
|
||||||
|
<div class="header text-center">
|
||||||
|
{% if template.company and template.company.logo %}
|
||||||
|
<img src="{{ template.company.logo.url }}" alt="logo" style="max-height:90px">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div>مهر و امضای شرکت</div>
|
<h4 class="mt-2">{{ cert.rendered_title }}</h4>
|
||||||
|
{% if template.company %}
|
||||||
|
<div class="text-muted company-name">{{ template.company.name }}</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Certificate body -->
|
||||||
|
<div class="body-text">
|
||||||
|
{{ cert.rendered_body|safe }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Signature -->
|
||||||
|
<div class="signature-section d-flex justify-content-end">
|
||||||
|
<div class="text-center">
|
||||||
|
<div>مهر و امضای تایید کننده</div>
|
||||||
|
<div class="text-muted">{{ template.company.name }}</div>
|
||||||
|
{% if template.company and template.company.signature %}
|
||||||
|
<img src="{{ template.company.signature.url }}" alt="seal" style="max-height:200px">
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
<script>window.print()</script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.onload = function() { window.print(); setTimeout(function(){ window.close(); }, 200); };
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
|
@ -18,40 +18,49 @@
|
||||||
<link rel="stylesheet" href="{% static 'assets/vendor/libs/bs-stepper/bs-stepper.css' %}">
|
<link rel="stylesheet" href="{% static 'assets/vendor/libs/bs-stepper/bs-stepper.css' %}">
|
||||||
<!-- Persian Date Picker CSS -->
|
<!-- Persian Date Picker CSS -->
|
||||||
<link rel="stylesheet" href="https://unpkg.com/persian-datepicker@latest/dist/css/persian-datepicker.min.css">
|
<link rel="stylesheet" href="https://unpkg.com/persian-datepicker@latest/dist/css/persian-datepicker.min.css">
|
||||||
<style>
|
|
||||||
@media print {
|
|
||||||
.no-print { display: none !important; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include '_toasts.html' %}
|
{% include '_toasts.html' %}
|
||||||
|
|
||||||
|
<!-- Instance Info Modal -->
|
||||||
|
{% instance_info_modal instance %}
|
||||||
|
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<div class="container-xxl flex-grow-1 container-p-y">
|
<div class="container-xxl flex-grow-1 container-p-y">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 mb-4">
|
<div class="col-12 mb-4">
|
||||||
<div class="d-flex align-items-center justify-content-between mb-3 no-print">
|
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||||
<div>
|
<div>
|
||||||
<h4 class="mb-1">{{ step.name }}: {{ instance.process.name }}</h4>
|
<h4 class="mb-1">{{ step.name }}: {{ instance.process.name }}</h4>
|
||||||
<small class="text-muted d-block">
|
<small class="text-muted d-block">
|
||||||
اشتراک آب: {{ instance.well.water_subscription_number|default:"-" }}
|
{% instance_info instance %}
|
||||||
| نماینده: {{ instance.representative.profile.national_code|default:"-" }}
|
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
<a class="btn btn-outline-secondary" target="_blank" href="{% url 'certificates:certificate_print' instance.id %}"><i class="bx bx-printer"></i> پرینت</a>
|
<a class="btn btn-outline-secondary" target="_blank" href="{% url 'certificates:certificate_print' instance.id %}">
|
||||||
|
<i class="bx bx-printer me-2"></i> پرینت
|
||||||
|
</a>
|
||||||
|
|
||||||
<a href="{% url 'processes:request_list' %}" class="btn btn-outline-secondary">بازگشت</a>
|
<a href="{% url 'processes:request_list' %}" class="btn btn-outline-secondary">
|
||||||
|
<i class="bx bx-chevron-right bx-sm ms-sm-n2"></i>
|
||||||
|
بازگشت
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="bs-stepper wizard-vertical vertical mt-2 no-print">
|
<div class="bs-stepper wizard-vertical vertical mt-2">
|
||||||
{% stepper_header instance step %}
|
{% stepper_header instance step %}
|
||||||
<div class="bs-stepper-content">
|
<div class="bs-stepper-content">
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
<div class="d-flex mb-2">
|
||||||
|
<div class="ms-auto text-end">
|
||||||
|
<div>شماره درخواست: {{ instance.code }}</div>
|
||||||
|
<div>تاریخ: {{ cert.jissued_at }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="text-center mb-3">
|
<div class="text-center mb-3">
|
||||||
{% if template.company and template.company.logo %}
|
{% if template.company and template.company.logo %}
|
||||||
<img src="{{ template.company.logo.url }}" alt="logo" style="max-height:80px">
|
<img src="{{ template.company.logo.url }}" alt="logo" style="max-height:80px">
|
||||||
|
@ -62,21 +71,22 @@
|
||||||
<div class="mt-3" style="white-space:pre-line; line-height:1.9;">
|
<div class="mt-3" style="white-space:pre-line; line-height:1.9;">
|
||||||
{{ cert.rendered_body|safe }}
|
{{ cert.rendered_body|safe }}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-4 d-flex justify-content-between align-items-end">
|
<div class="signature-section d-flex justify-content-end">
|
||||||
<div>
|
|
||||||
<div>تاریخ صدور: {{ cert.issued_at }}</div>
|
|
||||||
</div>
|
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
|
<div>مهر و امضای تایید کننده</div>
|
||||||
|
<div class="text-muted">{{ template.company.name }}</div>
|
||||||
{% if template.company and template.company.signature %}
|
{% if template.company and template.company.signature %}
|
||||||
<img src="{{ template.company.signature.url }}" alt="seal" style="max-height:100px">
|
<img src="{{ template.company.signature.url }}" alt="seal" style="max-height:200px">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div>مهر و امضای شرکت</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer d-flex justify-content-between">
|
<div class="card-footer d-flex justify-content-between">
|
||||||
{% if previous_step %}
|
{% if previous_step %}
|
||||||
<a href="{% url 'processes:step_detail' instance.id previous_step.id %}" class="btn btn-label-secondary">قبلی</a>
|
<a href="{% url 'processes:step_detail' instance.id previous_step.id %}" class="btn btn-label-secondary">
|
||||||
|
<i class="bx bx-chevron-right bx-sm ms-sm-n2"></i>
|
||||||
|
قبلی
|
||||||
|
</a>
|
||||||
{% else %}<span></span>{% endif %}
|
{% else %}<span></span>{% endif %}
|
||||||
<form method="post">
|
<form method="post">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
|
|
|
@ -33,13 +33,14 @@ def _render_template(template: CertificateTemplate, instance: ProcessInstance):
|
||||||
'company_name': (template.company.name if template.company else '') or '',
|
'company_name': (template.company.name if template.company else '') or '',
|
||||||
'customer_full_name': rep.get_full_name() if rep else '',
|
'customer_full_name': rep.get_full_name() if rep else '',
|
||||||
'water_subscription_number': getattr(well, 'water_subscription_number', '') or '',
|
'water_subscription_number': getattr(well, 'water_subscription_number', '') or '',
|
||||||
'address': getattr(well, 'address', '') or '',
|
'address': getattr(well, 'county', '') or '',
|
||||||
'visit_date_jalali': _to_jalali(getattr(latest_report, 'visited_date', None)) if latest_report else '',
|
'visit_date_jalali': _to_jalali(getattr(latest_report, 'visited_date', None)) if latest_report else '',
|
||||||
}
|
}
|
||||||
title = (template.title or '').format(**ctx)
|
title = (template.title or '').format(**ctx)
|
||||||
body = (template.body or '')
|
body = (template.body or '')
|
||||||
|
# Render body placeholders with bold values
|
||||||
for k, v in ctx.items():
|
for k, v in ctx.items():
|
||||||
body = body.replace(f"{{{{ {k} }}}}", str(v))
|
body = body.replace(f"{{{{ {k} }}}}", f"<strong>{str(v)}</strong>")
|
||||||
return title, body
|
return title, body
|
||||||
|
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ def certificate_step(request, instance_id, step_id):
|
||||||
# Ensure all previous steps are completed and invoice settled
|
# Ensure all previous steps are completed and invoice settled
|
||||||
prior_steps = instance.process.steps.filter(order__lt=instance.current_step.order if instance.current_step else 9999)
|
prior_steps = instance.process.steps.filter(order__lt=instance.current_step.order if instance.current_step else 9999)
|
||||||
incomplete = StepInstance.objects.filter(process_instance=instance, step__in=prior_steps).exclude(status='completed').exists()
|
incomplete = StepInstance.objects.filter(process_instance=instance, step__in=prior_steps).exclude(status='completed').exists()
|
||||||
|
|
||||||
if incomplete:
|
if incomplete:
|
||||||
messages.error(request, 'ابتدا همه مراحل قبلی را تکمیل کنید')
|
messages.error(request, 'ابتدا همه مراحل قبلی را تکمیل کنید')
|
||||||
return redirect('processes:request_list')
|
return redirect('processes:request_list')
|
||||||
|
@ -87,6 +89,17 @@ def certificate_step(request, instance_id, step_id):
|
||||||
except Exception:
|
except Exception:
|
||||||
messages.error(request, 'شما مجوز تایید این مرحله را ندارید')
|
messages.error(request, 'شما مجوز تایید این مرحله را ندارید')
|
||||||
return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
|
return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
|
||||||
|
# Safety check: ensure ALL previous steps are completed before approval
|
||||||
|
try:
|
||||||
|
prev_steps_qs = instance.process.steps.filter(order__lt=step.order)
|
||||||
|
has_incomplete = StepInstance.objects.filter(process_instance=instance, step__in=prev_steps_qs).exclude(status='completed').exists()
|
||||||
|
if has_incomplete:
|
||||||
|
messages.error(request, 'ابتدا همه مراحل قبلی را تکمیل کنید')
|
||||||
|
return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
|
||||||
|
except Exception:
|
||||||
|
messages.error(request, 'خطا در بررسی مراحل قبلی')
|
||||||
|
return redirect('processes:step_detail', instance_id=instance.id, step_id=step.id)
|
||||||
|
|
||||||
cert.approved = True
|
cert.approved = True
|
||||||
cert.approved_at = timezone.now()
|
cert.approved_at = timezone.now()
|
||||||
cert.save()
|
cert.save()
|
||||||
|
|
BIN
db.sqlite3
BIN
db.sqlite3
Binary file not shown.
|
@ -278,8 +278,8 @@
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
{% if invoice.remaining_amount != 0 %}
|
{% if invoice.remaining_amount != 0 %}
|
||||||
<div class="alert alert-warning" role="alert">
|
<div class="alert alert-warning" role="alert">
|
||||||
مانده فاکتور صفر نیست: <strong>{{ invoice.remaining_amount|floatformat:0|intcomma:False }} تومان</strong><br>
|
مانده فاکتور: <strong>{{ invoice.remaining_amount|floatformat:0|intcomma:False }} تومان</strong><br>
|
||||||
تا صفر نشود امکان تایید نیست.
|
امکان تایید تا تسویه کامل فاکتور وجود ندارد.
|
||||||
</div>
|
</div>
|
||||||
{% else %}
|
{% else %}
|
||||||
آیا از تایید این مرحله اطمینان دارید؟
|
آیا از تایید این مرحله اطمینان دارید؟
|
||||||
|
|
|
@ -1105,7 +1105,7 @@ def delete_final_payment(request, instance_id, step_id, payment_id):
|
||||||
return JsonResponse({'success': False, 'message': 'شما مجوز حذف تراکنش تسویه را ندارید'}, status=403)
|
return JsonResponse({'success': False, 'message': 'شما مجوز حذف تراکنش تسویه را ندارید'}, status=403)
|
||||||
payment.delete()
|
payment.delete()
|
||||||
invoice.refresh_from_db()
|
invoice.refresh_from_db()
|
||||||
|
|
||||||
# On delete, return to awaiting approval
|
# On delete, return to awaiting approval
|
||||||
try:
|
try:
|
||||||
si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step)
|
si, _ = StepInstance.objects.get_or_create(process_instance=instance, step=step)
|
||||||
|
@ -1149,30 +1149,3 @@ def delete_final_payment(request, instance_id, step_id, payment_id):
|
||||||
'remaining_amount': str(invoice.remaining_amount),
|
'remaining_amount': str(invoice.remaining_amount),
|
||||||
}})
|
}})
|
||||||
|
|
||||||
|
|
||||||
@require_POST
|
|
||||||
@login_required
|
|
||||||
def approve_final_settlement(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)
|
|
||||||
invoice = get_object_or_404(Invoice, process_instance=instance)
|
|
||||||
# Block approval if any remaining exists (positive or negative)
|
|
||||||
invoice.calculate_totals()
|
|
||||||
if invoice.remaining_amount != 0:
|
|
||||||
return JsonResponse({
|
|
||||||
'success': False,
|
|
||||||
'message': f"تا زمانی که مانده فاکتور صفر نشده امکان تایید نیست (مانده فعلی: {invoice.remaining_amount})"
|
|
||||||
})
|
|
||||||
# complete step
|
|
||||||
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()
|
|
||||||
# move next
|
|
||||||
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])
|
|
||||||
return JsonResponse({'success': True, 'message': 'تسویه حساب نهایی ثبت شد', 'redirect': redirect_url})
|
|
||||||
|
|
|
@ -143,9 +143,9 @@ class ProcessInstanceAdmin(SimpleHistoryAdmin):
|
||||||
|
|
||||||
@admin.register(StepInstance)
|
@admin.register(StepInstance)
|
||||||
class StepInstanceAdmin(SimpleHistoryAdmin):
|
class StepInstanceAdmin(SimpleHistoryAdmin):
|
||||||
list_display = ['process_instance', 'step', 'assigned_to', 'status_display', 'rejection_count', 'edit_count', 'started_at', 'completed_at']
|
list_display = ['process_instance', 'process_instance__code', 'step', 'assigned_to', 'status_display', 'rejection_count', 'edit_count', 'started_at', 'completed_at']
|
||||||
list_filter = ['status', 'step__process', 'started_at']
|
list_filter = ['status', 'step__process', 'started_at']
|
||||||
search_fields = ['process_instance__name', 'step__name', 'assigned_to__username']
|
search_fields = ['process_instance__name', 'process_instance__code', 'step__name', 'assigned_to__username']
|
||||||
readonly_fields = ['started_at', 'completed_at']
|
readonly_fields = ['started_at', 'completed_at']
|
||||||
ordering = ['process_instance', 'step__order']
|
ordering = ['process_instance', 'step__order']
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
{% extends '_base.html' %}
|
{% extends '_base.html' %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
{% load common_tags %}
|
{% load common_tags %}
|
||||||
|
{% load processes_tags %}
|
||||||
|
|
||||||
{% block sidebar %}
|
{% block sidebar %}
|
||||||
{% include 'sidebars/admin.html' %}
|
{% include 'sidebars/admin.html' %}
|
||||||
|
@ -15,23 +16,34 @@
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
{% include '_toasts.html' %}
|
{% include '_toasts.html' %}
|
||||||
|
|
||||||
|
<!-- Instance Info Modal -->
|
||||||
|
{% instance_info_modal instance %}
|
||||||
|
|
||||||
|
|
||||||
<div class="container-xxl flex-grow-1 container-p-y">
|
<div class="container-xxl flex-grow-1 container-p-y">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-12 mb-4">
|
<div class="col-12 mb-4">
|
||||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||||
<div>
|
<div>
|
||||||
<h4 class="mb-1">گزارش نهایی درخواست {{ instance.code }}</h4>
|
<h4 class="mb-1">گزارش نهایی درخواست {{ instance.code }}</h4>
|
||||||
<small class="text-muted d-block">
|
<small class="text-muted d-block">
|
||||||
اشتراک آب: {{ instance.well.water_subscription_number|default:"-" }}
|
{% instance_info instance %}
|
||||||
| نماینده: {{ instance.representative.profile.national_code|default:"-" }}
|
|
||||||
</small>
|
</small>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
{% if invoice %}
|
{% if invoice %}
|
||||||
<a href="{% url 'invoices:final_invoice_print' instance.id %}" target="_blank" class="btn btn-outline-secondary"><i class="bx bx-printer"></i> پرینت فاکتور</a>
|
<a href="{% url 'invoices:final_invoice_print' instance.id %}" target="_blank" class="btn btn-outline-secondary">
|
||||||
|
<i class="bx bx-printer me-2"></i> پرینت فاکتور
|
||||||
|
</a>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{% url 'certificates:certificate_print' instance.id %}" target="_blank" class="btn btn-outline-secondary"><i class="bx bx-printer"></i> پرینت گواهی</a>
|
<a href="{% url 'certificates:certificate_print' instance.id %}" target="_blank" class="btn btn-outline-secondary">
|
||||||
<a href="{% url 'processes:request_list' %}" class="btn btn-outline-secondary">بازگشت</a>
|
<i class="bx bx-printer me-2"></i> پرینت گواهی
|
||||||
|
</a>
|
||||||
|
<a href="{% url 'processes:request_list' %}" class="btn btn-outline-secondary">
|
||||||
|
<i class="bx bx-chevron-right bx-sm ms-sm-n2"></i>
|
||||||
|
بازگشت
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue