from __future__ import annotations from fastapi import APIRouter, Form, Request from fastapi.responses import RedirectResponse from sqlalchemy import select 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, ClientServiceTaskInstance from app.modules.services.due_dates import apply_due_date_rule_to_subscription from app.modules.services.execution import ( aqmm_task_summary_for_subscription, update_engagement_closure_from_sources, save_engagement_closure_confirmations, approve_engagement_closure, reopen_engagement_closure, ) from app.modules.clients.models import Client from app.modules.services.client_services import ( SUBSCRIPTION_STATUSES, assessment_year_from_financial_year, current_financial_year, get_enabled_firm_service, get_existing_subscription, get_subscription, normalize_financial_year, list_assignable_users, list_clients_for_assignment, list_enabled_services_for_assignment, list_subscription_payload, list_review_partners, parse_date, review_partner_required_for_engagement, ensure_engagement_quality_workflow, enforce_quality_gate_on_subscription, request_engagement_quality_declarations, list_engagement_quality_declarations, respond_engagement_quality_declaration, verify_engagement_kyc_from_permanent_documents, get_latest_engagement_kyc_verification, get_current_engagement_letter, mark_engagement_letter_completed, approve_engagement_quality_workflow, ) 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.tenancy.year_control import redirect_if_financial_year_locked, is_row_financial_year_locked router = APIRouter(prefix="/services/engagements", tags=["services-engagements-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 _active_financial_year(request: Request) -> str: return normalize_financial_year( request.session.get("active_financial_year") or getattr(request.state, "year_code", None) ) 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") def _can_lock_engagements(db, user) -> bool: roles = set(get_user_roles(db, user.id)) return bool(roles.intersection({"Firm Admin", "Partner"})) def _user_can_lock_subscription(db, user, row: ClientServiceSubscription) -> bool: roles = set(get_user_roles(db, user.id)) if "Firm Admin" in roles: return True if "Partner" in roles: client = getattr(row, "client", None) return ( getattr(row, "assigned_partner_user_id", None) == user.id or getattr(client, "partner_id", None) == user.id ) return False def _lock_subscription_row(row: ClientServiceSubscription, user) -> bool: if getattr(row, "is_locked", False): return False from datetime import datetime, timezone row.is_locked = True row.status = "completed" if row.status == "active" else row.status row.locked_at_utc = datetime.now(timezone.utc) row.locked_by_user_id = user.id row.updated_by_user_id = user.id return True @router.get("") def subscription_list(request: Request, q: str = "", financial_year: str = "", include_inactive: bool = True, locked: int = 0, skipped: int = 0): 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) selected_financial_year = normalize_financial_year(financial_year or _active_financial_year(request)) rows = list_subscription_payload( db, tenant_id=tenant_id, branch_id=branch_id, financial_year=selected_financial_year, q=q, include_inactive=include_inactive, ) return _render( request, "modules/services/templates/services/engagements/list.html", db, user, title="Engagement Subscriptions", rows=rows, q=q, financial_year=selected_financial_year, include_inactive=include_inactive, locked_count=locked, skipped_count=skipped, can_manage=_can_manage_client_services(db, user), can_lock_engagements=_can_lock_engagements(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) review_partners = list_review_partners(db, tenant_id=tenant_id, branch_id=branch_id) return _render( request, "modules/services/templates/services/engagements/form.html", db, user, title="Assign Service to Client", mode="create", subscription=None, clients=clients, enabled_services=enabled_services, assignable_users=assignable_users, review_partners=review_partners, selected_client_id=client_id, financial_year=_active_financial_year(request), ) 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(""), review_partner_user_id: str = Form(""), financial_year: str = Form(""), start_date: str = Form(""), end_date: str = Form(""), expiry_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) selected_financial_year = normalize_financial_year(financial_year or _active_financial_year(request)) locked_response = redirect_if_financial_year_locked(db, tenant_id=tenant_id, year_code=selected_financial_year, redirect_url=f"/services/engagements?financial_year={selected_financial_year}") if locked_response: return locked_response 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/engagements/new", status_code=303) existing = get_existing_subscription( db, tenant_id=tenant_id, client_id=client_id, service_catalogue_id=service_catalogue_id, financial_year=selected_financial_year, ) client = db.get(Client, client_id) if not client or client.tenant_id != tenant_id: return RedirectResponse(url="/services/engagements/new", status_code=303) if existing: row = existing else: row = ClientServiceSubscription( tenant_id=tenant_id, client_id=client_id, service_catalogue_id=service_catalogue_id, financial_year=selected_financial_year, assessment_year=assessment_year_from_financial_year(selected_financial_year), engagement_type=getattr(firm_selection.catalogue, "engagement_type", "non_assurance") or "non_assurance", created_by_user_id=user.id, ) db.add(row) if getattr(row, "is_locked", False) or is_row_financial_year_locked(db, row): return RedirectResponse(url=f"/services/engagements/{row.id}?year_locked=1", status_code=303) row.financial_year = selected_financial_year row.assessment_year = assessment_year_from_financial_year(selected_financial_year) if not getattr(row, "engagement_type", None): row.engagement_type = getattr(firm_selection.catalogue, "engagement_type", "non_assurance") or "non_assurance" 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 if review_partner_required_for_engagement(db, tenant_id=tenant_id, engagement_type=row.engagement_type): row.review_partner_user_id = int(review_partner_user_id) if review_partner_user_id.strip() else getattr(client, "default_review_partner_user_id", None) else: row.review_partner_user_id = None row.start_date = parse_date(start_date) row.end_date = parse_date(end_date) row.expiry_date = parse_date(expiry_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 apply_due_date_rule_to_subscription(db, row) ensure_engagement_quality_workflow(db, subscription=row, actor_user_id=user.id, create_declarations=False) enforce_quality_gate_on_subscription(row) db.commit() db.refresh(row) return RedirectResponse(url=f"/services/engagements/{row.id}", status_code=303) finally: db.close() @router.post("/bulk-lock") def subscription_bulk_lock( request: Request, subscription_ids: list[int] = Form([]), financial_year: str = Form(""), q: str = Form(""), include_inactive: str | None = Form(None), csrf_token: str = Form(...), ): validate_csrf(request, csrf_token) db = CommonSessionLocal() locked_count = 0 skipped_count = 0 try: user = get_current_user(request, db=db) if not user: return RedirectResponse(url="/login", status_code=303) if not _can_lock_engagements(db, user): return _redirect_denied() tenant_id = _active_tenant_id(request, user) selected_ids = [int(value) for value in subscription_ids if value] for subscription_id in selected_ids: row = get_subscription(db, subscription_id=subscription_id, tenant_id=tenant_id) if not row or not _user_can_lock_subscription(db, user, row): skipped_count += 1 continue if _lock_subscription_row(row, user): locked_count += 1 else: skipped_count += 1 if locked_count: db.commit() params = [] fy = normalize_financial_year(financial_year or _active_financial_year(request)) if fy: params.append(f"financial_year={fy}") if q.strip(): from urllib.parse import quote_plus params.append(f"q={quote_plus(q.strip())}") if include_inactive: params.append("include_inactive=true") params.append(f"locked={locked_count}") params.append(f"skipped={skipped_count}") suffix = "?" + "&".join(params) if params else "" return RedirectResponse(url=f"/services/engagements{suffix}", 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/engagements", status_code=303) active_fy = _active_financial_year(request) if row.financial_year != active_fy: return RedirectResponse(url=f"/services/engagements?financial_year={active_fy}", status_code=303) ensure_engagement_quality_workflow(db, subscription=row, actor_user_id=user.id, create_declarations=False) db.flush() declarations = list_engagement_quality_declarations(db, subscription_id=row.id) my_pending_declarations = [d for d in declarations if d.requested_user_id == user.id and d.status == "pending"] kyc_verification = get_latest_engagement_kyc_verification(db, subscription_id=row.id) engagement_letter = get_current_engagement_letter(db, subscription_id=row.id) aqmm_task_summary = aqmm_task_summary_for_subscription(db, subscription_id=row.id) closure_checklist, closure_summary = update_engagement_closure_from_sources(db, subscription=row, actor_user_id=user.id) tasks = db.execute( select(ClientServiceTaskInstance) .where(ClientServiceTaskInstance.subscription_id == row.id) .order_by(ClientServiceTaskInstance.sequence_no.asc(), ClientServiceTaskInstance.id.asc()) ).scalars().all() return _render( request, "modules/services/templates/services/engagements/detail.html", db, user, title="Engagement Subscription", row=row, tasks=tasks, declarations=declarations, my_pending_declarations=my_pending_declarations, kyc_verification=kyc_verification, engagement_letter=engagement_letter, aqmm_task_summary=aqmm_task_summary, closure_checklist=closure_checklist, closure_summary=closure_summary, can_manage=_can_manage_client_services(db, user), ) finally: db.close() @router.post("/{subscription_id}/aqmm/initiate") def subscription_aqmm_initiate(request: Request, subscription_id: int, 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 row: ensure_engagement_quality_workflow(db, subscription=row, actor_user_id=user.id, create_declarations=True) enforce_quality_gate_on_subscription(row) db.commit() return RedirectResponse(url=f"/services/engagements/{subscription_id}", status_code=303) finally: db.close() @router.post("/{subscription_id}/aqmm/request-declarations") def subscription_aqmm_request_declarations(request: Request, subscription_id: int, 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 row: request_engagement_quality_declarations(db, subscription=row, actor_user_id=user.id) enforce_quality_gate_on_subscription(row) db.commit() return RedirectResponse(url=f"/services/engagements/{subscription_id}", status_code=303) finally: db.close() @router.post("/{subscription_id}/aqmm/declarations/{declaration_id}/respond") def subscription_aqmm_declaration_respond( request: Request, subscription_id: int, declaration_id: int, status: str = Form(...), notes: str = Form(""), 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) respond_engagement_quality_declaration( db, declaration_id=declaration_id, current_user_id=user.id, status=status, notes=notes, request=request, ) db.commit() return RedirectResponse(url=f"/services/engagements/{subscription_id}", status_code=303) finally: db.close() @router.post("/{subscription_id}/aqmm/kyc/verify") def subscription_aqmm_kyc_verify(request: Request, subscription_id: int, notes: str = Form(""), 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 row: verify_engagement_kyc_from_permanent_documents(db, subscription=row, actor_user_id=user.id, notes=notes) enforce_quality_gate_on_subscription(row) db.commit() return RedirectResponse(url=f"/services/engagements/{subscription_id}", status_code=303) finally: db.close() @router.post("/{subscription_id}/aqmm/engagement-letter/complete") def subscription_aqmm_engagement_letter_complete( request: Request, subscription_id: int, acceptance_mode: str = Form(...), notes: str = Form(""), 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 row: mark_engagement_letter_completed(db, subscription=row, actor_user_id=user.id, acceptance_mode=acceptance_mode, notes=notes, request=request) enforce_quality_gate_on_subscription(row) db.commit() return RedirectResponse(url=f"/services/engagements/{subscription_id}", status_code=303) finally: db.close() @router.post("/{subscription_id}/aqmm/approve") def subscription_aqmm_approve(request: Request, subscription_id: int, 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 row: try: approve_engagement_quality_workflow(db, subscription=row, actor_user_id=user.id) db.commit() except ValueError: db.rollback() return RedirectResponse(url=f"/services/engagements/{subscription_id}", status_code=303) finally: db.close() @router.post("/{subscription_id}/closure/sync") def subscription_closure_sync(request: Request, subscription_id: int, 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.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 row: update_engagement_closure_from_sources(db, subscription=row, actor_user_id=user.id) db.commit() return RedirectResponse(url=f"/services/engagements/{subscription_id}", status_code=303) finally: db.close() @router.post("/{subscription_id}/closure/update") def subscription_closure_update( request: Request, subscription_id: int, deliverables_sent_to_client: str | None = Form(None), billing_reviewed: str | None = Form(None), open_points_closed: str | None = Form(None), client_communication_completed: str | None = Form(None), closure_note: str = Form(""), 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 row: try: save_engagement_closure_confirmations( db, subscription=row, deliverables_sent_to_client=deliverables_sent_to_client is not None, billing_reviewed=billing_reviewed is not None, open_points_closed=open_points_closed is not None, client_communication_completed=client_communication_completed is not None, closure_note=closure_note, actor_user_id=user.id, ) db.commit() except ValueError: db.rollback() return RedirectResponse(url=f"/services/engagements/{subscription_id}", status_code=303) finally: db.close() @router.post("/{subscription_id}/closure/approve") def subscription_closure_approve(request: Request, subscription_id: int, 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) if not _can_lock_engagements(db, user): return _redirect_denied() tenant_id = _active_tenant_id(request, user) row = get_subscription(db, subscription_id=subscription_id, tenant_id=tenant_id) if row: try: approve_engagement_closure(db, subscription=row, actor_user_id=user.id) db.commit() except ValueError: db.rollback() return RedirectResponse(url=f"/services/engagements/{subscription_id}", status_code=303) finally: db.close() @router.post("/{subscription_id}/closure/reopen") def subscription_closure_reopen( request: Request, subscription_id: int, reason: str = Form(""), 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) if not _can_lock_engagements(db, user): return _redirect_denied() tenant_id = _active_tenant_id(request, user) row = get_subscription(db, subscription_id=subscription_id, tenant_id=tenant_id) if row: try: reopen_engagement_closure(db, subscription=row, reason=reason, actor_user_id=user.id) db.commit() except ValueError: db.rollback() return RedirectResponse(url=f"/services/engagements/{subscription_id}", status_code=303) 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/engagements", status_code=303) active_fy = _active_financial_year(request) if row.financial_year != active_fy: return RedirectResponse(url=f"/services/engagements?financial_year={active_fy}", status_code=303) if getattr(row, "is_locked", False) or is_row_financial_year_locked(db, row): return RedirectResponse(url=f"/services/engagements/{row.id}?year_locked=1", 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) review_partners = list_review_partners(db, tenant_id=tenant_id, branch_id=branch_id) return _render( request, "modules/services/templates/services/engagements/form.html", db, user, title="Edit Engagement Subscription", mode="edit", subscription=row, clients=clients, enabled_services=enabled_services, assignable_users=assignable_users, review_partners=review_partners, selected_client_id=row.client_id, financial_year=row.financial_year, ) 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(""), review_partner_user_id: str = Form(""), start_date: str = Form(""), end_date: str = Form(""), expiry_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/engagements", status_code=303) active_fy = _active_financial_year(request) if row.financial_year != active_fy: return RedirectResponse(url=f"/services/engagements?financial_year={active_fy}", status_code=303) if getattr(row, "is_locked", False) or is_row_financial_year_locked(db, row): return RedirectResponse(url=f"/services/engagements/{row.id}?year_locked=1", 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 if review_partner_required_for_engagement(db, tenant_id=tenant_id, engagement_type=row.engagement_type): row.review_partner_user_id = int(review_partner_user_id) if review_partner_user_id.strip() else getattr(row.client, "default_review_partner_user_id", None) else: row.review_partner_user_id = None row.start_date = parse_date(start_date) row.end_date = parse_date(end_date) row.expiry_date = parse_date(expiry_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 apply_due_date_rule_to_subscription(db, row) ensure_engagement_quality_workflow(db, subscription=row, actor_user_id=user.id, create_declarations=False) enforce_quality_gate_on_subscription(row) db.commit() return RedirectResponse(url=f"/services/engagements/{row.id}", status_code=303) finally: db.close() @router.post("/{subscription_id}/lock") def subscription_lock(request: Request, subscription_id: int, 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) if not _can_lock_engagements(db, user): return _redirect_denied() tenant_id = _active_tenant_id(request, user) row = get_subscription(db, subscription_id=subscription_id, tenant_id=tenant_id) if row and row.financial_year != _active_financial_year(request): return RedirectResponse(url=f"/services/engagements?financial_year={_active_financial_year(request)}", status_code=303) if row and is_row_financial_year_locked(db, row): return RedirectResponse(url=f"/services/engagements/{row.id}?year_locked=1", status_code=303) if row and _user_can_lock_subscription(db, user, row): if _lock_subscription_row(row, user): db.commit() return RedirectResponse(url=f"/services/engagements/{subscription_id}", status_code=303) finally: db.close()