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.accounting.taxonomy_service import ensure_standard_taxonomy, grouped_taxonomy, list_taxonomy, set_nature_active 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.documents.services import build_document_scope router = APIRouter(prefix="/tools/accounting/taxonomy", tags=["accounting-taxonomy-ui"]) def _denied(): from app.core.http_responses import ui_access_denied return ui_access_denied() def _require_partner_permission(request: Request, db, permission: str): user = get_current_user(request, db) if not user: return None, RedirectResponse(url="/login", status_code=303) roles = set(get_user_roles(db, user.id)) if "Partner" not in roles: return None, _denied() try: require_permission(db, user, permission) except Exception: return None, _denied() return user, None def _context(request: 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 accounting_taxonomy(request: Request, status: str = "all", group: str = "", updated: int = 0): db = CommonSessionLocal() try: user, response = _require_partner_permission(request, db, "accounting.taxonomy.view") if response: return response scope = build_document_scope(request, db, user) ensure_standard_taxonomy(db, scope.tenant_id, user.id) status = status if status in {"all", "active", "inactive"} else "all" allowed_groups = {"purchase", "expense", "repairs", "capital", "statutory", "finance", "review"} group = group if group in allowed_groups else "" rows = list_taxonomy(db, scope.tenant_id, status=status, group=group) all_rows = list_taxonomy(db, scope.tenant_id) counts = { "total": sum(1 for r in all_rows if r.is_posting_nature), "active": sum(1 for r in all_rows if r.is_posting_nature and r.is_active), "inactive": sum(1 for r in all_rows if r.is_posting_nature and not r.is_active), "capital": sum(1 for r in all_rows if r.is_posting_nature and r.capital_revenue == "capital"), } return templates.TemplateResponse( "modules/accounting/templates/accounting/taxonomy.html", _context( request, db, user, taxonomy_groups=grouped_taxonomy(rows), rows=rows, counts=counts, selected_status=status, selected_group=group, updated=updated, ), ) finally: db.close() @router.post("/{nature_id}/active") def update_accounting_nature_active( nature_id: int, request: Request, csrf_token: str = Form(...), is_active: str = Form(...), status: str = Form("all"), group: str = Form(""), ): db = CommonSessionLocal() try: user, response = _require_partner_permission(request, db, "accounting.taxonomy.manage") if response: return response validate_csrf(request, csrf_token) scope = build_document_scope(request, db, user) try: row = set_nature_active( db, tenant_id=scope.tenant_id, nature_id=nature_id, is_active=is_active.strip().lower() in {"1", "true", "yes", "on"}, actor_user_id=user.id, ) except ValueError: return _denied() if row is None: return _denied() query = f"?updated=1&status={status if status in {'all','active','inactive'} else 'all'}" if group: query += f"&group={group}" return RedirectResponse(url=f"/tools/accounting/taxonomy{query}", status_code=303) finally: db.close()