fix: regenerate SN archive with task-specific DOCX headers
Invalidate cached monthly service-task ZIP archives so exports are rebuilt with the latest task/work-order/vehicle header data. Keep stale detection for work-order archives and add regression tests for per-task header isolation and stale cache replacement. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -23,7 +23,7 @@ from django.http import HttpResponse, FileResponse
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.html import escape
|
||||
from django.db.models import Q
|
||||
from django.db.models import Q, Max
|
||||
from django.db import transaction
|
||||
from reportlab.lib.pagesizes import A4
|
||||
from reportlab.lib import colors
|
||||
@@ -2959,6 +2959,33 @@ def _get_cached_generated_archive(*, user, archive_type, year, month):
|
||||
)
|
||||
|
||||
|
||||
def _latest_monthly_archive_source_update(*, user, archive_type, year, month):
|
||||
from modules.task_management.models import Task
|
||||
|
||||
base_tasks = Task.objects.filter(
|
||||
assigned_to=user,
|
||||
is_active=True,
|
||||
scheduled_date__year=year,
|
||||
scheduled_date__month=month,
|
||||
work_order__isnull=False,
|
||||
work_order__is_active=True,
|
||||
)
|
||||
latest_candidates = [
|
||||
base_tasks.aggregate(value=Max('updated_at')).get('value'),
|
||||
base_tasks.aggregate(value=Max('work_order__updated_at')).get('value'),
|
||||
base_tasks.aggregate(value=Max('work_order__vehicle__updated_at')).get('value'),
|
||||
base_tasks.aggregate(value=Max('work_hours_table__updated_at')).get('value'),
|
||||
]
|
||||
if archive_type == 'work_orders':
|
||||
latest_candidates.append(
|
||||
base_tasks.aggregate(value=Max('work_order__invoices__updated_at')).get('value')
|
||||
)
|
||||
latest_values = [value for value in latest_candidates if value is not None]
|
||||
if not latest_values:
|
||||
return None
|
||||
return max(latest_values)
|
||||
|
||||
|
||||
def _request_monthly_archive_generation(*, request, archive_type):
|
||||
year, month = _parse_year_month_params(request)
|
||||
_cleanup_expired_generated_archive_records()
|
||||
@@ -2970,21 +2997,39 @@ def _request_monthly_archive_generation(*, request, archive_type):
|
||||
month=month,
|
||||
)
|
||||
if cached:
|
||||
_notify_monthly_archive_request(
|
||||
user=request.user,
|
||||
archive_type=archive_type,
|
||||
stage='completed',
|
||||
year=year,
|
||||
month=month,
|
||||
generated_archive=cached,
|
||||
)
|
||||
return {
|
||||
'status': 'ready',
|
||||
'generated_archive_id': str(cached.pk),
|
||||
'download_url': f"fleet/reports/generated-archives/{cached.pk}/download/",
|
||||
'filename': cached.filename,
|
||||
'expires_at': cached.expires_at.isoformat() if cached.expires_at else None,
|
||||
}
|
||||
if archive_type == 'service_tasks':
|
||||
cached.is_active = False
|
||||
cached.status = 'failed'
|
||||
cached.error_message = 'ZIP arhiva servisnih taskova se regenerira za najnoviji kontekst.'
|
||||
cached.save(update_fields=['is_active', 'status', 'error_message', 'updated_at'])
|
||||
else:
|
||||
latest_source_update = _latest_monthly_archive_source_update(
|
||||
user=request.user,
|
||||
archive_type=archive_type,
|
||||
year=year,
|
||||
month=month,
|
||||
)
|
||||
if latest_source_update and cached.updated_at and cached.updated_at < latest_source_update:
|
||||
cached.is_active = False
|
||||
cached.status = 'failed'
|
||||
cached.error_message = 'ZIP arhiva zastarjela zbog novijih izmjena izvora.'
|
||||
cached.save(update_fields=['is_active', 'status', 'error_message', 'updated_at'])
|
||||
else:
|
||||
_notify_monthly_archive_request(
|
||||
user=request.user,
|
||||
archive_type=archive_type,
|
||||
stage='completed',
|
||||
year=year,
|
||||
month=month,
|
||||
generated_archive=cached,
|
||||
)
|
||||
return {
|
||||
'status': 'ready',
|
||||
'generated_archive_id': str(cached.pk),
|
||||
'download_url': f"fleet/reports/generated-archives/{cached.pk}/download/",
|
||||
'filename': cached.filename,
|
||||
'expires_at': cached.expires_at.isoformat() if cached.expires_at else None,
|
||||
}
|
||||
|
||||
existing_pending = (
|
||||
GeneratedFleetArchive.objects
|
||||
|
||||
Reference in New Issue
Block a user