Separate client service subscriptions from engagement instances
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Request
|
||||
from fastapi.responses import RedirectResponse
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
from app.core.db.common import CommonSessionLocal
|
||||
from app.core.security.csrf import get_or_create_csrf_token
|
||||
from app.core.security.session_auth import get_current_user
|
||||
from app.core.templating import templates
|
||||
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.services.models import ClientServicePlan, ClientServiceSubscription
|
||||
|
||||
router = APIRouter(prefix="/services/subscriptions", tags=["client-service-subscriptions-ui"])
|
||||
|
||||
def _tenant_id(request, user):
|
||||
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 _branch_id(request, user):
|
||||
value = request.session.get("active_branch_id")
|
||||
if value in (None, "", 0, "0"):
|
||||
return int(getattr(user, "branch_id", 0) or 0) or None
|
||||
return int(value)
|
||||
|
||||
def _ctx(request, db, user, **extra):
|
||||
data={"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)}
|
||||
data.update(extra); return data
|
||||
|
||||
@router.get("")
|
||||
def subscription_master_list(request: Request, q: str = "", include_inactive: bool = False):
|
||||
db=CommonSessionLocal()
|
||||
try:
|
||||
user=get_current_user(request, db=db)
|
||||
if not user: return RedirectResponse("/login",303)
|
||||
try: require_permission(db,user,"clients.view")
|
||||
except Exception:
|
||||
from app.core.http_responses import ui_access_denied
|
||||
return ui_access_denied()
|
||||
tenant_id=_tenant_id(request,user); branch_id=_branch_id(request,user)
|
||||
counts=(select(ClientServiceSubscription.service_plan_id, func.count(ClientServiceSubscription.id).label("engagement_count"), func.max(ClientServiceSubscription.current_due_date).label("latest_due_date")).where(ClientServiceSubscription.service_plan_id.is_not(None)).group_by(ClientServiceSubscription.service_plan_id).subquery())
|
||||
stmt=(select(ClientServicePlan, counts.c.engagement_count, counts.c.latest_due_date).options(selectinload(ClientServicePlan.client),selectinload(ClientServicePlan.catalogue),selectinload(ClientServicePlan.default_partner),selectinload(ClientServicePlan.default_performing_partner),selectinload(ClientServicePlan.default_manager),selectinload(ClientServicePlan.default_staff),selectinload(ClientServicePlan.default_review_partner)).outerjoin(counts,counts.c.service_plan_id==ClientServicePlan.id).where(ClientServicePlan.tenant_id==tenant_id))
|
||||
if branch_id: stmt=stmt.where(ClientServicePlan.branch_id==branch_id)
|
||||
if not include_inactive: stmt=stmt.where(ClientServicePlan.is_active.is_(True))
|
||||
if q.strip():
|
||||
from app.modules.clients.models import Client
|
||||
from app.modules.services.models import ServiceCatalogue
|
||||
term=f"%{q.strip()}%"
|
||||
stmt=stmt.join(Client,Client.id==ClientServicePlan.client_id).join(ServiceCatalogue,ServiceCatalogue.id==ClientServicePlan.service_catalogue_id).where((Client.client_name.ilike(term)) | (Client.client_code.ilike(term)) | (ServiceCatalogue.service_name.ilike(term)) | (ServiceCatalogue.service_code.ilike(term)))
|
||||
rows=db.execute(stmt.order_by(ClientServicePlan.is_active.desc(),ClientServicePlan.id.desc())).all()
|
||||
return templates.TemplateResponse("modules/services/templates/services/subscriptions/list.html",_ctx(request,db,user,title="Client Service Subscriptions",rows=rows,q=q,include_inactive=include_inactive))
|
||||
finally: db.close()
|
||||
|
||||
@router.get("/{plan_id}")
|
||||
def subscription_master_detail(request: Request, plan_id: int):
|
||||
db=CommonSessionLocal()
|
||||
try:
|
||||
user=get_current_user(request, db=db)
|
||||
if not user: return RedirectResponse("/login",303)
|
||||
try: require_permission(db,user,"clients.view")
|
||||
except Exception:
|
||||
from app.core.http_responses import ui_access_denied
|
||||
return ui_access_denied()
|
||||
tenant_id=_tenant_id(request,user)
|
||||
plan=db.execute(select(ClientServicePlan).options(selectinload(ClientServicePlan.client),selectinload(ClientServicePlan.catalogue),selectinload(ClientServicePlan.default_partner),selectinload(ClientServicePlan.default_performing_partner),selectinload(ClientServicePlan.default_manager),selectinload(ClientServicePlan.default_staff),selectinload(ClientServicePlan.default_review_partner)).where(ClientServicePlan.id==plan_id,ClientServicePlan.tenant_id==tenant_id)).scalar_one_or_none()
|
||||
if not plan: return RedirectResponse("/services/subscriptions",303)
|
||||
engagements=db.execute(select(ClientServiceSubscription).where(ClientServiceSubscription.service_plan_id==plan.id).order_by(ClientServiceSubscription.financial_year.desc(),ClientServiceSubscription.period_label.asc(),ClientServiceSubscription.id.desc())).scalars().all()
|
||||
return templates.TemplateResponse("modules/services/templates/services/subscriptions/detail.html",_ctx(request,db,user,title="Client Service Subscription",plan=plan,engagements=engagements))
|
||||
finally: db.close()
|
||||
Reference in New Issue
Block a user