73 lines
3.8 KiB
Python
73 lines
3.8 KiB
Python
from __future__ import annotations
|
||
|
||
# ruff: noqa: F405
|
||
|
||
|
||
import pandas as pd
|
||
import streamlit as st
|
||
|
||
from jarca_ledger_mcp.adapters.streamlit import categorization_gateway
|
||
from dashboard.common import * # noqa: F403, F405
|
||
|
||
def page_categories(db_path: str) -> None:
|
||
st.caption("카테고리 삭제는 기본적으로 비활성화 처리합니다. 기존 거래의 과거 category 값은 보존됩니다.")
|
||
|
||
c1, c2 = st.columns(2)
|
||
if c1.button("기본 카테고리 seed/update"):
|
||
res = categorization_gateway.seed_default_ledger_categories(db_path=db_path)
|
||
clear_cache()
|
||
st.success(f"{res.get('created_or_updated', 0)}개 카테고리를 생성/갱신했습니다.")
|
||
st.rerun()
|
||
show_inactive = c2.checkbox("비활성 카테고리도 보기", value=False)
|
||
|
||
render_section_label("➕", "카테고리 추가")
|
||
with st.form("create_ledger_category"):
|
||
cols = st.columns([2, 2, 2, 1, 3])
|
||
category = cols[0].text_input("대분류", placeholder="예: 반려동물")
|
||
subcategory = cols[1].text_input("소분류", placeholder="예: 사료")
|
||
cashflow_kind_label = cols[2].selectbox("현금흐름 성격", [CASHFLOW_KIND_LABELS[k] for k in CASHFLOW_KIND_OPTIONS], index=1)
|
||
sort_order = cols[3].number_input("정렬", value=100, step=10)
|
||
memo = cols[4].text_input("메모", value="dashboard: user category")
|
||
submitted = st.form_submit_button("추가/재활성화", type="primary")
|
||
if submitted:
|
||
cashflow_kind = next(k for k, v in CASHFLOW_KIND_LABELS.items() if v == cashflow_kind_label)
|
||
res = categorization_gateway.create_ledger_category(category, subcategory, int(sort_order), memo, cashflow_kind, db_path=db_path)
|
||
clear_cache()
|
||
if res.get("success"):
|
||
st.success("카테고리를 저장했습니다.")
|
||
st.rerun()
|
||
else:
|
||
st.error(res.get("error", res))
|
||
|
||
result = categorization_gateway.list_ledger_categories(active_only=not show_inactive, include_usage=True, db_path=db_path)
|
||
rows = result.get("categories", [])
|
||
render_section_label("🗂️", "카테고리 목록")
|
||
if rows:
|
||
df = pd.DataFrame(rows)
|
||
if "cashflow_kind" in df:
|
||
df["cashflow_kind_label"] = df["cashflow_kind"].map(CASHFLOW_KIND_LABELS).fillna(df["cashflow_kind"])
|
||
if "total_amount" in df:
|
||
df["total_amount_display"] = df["total_amount"].map(format_krw)
|
||
st.dataframe(df, width="stretch", height=360)
|
||
else:
|
||
st.info("등록된 카테고리가 없습니다. 기본 카테고리를 seed하거나 새 카테고리를 추가하세요.")
|
||
return
|
||
|
||
render_section_label("🛡️", "카테고리 삭제/비활성화")
|
||
options = {
|
||
f"{r['category']} / {r['subcategory'] or '-'} · 사용 {r.get('transaction_count', 0)}건 · {'활성' if r.get('active') else '비활성'}": r
|
||
for r in rows
|
||
}
|
||
selected = st.selectbox("대상", list(options.keys()))
|
||
target = options[selected]
|
||
hard_delete = st.checkbox("DB에서 완전 삭제", value=False, help="보통은 끄고 비활성화만 하세요. 거래 기록은 별도 보존됩니다.")
|
||
force = st.checkbox("사용 중이어도 강제", value=False, help="거래/예산/규칙에서 사용 중인 카테고리도 삭제/비활성화합니다. 거래 category 값은 변경하지 않습니다.")
|
||
if st.button("삭제/비활성화 실행", type="secondary"):
|
||
res = categorization_gateway.delete_ledger_category(target["category"], target.get("subcategory", ""), hard_delete=hard_delete, force=force, db_path=db_path)
|
||
clear_cache()
|
||
if res.get("success"):
|
||
st.warning(f"처리했습니다. 사용 거래 {res.get('transaction_count', 0)}건")
|
||
st.rerun()
|
||
else:
|
||
st.error(res.get("error", res))
|