feat: dodaj display_code polje za putne naloge (MT070626 format)
- Dodano polje 'display_code' na WorkOrder model (format: 2 inicijala + ddmmyy) - Migracije za kreiranje polja i backfill postojećih naloga - Ažuriran WorkOrderSerializer za validaciju i serijalizaciju display_code - Backend tasks za auto-generiranje display_code pri kreiranju - Frontend komponente ažurirane za prikaz display_code umjesto UUID: * FleetDashboardShell tablica putnih naloga * WorkOrderDetailModal * TaskCreateModal * Sve PDF stranice - displayIds.js utility za formatiranjeDisplay ID-eva - Admin interface prikazuje display_code
This commit is contained in:
@@ -142,17 +142,26 @@ def _parse_format(value):
|
||||
GENERATED_PDF_TTL_HOURS = 24
|
||||
|
||||
|
||||
def _work_order_display_code(work_order):
|
||||
normalized = str(getattr(work_order, 'display_code', '') or '').strip().upper()
|
||||
if normalized:
|
||||
return normalized
|
||||
return 'NALOG'
|
||||
|
||||
|
||||
def _pdf_filename(work_order, pdf_type):
|
||||
display_code = _work_order_display_code(work_order)
|
||||
if pdf_type == 'invoices':
|
||||
return f"{work_order.pk}.work-order-invoices.pdf"
|
||||
return f"{display_code}.work-order-invoices.pdf"
|
||||
if pdf_type == 'service_records':
|
||||
return f"{work_order.pk}.work-order-service-records.pdf"
|
||||
return f"{work_order.pk}.work-order.pdf"
|
||||
return f"{display_code}.work-order-service-records.pdf"
|
||||
return f"{display_code}.work-order.pdf"
|
||||
|
||||
|
||||
def _get_cached_pdf(work_order, pdf_type):
|
||||
now = timezone.now()
|
||||
return (
|
||||
expected_filename = _pdf_filename(work_order, pdf_type)
|
||||
candidates = list(
|
||||
GeneratedWorkOrderPdf.objects
|
||||
.filter(
|
||||
is_active=True,
|
||||
@@ -164,8 +173,11 @@ def _get_cached_pdf(work_order, pdf_type):
|
||||
.exclude(file='')
|
||||
.exclude(file__isnull=True)
|
||||
.order_by('-created_at')
|
||||
.first()
|
||||
)
|
||||
for candidate in candidates:
|
||||
if str(candidate.filename or '').strip() == expected_filename:
|
||||
return candidate
|
||||
return None
|
||||
|
||||
|
||||
def _cleanup_expired_generated_pdfs():
|
||||
@@ -203,17 +215,18 @@ def _notify_pdf_request(*, user, work_order, doc_type, stage, generated_pdf=None
|
||||
else:
|
||||
doc_label = "računa putnog naloga"
|
||||
|
||||
display_code = _work_order_display_code(work_order)
|
||||
if stage == 'requested':
|
||||
title = f"Zahtjev za PDF {doc_label}"
|
||||
message = f"Zaprimljen je zahtjev za generiranje PDF dokumenta za WO-{work_order.pk}."
|
||||
message = f"Zaprimljen je zahtjev za generiranje PDF dokumenta za putni nalog {display_code}."
|
||||
level = "info"
|
||||
elif stage == 'failed':
|
||||
title = f"Greška kod PDF-a ({doc_label})"
|
||||
message = f"Generiranje PDF dokumenta nije uspjelo za WO-{work_order.pk}."
|
||||
message = f"Generiranje PDF dokumenta nije uspjelo za putni nalog {display_code}."
|
||||
level = "warning"
|
||||
else:
|
||||
title = f"PDF spreman ({doc_label})"
|
||||
message = f"PDF dokument je uspješno generiran za WO-{work_order.pk}."
|
||||
message = f"PDF dokument je uspješno generiran za putni nalog {display_code}."
|
||||
level = "success"
|
||||
|
||||
metadata = {
|
||||
@@ -337,6 +350,7 @@ def _build_work_order_pdf(work_order):
|
||||
invoices = list(work_order.invoices.filter(is_active=True).order_by('-datum', '-created_at'))
|
||||
now_local = timezone.localtime(timezone.now())
|
||||
tz = timezone.get_current_timezone()
|
||||
display_code = _work_order_display_code(work_order)
|
||||
|
||||
def _fmt_date(value):
|
||||
if not value:
|
||||
@@ -437,7 +451,7 @@ def _build_work_order_pdf(work_order):
|
||||
_paragraph("N A L O G<br/>ZA SLUŽBENO PUTOVANJE", bold=True, size=11, align=1),
|
||||
_paragraph(
|
||||
f"Mjesto i datum:<br/><b>{place_label} {_fmt_date(now_local.date())}</b><br/>"
|
||||
f"Broj naloga:<br/><b>{work_order.pk}</b>",
|
||||
f"Broj naloga:<br/><b>{display_code}</b>",
|
||||
size=8.8,
|
||||
),
|
||||
]]
|
||||
@@ -686,7 +700,7 @@ def _build_work_order_service_records_pdf(work_order):
|
||||
upgrade_hours_str = str(getattr(vehicle, 'superstructure_working_hours', '-') or '-')
|
||||
chassis_hours_str = str(getattr(vehicle, 'chassis_working_hours', '-') or '-')
|
||||
mileage_str = str(vehicle.current_mileage or '-')
|
||||
nalog_str = str(work_order.pk)
|
||||
nalog_str = _work_order_display_code(work_order)
|
||||
generated_date_str = timezone.localtime(timezone.now()).strftime('%d.%m.%Y')
|
||||
|
||||
HEADER_H = 86
|
||||
@@ -1064,7 +1078,7 @@ def _build_service_record_pdf(service_record):
|
||||
upgrade_hours_str = str(getattr(vehicle, 'superstructure_working_hours', '-') or '-')
|
||||
chassis_hours_str = str(getattr(vehicle, 'chassis_working_hours', '-') or '-')
|
||||
mileage_str = str(vehicle.current_mileage or '-')
|
||||
nalog_str = str(getattr(work_order, 'pk', '-') or '-')
|
||||
nalog_str = _work_order_display_code(work_order) if work_order else '-'
|
||||
generated_date_str = timezone.localtime(timezone.now()).strftime('%d.%m.%Y')
|
||||
|
||||
HEADER_H = 86
|
||||
@@ -1487,7 +1501,7 @@ class WorkOrderViewSet(viewsets.ModelViewSet):
|
||||
pdf_bytes = _build_work_order_pdf(work_order)
|
||||
_notify_pdf_request(user=request.user, work_order=work_order, doc_type='work_order', stage='completed')
|
||||
response = HttpResponse(pdf_bytes, content_type='application/pdf')
|
||||
response['Content-Disposition'] = f'attachment; filename="{work_order.pk}.work-order.pdf"'
|
||||
response['Content-Disposition'] = f'attachment; filename="{_pdf_filename(work_order, "work_order")}"'
|
||||
return response
|
||||
|
||||
@action(detail=True, methods=['post'], url_path='pdf-request')
|
||||
@@ -1508,7 +1522,7 @@ class WorkOrderViewSet(viewsets.ModelViewSet):
|
||||
pdf_bytes = _build_work_order_service_records_pdf(work_order)
|
||||
_notify_pdf_request(user=request.user, work_order=work_order, doc_type='service_records', stage='completed')
|
||||
response = HttpResponse(pdf_bytes, content_type='application/pdf')
|
||||
response['Content-Disposition'] = f'attachment; filename="{work_order.pk}.work-order-service-records.pdf"'
|
||||
response['Content-Disposition'] = f'attachment; filename="{_pdf_filename(work_order, "service_records")}"'
|
||||
return response
|
||||
|
||||
@action(detail=True, methods=['post'], url_path='service-records-pdf-request')
|
||||
@@ -1545,7 +1559,7 @@ class WorkOrderViewSet(viewsets.ModelViewSet):
|
||||
except (ValueError, TypeError):
|
||||
raise DRFValidationError({"detail": "Neispravan PDF sadržaj računa."})
|
||||
|
||||
filename = payload.get('filename') or f"{work_order.pk}.work-order-invoices.pdf"
|
||||
filename = payload.get('filename') or _pdf_filename(work_order, 'invoices')
|
||||
_notify_pdf_request(user=request.user, work_order=work_order, doc_type='invoices', stage='completed')
|
||||
response = HttpResponse(pdf_bytes, content_type='application/pdf')
|
||||
response['Content-Disposition'] = f'attachment; filename="{filename}"'
|
||||
@@ -1600,7 +1614,7 @@ class WorkOrderViewSet(viewsets.ModelViewSet):
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Računi putnog naloga WO-{work_order.pk}</title>
|
||||
<title>Računi putnog naloga {_work_order_display_code(work_order)}</title>
|
||||
<style>
|
||||
body {{ font-family: Arial, sans-serif; margin: 24px; color: #111827; background: #f8fafc; }}
|
||||
.card {{ background: #fff; border: 1px solid #e5e7eb; border-radius: 12px; padding: 16px; margin-bottom: 16px; }}
|
||||
@@ -1614,7 +1628,7 @@ class WorkOrderViewSet(viewsets.ModelViewSet):
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<h1>Putni nalog WO-{work_order.pk} — Računi</h1>
|
||||
<h1>Putni nalog {_work_order_display_code(work_order)} — Računi</h1>
|
||||
<p class="muted">Dizalica: {escape(work_order.vehicle.registration_number)} | Datum naloga: {escape(str(work_order.date))}</p>
|
||||
<div class="actions">
|
||||
<a href="{escape(pdf_url)}">Preuzmi PDF putnog naloga</a>
|
||||
@@ -1693,13 +1707,13 @@ class WorkOrderViewSet(viewsets.ModelViewSet):
|
||||
raise DRFValidationError({"recipient": "Nije pronađena email adresa za slanje."})
|
||||
|
||||
pdf_bytes = _build_work_order_pdf(work_order)
|
||||
subject = _first_non_empty(request.data.get('subject')) or f"Putni nalog WO-{work_order.pk}"
|
||||
subject = _first_non_empty(request.data.get('subject')) or f"Putni nalog {_work_order_display_code(work_order)}"
|
||||
body = _first_non_empty(request.data.get('message')) or "U prilogu je PDF putnog naloga."
|
||||
_send_document_email(
|
||||
recipient=recipient,
|
||||
subject=subject,
|
||||
body=body,
|
||||
filename=f"{work_order.pk}.work-order.pdf",
|
||||
filename=_pdf_filename(work_order, 'work_order'),
|
||||
pdf_bytes=pdf_bytes,
|
||||
)
|
||||
return Response({"sent": True, "recipient": recipient}, status=status.HTTP_200_OK)
|
||||
|
||||
Reference in New Issue
Block a user