Add bulk upgrade to system for firm tasks
This commit is contained in:
@@ -26,6 +26,8 @@
|
|||||||
{% if request.query_params.get('imported') %}<div class="rounded-2xl 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.</div>{% endif %}
|
{% if request.query_params.get('imported') %}<div class="rounded-2xl 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.</div>{% endif %}
|
||||||
{% if request.query_params.get('import_error') %}<div class="rounded-2xl border border-red-200 bg-red-50 p-4 text-sm text-red-800">Excel import failed validation. No partial update was committed.</div>{% endif %}
|
{% if request.query_params.get('import_error') %}<div class="rounded-2xl border border-red-200 bg-red-50 p-4 text-sm text-red-800">Excel import failed validation. No partial update was committed.</div>{% endif %}
|
||||||
{% if request.query_params.get('system_upgraded') %}<div class="rounded-2xl border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-800">System update applied to the firm task. Safe unstarted tasks in open engagements were refreshed automatically.</div>{% endif %}
|
{% if request.query_params.get('system_upgraded') %}<div class="rounded-2xl border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-800">System update applied to the firm task. Safe unstarted tasks in open engagements were refreshed automatically.</div>{% endif %}
|
||||||
|
{% if request.query_params.get('system_bulk_upgraded') %}<div class="rounded-2xl border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-800"><span class="font-semibold">Full system upgrade completed.</span> {{ request.query_params.get('bulk_count','0') }} firm task(s) were aligned to the latest system defaults{% if request.query_params.get('bulk_retired','0') != '0' %}, including {{ request.query_params.get('bulk_retired','0') }} retired system task(s){% endif %}. Open engagements: {{ request.query_params.get('engagement_created','0') }} task(s) added, {{ request.query_params.get('engagement_updated','0') }} pending task(s) refreshed, {{ request.query_params.get('engagement_deactivated','0') }} pending task(s) deactivated. Started/completed/reviewed history was preserved.</div>{% endif %}
|
||||||
|
{% if request.query_params.get('system_bulk_none') %}<div class="rounded-2xl border border-blue-200 bg-blue-50 p-4 text-sm text-blue-800">All firm tasks for this service are already aligned with the latest system defaults. Firm-only tasks were left unchanged.</div>{% endif %}
|
||||||
{% if request.query_params.get('system_kept') %}<div class="rounded-2xl border border-blue-200 bg-blue-50 p-4 text-sm text-blue-800">Firm customization retained for this system revision. You will be prompted again when the system default changes in a later revision.</div>{% endif %}
|
{% if request.query_params.get('system_kept') %}<div class="rounded-2xl border border-blue-200 bg-blue-50 p-4 text-sm text-blue-800">Firm customization retained for this system revision. You will be prompted again when the system default changes in a later revision.</div>{% endif %}
|
||||||
{% if request.query_params.get('sync_success') %}<div class="rounded-2xl border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-800">System defaults synchronized: {{ request.query_params.get('created','0') }} created, {{ request.query_params.get('updated','0') }} updated, {{ request.query_params.get('custom_updates','0') }} customized task(s) awaiting review.</div>{% endif %}
|
{% if request.query_params.get('sync_success') %}<div class="rounded-2xl border border-emerald-200 bg-emerald-50 p-4 text-sm text-emerald-800">System defaults synchronized: {{ request.query_params.get('created','0') }} created, {{ request.query_params.get('updated','0') }} updated, {{ request.query_params.get('custom_updates','0') }} customized task(s) awaiting review.</div>{% endif %}
|
||||||
|
|
||||||
@@ -61,10 +63,18 @@
|
|||||||
<p class="text-sm text-slate-500">Create a task template for this firm and service.</p>
|
<p class="text-sm text-slate-500">Create a task template for this firm and service.</p>
|
||||||
</div>
|
</div>
|
||||||
{% if active_default_task_count > 0 %}
|
{% if active_default_task_count > 0 %}
|
||||||
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<form method="post" action="/services/catalogue/{{ service.id }}/firm-tasks/sync-system-defaults">
|
<form method="post" action="/services/catalogue/{{ service.id }}/firm-tasks/sync-system-defaults">
|
||||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
<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>
|
<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>
|
||||||
</form>
|
</form>
|
||||||
|
{% if system_update_available_count > 0 %}
|
||||||
|
<form method="post" action="/services/templates/{{ service.id }}/system-updates/upgrade-all" onsubmit="return confirm('Upgrade all {{ system_update_available_count }} firm task(s) with pending system updates to the latest System Default? Firm-only tasks will remain unchanged. Safe pending/unstarted tasks in open engagements will be synchronized; started, completed and reviewed history will be preserved.');">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||||
|
<button class="rounded-xl bg-violet-700 px-4 py-2 text-sm font-semibold text-white hover:bg-violet-800" type="submit">Upgrade All to System ({{ system_update_available_count }})</button>
|
||||||
|
</form>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -163,6 +173,21 @@
|
|||||||
</section>
|
</section>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if can_manage_tasks and system_update_available_count > 0 %}
|
||||||
|
<section class="rounded-2xl border border-violet-200 bg-violet-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-violet-950">System updates awaiting firm decision</h3>
|
||||||
|
<p class="mt-1 text-xs text-violet-800">{{ system_update_available_count }} customized task(s) have a newer system revision. Use <span class="font-semibold">Upgrade All to System</span> for full alignment, or continue using the existing task-by-task Upgrade / Keep controls below.</p>
|
||||||
|
</div>
|
||||||
|
<form method="post" action="/services/templates/{{ service.id }}/system-updates/upgrade-all" onsubmit="return confirm('Upgrade all {{ system_update_available_count }} firm task(s) with pending system updates to the latest System Default? Firm-only tasks will remain unchanged. Safe pending/unstarted tasks in open engagements will be synchronized; started, completed and reviewed history will be preserved.');">
|
||||||
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||||
|
<button class="whitespace-nowrap rounded-xl bg-violet-700 px-4 py-2 text-sm font-semibold text-white hover:bg-violet-800" type="submit">Upgrade All to System ({{ system_update_available_count }})</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
<section class="overflow-hidden rounded-2xl bg-white shadow-soft">
|
<section class="overflow-hidden rounded-2xl bg-white shadow-soft">
|
||||||
<div class="flex items-center justify-between gap-3 border-b border-slate-100 px-5 py-4">
|
<div class="flex items-center justify-between gap-3 border-b border-slate-100 px-5 py-4">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1255,6 +1255,7 @@ def task_templates_detail(request: Request, catalogue_id: int):
|
|||||||
task_template_file_map=task_template_file_map,
|
task_template_file_map=task_template_file_map,
|
||||||
system_update_diff_map=system_update_diff_map,
|
system_update_diff_map=system_update_diff_map,
|
||||||
system_update_retirement_map=system_update_retirement_map,
|
system_update_retirement_map=system_update_retirement_map,
|
||||||
|
system_update_available_count=sum(1 for task in tasks if getattr(task, "system_update_available", False) and getattr(task, "source_system_task_id", None)),
|
||||||
can_manage_tasks=_can_manage_firm_tasks(db, user),
|
can_manage_tasks=_can_manage_firm_tasks(db, user),
|
||||||
default_tasks=default_tasks,
|
default_tasks=default_tasks,
|
||||||
active_default_task_count=sum(1 for task in default_tasks if task.is_active),
|
active_default_task_count=sum(1 for task in default_tasks if task.is_active),
|
||||||
@@ -1645,6 +1646,101 @@ def firm_task_template_toggle_active(
|
|||||||
db.close()
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
@router.post('/templates/{catalogue_id}/system-updates/upgrade-all')
|
||||||
|
def firm_tasks_accept_all_system_updates(
|
||||||
|
request: Request,
|
||||||
|
catalogue_id: int,
|
||||||
|
csrf_token: str = Form(...),
|
||||||
|
):
|
||||||
|
"""Upgrade every pending firm-task system revision for the active firm/service.
|
||||||
|
|
||||||
|
Firm-only tasks are intentionally untouched. Existing per-task upgrade/keep actions
|
||||||
|
remain available. Engagement synchronization runs once after all template updates so
|
||||||
|
pending/unstarted open-engagement tasks are refreshed safely without rewriting
|
||||||
|
started/completed/reviewed history.
|
||||||
|
"""
|
||||||
|
validate_csrf(request, csrf_token)
|
||||||
|
db = CommonSessionLocal()
|
||||||
|
try:
|
||||||
|
user = get_current_user(request, db=db)
|
||||||
|
if not user:
|
||||||
|
return RedirectResponse(url='/login', status_code=303)
|
||||||
|
if not _can_manage_firm_tasks(db, user):
|
||||||
|
return _redirect_denied()
|
||||||
|
|
||||||
|
tenant_id = _active_tenant_id(request, user)
|
||||||
|
selection = get_firm_selection(db, tenant_id=tenant_id, catalogue_id=catalogue_id)
|
||||||
|
if not selection or not selection.is_enabled:
|
||||||
|
return RedirectResponse(url='/services', status_code=303)
|
||||||
|
|
||||||
|
pending_tasks = db.scalars(
|
||||||
|
select(FirmServiceTaskTemplate)
|
||||||
|
.where(
|
||||||
|
FirmServiceTaskTemplate.tenant_id == tenant_id,
|
||||||
|
FirmServiceTaskTemplate.service_catalogue_id == catalogue_id,
|
||||||
|
FirmServiceTaskTemplate.system_update_available.is_(True),
|
||||||
|
FirmServiceTaskTemplate.source_system_task_id.is_not(None),
|
||||||
|
)
|
||||||
|
.order_by(FirmServiceTaskTemplate.sequence_no, FirmServiceTaskTemplate.id)
|
||||||
|
).all()
|
||||||
|
|
||||||
|
if not pending_tasks:
|
||||||
|
return RedirectResponse(
|
||||||
|
url=f'/services/templates/{catalogue_id}?system_bulk_none=1',
|
||||||
|
status_code=303,
|
||||||
|
)
|
||||||
|
|
||||||
|
upgraded = 0
|
||||||
|
retired = 0
|
||||||
|
for task in pending_tasks:
|
||||||
|
source = db.get(ServiceDefaultTaskTemplate, int(task.source_system_task_id))
|
||||||
|
if source is None:
|
||||||
|
continue
|
||||||
|
if not bool(source.is_active):
|
||||||
|
retired += 1
|
||||||
|
accept_system_update_for_firm_task(
|
||||||
|
db,
|
||||||
|
task=task,
|
||||||
|
user_id=user.id,
|
||||||
|
sync_open_engagements=False,
|
||||||
|
)
|
||||||
|
upgraded += 1
|
||||||
|
|
||||||
|
engagement = sync_open_engagement_tasks_for_service(
|
||||||
|
db,
|
||||||
|
tenant_id=tenant_id,
|
||||||
|
catalogue_id=catalogue_id,
|
||||||
|
user_id=user.id,
|
||||||
|
include_started_open_tasks=False,
|
||||||
|
safe_system_rollout=True,
|
||||||
|
)
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
return RedirectResponse(
|
||||||
|
url=(
|
||||||
|
f'/services/templates/{catalogue_id}?system_bulk_upgraded=1'
|
||||||
|
f'&bulk_count={upgraded}'
|
||||||
|
f'&bulk_retired={retired}'
|
||||||
|
f'&engagement_created={engagement.get("created", 0)}'
|
||||||
|
f'&engagement_updated={engagement.get("updated_pending", 0)}'
|
||||||
|
f'&engagement_deactivated={engagement.get("deactivated_pending", 0)}'
|
||||||
|
f'&engagement_preserved={engagement.get("preserved_history", 0)}'
|
||||||
|
),
|
||||||
|
status_code=303,
|
||||||
|
)
|
||||||
|
except ValueError:
|
||||||
|
db.rollback()
|
||||||
|
return RedirectResponse(
|
||||||
|
url=f'/services/templates/{catalogue_id}?error=system_bulk_upgrade_failed',
|
||||||
|
status_code=303,
|
||||||
|
)
|
||||||
|
except Exception:
|
||||||
|
db.rollback()
|
||||||
|
raise
|
||||||
|
finally:
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
@router.post('/templates/{catalogue_id}/tasks/{task_id}/system-update/upgrade')
|
@router.post('/templates/{catalogue_id}/tasks/{task_id}/system-update/upgrade')
|
||||||
def firm_task_accept_system_update(
|
def firm_task_accept_system_update(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|||||||
Reference in New Issue
Block a user