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
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:
"""워크플로/첨부/집계는 DB 모드 전용. JSON 폴백에서는 501."""
if not hasattr(store, "submit"):
@@ -301,26 +289,26 @@ async def reports_page(request: Request) -> HTMLResponse:
# ────────────────────────────────────────────────────────────
# 관리자 설정 — 분류 관리
# 승인자 설정 — 분류 관리
# ────────────────────────────────────────────────────────────
@router.get("/settings", response_class=HTMLResponse)
async def settings_page(request: Request) -> HTMLResponse:
"""관리자 전용 설정 — 분류 추가/삭제."""
"""승인자/관리자 설정 — 분류 추가/삭제."""
from app.main import ( # noqa: WPS433
build_erp_nav,
get_current_user_record,
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)
if user is None:
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(
request,
"denied.html",
{"reason": "개인경비 설정은 관리자만 접근할 수 있습니다."},
{"reason": "개인경비 설정은 승인자만 접근할 수 있습니다."},
status_code=403,
)
return render_template(
@@ -328,7 +316,7 @@ async def settings_page(request: Request) -> HTMLResponse:
"expense/settings.html",
{
"user": user,
"is_admin": True,
"is_admin": is_admin(user),
"categories": _categories(request),
"nav_items": build_erp_nav(user, active="expense"),
"page_title": "개인경비 — 설정",
@@ -349,7 +337,7 @@ async def list_categories(
async def add_category(
request: Request,
body: CategoryBody,
user: dict[str, Any] = Depends(_require_admin),
user: dict[str, Any] = Depends(_require_approver),
) -> JSONResponse:
try:
categories = _category_store(request).add(body.name)
@@ -362,7 +350,7 @@ async def add_category(
async def delete_category(
request: Request,
name: str,
user: dict[str, Any] = Depends(_require_admin),
user: dict[str, Any] = Depends(_require_approver),
) -> JSONResponse:
try:
categories = _category_store(request).delete(name)
@@ -15,7 +15,7 @@
{% if is_approver %}
<a class="erp-btn erp-btn-outline" href="/expense/api/export.xlsx?scope=all">엑셀(전체)</a>
{% endif %}
{% if is_admin %}
{% if is_approver %}
<a class="erp-btn erp-btn-outline" href="/expense/settings">⚙ 설정</a>
{% endif %}
</div>