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:
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user