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
+315 -1
View File
@@ -3,11 +3,12 @@ from datetime import date
from fastapi import APIRouter, Form, Request
from fastapi.responses import HTMLResponse, RedirectResponse
from sqlalchemy import or_, select
from sqlalchemy.exc import IntegrityError
from app.core.db.common import CommonSessionLocal
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.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.core.rbac.deps import get_user_permissions, get_user_roles
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")
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)
def client_register(request:Request,client_id:int):
with CommonSessionLocal() as db: