Add engagement financial year correction workflow
This commit is contained in:
@@ -8,6 +8,37 @@
|
||||
{% if can_manage and not row.is_locked %}<a href="/services/engagements/{{ row.id }}/edit" class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-medium text-white hover:bg-brand-700">Edit</a>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% if request.query_params.get('fy_error') %}
|
||||
<div class="rounded-2xl border border-red-200 bg-red-50 p-4 text-sm font-medium text-red-800">{{ request.query_params.get('fy_error') }}</div>
|
||||
{% endif %}
|
||||
|
||||
{% if can_correct_engagement_fy and not row.is_locked and row.is_active and row.status not in ['completed', 'cancelled', 'inactive'] %}
|
||||
<section class="rounded-2xl border border-indigo-200 bg-indigo-50 p-5 shadow-soft">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div>
|
||||
<h3 class="text-sm font-semibold text-indigo-950">Correct Financial Year</h3>
|
||||
<p class="mt-1 text-sm text-indigo-800">Partner/Firm Admin correction for an engagement created under the wrong FY. Existing tasks, evidence, comments and review history are preserved; FY/AY metadata and due-date calculation are synchronised.</p>
|
||||
</div>
|
||||
<span class="rounded-full bg-white px-3 py-1 text-xs font-semibold text-indigo-700">Current FY {{ row.financial_year }}</span>
|
||||
</div>
|
||||
<form method="post" action="/services/engagements/{{ row.id }}/correct-financial-year" class="mt-4 grid gap-3 md:grid-cols-[180px_1fr_auto]" onsubmit="return confirm('Correct this engagement financial year? Linked task and document FY metadata will be updated while existing execution/review history is preserved.');">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-indigo-700">Correct FY</label>
|
||||
<input type="text" name="target_financial_year" required placeholder="2025-26" class="w-full rounded-xl border border-indigo-200 bg-white px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-indigo-700">Reason</label>
|
||||
<input type="text" name="correction_reason" required placeholder="Engagement was created under incorrect financial year" class="w-full rounded-xl border border-indigo-200 bg-white px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div class="flex items-end">
|
||||
<button type="submit" class="rounded-xl bg-indigo-700 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-800">Update FY</button>
|
||||
</div>
|
||||
</form>
|
||||
<p class="mt-3 text-xs text-indigo-700">Locked/closed engagements, locked FYs, released final documents/UDINs and duplicate target engagements are blocked from correction.</p>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
{% if row.is_locked %}<div class="rounded-2xl border border-amber-200 bg-amber-50 p-4 text-sm text-amber-800">This engagement is locked as historical record. It cannot be edited.</div>{% endif %}
|
||||
<div class="grid gap-6 lg:grid-cols-2">
|
||||
<section class="rounded-2xl bg-white p-5 shadow-soft"><h3 class="text-sm font-semibold text-slate-900">Client & Service</h3><dl class="mt-4 space-y-3 text-sm"><div><dt class="text-slate-500">Client</dt><dd class="font-medium text-slate-900">{{ row.client.client_name if row.client else '-' }}</dd></div><div><dt class="text-slate-500">Service</dt><dd class="font-medium text-slate-900">{{ row.catalogue.service_name if row.catalogue else '-' }}</dd></div><div><dt class="text-slate-500">Financial Year</dt><dd>{{ row.financial_year or '-' }}</dd></div><div><dt class="text-slate-500">Assessment Year</dt><dd>{{ row.assessment_year or '-' }}</dd></div><div><dt class="text-slate-500">Return / Engagement Period</dt><dd>{{ row.period_label or 'Not applicable' }}</dd></div><div><dt class="text-slate-500">Engagement Type</dt><dd>{{ 'Assurance' if row.engagement_type == 'assurance' else 'Non-Assurance' }}</dd></div><div><dt class="text-slate-500">Original Due Date</dt><dd>{{ row.original_due_date or '-' }}</dd></div><div><dt class="text-slate-500">Expiry Date</dt><dd>{{ row.expiry_date or '-' }}</dd></div><div><dt class="text-slate-500">Current Due Date</dt><dd class="font-medium text-slate-900">{{ row.current_due_date or '-' }}{% if row.due_date_source %}<span class="ml-2 rounded-full bg-slate-100 px-2 py-1 text-xs font-medium text-slate-600">{{ row.due_date_source|replace('_',' ')|title }}</span>{% endif %}</dd></div><div><dt class="text-slate-500">Status</dt><dd>{{ 'Locked' if row.is_locked else row.status|replace('_',' ')|title }}{% if not row.is_active %} / Inactive{% endif %}</dd></div><div><dt class="text-slate-500">Engagement Dates</dt><dd>{{ row.start_date or '-' }} to {{ row.end_date or '-' }}</dd></div></dl></section>
|
||||
|
||||
@@ -28,6 +28,14 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if fy_corrected_count or fy_skipped_count or fy_error %}
|
||||
<div class="rounded-2xl border {% if fy_error and not fy_corrected_count %}border-red-200 bg-red-50{% else %}border-indigo-200 bg-indigo-50{% endif %} p-4 text-sm shadow-soft">
|
||||
{% if fy_corrected_count %}<span class="font-medium text-emerald-700">{{ fy_corrected_count }} engagement{{ 's' if fy_corrected_count != 1 else '' }} moved to the corrected financial year.</span>{% endif %}
|
||||
{% if fy_skipped_count %}<span class="ml-2 font-medium text-amber-700">{{ fy_skipped_count }} selected engagement{{ 's' if fy_skipped_count != 1 else '' }} skipped.</span>{% endif %}
|
||||
{% if fy_error %}<span class="ml-2 text-red-700">{{ fy_error }}</span>{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if locked_count or skipped_count %}
|
||||
<div class="rounded-2xl border border-slate-200 bg-white p-4 text-sm shadow-soft">
|
||||
{% if locked_count %}<span class="font-medium text-emerald-700">{{ locked_count }} engagement{{ 's' if locked_count != 1 else '' }} locked.</span>{% endif %}
|
||||
@@ -51,15 +59,35 @@
|
||||
<button class="rounded-xl border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Filter</button>
|
||||
</form>
|
||||
|
||||
<form method="post" action="/services/engagements/bulk-lock" class="space-y-3" onsubmit="return confirm('Lock selected engagements? Locked engagements become read-only history.');">
|
||||
<form method="post" action="/services/engagements/bulk-lock" class="space-y-3">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
<input type="hidden" name="financial_year" value="{{ financial_year or '' }}">
|
||||
<input type="hidden" name="q" value="{{ q or '' }}">
|
||||
{% if include_inactive %}<input type="hidden" name="include_inactive" value="true">{% endif %}
|
||||
{% if can_lock_engagements %}
|
||||
<div class="flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-slate-200 bg-white p-4 shadow-soft">
|
||||
<p class="text-sm text-slate-600">Select completed engagements and lock them in bulk. Locked engagements become read-only history.</p>
|
||||
<button type="submit" class="rounded-xl bg-amber-600 px-4 py-2 text-sm font-medium text-white hover:bg-amber-700">Lock Selected Engagements</button>
|
||||
{% if can_lock_engagements or can_correct_engagement_fy %}
|
||||
<div class="space-y-4 rounded-2xl border border-slate-200 bg-white p-4 shadow-soft">
|
||||
{% if can_lock_engagements %}
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<p class="text-sm text-slate-600">Select engagements below. Completed engagements can be locked as read-only history.</p>
|
||||
<button type="submit" class="rounded-xl bg-amber-600 px-4 py-2 text-sm font-medium text-white hover:bg-amber-700" onclick="return confirm('Lock selected engagements? Locked engagements become read-only history.');">Lock Selected Engagements</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if can_correct_engagement_fy %}
|
||||
<div class="border-t border-slate-200 pt-4">
|
||||
<div class="flex flex-wrap items-end gap-3">
|
||||
<div class="min-w-[150px]">
|
||||
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-indigo-700">Correct selected to FY</label>
|
||||
<input type="text" name="target_financial_year" placeholder="2025-26" class="w-40 rounded-xl border border-indigo-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div class="min-w-[280px] flex-1">
|
||||
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-indigo-700">Correction reason</label>
|
||||
<input type="text" name="correction_reason" placeholder="Engagements created under incorrect financial year" class="w-full rounded-xl border border-indigo-200 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<button type="submit" formaction="/services/engagements/bulk-correct-financial-year" class="rounded-xl bg-indigo-700 px-4 py-2 text-sm font-semibold text-white hover:bg-indigo-800" onclick="return confirm('Correct FY for the selected open engagements? Existing task/evidence/review history will be preserved.');">Update Selected FY</button>
|
||||
</div>
|
||||
<p class="mt-2 text-xs text-slate-500">Partner/Firm Admin only. Only open, active, unlocked engagements are changed. Duplicate target engagements and locked years are skipped.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -67,7 +95,7 @@
|
||||
<table class="min-w-full divide-y divide-slate-200">
|
||||
<thead class="bg-slate-50">
|
||||
<tr>
|
||||
{% if can_lock_engagements %}<th class="w-10 px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500"><input type="checkbox" onclick="document.querySelectorAll('.engagement-lock-checkbox').forEach(cb => cb.checked = this.checked)"></th>{% endif %}
|
||||
{% if can_lock_engagements %}<th class="w-10 px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500"><input type="checkbox" onclick="document.querySelectorAll('.engagement-select-checkbox').forEach(cb => cb.checked = this.checked)"></th>{% endif %}
|
||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Client</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Service</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">FY / AY</th>
|
||||
@@ -81,7 +109,7 @@
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
{% for row in rows %}
|
||||
<tr>
|
||||
{% if can_lock_engagements %}<td class="px-4 py-3 text-sm">{% if not row.is_locked %}<input type="checkbox" class="engagement-lock-checkbox rounded border-slate-300" name="subscription_ids" value="{{ row.id }}">{% endif %}</td>{% endif %}
|
||||
{% if can_lock_engagements %}<td class="px-4 py-3 text-sm">{% if not row.is_locked %}<input type="checkbox" class="engagement-select-checkbox rounded border-slate-300" name="subscription_ids" value="{{ row.id }}">{% endif %}</td>{% endif %}
|
||||
<td class="px-4 py-3 text-sm"><div class="font-medium text-slate-900">{{ row.client.client_name if row.client else '-' }}</div><div class="text-xs text-slate-500">{{ row.client.client_code if row.client else '' }}</div></td>
|
||||
<td class="px-4 py-3 text-sm"><div class="font-medium text-slate-900">{{ row.catalogue.service_name if row.catalogue else '-' }}</div><div class="text-xs text-slate-500">{{ row.catalogue.service_code if row.catalogue else '' }}</div></td>
|
||||
<td class="px-4 py-3 text-xs text-slate-600"><div>FY: {{ row.financial_year or '-' }}</div><div>AY: {{ row.assessment_year or '-' }}</div><div>Period: {{ row.period_label or '-' }}</div></td>
|
||||
|
||||
Reference in New Issue
Block a user