huge fix
This commit is contained in:
parent
810c87e2e0
commit
b5bf3a5dbe
51 changed files with 2397 additions and 326 deletions
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 5.2.4 on 2025-09-27 15:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('certificates', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='certificateinstance',
|
||||
name='hologram_code',
|
||||
field=models.CharField(blank=True, max_length=50, null=True, verbose_name='کد یکتا هولوگرام'),
|
||||
),
|
||||
]
|
||||
|
|
@ -28,6 +28,7 @@ class CertificateInstance(BaseModel):
|
|||
issued_at = models.DateField(auto_now_add=True, verbose_name='تاریخ صدور')
|
||||
approved = models.BooleanField(default=False, verbose_name='تایید شده')
|
||||
approved_at = models.DateTimeField(null=True, blank=True, verbose_name='تاریخ تایید')
|
||||
hologram_code = models.CharField(max_length=50, null=True, blank=True, verbose_name='کد یکتا هولوگرام')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'گواهی'
|
||||
|
|
|
|||
|
|
@ -18,19 +18,20 @@
|
|||
<link rel="stylesheet" href="{% static 'assets/css/persian-fonts.css' %}">
|
||||
|
||||
<style>
|
||||
@page { size: A4; margin: 1cm; }
|
||||
@page { size: A4 landscape; margin: 1cm; }
|
||||
@media print { body { print-color-adjust: exact; } .no-print { display: none !important; } }
|
||||
.header { border-bottom: 1px solid #dee2e6; padding-bottom: 16px; margin-bottom: 24px; }
|
||||
.header { border-bottom: 0px solid #dee2e6; padding-bottom: 10px; margin-bottom: 10px; }
|
||||
.company-name { font-weight: 600; }
|
||||
.body-text { white-space: pre-line; line-height: 1.9; }
|
||||
.signature-section { margin-top: 40px; border-top: 1px solid #dee2e6; padding-top: 24px; }
|
||||
.signature-section { margin-top: 40px; border-top: 0px 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="d-flex">
|
||||
<div class="ms-auto text-end">
|
||||
<div class="">کد یکتا هولوگرام: {{ cert.hologram_code|default:'-' }}</div>
|
||||
<div class="">شماره درخواست: {{ instance.code }}</div>
|
||||
<div class="">تاریخ: {{ cert.jissued_at }}</div>
|
||||
</div>
|
||||
|
|
@ -38,10 +39,7 @@
|
|||
|
||||
<!-- 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 %}
|
||||
<h4 class="mt-2">{{ cert.rendered_title }}</h4>
|
||||
<h4 class="">{{ cert.rendered_title }}</h4>
|
||||
{% if template.company %}
|
||||
<div class="text-muted company-name">{{ template.company.name }}</div>
|
||||
{% endif %}
|
||||
|
|
@ -51,17 +49,41 @@
|
|||
<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 %}
|
||||
<h6 class="my-2">مشخصات چاه و کنتور هوشمند</h6>
|
||||
<div class="row" style="font-size: 14px;">
|
||||
<div class="col-4">
|
||||
<div>موقعیت مکانی (UTM): {{ latest_report.utm_x|default:'-' }} , {{ latest_report.utm_y|default:'-' }}</div>
|
||||
<div>نیرو محرکه چاه: {{ latest_report.driving_force|default:'-' }}</div>
|
||||
<div>نوع کنتور: {{ latest_report.get_meter_type_display|default:'-' }}</div>
|
||||
<div>قطر لوله آبده (اینچ): {{ latest_report.discharge_pipe_diameter|default:'-' }}</div>
|
||||
<div>نوع مصرف: {{ latest_report.get_usage_type_display|default:'-' }}</div>
|
||||
<div>شماره سیمکارت: {{ latest_report.sim_number|default:'-' }}</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div>سایز کنتور: {{ latest_report.meter_size|default:'-' }}</div>
|
||||
<div>شماره پروانه بهرهبرداری چاه: {{ latest_report.exploitation_license_number|default:'-' }}</div>
|
||||
<div>قدرت موتور: {{ latest_report.motor_power|default:'-' }}</div>
|
||||
<div>دبی قبل از کالیبراسیون: {{ latest_report.pre_calibration_flow_rate|default:'-' }}</div>
|
||||
<div>دبی بعد از کالیبراسیون: {{ latest_report.post_calibration_flow_rate|default:'-' }}</div>
|
||||
<div>نام شرکت کنتورساز: {{ latest_report.water_meter_manufacturer.name|default:'-' }}</div>
|
||||
<div>شماره سریال کنتور: {{ instance.well.water_meter_serial_number|default:'-' }}</div>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<!-- 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>
|
||||
|
||||
<script>
|
||||
|
|
|
|||
|
|
@ -38,9 +38,9 @@
|
|||
</small>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a class="btn btn-outline-secondary" target="_blank" href="{% url 'certificates:certificate_print' instance.id %}">
|
||||
<button class="btn btn-outline-secondary" type="button" data-bs-toggle="modal" data-bs-target="#printHologramModal">
|
||||
<i class="bx bx-printer me-2"></i> پرینت
|
||||
</a>
|
||||
</button>
|
||||
|
||||
<a href="{% url 'processes:request_list' %}" class="btn btn-outline-secondary">
|
||||
<i class="bx bx-chevron-right bx-sm ms-sm-n2"></i>
|
||||
|
|
@ -61,16 +61,33 @@
|
|||
<div>تاریخ: {{ cert.jissued_at }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="text-center mb-3">
|
||||
{% if template.company and template.company.logo %}
|
||||
<img src="{{ template.company.logo.url }}" alt="logo" style="max-height:80px">
|
||||
{% endif %}
|
||||
<div class="text-center">
|
||||
<h5 class="mt-2">{{ cert.rendered_title }}</h5>
|
||||
{% if template.company %}<div class="text-muted">{{ template.company.name }}</div>{% endif %}
|
||||
</div>
|
||||
<div class="mt-3" style="white-space:pre-line; line-height:1.9;">
|
||||
<div class="mb-3" style="white-space:pre-line; line-height:1.9;">
|
||||
{{ cert.rendered_body|safe }}
|
||||
</div>
|
||||
<h6 class="mb-2">مشخصات چاه و کنتور هوشمند</h6>
|
||||
<div class="row g-2 small">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="d-flex gap-2"><span class="text-muted">موقعیت مکانی (UTM):</span><span class="fw-medium">{{ latest_report.utm_x|default:'-' }} , {{ latest_report.utm_y|default:'-' }}</span></div>
|
||||
<div class="d-flex gap-2"><span class="text-muted">نیرو محرکه چاه:</span><span class="fw-medium">{{ latest_report.driving_force|default:'-' }}</span></div>
|
||||
<div class="d-flex gap-2"><span class="text-muted">نوع کنتور:</span><span class="fw-medium">{{ latest_report.get_meter_type_display|default:'-' }}</span></div>
|
||||
<div class="d-flex gap-2"><span class="text-muted">قطر لوله آبده (اینچ):</span><span class="fw-medium">{{ latest_report.discharge_pipe_diameter|default:'-' }}</span></div>
|
||||
<div class="d-flex gap-2"><span class="text-muted">نوع مصرف:</span><span class="fw-medium">{{ latest_report.get_usage_type_display|default:'-' }}</span></div>
|
||||
<div class="d-flex gap-2"><span class="text-muted">شماره سیمکارت:</span><span class="fw-medium">{{ latest_report.sim_number|default:'-' }}</span></div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="d-flex gap-2"><span class="text-muted">سایز کنتور:</span><span class="fw-medium">{{ latest_report.meter_size|default:'-' }}</span></div>
|
||||
<div class="d-flex gap-2"><span class="text-muted">شماره پروانه بهرهبرداری چاه:</span><span class="fw-medium">{{ latest_report.exploitation_license_number|default:'-' }}</span></div>
|
||||
<div class="d-flex gap-2"><span class="text-muted">قدرت موتور:</span><span class="fw-medium">{{ latest_report.motor_power|default:'-' }}</span></div>
|
||||
<div class="d-flex gap-2"><span class="text-muted">دبی قبل از کالیبراسیون:</span><span class="fw-medium">{{ latest_report.pre_calibration_flow_rate|default:'-' }}</span></div>
|
||||
<div class="d-flex gap-2"><span class="text-muted">دبی بعد از کالیبراسیون:</span><span class="fw-medium">{{ latest_report.post_calibration_flow_rate|default:'-' }}</span></div>
|
||||
<div class="d-flex gap-2"><span class="text-muted">نام شرکت کنتورساز:</span><span class="fw-medium">{{ latest_report.water_meter_manufacturer.name|default:'-' }}</span></div>
|
||||
<div class="d-flex gap-2"><span class="text-muted">شماره سریال کنتور:</span><span class="fw-medium">{{ instance.well.water_meter_serial_number|default:'-' }}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="signature-section d-flex justify-content-end">
|
||||
<div class="text-center">
|
||||
<div>مهر و امضای تایید کننده</div>
|
||||
|
|
@ -103,6 +120,29 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Print Hologram Modal -->
|
||||
<div class="modal fade" id="printHologramModal" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form method="post" action="{% url 'certificates:certificate_print' instance.id %}" target="_blank">
|
||||
{% csrf_token %}
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">کد یکتا هولوگرام</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<label class="form-label">کد هولوگرام</label>
|
||||
<input type="text" class="form-control" name="hologram_code" value="{{ cert.hologram_code|default:'' }}" placeholder="مثال: 123456" required>
|
||||
<div class="form-text">این کد باید با کد هولوگرام روی گواهی یکسان باشد.</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-label-secondary" data-bs-dismiss="modal">انصراف</button>
|
||||
<button type="submit" class="btn btn-primary">ثبت و پرینت</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -52,16 +52,20 @@ def certificate_step(request, instance_id, step_id):
|
|||
# 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)
|
||||
incomplete = StepInstance.objects.filter(process_instance=instance, step__in=prior_steps).exclude(status='completed').exists()
|
||||
previous_step = instance.process.steps.filter(order__lt=instance.current_step.order).last() if instance.current_step else None
|
||||
prev_si = StepInstance.objects.filter(process_instance=instance, step=previous_step).first() if previous_step else None
|
||||
|
||||
if incomplete:
|
||||
if incomplete and not prev_si.status == 'approved':
|
||||
messages.error(request, 'ابتدا همه مراحل قبلی را تکمیل کنید')
|
||||
return redirect('processes:request_list')
|
||||
|
||||
inv = Invoice.objects.filter(process_instance=instance).first()
|
||||
if inv:
|
||||
inv.calculate_totals()
|
||||
if inv.remaining_amount != 0:
|
||||
messages.error(request, 'مانده فاکتور باید صفر باشد')
|
||||
return redirect('processes:request_list')
|
||||
if prev_si and not prev_si.status == 'approved':
|
||||
inv.calculate_totals()
|
||||
if inv.remaining_amount != 0:
|
||||
messages.error(request, 'مانده فاکتور باید صفر باشد')
|
||||
return redirect('processes:request_list')
|
||||
|
||||
template = CertificateTemplate.objects.filter(is_active=True).order_by('-created').first()
|
||||
if not template:
|
||||
|
|
@ -117,6 +121,8 @@ def certificate_step(request, instance_id, step_id):
|
|||
instance.save()
|
||||
return redirect('processes:instance_summary', instance_id=instance.id)
|
||||
|
||||
# latest installation report for details
|
||||
latest_report = InstallationReport.objects.filter(assignment__process_instance=instance).order_by('-created').first()
|
||||
return render(request, 'certificates/step.html', {
|
||||
'instance': instance,
|
||||
'template': template,
|
||||
|
|
@ -124,6 +130,7 @@ def certificate_step(request, instance_id, step_id):
|
|||
'previous_step': previous_step,
|
||||
'next_step': next_step,
|
||||
'step': step,
|
||||
'latest_report': latest_report,
|
||||
})
|
||||
|
||||
|
||||
|
|
@ -131,11 +138,32 @@ def certificate_step(request, instance_id, step_id):
|
|||
def certificate_print(request, instance_id):
|
||||
instance = get_scoped_instance_or_404(request, instance_id)
|
||||
cert = CertificateInstance.objects.filter(process_instance=instance).order_by('-created').first()
|
||||
latest_report = InstallationReport.objects.filter(assignment__process_instance=instance).order_by('-created').first()
|
||||
if request.method == 'POST':
|
||||
# Save/update hologram code then print
|
||||
code = (request.POST.get('hologram_code') or '').strip()
|
||||
if cert:
|
||||
if code:
|
||||
cert.hologram_code = code
|
||||
cert.save(update_fields=['hologram_code'])
|
||||
else:
|
||||
template = CertificateTemplate.objects.filter(is_active=True).order_by('-created').first()
|
||||
if template:
|
||||
title, body = _render_template(template, instance)
|
||||
cert = CertificateInstance.objects.create(process_instance=instance, template=template, rendered_title=title, rendered_body=body, hologram_code=code or None)
|
||||
# proceed to rendering page after saving code
|
||||
return render(request, 'certificates/print.html', {
|
||||
'instance': instance,
|
||||
'cert': cert,
|
||||
'template': cert.template if cert else None,
|
||||
'latest_report': latest_report,
|
||||
})
|
||||
template = cert.template if cert else None
|
||||
return render(request, 'certificates/print.html', {
|
||||
'instance': instance,
|
||||
'cert': cert,
|
||||
'template': template,
|
||||
'latest_report': latest_report,
|
||||
})
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue