343 lines
12 KiB
Python
343 lines
12 KiB
Python
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, Form, Request
|
|
from fastapi.responses import RedirectResponse
|
|
|
|
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.services.models import ClientServiceSubscription
|
|
from app.modules.services.client_services import (
|
|
SUBSCRIPTION_STATUSES,
|
|
get_enabled_firm_service,
|
|
get_existing_subscription,
|
|
get_subscription,
|
|
list_assignable_users,
|
|
list_clients_for_assignment,
|
|
list_enabled_services_for_assignment,
|
|
list_subscription_payload,
|
|
parse_date,
|
|
)
|
|
from app.modules.core.rbac.deps import get_user_permissions, get_user_roles
|
|
from app.modules.core.rbac.permission_guard import require_permission
|
|
|
|
router = APIRouter(prefix="/services/client-services", tags=["services-client-services-ui"])
|
|
|
|
|
|
def _base_ctx(request: Request, user, db, **ctx):
|
|
base = {
|
|
"request": request,
|
|
"current_user": user,
|
|
"current_user_roles": get_user_roles(db, user.id),
|
|
"current_user_permissions": get_user_permissions(db, user.id),
|
|
"csrf_token": get_or_create_csrf_token(request),
|
|
"subscription_statuses": SUBSCRIPTION_STATUSES,
|
|
}
|
|
base.update(ctx)
|
|
return base
|
|
|
|
|
|
def _render(request: Request, template: str, db, user, **ctx):
|
|
return templates.TemplateResponse(template, _base_ctx(request, user, db, **ctx))
|
|
|
|
|
|
def _redirect_denied():
|
|
from app.core.http_responses import ui_access_denied
|
|
return ui_access_denied()
|
|
|
|
|
|
def _has_perm(db, user, code: str) -> bool:
|
|
try:
|
|
require_permission(db, user, code)
|
|
return True
|
|
except Exception:
|
|
return False
|
|
|
|
|
|
def _active_tenant_id(request: Request, user) -> int:
|
|
return int(
|
|
request.session.get("active_tenant_id")
|
|
or request.session.get("selected_tenant_id")
|
|
or request.session.get("tenant_id")
|
|
or user.tenant_id
|
|
)
|
|
|
|
|
|
def _active_branch_id(request: Request, user, db) -> int | None:
|
|
value = request.session.get("active_branch_id")
|
|
if value in (None, "", 0, "0"):
|
|
if _has_perm(db, user, "clients.cross_branch"):
|
|
return None
|
|
return int(getattr(user, "branch_id", 0) or 0) or None
|
|
return int(value)
|
|
|
|
|
|
def _locked_partner_id(db, user) -> int | None:
|
|
return int(user.id) if _has_perm(db, user, "clients.view.own_only") else None
|
|
|
|
|
|
def _can_manage_client_services(db, user) -> bool:
|
|
return _has_perm(db, user, "clients.edit")
|
|
|
|
|
|
@router.get("")
|
|
def subscription_list(request: Request, q: str = "", include_inactive: bool = True):
|
|
db = CommonSessionLocal()
|
|
try:
|
|
user = get_current_user(request, db=db)
|
|
if not user:
|
|
return RedirectResponse(url="/login", status_code=303)
|
|
try:
|
|
require_permission(db, user, "clients.view")
|
|
except Exception:
|
|
return _redirect_denied()
|
|
|
|
tenant_id = _active_tenant_id(request, user)
|
|
branch_id = _active_branch_id(request, user, db)
|
|
rows = list_subscription_payload(
|
|
db,
|
|
tenant_id=tenant_id,
|
|
branch_id=branch_id,
|
|
q=q,
|
|
include_inactive=include_inactive,
|
|
)
|
|
return _render(
|
|
request,
|
|
"modules/services/templates/services/client_services/list.html",
|
|
db,
|
|
user,
|
|
title="Client Service Subscriptions",
|
|
rows=rows,
|
|
q=q,
|
|
include_inactive=include_inactive,
|
|
can_manage=_can_manage_client_services(db, user),
|
|
)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@router.get("/new")
|
|
def subscription_create_page(request: Request, client_id: int | None = None):
|
|
db = CommonSessionLocal()
|
|
try:
|
|
user = get_current_user(request, db=db)
|
|
if not user:
|
|
return RedirectResponse(url="/login", status_code=303)
|
|
try:
|
|
require_permission(db, user, "clients.edit")
|
|
except Exception:
|
|
return _redirect_denied()
|
|
|
|
tenant_id = _active_tenant_id(request, user)
|
|
branch_id = _active_branch_id(request, user, db)
|
|
clients = list_clients_for_assignment(
|
|
db,
|
|
tenant_id=tenant_id,
|
|
branch_id=branch_id,
|
|
partner_id=_locked_partner_id(db, user),
|
|
)
|
|
enabled_services = list_enabled_services_for_assignment(db, tenant_id=tenant_id)
|
|
assignable_users = list_assignable_users(db, tenant_id=tenant_id, branch_id=branch_id)
|
|
|
|
return _render(
|
|
request,
|
|
"modules/services/templates/services/client_services/form.html",
|
|
db,
|
|
user,
|
|
title="Assign Service to Client",
|
|
mode="create",
|
|
subscription=None,
|
|
clients=clients,
|
|
enabled_services=enabled_services,
|
|
assignable_users=assignable_users,
|
|
selected_client_id=client_id,
|
|
)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@router.post("/new")
|
|
def subscription_create_submit(
|
|
request: Request,
|
|
client_id: int = Form(...),
|
|
service_catalogue_id: int = Form(...),
|
|
assigned_partner_user_id: str = Form(""),
|
|
assigned_manager_user_id: str = Form(""),
|
|
assigned_staff_user_id: str = Form(""),
|
|
start_date: str = Form(""),
|
|
end_date: str = Form(""),
|
|
status: str = Form("active"),
|
|
remarks: str = Form(""),
|
|
is_active: str | None = Form(None),
|
|
csrf_token: str = Form(...),
|
|
):
|
|
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)
|
|
try:
|
|
require_permission(db, user, "clients.edit")
|
|
except Exception:
|
|
return _redirect_denied()
|
|
|
|
tenant_id = _active_tenant_id(request, user)
|
|
firm_selection = get_enabled_firm_service(db, tenant_id=tenant_id, service_catalogue_id=service_catalogue_id)
|
|
if not firm_selection:
|
|
return RedirectResponse(url="/services/client-services/new", status_code=303)
|
|
|
|
existing = get_existing_subscription(
|
|
db,
|
|
tenant_id=tenant_id,
|
|
client_id=client_id,
|
|
service_catalogue_id=service_catalogue_id,
|
|
)
|
|
if existing:
|
|
row = existing
|
|
else:
|
|
row = ClientServiceSubscription(
|
|
tenant_id=tenant_id,
|
|
client_id=client_id,
|
|
service_catalogue_id=service_catalogue_id,
|
|
created_by_user_id=user.id,
|
|
)
|
|
db.add(row)
|
|
|
|
row.branch_id = firm_selection.default_branch_id or getattr(user, "branch_id", None)
|
|
row.firm_service_selection_id = firm_selection.id
|
|
row.assigned_partner_user_id = int(assigned_partner_user_id) if assigned_partner_user_id.strip() else None
|
|
row.assigned_manager_user_id = int(assigned_manager_user_id) if assigned_manager_user_id.strip() else None
|
|
row.assigned_staff_user_id = int(assigned_staff_user_id) if assigned_staff_user_id.strip() else None
|
|
row.start_date = parse_date(start_date)
|
|
row.end_date = parse_date(end_date)
|
|
row.status = status or "active"
|
|
row.remarks = remarks.strip() or None
|
|
row.is_active = is_active is not None
|
|
row.updated_by_user_id = user.id
|
|
|
|
db.commit()
|
|
db.refresh(row)
|
|
return RedirectResponse(url=f"/services/client-services/{row.id}", status_code=303)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@router.get("/{subscription_id}")
|
|
def subscription_detail(request: Request, subscription_id: int):
|
|
db = CommonSessionLocal()
|
|
try:
|
|
user = get_current_user(request, db=db)
|
|
if not user:
|
|
return RedirectResponse(url="/login", status_code=303)
|
|
try:
|
|
require_permission(db, user, "clients.view")
|
|
except Exception:
|
|
return _redirect_denied()
|
|
|
|
tenant_id = _active_tenant_id(request, user)
|
|
row = get_subscription(db, subscription_id=subscription_id, tenant_id=tenant_id)
|
|
if not row:
|
|
return RedirectResponse(url="/services/client-services", status_code=303)
|
|
|
|
return _render(
|
|
request,
|
|
"modules/services/templates/services/client_services/detail.html",
|
|
db,
|
|
user,
|
|
title="Client Service Subscription",
|
|
row=row,
|
|
can_manage=_can_manage_client_services(db, user),
|
|
)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@router.get("/{subscription_id}/edit")
|
|
def subscription_edit_page(request: Request, subscription_id: int):
|
|
db = CommonSessionLocal()
|
|
try:
|
|
user = get_current_user(request, db=db)
|
|
if not user:
|
|
return RedirectResponse(url="/login", status_code=303)
|
|
try:
|
|
require_permission(db, user, "clients.edit")
|
|
except Exception:
|
|
return _redirect_denied()
|
|
|
|
tenant_id = _active_tenant_id(request, user)
|
|
branch_id = _active_branch_id(request, user, db)
|
|
row = get_subscription(db, subscription_id=subscription_id, tenant_id=tenant_id)
|
|
if not row:
|
|
return RedirectResponse(url="/services/client-services", status_code=303)
|
|
|
|
clients = list_clients_for_assignment(
|
|
db,
|
|
tenant_id=tenant_id,
|
|
branch_id=branch_id,
|
|
partner_id=_locked_partner_id(db, user),
|
|
)
|
|
enabled_services = list_enabled_services_for_assignment(db, tenant_id=tenant_id)
|
|
assignable_users = list_assignable_users(db, tenant_id=tenant_id, branch_id=branch_id)
|
|
return _render(
|
|
request,
|
|
"modules/services/templates/services/client_services/form.html",
|
|
db,
|
|
user,
|
|
title="Edit Client Service Subscription",
|
|
mode="edit",
|
|
subscription=row,
|
|
clients=clients,
|
|
enabled_services=enabled_services,
|
|
assignable_users=assignable_users,
|
|
selected_client_id=row.client_id,
|
|
)
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
@router.post("/{subscription_id}/edit")
|
|
def subscription_edit_submit(
|
|
request: Request,
|
|
subscription_id: int,
|
|
assigned_partner_user_id: str = Form(""),
|
|
assigned_manager_user_id: str = Form(""),
|
|
assigned_staff_user_id: str = Form(""),
|
|
start_date: str = Form(""),
|
|
end_date: str = Form(""),
|
|
status: str = Form("active"),
|
|
remarks: str = Form(""),
|
|
is_active: str | None = Form(None),
|
|
csrf_token: str = Form(...),
|
|
):
|
|
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)
|
|
try:
|
|
require_permission(db, user, "clients.edit")
|
|
except Exception:
|
|
return _redirect_denied()
|
|
|
|
tenant_id = _active_tenant_id(request, user)
|
|
row = get_subscription(db, subscription_id=subscription_id, tenant_id=tenant_id)
|
|
if not row:
|
|
return RedirectResponse(url="/services/client-services", status_code=303)
|
|
|
|
row.assigned_partner_user_id = int(assigned_partner_user_id) if assigned_partner_user_id.strip() else None
|
|
row.assigned_manager_user_id = int(assigned_manager_user_id) if assigned_manager_user_id.strip() else None
|
|
row.assigned_staff_user_id = int(assigned_staff_user_id) if assigned_staff_user_id.strip() else None
|
|
row.start_date = parse_date(start_date)
|
|
row.end_date = parse_date(end_date)
|
|
row.status = status or "active"
|
|
row.remarks = remarks.strip() or None
|
|
row.is_active = is_active is not None
|
|
row.updated_by_user_id = user.id
|
|
db.commit()
|
|
return RedirectResponse(url=f"/services/client-services/{row.id}", status_code=303)
|
|
finally:
|
|
db.close()
|