Prepare ERP source for Gitea deployment
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
{% extends "ui/templates/base/layout.html" %}
|
||||
{% block content %}
|
||||
<div class="space-y-6">
|
||||
<div class="flex items-center justify-between">
|
||||
<div><h2 class="text-xl font-semibold text-slate-900">Engagement</h2><p class="text-sm text-slate-500">{{ row.client.client_name if row.client else '-' }} · {{ row.catalogue.service_name if row.catalogue else '-' }} · FY {{ row.financial_year }}</p></div>
|
||||
<div class="flex gap-2">
|
||||
<a href="/services/engagements?financial_year={{ row.financial_year }}" class="rounded-xl border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Back</a>
|
||||
{% 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 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">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">Period</dt><dd>{{ row.start_date or '-' }} to {{ row.end_date or '-' }}</dd></div></dl></section>
|
||||
<section class="rounded-2xl bg-white p-5 shadow-soft"><h3 class="text-sm font-semibold text-slate-900">Assignment</h3><dl class="mt-4 space-y-3 text-sm"><div><dt class="text-slate-500">Partner</dt><dd>{{ row.assigned_partner.full_name if row.assigned_partner and row.assigned_partner.full_name else (row.assigned_partner.email if row.assigned_partner else '-') }}</dd></div><div><dt class="text-slate-500">Manager</dt><dd>{{ row.assigned_manager.full_name if row.assigned_manager and row.assigned_manager.full_name else (row.assigned_manager.email if row.assigned_manager else '-') }}</dd></div><div><dt class="text-slate-500">Staff</dt><dd>{{ row.assigned_staff.full_name if row.assigned_staff and row.assigned_staff.full_name else (row.assigned_staff.email if row.assigned_staff else '-') }}</dd></div><div><dt class="text-slate-500">Review Partner</dt><dd>{{ row.review_partner.full_name if row.review_partner and row.review_partner.full_name else (row.review_partner.email if row.review_partner else '-') }}</dd></div></dl></section>
|
||||
</div>
|
||||
{% if can_manage and not row.is_locked %}<form method="post" action="/services/engagements/{{ row.id }}/lock" class="rounded-2xl border border-amber-200 bg-white p-5 shadow-soft"><input type="hidden" name="csrf_token" value="{{ csrf_token }}"><h3 class="text-sm font-semibold text-slate-900">Year-end Lock</h3><p class="mt-2 text-sm text-slate-600">Lock this engagement when the year is complete. After locking, it becomes read-only history.</p><button class="mt-4 rounded-xl bg-amber-600 px-4 py-2 text-sm font-medium text-white hover:bg-amber-700">Lock Engagement</button></form>{% 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">Engagement Tasks & Task Documents</h3>
|
||||
<p class="text-sm text-slate-500">Open each task to upload documents against its configured document requirements.</p>
|
||||
</div>
|
||||
<table class="min-w-full divide-y divide-slate-200">
|
||||
<thead class="bg-slate-50"><tr><th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Seq</th><th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Task</th><th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Status</th><th class="px-4 py-3 text-right text-xs font-semibold uppercase tracking-wide text-slate-500">Documents</th></tr></thead>
|
||||
<tbody class="divide-y divide-slate-100">
|
||||
{% for task in tasks or [] %}
|
||||
<tr>
|
||||
<td class="px-4 py-3 text-sm font-medium text-slate-900">{{ task.sequence_no }}</td>
|
||||
<td class="px-4 py-3 text-sm"><div class="font-medium text-slate-900">{{ task.task_name }}</div>{% if task.description %}<div class="text-xs text-slate-500">{{ task.description }}</div>{% endif %}</td>
|
||||
<td class="px-4 py-3 text-sm text-slate-700">{{ task.status.replace('_',' ').title() }}</td>
|
||||
<td class="px-4 py-3 text-right"><a href="/documents/tasks/{{ task.id }}" class="text-sm font-medium text-brand-700 hover:underline">Open Task Documents</a></td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="4" class="px-4 py-8 text-center text-sm text-slate-500">No execution tasks generated yet. Generate work tracker tasks first.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
{% if row.remarks %}<section class="rounded-2xl bg-white p-5 shadow-soft"><h3 class="text-sm font-semibold text-slate-900">Remarks</h3><p class="mt-3 text-sm leading-6 text-slate-700">{{ row.remarks }}</p></section>{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,66 @@
|
||||
{% extends "ui/templates/base/layout.html" %}
|
||||
{% block content %}
|
||||
<div class="mx-auto max-w-4xl rounded-2xl bg-white p-6 shadow-soft">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-slate-900">{% if mode == 'edit' %}Edit Engagement{% else %}Assign Service to Client{% endif %}</h2>
|
||||
<p class="text-sm text-slate-500">Engagements are maintained financial-year wise. Review partner is used only for assurance engagements of partnership audit firms.</p>
|
||||
</div>
|
||||
<a href="/services/engagements" class="rounded-xl border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Back</a>
|
||||
</div>
|
||||
|
||||
<form method="post" class="mt-6 grid gap-5 md:grid-cols-2">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
||||
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">Financial Year</label>
|
||||
{% if mode == 'edit' %}
|
||||
<div class="rounded-xl border border-slate-200 bg-slate-50 px-4 py-2 text-sm text-slate-700">{{ subscription.financial_year }}</div>
|
||||
{% else %}
|
||||
<input type="text" name="financial_year" value="{{ financial_year or '2025-26' }}" required placeholder="2025-26" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm">
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">Client</label>
|
||||
{% if mode == 'edit' %}
|
||||
<div class="rounded-xl border border-slate-200 bg-slate-50 px-4 py-2 text-sm text-slate-700">{{ subscription.client.client_name if subscription and subscription.client else '-' }}</div>
|
||||
{% else %}
|
||||
<select name="client_id" required class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm">
|
||||
<option value="">Select client</option>
|
||||
{% for client in clients %}<option value="{{ client.id }}" {% if selected_client_id and client.id == selected_client_id %}selected{% endif %}>{{ client.client_name }} ({{ client.client_code }})</option>{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-medium text-slate-700">Enabled Service</label>
|
||||
{% if mode == 'edit' %}
|
||||
<div class="rounded-xl border border-slate-200 bg-slate-50 px-4 py-2 text-sm text-slate-700">
|
||||
{{ subscription.catalogue.service_name if subscription and subscription.catalogue else '-' }} · {{ 'Assurance' if subscription and subscription.engagement_type == 'assurance' else 'Non-Assurance' }}
|
||||
</div>
|
||||
{% else %}
|
||||
<select name="service_catalogue_id" required class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm">
|
||||
<option value="">Select service</option>
|
||||
{% for selection in enabled_services %}
|
||||
<option value="{{ selection.catalogue.id }}">{{ selection.catalogue.service_name }} ({{ selection.catalogue.service_code }}) - {{ 'Assurance' if selection.catalogue.engagement_type == 'assurance' else 'Non-Assurance' }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div><label class="mb-2 block text-sm font-medium text-slate-700">Assigned Partner</label><select name="assigned_partner_user_id" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm"><option value="">Not assigned</option>{% for u in assignable_users %}<option value="{{ u.id }}" {% if subscription and subscription.assigned_partner_user_id == u.id %}selected{% endif %}>{{ u.full_name or u.email }}</option>{% endfor %}</select></div>
|
||||
<div><label class="mb-2 block text-sm font-medium text-slate-700">Assigned Manager</label><select name="assigned_manager_user_id" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm"><option value="">Not assigned</option>{% for u in assignable_users %}<option value="{{ u.id }}" {% if subscription and subscription.assigned_manager_user_id == u.id %}selected{% endif %}>{{ u.full_name or u.email }}</option>{% endfor %}</select></div>
|
||||
<div><label class="mb-2 block text-sm font-medium text-slate-700">Assigned Staff</label><select name="assigned_staff_user_id" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm"><option value="">Not assigned</option>{% for u in assignable_users %}<option value="{{ u.id }}" {% if subscription and subscription.assigned_staff_user_id == u.id %}selected{% endif %}>{{ u.full_name or u.email }}</option>{% endfor %}</select></div>
|
||||
<div><label class="mb-2 block text-sm font-medium text-slate-700">Review Partner</label><select name="review_partner_user_id" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm"><option value="">Use client default / Not required</option>{% for u in review_partners or [] %}<option value="{{ u.id }}" {% if subscription and subscription.review_partner_user_id == u.id %}selected{% endif %}>{{ u.full_name or u.email }}</option>{% endfor %}</select><p class="mt-1 text-xs text-slate-500">Saved only for assurance engagements of partnership audit firms.</p></div>
|
||||
|
||||
<div><label class="mb-2 block text-sm font-medium text-slate-700">Status</label><select name="status" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm">{% for value, label in subscription_statuses %}<option value="{{ value }}" {% if subscription and subscription.status == value %}selected{% elif not subscription and value == 'active' %}selected{% endif %}>{{ label }}</option>{% endfor %}</select></div>
|
||||
<div><label class="mb-2 block text-sm font-medium text-slate-700">Start Date</label><input type="date" name="start_date" value="{{ subscription.start_date if subscription and subscription.start_date else '' }}" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm"></div>
|
||||
<div><label class="mb-2 block text-sm font-medium text-slate-700">End Date</label><input type="date" name="end_date" value="{{ subscription.end_date if subscription and subscription.end_date else '' }}" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm"></div>
|
||||
<div><label class="mb-2 block text-sm font-medium text-slate-700">Expiry Date</label><input type="date" name="expiry_date" value="{{ subscription.expiry_date if subscription and subscription.expiry_date else '' }}" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm"><p class="mt-1 text-xs text-slate-500">Use for renewal-before-expiry services like DSC/FSSAI. Current due date is calculated from the service due-date rule.</p></div>
|
||||
<div class="flex items-center gap-6 pt-7 text-sm text-slate-700"><label class="inline-flex items-center gap-2"><input type="checkbox" name="is_active" {% if not subscription or subscription.is_active %}checked{% endif %}> Active</label></div>
|
||||
<div class="md:col-span-2"><label class="mb-2 block text-sm font-medium text-slate-700">Remarks</label><textarea name="remarks" rows="4" class="w-full rounded-xl border border-slate-300 px-4 py-2 text-sm">{{ subscription.remarks if subscription and subscription.remarks else '' }}</textarea></div>
|
||||
<div class="md:col-span-2 flex justify-end gap-3"><a href="/services/engagements" class="rounded-xl border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">Cancel</a><button class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-medium text-white hover:bg-brand-700">Save</button></div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,88 @@
|
||||
{% extends "ui/templates/base/layout.html" %}
|
||||
{% block content %}
|
||||
<div class="space-y-6">
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<div>
|
||||
<h2 class="text-xl font-semibold text-slate-900">Engagements</h2>
|
||||
<p class="text-sm text-slate-500">Year-wise client service engagements with locking for completed years.</p>
|
||||
</div>
|
||||
{% if can_manage %}
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<a href="/services/bulk-imports" class="rounded-xl border border-emerald-300 px-4 py-2 text-sm font-medium text-emerald-700 hover:bg-emerald-50">Bulk Assign by Excel</a>
|
||||
<a href="/services/engagements/new" class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-medium text-white hover:bg-brand-700">Assign Service</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% 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 %}
|
||||
{% if skipped_count %}<span class="ml-2 font-medium text-amber-700">{{ skipped_count }} skipped because already locked or not permitted.</span>{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<form method="get" class="flex flex-wrap items-end gap-3 rounded-2xl bg-white p-4 shadow-soft">
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Financial Year</label>
|
||||
<input type="text" name="financial_year" value="{{ financial_year or '' }}" placeholder="2025-26" class="w-36 rounded-xl border border-slate-300 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<div>
|
||||
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">Search</label>
|
||||
<input type="text" name="q" value="{{ q or '' }}" placeholder="Client or service" class="w-72 rounded-xl border border-slate-300 px-3 py-2 text-sm">
|
||||
</div>
|
||||
<label class="inline-flex items-center gap-2 pb-2 text-sm text-slate-700">
|
||||
<input type="checkbox" name="include_inactive" value="true" {% if include_inactive %}checked{% endif %}>
|
||||
Include inactive
|
||||
</label>
|
||||
<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.');">
|
||||
<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>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="overflow-hidden rounded-2xl bg-white shadow-soft">
|
||||
<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 %}
|
||||
<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>
|
||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Due Date</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Type</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Assigned Users</th>
|
||||
<th class="px-4 py-3 text-left text-xs font-semibold uppercase tracking-wide text-slate-500">Status</th>
|
||||
<th class="px-4 py-3 text-right text-xs font-semibold uppercase tracking-wide text-slate-500">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<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 %}
|
||||
<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></td>
|
||||
<td class="px-4 py-3 text-xs text-slate-600"><div>Current: <span class="font-medium text-slate-900">{{ row.current_due_date or '-' }}</span></div><div>Original: {{ row.original_due_date or '-' }}</div>{% if row.expiry_date %}<div>Expiry: {{ row.expiry_date }}</div>{% endif %}{% if row.due_date_source %}<div class="text-slate-500">{{ row.due_date_source|replace('_',' ')|title }}</div>{% endif %}</td>
|
||||
<td class="px-4 py-3 text-sm text-slate-700">{{ 'Assurance' if row.engagement_type == 'assurance' else 'Non-Assurance' }}</td>
|
||||
<td class="px-4 py-3 text-xs text-slate-600"><div>Partner: {{ row.assigned_partner.full_name if row.assigned_partner and row.assigned_partner.full_name else (row.assigned_partner.email if row.assigned_partner else '-') }}</div><div>Manager: {{ row.assigned_manager.full_name if row.assigned_manager and row.assigned_manager.full_name else (row.assigned_manager.email if row.assigned_manager else '-') }}</div><div>Staff: {{ row.assigned_staff.full_name if row.assigned_staff and row.assigned_staff.full_name else (row.assigned_staff.email if row.assigned_staff else '-') }}</div><div>Review: {{ row.review_partner.full_name if row.review_partner and row.review_partner.full_name else (row.review_partner.email if row.review_partner else '-') }}</div></td>
|
||||
<td class="px-4 py-3 text-sm"><span class="rounded-full px-2 py-1 text-xs font-medium {% if row.is_locked %}bg-amber-100 text-amber-700{% elif row.is_active and row.status == 'active' %}bg-emerald-100 text-emerald-700{% else %}bg-slate-200 text-slate-700{% endif %}">{{ 'Locked' if row.is_locked else row.status|replace('_',' ')|title }}{% if not row.is_active %} / Inactive{% endif %}</span></td>
|
||||
<td class="px-4 py-3 text-right text-sm"><a href="/services/engagements/{{ row.id }}" class="font-medium text-brand-700 hover:underline">View</a>{% if can_manage and not row.is_locked %}<a href="/services/engagements/{{ row.id }}/edit" class="ml-3 font-medium text-brand-700 hover:underline">Edit</a>{% endif %}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="{{ 9 if can_lock_engagements else 8 }}" class="px-4 py-8 text-center text-sm text-slate-500">No engagements found for this financial year.</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user