fix: address account valuation review findings
This commit is contained in:
@@ -4,6 +4,7 @@ import sys
|
||||
from pathlib import Path
|
||||
|
||||
import streamlit as st
|
||||
from jarca_ledger_mcp.adapters.streamlit import overview_gateway
|
||||
|
||||
# Streamlit executes this file as a script, so ensure absolute dashboard imports
|
||||
# resolve identically to package imports used by tests.
|
||||
@@ -38,7 +39,11 @@ def main() -> None:
|
||||
return
|
||||
|
||||
db_path = get_db_path()
|
||||
db_status = overview_gateway.get_ledger_db_status(db_path=db_path, include_hash=False, include_integrity=False)
|
||||
inject_app_css()
|
||||
if not db_status.get("success"):
|
||||
st.error("Ledger DB를 사용할 수 없습니다: " + db_status.get("error", "ledger database is unavailable"))
|
||||
return
|
||||
pages = ["월정산", "요약", "자산/마감", "거래내역", "확인필요 큐", "미분류", "분류 규칙", "카테고리", "계좌", "Import"]
|
||||
query_page = st.query_params.get("page")
|
||||
page = validate_dashboard_page(query_page, pages)
|
||||
|
||||
@@ -7,8 +7,39 @@ import pandas as pd
|
||||
import streamlit as st
|
||||
|
||||
from jarca_ledger_mcp.adapters.streamlit import assets_gateway
|
||||
from jarca_ledger_mcp.adapters.streamlit import overview_gateway
|
||||
from dashboard.common import * # noqa: F403, F405
|
||||
|
||||
|
||||
def _render_asset_evidence(report: dict) -> None:
|
||||
"""Render allowlisted evidence, never free-form source notes or memos."""
|
||||
warnings = report.get("warnings") or []
|
||||
exclusions = report.get("excluded_items") or report.get("exclusions") or []
|
||||
carried = report.get("carried_items") or []
|
||||
provenance = report.get("provenance") or {}
|
||||
approval = report.get("approval") or provenance.get("approval") or {}
|
||||
correction = report.get("correction") or provenance.get("correction") or {}
|
||||
st.markdown("**근거 상세 (코드 / 출처 식별자)**")
|
||||
if warnings:
|
||||
for item in warnings:
|
||||
st.markdown(f"- 경고 `{item.get('code', '-')}` · 출처 {item.get('source_type', '-')} / {item.get('source_id', '-')} · 대상 {item.get('name', '-')}")
|
||||
else:
|
||||
st.caption("경고 상세 없음")
|
||||
if exclusions:
|
||||
st.markdown("**제외 상세**")
|
||||
for item in exclusions:
|
||||
st.markdown(f"- 제외 코드 `{item.get('reason', '-')}` · 출처 {item.get('source_type', '-')} / {item.get('source_id', '-')} · 대상 {item.get('name', '-')}")
|
||||
if carried:
|
||||
st.markdown("**이월 항목**")
|
||||
for item in carried:
|
||||
st.markdown(f"- 이월 · 출처 {item.get('source_type', '-')} / {item.get('source_id', '-')} · 평가일 {item.get('valuation_date', '-')} · 근거 {item.get('evidence_kind', '-')}")
|
||||
if approval:
|
||||
st.markdown(f"**조건부 승인** · 승인자 {approval.get('approved_by', '-')} · 사유 {approval.get('approval_reason', '-')} · 참조 {approval.get('approval_reference', '-')}")
|
||||
if correction:
|
||||
st.markdown(f"**정정** · 원본 run {correction.get('correction_of_run_id', '-')} · 사유 {correction.get('correction_reason', '-')}")
|
||||
st.caption(f"canonical provenance · 평가일 {provenance.get('valuation_date', '-')} · 검증 {provenance.get('verification_status', '-')} · 유형 {provenance.get('evidence_kind', '-')} · run {provenance.get('snapshot_run_id', '-')}")
|
||||
|
||||
|
||||
def page_assets(db_path: str) -> None:
|
||||
st.caption("표시 기준: preview는 live_authority 추정값, close 이후 조회는 closed snapshot 값입니다. 실운영 전 synthetic/validation DB에서 먼저 검증하세요.")
|
||||
|
||||
@@ -39,7 +70,7 @@ def page_assets(db_path: str) -> None:
|
||||
df = pd.DataFrame(assets)
|
||||
df["bucket_label"] = df["liquidity_bucket"].map(LIQUIDITY_BUCKET_LABELS).fillna(df["liquidity_bucket"])
|
||||
df["valuation_display"] = df["valuation_amount"].map(format_krw)
|
||||
st.dataframe(df[["name", "active", "bucket_label", "valuation_display", "valuation_date", "source_note", "memo", "id"]], width="stretch", height=220)
|
||||
st.dataframe(df[["name", "active", "bucket_label", "valuation_display", "valuation_date", "id"]], width="stretch", height=220)
|
||||
with st.form("manual_asset_update"):
|
||||
asset_options = {f"{a['name']} ({a['id']})": a for a in assets}
|
||||
selected_label = st.selectbox("수정/비활성 대상", list(asset_options.keys()))
|
||||
@@ -78,6 +109,9 @@ def page_assets(db_path: str) -> None:
|
||||
c1, c2, c3, c4 = st.columns([1, 1, 1.5, 1.5])
|
||||
basis_date = c1.text_input("마감 기준일", value=f"{current_kst_month()}-01", help="YYYY-MM-DD")
|
||||
selection_policy = c2.selectbox("선택 정책", ["exact", "as_of"], format_func=lambda value: "정확히 해당일" if value == "exact" else "기준일 현재(as-of)")
|
||||
snapshot_report = overview_gateway.get_asset_snapshot_report(basis_date, selection_policy=selection_policy, db_path=db_path)
|
||||
if snapshot_report.get("success"):
|
||||
_render_asset_evidence(snapshot_report)
|
||||
close_note = c3.text_input("Close 출처 메모", value="month-end close")
|
||||
correction_reason = c4.text_input("정정 사유(재마감 시 필수)", value="")
|
||||
p1, p2 = st.columns(2)
|
||||
@@ -110,6 +144,7 @@ def page_assets(db_path: str) -> None:
|
||||
st.dataframe(pd.DataFrame(preview.get("included_items", [])), width="stretch", height=220)
|
||||
if preview.get("warnings"):
|
||||
st.warning(preview.get("warnings"))
|
||||
_render_asset_evidence(preview)
|
||||
else:
|
||||
st.error(preview.get("error", preview))
|
||||
if close_res:
|
||||
|
||||
@@ -11,6 +11,32 @@ from jarca_ledger_mcp.adapters.streamlit import overview_gateway
|
||||
from jarca_ledger_mcp.adapters.streamlit import reconciliation_gateway
|
||||
from dashboard.common import * # noqa: F403, F405
|
||||
|
||||
|
||||
def _render_asset_evidence(report: dict) -> None:
|
||||
warnings = report.get("warnings") or []
|
||||
exclusions = report.get("excluded_items") or report.get("exclusions") or []
|
||||
carried = report.get("carried_items") or []
|
||||
provenance = report.get("provenance") or {}
|
||||
approval = report.get("approval") or provenance.get("approval") or {}
|
||||
correction = report.get("correction") or provenance.get("correction") or {}
|
||||
st.markdown("**근거 상세 (코드 / 출처 식별자)**")
|
||||
for item in warnings:
|
||||
st.markdown(f"- 경고 `{item.get('code', '-')}` · 출처 {item.get('source_type', '-')} / {item.get('source_id', '-')} · 대상 {item.get('name', '-')}")
|
||||
if exclusions:
|
||||
st.markdown("**제외 상세**")
|
||||
for item in exclusions:
|
||||
st.markdown(f"- 제외 코드 `{item.get('reason', '-')}` · 출처 {item.get('source_type', '-')} / {item.get('source_id', '-')} · 대상 {item.get('name', '-')}")
|
||||
if carried:
|
||||
st.markdown("**이월 항목**")
|
||||
for item in carried:
|
||||
st.markdown(f"- 이월 · 출처 {item.get('source_type', '-')} / {item.get('source_id', '-')} · 평가일 {item.get('valuation_date', '-')}")
|
||||
if approval:
|
||||
st.markdown(f"**조건부 승인** · 승인자 {approval.get('approved_by', '-')} · 사유 {approval.get('approval_reason', '-')} · 참조 {approval.get('approval_reference', '-')}")
|
||||
if correction:
|
||||
st.markdown(f"**정정** · 원본 run {correction.get('correction_of_run_id', '-')} · 사유 {correction.get('correction_reason', '-')}")
|
||||
st.caption(f"canonical provenance · 평가일 {provenance.get('valuation_date', '-')} · 검증 {provenance.get('verification_status', '-')} · 유형 {provenance.get('evidence_kind', '-')} · run {provenance.get('snapshot_run_id', '-')}")
|
||||
|
||||
|
||||
def page_monthly_settlement(db_path: str) -> None:
|
||||
query_month = st.query_params.get("month", current_kst_month())
|
||||
if isinstance(query_month, list):
|
||||
@@ -177,6 +203,7 @@ def page_monthly_settlement(db_path: str) -> None:
|
||||
st.markdown(
|
||||
item_line,
|
||||
)
|
||||
st.markdown(f"상태: {reconciliation_status_label.get(raw_status, '정상' if idx == 1 else raw_status)}")
|
||||
else:
|
||||
st.caption("잔액대조 대상 계좌가 현재 없습니다.")
|
||||
|
||||
@@ -187,7 +214,11 @@ def page_monthly_settlement(db_path: str) -> None:
|
||||
if asset_snapshot.get("success"):
|
||||
latest_asset_status = "마감 완료" if bool(asset_snapshot.get("is_closed_snapshot")) else "미마감"
|
||||
warnings = asset_snapshot.get("warnings") if isinstance(asset_snapshot, dict) else []
|
||||
missing_valuation_items = asset_snapshot.get("missing_valuation_items") if isinstance(asset_snapshot, dict) else []
|
||||
missing_valuation_items = (
|
||||
asset_snapshot.get("excluded_items", asset_snapshot.get("missing_valuation_items", []))
|
||||
if isinstance(asset_snapshot, dict)
|
||||
else []
|
||||
)
|
||||
warning_text = "경고 없음"
|
||||
if warnings or missing_valuation_items:
|
||||
warning_text = f"경고 {len(warnings or [])}건 / 제외 {len(missing_valuation_items or [])}건"
|
||||
@@ -203,6 +234,7 @@ def page_monthly_settlement(db_path: str) -> None:
|
||||
m4.metric("자산 마감 상태", latest_asset_status)
|
||||
st.caption(f"자산 상세: {warning_text}")
|
||||
st.caption(f"마감 기준일: {asset_snapshot.get('basis_date', basis_date)} · 선택 정책: {asset_snapshot.get('selection_policy', 'exact')} · close_status: {asset_snapshot.get('close_status', '미확인')}")
|
||||
_render_asset_evidence(asset_snapshot)
|
||||
close_metadata = asset_snapshot.get("close_metadata", {}) if isinstance(asset_snapshot, dict) else {}
|
||||
if close_metadata.get("approved_by") or close_metadata.get("approval_reason"):
|
||||
st.caption(f"조건부 승인: {close_metadata.get('approved_by', '-')} · {close_metadata.get('approval_reason', '-')} · {close_metadata.get('approval_reference', '-')}")
|
||||
|
||||
Reference in New Issue
Block a user