feat(expense): 분류 설정 메뉴를 승인자에게도 노출

- /expense/settings 및 분류 CRUD 가드 관리자 → 승인자(expense_approver|admin)
- index 설정 버튼 is_approver 조건으로 노출
- 미사용 _require_admin 헬퍼 제거

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-02 17:28:26 +09:00
parent 0b04a05b26
commit 506db5ff07
2 changed files with 9 additions and 21 deletions
+8 -20
View File
@@ -109,18 +109,6 @@ def _require_approver(request: Request) -> dict[str, Any]:
return user return user
def _require_admin(request: Request) -> dict[str, Any]:
from app.main import get_current_user_record # noqa: WPS433
from app.store import is_admin # noqa: WPS433
user = get_current_user_record(request)
if user is None:
raise HTTPException(status_code=401, detail="로그인이 필요합니다.")
if not is_admin(user):
raise HTTPException(status_code=403, detail="관리자 권한이 필요합니다.")
return user
def _require_db_store(store: Any) -> None: def _require_db_store(store: Any) -> None:
"""워크플로/첨부/집계는 DB 모드 전용. JSON 폴백에서는 501.""" """워크플로/첨부/집계는 DB 모드 전용. JSON 폴백에서는 501."""
if not hasattr(store, "submit"): if not hasattr(store, "submit"):
@@ -301,26 +289,26 @@ async def reports_page(request: Request) -> HTMLResponse:
# ──────────────────────────────────────────────────────────── # ────────────────────────────────────────────────────────────
# 관리자 설정 — 분류 관리 # 승인자 설정 — 분류 관리
# ──────────────────────────────────────────────────────────── # ────────────────────────────────────────────────────────────
@router.get("/settings", response_class=HTMLResponse) @router.get("/settings", response_class=HTMLResponse)
async def settings_page(request: Request) -> HTMLResponse: async def settings_page(request: Request) -> HTMLResponse:
"""관리자 전용 설정 — 분류 추가/삭제.""" """승인자/관리자 설정 — 분류 추가/삭제."""
from app.main import ( # noqa: WPS433 from app.main import ( # noqa: WPS433
build_erp_nav, build_erp_nav,
get_current_user_record, get_current_user_record,
render_template, render_template,
) )
from app.store import is_admin # noqa: WPS433 from app.store import has_module, is_admin # noqa: WPS433
user = get_current_user_record(request) user = get_current_user_record(request)
if user is None: if user is None:
return RedirectResponse(url="/login", status_code=303) return RedirectResponse(url="/login", status_code=303)
if not is_admin(user): if not (is_admin(user) or has_module(user, "expense_approver")):
return render_template( return render_template(
request, request,
"denied.html", "denied.html",
{"reason": "개인경비 설정은 관리자만 접근할 수 있습니다."}, {"reason": "개인경비 설정은 승인자만 접근할 수 있습니다."},
status_code=403, status_code=403,
) )
return render_template( return render_template(
@@ -328,7 +316,7 @@ async def settings_page(request: Request) -> HTMLResponse:
"expense/settings.html", "expense/settings.html",
{ {
"user": user, "user": user,
"is_admin": True, "is_admin": is_admin(user),
"categories": _categories(request), "categories": _categories(request),
"nav_items": build_erp_nav(user, active="expense"), "nav_items": build_erp_nav(user, active="expense"),
"page_title": "개인경비 — 설정", "page_title": "개인경비 — 설정",
@@ -349,7 +337,7 @@ async def list_categories(
async def add_category( async def add_category(
request: Request, request: Request,
body: CategoryBody, body: CategoryBody,
user: dict[str, Any] = Depends(_require_admin), user: dict[str, Any] = Depends(_require_approver),
) -> JSONResponse: ) -> JSONResponse:
try: try:
categories = _category_store(request).add(body.name) categories = _category_store(request).add(body.name)
@@ -362,7 +350,7 @@ async def add_category(
async def delete_category( async def delete_category(
request: Request, request: Request,
name: str, name: str,
user: dict[str, Any] = Depends(_require_admin), user: dict[str, Any] = Depends(_require_approver),
) -> JSONResponse: ) -> JSONResponse:
try: try:
categories = _category_store(request).delete(name) categories = _category_store(request).delete(name)
@@ -15,7 +15,7 @@
{% if is_approver %} {% if is_approver %}
<a class="erp-btn erp-btn-outline" href="/expense/api/export.xlsx?scope=all">엑셀(전체)</a> <a class="erp-btn erp-btn-outline" href="/expense/api/export.xlsx?scope=all">엑셀(전체)</a>
{% endif %} {% endif %}
{% if is_admin %} {% if is_approver %}
<a class="erp-btn erp-btn-outline" href="/expense/settings">⚙ 설정</a> <a class="erp-btn erp-btn-outline" href="/expense/settings">⚙ 설정</a>
{% endif %} {% endif %}
</div> </div>