fix: regenerate SN archive with task-specific DOCX headers
Some checks failed
ERP CI/CD Pipeline / test (push) Has been cancelled
ERP CI/CD Pipeline / Deploy (server git pull + compose) (push) Has been cancelled

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:
mariomitte
2026-08-06 20:58:57 +02:00
parent 8ad1f40601
commit 8b90916641
2 changed files with 144 additions and 16 deletions

View File

@@ -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