[verified] Implement account valuation history for issue 12
This commit is contained in:
@@ -176,6 +176,50 @@ def page_accounts(db_path: str) -> None:
|
||||
accounts_data = accounts_res.get("accounts", []) if accounts_res.get("success") else []
|
||||
if accounts_data:
|
||||
render_account_list(accounts_data)
|
||||
selected_id = st.selectbox("평가 이력 계좌", [a["id"] for a in accounts_data], format_func=lambda value: next((a.get("name", value) for a in accounts_data if a["id"] == value), value))
|
||||
selected_account = next(a for a in accounts_data if a["id"] == selected_id)
|
||||
history = accounts_gateway.list_account_valuations(selected_id, db_path=db_path)
|
||||
valuations = history.get("valuations", []) if history.get("success") else []
|
||||
if valuations:
|
||||
st.dataframe(valuations, width="stretch", hide_index=True)
|
||||
# The list is historical insertion order; a rejected/corrected
|
||||
# tail is not necessarily the effective current valuation.
|
||||
basis = (selected_account.get("last_synced_at") or "")[:10]
|
||||
selected = accounts_gateway.select_account_valuation(selected_id, basis, as_of=True, db_path=db_path) if basis else {}
|
||||
selected_valuation = selected.get("valuation") if selected.get("success") else None
|
||||
latest = selected_valuation or next(
|
||||
(item for item in reversed(valuations) if item.get("verification_status") == "verified"), valuations[-1]
|
||||
)
|
||||
st.caption(f"최근 실제 기준일: {latest.get('basis_date', '-')} · 상태: {latest.get('verification_status', 'unknown')} · 종류: {latest.get('valuation_kind', 'unknown')}")
|
||||
else:
|
||||
st.warning("평가 이력이 없습니다.")
|
||||
with st.expander("계좌 평가 등록"):
|
||||
with st.form(f"account_valuation_register_{selected_id}"):
|
||||
v_date = st.text_input("평가 기준일", value=selected_account.get("last_synced_at", "")[:10])
|
||||
v_amount = st.number_input("평가 금액", value=int(selected_account.get("last_balance") or 0), step=1)
|
||||
default_kind = "loan_balance" if selected_account.get("type") == "loan" else ("total_value" if selected_account.get("type") == "investment" else "cash_balance")
|
||||
v_kind = st.selectbox("평가 종류", ["cash_balance", "balance", "total_value", "loan_balance", "loan"], index=["cash_balance", "balance", "total_value", "loan_balance", "loan"].index(default_kind))
|
||||
v_status = st.selectbox("검증 상태", ["unverified", "verified", "rejected"])
|
||||
v_definition = st.selectbox("대표 부채 정의(대출 필수)", ["", "principal_only", "principal_interest_fees"])
|
||||
v_submit = st.form_submit_button("평가 등록")
|
||||
if v_submit:
|
||||
result = accounts_gateway.register_account_valuation(selected_id, v_date, int(v_amount), valuation_kind=v_kind, verification_status=v_status, representative_liability_definition=v_definition or None, db_path=db_path)
|
||||
if result.get("success"):
|
||||
clear_cache(); st.success("평가를 등록했습니다."); st.rerun()
|
||||
else:
|
||||
st.error(result.get("error", result))
|
||||
with st.expander("계좌 평가 정정"):
|
||||
with st.form(f"account_valuation_correct_{selected_id}"):
|
||||
correction_id = st.text_input("정정 대상 평가 ID")
|
||||
correction_amount = st.number_input("정정 금액", value=0, step=1, key=f"correction_amount_{selected_id}")
|
||||
correction_reason = st.text_input("정정 사유")
|
||||
correction_submit = st.form_submit_button("평가 정정")
|
||||
if correction_submit:
|
||||
result = accounts_gateway.correct_account_valuation(correction_id, int(correction_amount), correction_reason, db_path=db_path)
|
||||
if result.get("success"):
|
||||
clear_cache(); st.success("평가를 정정했습니다."); st.rerun()
|
||||
else:
|
||||
st.error(result.get("error", result))
|
||||
account_edit_id = st.session_state.get("account_edit_id")
|
||||
if account_edit_id:
|
||||
account = next((item for item in accounts_data if item["id"] == account_edit_id), None)
|
||||
|
||||
@@ -75,15 +75,28 @@ def page_assets(db_path: str) -> None:
|
||||
st.divider()
|
||||
render_section_label("🔒", "월말 스냅샷 Preview / Close")
|
||||
st.caption("Preview는 DB를 변경하지 않습니다. 같은 기준일을 다시 close하면 정정 사유가 필요하며 기존 행을 덮어쓰지 않고 새 correction run을 append합니다.")
|
||||
c1, c2, c3 = st.columns([1, 1.5, 1.5])
|
||||
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")
|
||||
close_note = c2.text_input("Close 출처 메모", value="month-end close")
|
||||
correction_reason = c3.text_input("정정 사유(재마감 시 필수)", value="")
|
||||
selection_policy = c2.selectbox("선택 정책", ["exact", "as_of"], format_func=lambda value: "정확히 해당일" if value == "exact" else "기준일 현재(as-of)")
|
||||
close_note = c3.text_input("Close 출처 메모", value="month-end close")
|
||||
correction_reason = c4.text_input("정정 사유(재마감 시 필수)", value="")
|
||||
p1, p2 = st.columns(2)
|
||||
approval_required = st.checkbox("조건부 close 승인 명시", value=False)
|
||||
approval_cols = st.columns(3)
|
||||
approved_by = approval_cols[0].text_input("승인자")
|
||||
approval_reason = approval_cols[1].text_input("승인 사유")
|
||||
approval_reference = approval_cols[2].text_input("승인 근거 참조")
|
||||
allow_missing = st.checkbox("누락 제외 허용", value=False)
|
||||
allow_carry = st.checkbox("as-of 이월 허용", value=False)
|
||||
if p1.button("스냅샷 Preview", type="secondary"):
|
||||
st.session_state["asset_snapshot_preview"] = assets_gateway.preview_asset_snapshot_close(basis_date, db_path=db_path)
|
||||
st.session_state["asset_snapshot_preview"] = assets_gateway.preview_asset_snapshot_close(basis_date, selection_policy=selection_policy, db_path=db_path)
|
||||
if p2.button("스냅샷 Close 실행", type="primary"):
|
||||
st.session_state["asset_snapshot_close"] = assets_gateway.close_asset_snapshot(basis_date, source_note=close_note, correction_reason=correction_reason, db_path=db_path)
|
||||
st.session_state["asset_snapshot_close"] = assets_gateway.close_asset_snapshot(
|
||||
basis_date, source_note=close_note, correction_reason=correction_reason,
|
||||
selection_policy=selection_policy, allow_missing_exclusions=allow_missing,
|
||||
allow_as_of_carry_forward=allow_carry, approved_by=approved_by if approval_required else "",
|
||||
approval_reason=approval_reason if approval_required else "", approval_reference=approval_reference if approval_required else "", db_path=db_path,
|
||||
)
|
||||
clear_cache()
|
||||
preview = st.session_state.get("asset_snapshot_preview")
|
||||
close_res = st.session_state.get("asset_snapshot_close")
|
||||
|
||||
@@ -202,7 +202,10 @@ 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)}")
|
||||
st.caption(f"마감 기준일: {asset_snapshot.get('basis_date', basis_date)} · 선택 정책: {asset_snapshot.get('selection_policy', 'exact')} · close_status: {asset_snapshot.get('close_status', '미확인')}")
|
||||
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', '-')}")
|
||||
st.markdown("- [자산/마감 화면에서 스냅샷 확인](?page=자산/마감)")
|
||||
|
||||
render_section_label("📜", "월정산 결과 / Evidence")
|
||||
|
||||
Reference in New Issue
Block a user