From ef0779de6a9b4dafaacece65d34435c4f08b6274 Mon Sep 17 00:00:00 2001 From: aminhashemi92 Date: Thu, 9 Oct 2025 12:10:57 +0330 Subject: [PATCH] unqiue hologram code --- certificates/admin.py | 6 +- ...alter_certificateinstance_hologram_code.py | 18 ++++++ certificates/models.py | 2 +- certificates/views.py | 56 +++++++++++++++--- db.sqlite3 | Bin 4210688 -> 4235264 bytes 5 files changed, 70 insertions(+), 12 deletions(-) create mode 100644 certificates/migrations/0003_alter_certificateinstance_hologram_code.py diff --git a/certificates/admin.py b/certificates/admin.py index de9ba72..f1eff8a 100644 --- a/certificates/admin.py +++ b/certificates/admin.py @@ -12,9 +12,7 @@ class CertificateTemplateAdmin(admin.ModelAdmin): @admin.register(CertificateInstance) class CertificateInstanceAdmin(admin.ModelAdmin): - list_display = ('process_instance', 'rendered_title', 'issued_at', 'approved') + list_display = ('process_instance', 'rendered_title', 'hologram_code', 'issued_at', 'approved') list_filter = ('approved', 'issued_at') - search_fields = ('process_instance__code', 'rendered_title') + search_fields = ('process_instance__code', 'rendered_title', 'hologram_code') autocomplete_fields = ('process_instance', 'template') - - diff --git a/certificates/migrations/0003_alter_certificateinstance_hologram_code.py b/certificates/migrations/0003_alter_certificateinstance_hologram_code.py new file mode 100644 index 0000000..00fb9cd --- /dev/null +++ b/certificates/migrations/0003_alter_certificateinstance_hologram_code.py @@ -0,0 +1,18 @@ +# Generated by Django 5.2.4 on 2025-10-09 08:26 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('certificates', '0002_certificateinstance_hologram_code'), + ] + + operations = [ + migrations.AlterField( + model_name='certificateinstance', + name='hologram_code', + field=models.CharField(blank=True, max_length=50, null=True, unique=True, verbose_name='کد یکتا هولوگرام'), + ), + ] diff --git a/certificates/models.py b/certificates/models.py index a7afe72..c374035 100644 --- a/certificates/models.py +++ b/certificates/models.py @@ -28,7 +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='کد یکتا هولوگرام') + hologram_code = models.CharField(max_length=50, null=True, blank=True, verbose_name='کد یکتا هولوگرام', unique=True) class Meta: verbose_name = 'گواهی' diff --git a/certificates/views.py b/certificates/views.py index 5149334..c16e7e9 100644 --- a/certificates/views.py +++ b/certificates/views.py @@ -6,6 +6,7 @@ from django.urls import reverse from django.utils import timezone from django.template import Template, Context from django.utils.safestring import mark_safe +from django.db import IntegrityError from processes.models import ProcessInstance, StepInstance from invoices.models import Invoice @@ -157,15 +158,56 @@ def certificate_print(request, instance_id): if request.method == 'POST': # Save/update hologram code then print code = (request.POST.get('hologram_code') or '').strip() - if cert: - if code: + + if not code: + messages.error(request, 'کد یکتای هولوگرام الزامی است') + # Find certificate step to redirect back + certificate_step = instance.process.steps.filter(order=9).first() + if certificate_step and instance.current_step: + return redirect('processes:step_detail', instance_id=instance.id, step_id=certificate_step.id) + return redirect('processes:instance_summary', instance_id=instance.id) + + try: + if cert: + # Check if hologram code is already used by another certificate + if CertificateInstance.objects.filter(hologram_code=code).exclude(id=cert.id).exists(): + messages.error(request, 'این کد هولوگرام قبلاً استفاده شده است. لطفاً کد دیگری وارد کنید') + # Find certificate step to redirect back + certificate_step = instance.process.steps.filter(order=9).first() + if certificate_step and instance.current_step: + return redirect('processes:step_detail', instance_id=instance.id, step_id=certificate_step.id) + return redirect('processes:instance_summary', instance_id=instance.id) + 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) + else: + # Check if hologram code is already used + if CertificateInstance.objects.filter(hologram_code=code).exists(): + messages.error(request, 'این کد هولوگرام قبلاً استفاده شده است. لطفاً کد دیگری وارد کنید') + # Find certificate step to redirect back + certificate_step = instance.process.steps.filter(order=9).first() + if certificate_step and instance.current_step: + return redirect('processes:step_detail', instance_id=instance.id, step_id=certificate_step.id) + return redirect('processes:instance_summary', instance_id=instance.id) + + 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 + ) + except IntegrityError: + messages.error(request, 'این کد هولوگرام قبلاً استفاده شده است. لطفاً کد دیگری وارد کنید') + # Find certificate step to redirect back + certificate_step = instance.process.steps.filter(order=9).first() + if certificate_step and instance.current_step: + return redirect('processes:step_detail', instance_id=instance.id, step_id=certificate_step.id) + return redirect('processes:instance_summary', instance_id=instance.id) + # proceed to rendering page after saving code return render(request, 'certificates/print.html', { 'instance': instance, diff --git a/db.sqlite3 b/db.sqlite3 index 088d726c69e80fce0675e1bb4afcdf56d3caf92d..18e25d423e6879c79a1ca3cacd066535d7789263 100644 GIT binary patch delta 13018 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 F`+tXokX!%& delta 4794 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