fix: invalidate stale service records PDF cache on service record changes
Ensure admin/API deletes and updates of service records, photos, and attachments invalidate the cached service-records PDF. Also harden cached PDF lookup/download paths so a missing file is treated as stale cache instead of a 500. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -532,10 +532,30 @@ def _get_cached_pdf(work_order, pdf_type):
|
||||
)
|
||||
for candidate in candidates:
|
||||
if str(candidate.filename or '').strip() == expected_filename:
|
||||
return candidate
|
||||
file_name = getattr(candidate.file, 'name', '')
|
||||
storage = getattr(candidate.file, 'storage', None)
|
||||
if file_name and storage:
|
||||
try:
|
||||
if storage.exists(file_name):
|
||||
return candidate
|
||||
except (FileNotFoundError, OSError, ValueError):
|
||||
pass
|
||||
candidate.file = None
|
||||
candidate.is_active = False
|
||||
candidate.status = 'failed'
|
||||
candidate.error_message = 'PDF datoteka nije dostupna.'
|
||||
candidate.save(update_fields=['file', 'is_active', 'status', 'error_message', 'updated_at'])
|
||||
return None
|
||||
|
||||
|
||||
def _mark_generated_pdf_failed(generated_pdf, error_message):
|
||||
generated_pdf.file = None
|
||||
generated_pdf.is_active = False
|
||||
generated_pdf.status = 'failed'
|
||||
generated_pdf.error_message = error_message
|
||||
generated_pdf.save(update_fields=['file', 'is_active', 'status', 'error_message', 'updated_at'])
|
||||
|
||||
|
||||
def _cleanup_expired_generated_pdfs():
|
||||
now = timezone.now()
|
||||
expired = GeneratedWorkOrderPdf.objects.filter(
|
||||
@@ -546,10 +566,11 @@ def _cleanup_expired_generated_pdfs():
|
||||
for item in expired:
|
||||
if item.file:
|
||||
item.file.delete(save=False)
|
||||
item.file = None
|
||||
item.is_active = False
|
||||
item.status = 'failed'
|
||||
item.error_message = 'PDF cache istekao.'
|
||||
item.save(update_fields=['is_active', 'status', 'error_message', 'updated_at'])
|
||||
item.save(update_fields=['file', 'is_active', 'status', 'error_message', 'updated_at'])
|
||||
|
||||
|
||||
def _parse_amount_decimal(value):
|
||||
@@ -572,10 +593,11 @@ def _invalidate_work_order_pdf_cache(work_order, *, pdf_types=None):
|
||||
for cached in cache_qs:
|
||||
if cached.file:
|
||||
cached.file.delete(save=False)
|
||||
cached.file = None
|
||||
cached.is_active = False
|
||||
cached.status = 'failed'
|
||||
cached.error_message = 'PDF cache invalidiran zbog promjene podataka.'
|
||||
cached.save(update_fields=['is_active', 'status', 'error_message', 'updated_at'])
|
||||
cached.save(update_fields=['file', 'is_active', 'status', 'error_message', 'updated_at'])
|
||||
|
||||
|
||||
def _upsert_additional_cost_row_from_invoice(invoice):
|
||||
@@ -607,7 +629,11 @@ def _upsert_additional_cost_row_from_invoice(invoice):
|
||||
|
||||
|
||||
def _cached_pdf_file_response(generated_pdf, *, default_filename):
|
||||
generated_pdf.file.open('rb')
|
||||
try:
|
||||
generated_pdf.file.open('rb')
|
||||
except (FileNotFoundError, OSError, ValueError):
|
||||
_mark_generated_pdf_failed(generated_pdf, 'PDF datoteka nije dostupna.')
|
||||
raise DRFValidationError({"detail": "PDF nije dostupan ili je obrisan."})
|
||||
filename = generated_pdf.filename or default_filename
|
||||
response = FileResponse(generated_pdf.file, content_type='application/pdf')
|
||||
response['Content-Disposition'] = f'attachment; filename="{filename}"'
|
||||
@@ -3666,6 +3692,18 @@ class WorkOrderViewSet(viewsets.ModelViewSet):
|
||||
).exclude(file='').exclude(file__isnull=True).first()
|
||||
if generated_pdf is None:
|
||||
raise DRFValidationError({"detail": "PDF nije dostupan ili je istekao."})
|
||||
file_name = getattr(generated_pdf.file, 'name', '')
|
||||
storage = getattr(generated_pdf.file, 'storage', None)
|
||||
if not file_name or storage is None:
|
||||
_mark_generated_pdf_failed(generated_pdf, 'PDF datoteka nije dostupna.')
|
||||
raise DRFValidationError({"detail": "PDF nije dostupan ili je obrisan."})
|
||||
try:
|
||||
if not storage.exists(file_name):
|
||||
_mark_generated_pdf_failed(generated_pdf, 'PDF datoteka nije dostupna.')
|
||||
raise DRFValidationError({"detail": "PDF nije dostupan ili je obrisan."})
|
||||
except (FileNotFoundError, OSError, ValueError):
|
||||
_mark_generated_pdf_failed(generated_pdf, 'PDF datoteka nije dostupna.')
|
||||
raise DRFValidationError({"detail": "PDF nije dostupan ili je obrisan."})
|
||||
return _cached_pdf_file_response(generated_pdf, default_filename=_pdf_filename(work_order, generated_pdf.pdf_type))
|
||||
|
||||
@action(detail=True, methods=['get'], url_path='pdf-preview')
|
||||
|
||||
Reference in New Issue
Block a user