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:
mariomitte
2026-08-31 07:32:05 +02:00
parent 9bd5f53675
commit 2826ee9bab
3 changed files with 151 additions and 4 deletions

View File

@@ -2,10 +2,12 @@ import uuid
import re
from datetime import time
from django.db import models
from django.db.models.signals import post_save, pre_delete
from django.conf import settings
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
from django.utils import timezone
from django.dispatch import receiver
from core.base_models import BaseModel
from decimal import Decimal
@@ -142,6 +144,67 @@ class VehicleServiceAttachment(BaseModel):
def __str__(self):
return f"Attachment {self.pk} for {self.service_record} ({self.file.name if self.file else 'no-file'})"
def _service_record_work_order_id(instance):
if isinstance(instance, VehicleServiceRecord):
task = getattr(instance, 'task', None)
if task and getattr(task, 'work_order_id', None):
return task.work_order_id
if getattr(instance, 'task_id', None):
return VehicleServiceRecord.objects.filter(pk=instance.pk).values_list('task__work_order_id', flat=True).first()
return None
service_record = getattr(instance, 'service_record', None)
if service_record and getattr(service_record, 'task', None) and getattr(service_record.task, 'work_order_id', None):
return service_record.task.work_order_id
service_record_id = getattr(instance, 'service_record_id', None)
if not service_record_id:
return None
return VehicleServiceRecord.objects.filter(pk=service_record_id).values_list('task__work_order_id', flat=True).first()
def _invalidate_service_records_pdf_cache(work_order_id):
if not work_order_id:
return
cached_pdfs = GeneratedWorkOrderPdf.objects.filter(
is_active=True,
work_order_id=work_order_id,
pdf_type='service_records',
)
for cached in cached_pdfs:
file_name = getattr(cached.file, 'name', '')
if file_name:
try:
if cached.file.storage.exists(file_name):
cached.file.delete(save=False)
except (FileNotFoundError, OSError, ValueError):
pass
cached.file = None
cached.is_active = False
cached.status = 'failed'
cached.error_message = 'PDF cache invalidiran zbog promjene servisnih zapisa.'
cached.save(update_fields=['file', 'is_active', 'status', 'error_message', 'updated_at'])
@receiver(post_save, sender='fleet.VehicleServiceRecord')
@receiver(pre_delete, sender='fleet.VehicleServiceRecord')
def _invalidate_service_records_pdf_for_service_record(sender, instance, **kwargs):
_invalidate_service_records_pdf_cache(_service_record_work_order_id(instance))
@receiver(post_save, sender='fleet.VehicleServicePhoto')
@receiver(pre_delete, sender='fleet.VehicleServicePhoto')
def _invalidate_service_records_pdf_for_service_photo(sender, instance, **kwargs):
_invalidate_service_records_pdf_cache(_service_record_work_order_id(instance))
@receiver(post_save, sender='fleet.VehicleServiceAttachment')
@receiver(pre_delete, sender='fleet.VehicleServiceAttachment')
def _invalidate_service_records_pdf_for_service_attachment(sender, instance, **kwargs):
_invalidate_service_records_pdf_cache(_service_record_work_order_id(instance))
class Vehicle(BaseModel):
ASSET_TYPE_CHOICES = [
('vehicle', _("Vozilo")),