Fix retirement propagation and task counts

This commit is contained in:
A R R R Associates
2026-09-19 15:11:56 +05:30
parent f565e8309d
commit cc8691d4cd
4 changed files with 107 additions and 22 deletions
@@ -11,14 +11,21 @@
{% if request.query_params.get('imported') %}<div class="rounded-xl border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-800">Excel import completed: {{ request.query_params.get('created','0') }} created, {{ request.query_params.get('updated','0') }} updated, {{ request.query_params.get('retired','0') }} retired.{% if request.query_params.get('full_sync') == '1' %} Full Synchronization was applied.{% endif %}</div>{% endif %}
{% if request.query_params.get('import_error') %}<div class="rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-800">Excel import failed validation. No partial update was committed. Review the workbook and try again.</div>{% endif %}
<section class="grid gap-4 md:grid-cols-2">
<div class="rounded-2xl bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Active System Defaults</div><div class="mt-2 text-2xl font-semibold text-slate-900">{{ active_default_task_count }}</div><p class="mt-1 text-sm text-slate-500">These tasks are available for synchronization to firms and new engagement work.</p></div>
<div class="rounded-2xl bg-white p-5 shadow-soft"><div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Retired System Defaults</div><div class="mt-2 text-2xl font-semibold text-slate-900">{{ retired_default_task_count }}</div><p class="mt-1 text-sm text-slate-500">Soft-retired tasks remain in the database so historical engagement and audit records are preserved.</p></div>
</section>
<section class="rounded-2xl border border-emerald-200 bg-emerald-50 p-4">
<div class="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"><div><h3 class="text-sm font-semibold text-emerald-950">Export · Edit · Re-import This Service</h3><p class="mt-1 text-xs text-emerald-800">Export this service's current tasks, edit the Import Ready sheet, then re-import. Existing exports now carry a stable system_task_id so renaming and resequencing update the correct task.</p></div>
<form method="post" action="/services/catalogue/{{ service.id }}/defaults/import" enctype="multipart/form-data" class="flex flex-wrap items-center gap-2" onsubmit="if (this.full_sync.checked) return confirm('Full Synchronization will retire existing system-default tasks for this service that are missing from the workbook. Historical work will be preserved. Continue?');"><input type="hidden" name="csrf_token" value="{{ csrf_token }}"><input type="file" name="file" accept=".xlsx" required class="max-w-xs rounded-xl border border-emerald-200 bg-white px-3 py-2 text-xs"><label class="inline-flex items-center gap-2 rounded-xl border border-amber-200 bg-amber-50 px-3 py-2 text-xs font-medium text-amber-900"><input type="checkbox" name="full_sync"> Full Synchronization (retire missing tasks)</label><button class="rounded-xl bg-emerald-700 px-4 py-2 text-sm font-semibold text-white hover:bg-emerald-800">Import Updated Excel</button></form></div>
</section>
<section class="rounded-2xl bg-white p-5 shadow-soft">
<div class="mb-4 flex items-center justify-between gap-3">
<div><h3 class="text-sm font-semibold text-slate-900">System Default Task List</h3><p class="text-xs text-slate-500">Active tasks are shown by default. Retired tasks remain available for audit/history review.</p></div>
<a href="{% if show_retired %}/services/catalogue/{{ service.id }}/defaults{% else %}/services/catalogue/{{ service.id }}/defaults?show_retired=1{% endif %}" class="rounded-lg border border-slate-300 bg-white px-3 py-1.5 text-xs font-semibold text-slate-700 hover:bg-slate-50">{% if show_retired %}Hide Retired{% else %}Show Retired ({{ retired_default_task_count }}){% endif %}</a>
</div>
<div class="space-y-3">
{% for task in default_tasks %}
{% if task.is_active or is_system_admin %}
{% for task in display_default_tasks %}
<div class="rounded-xl border border-slate-200 p-4">
<div class="flex items-center justify-between gap-4">
<div>
@@ -40,8 +47,7 @@
<p class="mt-2 text-sm text-slate-700">{{ task.description }}</p>
{% endif %}
</div>
{% endif %}
{% else %}<div class="rounded-xl border border-dashed border-slate-300 p-6 text-sm text-slate-500">No default tasks configured yet.</div>{% endfor %}
{% else %}<div class="rounded-xl border border-dashed border-slate-300 p-6 text-sm text-slate-500">No active default tasks configured. Use Show Retired to view inactive historical defaults.</div>{% endfor %}
</div>
</section>
<section class="rounded-2xl bg-white p-5 shadow-soft">
@@ -43,13 +43,13 @@
</div>
<div class="rounded-2xl bg-white p-5 shadow-soft">
<div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Firm Tasks</div>
<div class="mt-2 text-lg font-semibold text-slate-900">{{ task_templates|length }}</div>
<p class="mt-1 text-sm text-slate-500">Tasks customised for the active firm.</p>
<div class="mt-2 text-lg font-semibold text-slate-900">{{ active_task_count }}</div>
<p class="mt-1 text-sm text-slate-500">Active tasks for the firm. {{ retired_task_count }} retired task(s) are preserved for history.</p>
</div>
<div class="rounded-2xl bg-white p-5 shadow-soft">
<div class="text-xs font-semibold uppercase tracking-wide text-slate-500">System Defaults</div>
<div class="mt-2 text-lg font-semibold text-slate-900">{{ default_tasks|length }}</div>
<p class="mt-1 text-sm text-slate-500">Inherited defaults stay synchronized automatically; customized tasks require Firm Admin approval before a later system update is applied.</p>
<div class="mt-2 text-lg font-semibold text-slate-900">{{ active_default_task_count }}</div>
<p class="mt-1 text-sm text-slate-500">Active inherited system defaults. {{ retired_default_task_count }} retired default(s) are preserved for history; customized tasks require Firm Admin approval before a later system update is applied.</p>
</div>
</section>
@@ -60,7 +60,7 @@
<h3 class="text-sm font-semibold text-slate-900">Add Firm Task</h3>
<p class="text-sm text-slate-500">Create a task template for this firm and service.</p>
</div>
{% if default_tasks|length > 0 %}
{% if active_default_task_count > 0 %}
<form method="post" action="/services/catalogue/{{ service.id }}/firm-tasks/sync-system-defaults">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<button class="rounded-xl border border-brand-300 px-4 py-2 text-sm font-medium text-brand-700 hover:bg-brand-50" type="submit">Sync System Defaults</button>
@@ -164,9 +164,12 @@
{% endif %}
<section class="overflow-hidden rounded-2xl bg-white shadow-soft">
<div class="border-b border-slate-100 px-5 py-4">
<h3 class="text-sm font-semibold text-slate-900">Firm Task List</h3>
<p class="text-sm text-slate-500">These tasks will be used when work is generated for this firm.</p>
<div class="flex items-center justify-between gap-3 border-b border-slate-100 px-5 py-4">
<div>
<h3 class="text-sm font-semibold text-slate-900">Firm Task List</h3>
<p class="text-sm text-slate-500">Active tasks are used for new work. Retired tasks remain stored for historical references.</p>
</div>
<a href="{% if show_retired %}/services/templates/{{ service.id }}{% else %}/services/templates/{{ service.id }}?show_retired=1{% endif %}" class="rounded-lg border border-slate-300 bg-white px-3 py-1.5 text-xs font-semibold text-slate-700 hover:bg-slate-50">{% if show_retired %}Hide Retired{% else %}Show Retired ({{ retired_task_count }}){% endif %}</a>
</div>
<table class="min-w-full divide-y divide-slate-200">
<thead class="bg-slate-50">
@@ -181,7 +184,7 @@
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
{% for task in task_templates %}
{% for task in display_task_templates %}
<tr>
<td class="px-4 py-3 text-sm font-medium text-slate-900 align-top">
<div class="flex items-center gap-2">
@@ -219,7 +222,8 @@
{% if task.system_update_available %}
{% set changes = system_update_diff_map.get(task.id, []) if system_update_diff_map else [] %}
<div class="mt-2 rounded-xl border border-violet-200 bg-violet-50 p-3 text-xs text-violet-950">
<div class="font-semibold">A newer system default is available. Your firm customization has not been overwritten.</div>
{% set retirement_update = system_update_retirement_map.get(task.id, false) if system_update_retirement_map else false %}
<div class="font-semibold">{% if retirement_update %}This system default has been retired. Your customized firm task has not been disabled automatically.{% else %}A newer system default is available. Your firm customization has not been overwritten.{% endif %}</div>
{% if changes %}
<div class="mt-2 space-y-1">
{% for change in changes[:5] %}
@@ -230,13 +234,13 @@
{% endif %}
{% if can_manage_tasks %}
<div class="mt-3 flex flex-wrap gap-2">
<form method="post" action="/services/templates/{{ service.id }}/tasks/{{ task.id }}/system-update/upgrade" onsubmit="return confirm('Upgrade this customized firm task to the latest system default? Safe unstarted tasks in open engagements will also be refreshed.');">
<form method="post" action="/services/templates/{{ service.id }}/tasks/{{ task.id }}/system-update/upgrade" onsubmit="return confirm('{% if retirement_update %}Retire this firm task in line with the system default? Safe pending/unstarted tasks in open engagements will also be deactivated; historical work is preserved.{% else %}Upgrade this customized firm task to the latest system default? Safe unstarted tasks in open engagements will also be refreshed.{% endif %}');">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<button class="rounded-lg bg-violet-700 px-3 py-1.5 text-[11px] font-semibold text-white hover:bg-violet-800">Upgrade to System</button>
<button class="rounded-lg bg-violet-700 px-3 py-1.5 text-[11px] font-semibold text-white hover:bg-violet-800">{% if retirement_update %}Retire with System{% else %}Upgrade to System{% endif %}</button>
</form>
<form method="post" action="/services/templates/{{ service.id }}/tasks/{{ task.id }}/system-update/keep-firm">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<button class="rounded-lg border border-violet-300 bg-white px-3 py-1.5 text-[11px] font-semibold text-violet-800 hover:bg-violet-100">Keep Firm Version</button>
<button class="rounded-lg border border-violet-300 bg-white px-3 py-1.5 text-[11px] font-semibold text-violet-800 hover:bg-violet-100">{% if retirement_update %}Keep Firm Task{% else %}Keep Firm Version{% endif %}</button>
</form>
</div>
{% endif %}
@@ -326,7 +330,7 @@
</td>
</tr>
{% else %}
<tr><td colspan="7" class="px-4 py-8 text-center text-sm text-slate-500">No firm task templates yet. Copy defaults or add tasks manually.</td></tr>
<tr><td colspan="7" class="px-4 py-8 text-center text-sm text-slate-500">No active firm task templates yet. Use Show Retired to view historical inactive tasks, or sync/add tasks as needed.</td></tr>
{% endfor %}
</tbody>
</table>