140 lines
6.2 KiB
Python
140 lines
6.2 KiB
Python
from django.shortcuts import render, get_object_or_404, redirect
|
|
from django.contrib.auth.decorators import login_required
|
|
from django.contrib import messages
|
|
from django.http import JsonResponse
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
|
|
from processes.models import ProcessInstance, StepInstance
|
|
from invoices.models import Invoice
|
|
from installations.models import InstallationReport
|
|
from .models import CertificateTemplate, CertificateInstance
|
|
from common.consts import UserRoles
|
|
|
|
from _helpers.jalali import Gregorian
|
|
|
|
|
|
def _to_jalali(date_obj):
|
|
try:
|
|
g = Gregorian(date_obj)
|
|
y, m, d = g.persian_tuple()
|
|
return f"{y}/{m:02d}/{d:02d}"
|
|
except Exception:
|
|
return ''
|
|
|
|
|
|
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()
|
|
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 '',
|
|
}
|
|
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"<strong>{str(v)}</strong>")
|
|
return title, body
|
|
|
|
|
|
@login_required
|
|
def certificate_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)
|
|
# 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()
|
|
|
|
if incomplete:
|
|
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')
|
|
|
|
template = CertificateTemplate.objects.filter(is_active=True).order_by('-created').first()
|
|
if not template:
|
|
return render(request, 'certificates/missing.html', {})
|
|
|
|
title, body = _render_template(template, instance)
|
|
cert, _ = CertificateInstance.objects.get_or_create(
|
|
process_instance=instance,
|
|
defaults={'template': template, 'rendered_title': title, 'rendered_body': body}
|
|
)
|
|
# keep rendered up-to-date
|
|
cert.template = template
|
|
cert.rendered_title = title
|
|
cert.rendered_body = body
|
|
cert.save()
|
|
|
|
previous_step = instance.process.steps.filter(order__lt=instance.current_step.order).last() if instance.current_step else None
|
|
next_step = instance.process.steps.filter(order__gt=instance.current_step.order).first() if instance.current_step else None
|
|
|
|
if request.method == 'POST':
|
|
# Only broker can approve and finish certificate step
|
|
try:
|
|
if not (hasattr(request.user, 'profile') and request.user.profile.has_role(UserRoles.BROKER)):
|
|
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)
|
|
# 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_at = timezone.now()
|
|
cert.save()
|
|
step_instance, _ = StepInstance.objects.get_or_create(process_instance=instance, step_id=step_id)
|
|
step_instance.status = 'completed'
|
|
step_instance.completed_at = timezone.now()
|
|
step_instance.save()
|
|
if next_step:
|
|
instance.current_step = next_step
|
|
instance.save()
|
|
return redirect('processes:step_detail', instance_id=instance.id, step_id=next_step.id)
|
|
# Mark the whole process instance as completed on the last step
|
|
instance.status = 'completed'
|
|
instance.save()
|
|
return redirect('processes:instance_summary', instance_id=instance.id)
|
|
|
|
return render(request, 'certificates/step.html', {
|
|
'instance': instance,
|
|
'template': template,
|
|
'cert': cert,
|
|
'previous_step': previous_step,
|
|
'next_step': next_step,
|
|
'step': step,
|
|
})
|
|
|
|
|
|
@login_required
|
|
def certificate_print(request, instance_id):
|
|
instance = get_object_or_404(ProcessInstance, id=instance_id)
|
|
cert = CertificateInstance.objects.filter(process_instance=instance).order_by('-created').first()
|
|
template = cert.template if cert else None
|
|
return render(request, 'certificates/print.html', {
|
|
'instance': instance,
|
|
'cert': cert,
|
|
'template': template,
|
|
})
|
|
|
|
|