Add bulk client registration addition

This commit is contained in:
A R R R Associates
2026-08-06 14:32:59 +05:30
parent a57c11da77
commit 4248aae4d3
3 changed files with 694 additions and 2 deletions
@@ -0,0 +1,373 @@
{% extends "ui/templates/base/layout.html" %}
{% block content %}
<div class="space-y-6">
{% set _uiux_partner_role_text = (current_user_roles or [])|join('|')|lower %}
{% if 'partner' in _uiux_partner_role_text %}
{% include "ui/templates/components/partner_navigation_v2.html" %}
{% endif %}
<div class="flex flex-wrap items-start justify-between gap-3">
<div>
<h1 class="text-2xl font-semibold text-slate-900">Bulk Registration Addition</h1>
<p class="text-sm text-slate-500">
Add the same registration type to multiple clients while entering each clients unique registration number.
</p>
</div>
<a href="/registrations"
class="rounded-xl border border-slate-300 px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50">
Registrations Dashboard
</a>
</div>
{% if created or skipped or invalid %}
<div class="grid gap-3 sm:grid-cols-3">
<div class="rounded-2xl bg-white p-4 shadow-soft">
<div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Created</div>
<div class="mt-1 text-2xl font-semibold text-emerald-700">{{ created }}</div>
</div>
<div class="rounded-2xl bg-white p-4 shadow-soft">
<div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Duplicates Skipped</div>
<div class="mt-1 text-2xl font-semibold text-amber-700">{{ skipped }}</div>
</div>
<div class="rounded-2xl bg-white p-4 shadow-soft">
<div class="text-xs font-semibold uppercase tracking-wide text-slate-500">Invalid / Missing</div>
<div class="mt-1 text-2xl font-semibold text-rose-700">{{ invalid }}</div>
</div>
</div>
{% endif %}
<form method="post" action="/registrations/bulk" id="bulk-registration-form" class="space-y-5">
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
<section class="rounded-2xl bg-white p-5 shadow-soft">
<h2 class="text-base font-semibold text-slate-900">Common Registration Settings</h2>
<div class="mt-4 grid gap-4 md:grid-cols-2 xl:grid-cols-3">
<div>
<label class="mb-1 block text-sm font-medium text-slate-700">Registration Type *</label>
<select name="registration_type_id" required
class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm">
<option value="">Select registration type</option>
{% for row in types %}
<option value="{{ row.id }}">{{ row.name }} ({{ row.code }})</option>
{% endfor %}
</select>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-slate-700">Default Scope</label>
<select name="default_scope"
class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm">
<option value="client">Client / Entity</option>
<option value="primary_business">Primary Business Unit</option>
<option value="primary_branch">Primary Client Branch</option>
</select>
<p class="mt-1 text-xs text-slate-500">
Per-client Business Unit and Branch selections in the table override this default.
</p>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-slate-700">Lifecycle Rule</label>
<select name="lifecycle_rule_id"
class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm">
<option value="">Automatic rule</option>
{% for rule, registration_type in rules %}
<option value="{{ rule.id }}">
{{ registration_type.name }} — {{ rule.cycle_type|replace('_', ' ')|title }}
</option>
{% endfor %}
</select>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-slate-700">Issue Date</label>
<input type="date" name="issue_date"
class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm">
</div>
<div>
<label class="mb-1 block text-sm font-medium text-slate-700">Effective From</label>
<input type="date" name="effective_from"
class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm">
</div>
<div>
<label class="mb-1 block text-sm font-medium text-slate-700">Valid Until</label>
<input type="date" name="valid_until"
class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm">
</div>
<div>
<label class="mb-1 block text-sm font-medium text-slate-700">Next Update / Confirmation</label>
<input type="date" name="next_action_date"
class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm">
</div>
<div>
<label class="mb-1 block text-sm font-medium text-slate-700">Jurisdiction</label>
<input name="jurisdiction"
class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm">
</div>
<div>
<label class="mb-1 block text-sm font-medium text-slate-700">State Code</label>
<input name="state_code" maxlength="10"
class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm">
</div>
<div>
<label class="mb-1 block text-sm font-medium text-slate-700">Responsible Party</label>
<select name="responsible_party"
class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm">
<option value="firm">Firm</option>
<option value="client">Client</option>
<option value="consultant">Consultant</option>
</select>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-slate-700">Assigned Consultant</label>
<select name="assigned_consultant_id"
class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm">
<option value="">No consultant</option>
{% for row in consultants %}
<option value="{{ row.id }}">
{{ row.display_name or row.name or ('Consultant #' ~ row.id) }}
</option>
{% endfor %}
</select>
</div>
<div class="flex items-end">
<label class="inline-flex items-center gap-2 pb-2 text-sm text-slate-700">
<input type="checkbox" name="auto_create_task" checked
class="rounded border-slate-300">
Auto-create renewal work
</label>
</div>
<div class="md:col-span-2 xl:col-span-3">
<label class="mb-1 block text-sm font-medium text-slate-700">Common Notes</label>
<textarea name="notes" rows="2"
class="w-full rounded-xl border border-slate-300 px-3 py-2 text-sm"></textarea>
</div>
</div>
</section>
<section class="rounded-2xl bg-white shadow-soft">
<div class="flex flex-wrap items-end justify-between gap-3 border-b border-slate-200 p-4">
<div class="flex flex-wrap items-end gap-3">
<div>
<label class="mb-1 block text-xs font-semibold uppercase tracking-wide text-slate-500">
Search Clients
</label>
<input type="search" id="client-search"
placeholder="Code, client name, PAN, GSTIN or trade name"
class="w-96 max-w-full rounded-xl border border-slate-300 px-3 py-2 text-sm">
</div>
<button type="button" id="clear-search"
class="rounded-xl border border-slate-300 px-4 py-2 text-sm text-slate-700">
Clear
</button>
</div>
<div class="text-sm text-slate-600">
<span id="selected-count" class="font-semibold text-slate-900">0</span> clients selected
</div>
</div>
<div class="max-h-[36rem] overflow-auto">
<table class="min-w-[1500px] divide-y divide-slate-200">
<thead class="sticky top-0 z-10 bg-slate-50">
<tr>
<th class="w-12 px-3 py-3 text-left">
<input type="checkbox" id="select-all-visible" class="rounded border-slate-300">
</th>
<th class="px-3 py-3 text-left text-xs font-semibold uppercase text-slate-500">Client</th>
<th class="px-3 py-3 text-left text-xs font-semibold uppercase text-slate-500">PAN / Existing GSTIN</th>
<th class="px-3 py-3 text-left text-xs font-semibold uppercase text-slate-500">Registration Number *</th>
<th class="px-3 py-3 text-left text-xs font-semibold uppercase text-slate-500">Business Unit</th>
<th class="px-3 py-3 text-left text-xs font-semibold uppercase text-slate-500">Client Branch</th>
<th class="px-3 py-3 text-left text-xs font-semibold uppercase text-slate-500">Legal Name</th>
<th class="px-3 py-3 text-left text-xs font-semibold uppercase text-slate-500">Trade / Unit Name</th>
<th class="px-3 py-3 text-left text-xs font-semibold uppercase text-slate-500">State</th>
</tr>
</thead>
<tbody class="divide-y divide-slate-100">
{% for client in clients %}
{% set client_businesses = businesses_by_client.get(client.id, []) %}
{% set client_branches = branches_by_client.get(client.id, []) %}
<tr class="client-row align-top"
data-search="{{ ((client.client_code or '') ~ ' ' ~ (client.client_name or '') ~ ' ' ~ (client.pan or '') ~ ' ' ~ (client.gstin or '') ~ ' ' ~ (client.trade_name or ''))|lower|e }}">
<td class="px-3 py-3">
<input type="checkbox" name="client_ids" value="{{ client.id }}"
class="client-checkbox rounded border-slate-300">
</td>
<td class="px-3 py-3 text-sm">
<div class="font-medium text-slate-900">{{ client.client_name }}</div>
<div class="text-xs text-slate-500">{{ client.client_code or '' }}</div>
</td>
<td class="px-3 py-3 text-xs text-slate-600">
<div>PAN: {{ client.pan or '-' }}</div>
<div>GSTIN: {{ client.gstin or '-' }}</div>
</td>
<td class="px-3 py-3">
<input name="registration_number_{{ client.id }}"
placeholder="Unique number"
class="w-48 rounded-lg border border-slate-300 px-2 py-1.5 text-sm uppercase">
</td>
<td class="px-3 py-3">
<select name="business_unit_id_{{ client.id }}"
class="business-select w-52 rounded-lg border border-slate-300 px-2 py-1.5 text-sm"
data-client="{{ client.id }}">
<option value="">Use default / Client level</option>
{% for row in client_businesses %}
<option value="{{ row.id }}">{{ row.business_name }}</option>
{% endfor %}
</select>
</td>
<td class="px-3 py-3">
<select name="client_branch_id_{{ client.id }}"
class="branch-select w-52 rounded-lg border border-slate-300 px-2 py-1.5 text-sm"
data-client="{{ client.id }}">
<option value="">Use default / No branch</option>
{% for row in client_branches %}
<option value="{{ row.id }}" data-business="{{ row.business_unit_id }}">
{{ row.branch_name }}
</option>
{% endfor %}
</select>
</td>
<td class="px-3 py-3">
<input name="legal_name_{{ client.id }}"
value="{{ client.client_name or '' }}"
class="w-52 rounded-lg border border-slate-300 px-2 py-1.5 text-sm">
</td>
<td class="px-3 py-3">
<input name="trade_name_{{ client.id }}"
value="{{ client.trade_name or '' }}"
class="w-52 rounded-lg border border-slate-300 px-2 py-1.5 text-sm">
</td>
<td class="px-3 py-3">
<input name="state_{{ client.id }}"
value="{{ client.state or '' }}"
class="w-40 rounded-lg border border-slate-300 px-2 py-1.5 text-sm">
</td>
</tr>
{% else %}
<tr>
<td colspan="9" class="px-4 py-8 text-center text-sm text-slate-500">
No active clients are available in the current Firm Branch.
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</section>
<div class="flex flex-wrap items-center justify-between gap-3 rounded-2xl bg-white p-4 shadow-soft">
<p class="text-sm text-slate-600">
Rows without a registration number are treated as invalid. Existing registration numbers are skipped without changing their records.
</p>
<div class="flex gap-3">
<a href="/registrations"
class="rounded-xl border border-slate-300 px-4 py-2 text-sm text-slate-700">
Cancel
</a>
<button type="submit"
class="rounded-xl bg-brand-600 px-4 py-2 text-sm font-medium text-white hover:bg-brand-700">
Add Registrations
</button>
</div>
</div>
</form>
</div>
<script>
(function () {
const form = document.getElementById('bulk-registration-form');
const search = document.getElementById('client-search');
const clear = document.getElementById('clear-search');
const rows = Array.from(document.querySelectorAll('.client-row'));
const boxes = Array.from(document.querySelectorAll('.client-checkbox'));
const selectAll = document.getElementById('select-all-visible');
const count = document.getElementById('selected-count');
function visibleRows() {
return rows.filter(row => !row.classList.contains('hidden'));
}
function refreshCount() {
count.textContent = String(boxes.filter(box => box.checked).length);
const visibleBoxes = visibleRows().map(row => row.querySelector('.client-checkbox'));
const checkedVisible = visibleBoxes.filter(box => box.checked).length;
selectAll.checked = visibleBoxes.length > 0 && checkedVisible === visibleBoxes.length;
selectAll.indeterminate = checkedVisible > 0 && checkedVisible < visibleBoxes.length;
}
function filterRows() {
const term = search.value.trim().toLowerCase();
rows.forEach(row => {
row.classList.toggle('hidden', term && !row.dataset.search.includes(term));
});
refreshCount();
}
search.addEventListener('input', filterRows);
clear.addEventListener('click', function () {
search.value = '';
filterRows();
search.focus();
});
boxes.forEach(box => box.addEventListener('change', refreshCount));
selectAll.addEventListener('change', function () {
visibleRows().forEach(row => {
row.querySelector('.client-checkbox').checked = selectAll.checked;
});
refreshCount();
});
document.querySelectorAll('.branch-select').forEach(branchSelect => {
branchSelect.addEventListener('change', function () {
const selected = branchSelect.options[branchSelect.selectedIndex];
const businessId = selected ? selected.dataset.business : '';
if (!businessId) return;
const businessSelect = document.querySelector(
`.business-select[data-client="${branchSelect.dataset.client}"]`
);
if (businessSelect) businessSelect.value = businessId;
});
});
form.addEventListener('submit', function (event) {
const selected = boxes.filter(box => box.checked);
if (!selected.length) {
event.preventDefault();
alert('Select at least one client.');
return;
}
const missing = selected.filter(box => {
const number = document.querySelector(
`[name="registration_number_${box.value}"]`
);
return !number || !number.value.trim();
});
if (missing.length) {
event.preventDefault();
alert('Enter the registration number for every selected client.');
return;
}
if (!confirm(`Add registrations for ${selected.length} selected client${selected.length === 1 ? '' : 's'}?`)) {
event.preventDefault();
}
});
refreshCount();
})();
</script>
{% endblock %}
+315 -1
View File
@@ -3,11 +3,12 @@ from datetime import date
from fastapi import APIRouter, Form, Request from fastapi import APIRouter, Form, Request
from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.responses import HTMLResponse, RedirectResponse
from sqlalchemy import or_, select from sqlalchemy import or_, select
from sqlalchemy.exc import IntegrityError
from app.core.db.common import CommonSessionLocal from app.core.db.common import CommonSessionLocal
from app.core.security.csrf import get_or_create_csrf_token, validate_csrf from app.core.security.csrf import get_or_create_csrf_token, validate_csrf
from app.core.security.session_auth import get_current_user from app.core.security.session_auth import get_current_user
from app.core.templating import templates from app.core.templating import templates
from app.modules.clients.models import Client from app.modules.clients.models import Client, ClientBusinessUnit, ClientBranch
from app.modules.consultants.models import ConsultantProfile from app.modules.consultants.models import ConsultantProfile
from app.modules.core.rbac.deps import get_user_permissions, get_user_roles from app.modules.core.rbac.deps import get_user_permissions, get_user_roles
from app.modules.core.rbac.permission_guard import require_permission from app.modules.core.rbac.permission_guard import require_permission
@@ -81,6 +82,319 @@ async def create_doc_template(request:Request,registration_type_id:int=Form(...)
user=_user(request,db);validate_csrf(request,csrf_token);require_permission(db,user,"clients.edit") user=_user(request,db);validate_csrf(request,csrf_token);require_permission(db,user,"clients.edit")
db.add(RegistrationDocumentTemplate(tenant_id=_tenant(request,user),registration_type_id=registration_type_id,action_type=action_type,document_name=document_name.strip(),description=description or None,mandatory=mandatory));db.commit();return RedirectResponse('/registrations/masters',303) db.add(RegistrationDocumentTemplate(tenant_id=_tenant(request,user),registration_type_id=registration_type_id,action_type=action_type,document_name=document_name.strip(),description=description or None,mandatory=mandatory));db.commit();return RedirectResponse('/registrations/masters',303)
@router.get("/bulk", response_class=HTMLResponse)
def bulk_registration_page(
request: Request,
created: int = 0,
skipped: int = 0,
invalid: int = 0,
):
with CommonSessionLocal() as db:
user = _user(request, db)
if not user:
return RedirectResponse("/login", 303)
require_permission(db, user, "clients.edit")
seed_registration_types(db)
tenant_id = _tenant(request, user)
branch_id = _branch(request, user)
clients_query = select(Client).where(
Client.tenant_id == tenant_id,
Client.is_active.is_(True),
)
if branch_id:
clients_query = clients_query.where(Client.branch_id == branch_id)
clients = db.execute(
clients_query.order_by(Client.client_name, Client.client_code)
).scalars().all()
client_ids = [row.id for row in clients]
business_units = []
client_branches = []
if client_ids:
business_units = db.execute(
select(ClientBusinessUnit).where(
ClientBusinessUnit.tenant_id == tenant_id,
ClientBusinessUnit.client_id.in_(client_ids),
ClientBusinessUnit.is_active.is_(True),
).order_by(
ClientBusinessUnit.client_id,
ClientBusinessUnit.is_primary.desc(),
ClientBusinessUnit.business_name,
)
).scalars().all()
client_branches = db.execute(
select(ClientBranch).where(
ClientBranch.tenant_id == tenant_id,
ClientBranch.client_id.in_(client_ids),
ClientBranch.is_active.is_(True),
).order_by(
ClientBranch.client_id,
ClientBranch.is_primary.desc(),
ClientBranch.branch_name,
)
).scalars().all()
businesses_by_client: dict[int, list[ClientBusinessUnit]] = {}
for row in business_units:
businesses_by_client.setdefault(row.client_id, []).append(row)
branches_by_client: dict[int, list[ClientBranch]] = {}
for row in client_branches:
branches_by_client.setdefault(row.client_id, []).append(row)
types = db.execute(
select(RegistrationType).where(
RegistrationType.is_active.is_(True)
).order_by(RegistrationType.sort_order, RegistrationType.name)
).scalars().all()
rules = db.execute(
select(RegistrationLifecycleRule, RegistrationType).join(
RegistrationType,
RegistrationType.id == RegistrationLifecycleRule.registration_type_id,
).where(
or_(
RegistrationLifecycleRule.tenant_id.is_(None),
RegistrationLifecycleRule.tenant_id == tenant_id,
),
RegistrationLifecycleRule.is_active.is_(True),
).order_by(RegistrationType.name, RegistrationLifecycleRule.id)
).all()
consultants = db.execute(
select(ConsultantProfile).where(
ConsultantProfile.tenant_id == tenant_id,
ConsultantProfile.is_active.is_(True),
)
).scalars().all()
return templates.TemplateResponse(
"modules/registrations/templates/registrations/bulk.html",
_ctx(
request,
user,
db,
title="Bulk Registration Addition",
clients=clients,
businesses_by_client=businesses_by_client,
branches_by_client=branches_by_client,
types=types,
rules=rules,
consultants=consultants,
created=created,
skipped=skipped,
invalid=invalid,
),
)
@router.post("/bulk")
async def bulk_registration_submit(
request: Request,
client_ids: list[int] = Form([]),
registration_type_id: int = Form(...),
default_scope: str = Form("client"),
jurisdiction: str = Form(""),
state_code: str = Form(""),
issue_date: str = Form(""),
effective_from: str = Form(""),
valid_until: str = Form(""),
next_action_date: str = Form(""),
lifecycle_rule_id: str = Form(""),
responsible_party: str = Form("firm"),
assigned_consultant_id: str = Form(""),
auto_create_task: bool = Form(False),
notes: str = Form(""),
csrf_token: str = Form(...),
):
validate_csrf(request, csrf_token)
with CommonSessionLocal() as db:
user = _user(request, db)
if not user:
return RedirectResponse("/login", 303)
require_permission(db, user, "clients.edit")
tenant_id = _tenant(request, user)
branch_id = _branch(request, user)
registration_type = db.get(RegistrationType, registration_type_id)
if not registration_type or not registration_type.is_active:
return RedirectResponse("/registrations/bulk?invalid=1", 303)
selected_ids = list(dict.fromkeys(int(value) for value in client_ids))
if not selected_ids:
return RedirectResponse("/registrations/bulk?invalid=1", 303)
clients_query = select(Client).where(
Client.tenant_id == tenant_id,
Client.id.in_(selected_ids),
Client.is_active.is_(True),
)
if branch_id:
clients_query = clients_query.where(Client.branch_id == branch_id)
permitted_clients = db.execute(clients_query).scalars().all()
clients_by_id = {row.id: row for row in permitted_clients}
form = await request.form()
created = 0
skipped = 0
invalid = 0
for client_id in selected_ids:
client = clients_by_id.get(client_id)
if not client:
invalid += 1
continue
registration_number = (
str(form.get(f"registration_number_{client_id}", ""))
.strip()
.upper()
)
if not registration_number:
invalid += 1
continue
business_unit_id = None
client_branch_id = None
raw_business_id = str(
form.get(f"business_unit_id_{client_id}", "")
).strip()
raw_branch_id = str(
form.get(f"client_branch_id_{client_id}", "")
).strip()
if raw_business_id:
business = db.get(ClientBusinessUnit, int(raw_business_id))
if (
not business
or business.tenant_id != tenant_id
or business.client_id != client.id
or not business.is_active
):
invalid += 1
continue
business_unit_id = business.id
if raw_branch_id:
client_branch = db.get(ClientBranch, int(raw_branch_id))
if (
not client_branch
or client_branch.tenant_id != tenant_id
or client_branch.client_id != client.id
or not client_branch.is_active
):
invalid += 1
continue
client_branch_id = client_branch.id
business_unit_id = client_branch.business_unit_id
if default_scope == "primary_business" and not business_unit_id:
business = db.execute(
select(ClientBusinessUnit).where(
ClientBusinessUnit.tenant_id == tenant_id,
ClientBusinessUnit.client_id == client.id,
ClientBusinessUnit.is_active.is_(True),
).order_by(
ClientBusinessUnit.is_primary.desc(),
ClientBusinessUnit.id,
)
).scalars().first()
if not business:
invalid += 1
continue
business_unit_id = business.id
if default_scope == "primary_branch" and not client_branch_id:
client_branch = db.execute(
select(ClientBranch).where(
ClientBranch.tenant_id == tenant_id,
ClientBranch.client_id == client.id,
ClientBranch.is_active.is_(True),
).order_by(
ClientBranch.is_primary.desc(),
ClientBranch.id,
)
).scalars().first()
if not client_branch:
invalid += 1
continue
client_branch_id = client_branch.id
business_unit_id = client_branch.business_unit_id
existing = db.execute(
select(ClientRegistration).where(
ClientRegistration.tenant_id == tenant_id,
ClientRegistration.registration_type_id == registration_type.id,
ClientRegistration.registration_number == registration_number,
)
).scalar_one_or_none()
if existing:
skipped += 1
continue
legal_name = str(form.get(f"legal_name_{client_id}", "")).strip()
trade_name = str(form.get(f"trade_name_{client_id}", "")).strip()
row_state = str(form.get(f"state_{client_id}", "")).strip()
db.add(
ClientRegistration(
tenant_id=tenant_id,
branch_id=client.branch_id,
client_id=client.id,
business_unit_id=business_unit_id,
client_branch_id=client_branch_id,
registration_type_id=registration_type.id,
registration_number=registration_number,
legal_name=legal_name or client.client_name,
trade_name=trade_name or client.trade_name or None,
state=row_state or client.state or None,
jurisdiction=jurisdiction.strip() or row_state or client.state or None,
state_code=state_code.strip().upper() or None,
issue_date=_d(issue_date),
effective_from=_d(effective_from),
valid_until=_d(valid_until),
next_action_date=_d(next_action_date),
lifecycle_rule_id=int(lifecycle_rule_id) if lifecycle_rule_id else None,
responsible_party=responsible_party,
assigned_user_id=client.partner_id,
assigned_consultant_id=(
int(assigned_consultant_id)
if assigned_consultant_id
else None
),
auto_create_task=auto_create_task,
status="active",
notes=notes.strip() or None,
created_by_user_id=user.id,
)
)
try:
db.flush()
except IntegrityError:
db.rollback()
skipped += 1
continue
created += 1
db.commit()
run_registration_lifecycle_once(db)
return RedirectResponse(
f"/registrations/bulk?created={created}&skipped={skipped}&invalid={invalid}",
status_code=303,
)
@router.get("/clients/{client_id}",response_class=HTMLResponse) @router.get("/clients/{client_id}",response_class=HTMLResponse)
def client_register(request:Request,client_id:int): def client_register(request:Request,client_id:int):
with CommonSessionLocal() as db: with CommonSessionLocal() as db:
@@ -70,7 +70,12 @@
{ {
'label': 'Registrations & Renewals', 'label': 'Registrations & Renewals',
'url': '/registrations', 'url': '/registrations',
'active': _partner_path == '/registrations' or (_partner_path.startswith('/registrations/') and not _partner_path.startswith('/registrations/masters')) 'active': _partner_path == '/registrations' or (_partner_path.startswith('/registrations/') and not _partner_path.startswith('/registrations/masters') and not _partner_path.startswith('/registrations/bulk'))
},
{
'label': 'Bulk Registration Addition',
'url': '/registrations/bulk',
'active': _partner_path.startswith('/registrations/bulk')
}, },
{ {
'label': 'Registration Masters', 'label': 'Registration Masters',