Issue #18의 기존 월정산 초안·승인·마감을 종합 월정산으로 확장한다. 기존 asset_snapshot_runs를 마감 헤더로 유지하고 여섯 총계와 집계 정책·버전·당시 근거를 고정 저장하며, 자산·부채 상세 및 카테고리별 수입·지출 상세를 동일 마감 ID에 연결한다. 별도 독립 현금흐름 정산은 만들지 않는다. 초안은 재계산 가능한 미리보기, 마감은 불변 스냅샷이라는 경계를 MCP와 Dashboard에 동일하게 적용한다.
핵심 저장·승인·정정·호환성 범위는 원본 요청에서 확정됐다. 사용자가 선택한 정책에 따라 환급은 환급 거래월의 원래 지출 카테고리에 음수 지출로 반영하고, 저축은 초안 정책의 명시적 대분류/세부분류 목록으로 식별하며, 포함/제외가 결정되지 않은 미분류 거래는 종합 승인을 차단한다. 미해결 질문은 없다. 이 검토는 Discovery 승인이나 구현 handoff를 수행하지 않는다.
문제
현재 get_monthly_cashflow_report()는 활성 거래와 현재 활성 카테고리의 cashflow_kind를 조회할 때마다 집계한다. 수입은 income, 지출은 expense만 합산하므로 원본의 생활비+저축+대출상환 정책과 동일하지 않다. 환급의 방향도 별도 정책으로 처리하지 않는다.
월정산 요약은 현재 거래 집계와 자산 마감 보고서를 조합하며, 초안 검토 지문과 최종 저장은 자산 평가 중심이다. 현금흐름 근거·정책·카테고리별 결과가 마감에 함께 고정되지 않아 원장이나 분류 변경 후 과거 월 결과가 달라질 수 있다. 최초 마감과 정정 마감을 월만으로 묶으면 서로 다른 결과가 섞일 위험도 있다.
포함 범위
기존 초안 lifecycle의 생성→수정→검토→승인→최종 마감을 확장한다. 초안에 현금흐름 집계 정책과 집계 로직 버전을 보존하고, 조회·검토에서 여섯 총계, 카테고리별 수입·지출 및 제외 내역, 자산·부채 평가자료, 미분류·자료 누락·검토 경고를 제공한다. 초안용 카테고리 상세 테이블은 필수로 추가하지 않는다.
asset_snapshot_runs에 settlement_month, currency, income_total, expense_total, net_cashflow, total_assets, total_liabilities, net_worth, cashflow_policy_json, aggregation_version, snapshot_schema_version, source_snapshot_json을 추가한다. 기존 기준일·승인·초안 연결·정정 연결·사유·멱등성 정보를 유지한다.
기존 asset_snapshots.snapshot_run_id 연결을 유지한다. 자산/부채 구분, 계좌·실물자산별 금액, 원본 평가일·평가자료 ID, exact/이월/대체 적용, 포함·제외 및 사유를 저장 근거와 반환 계약에서 보존한다. 기존 필드·근거 JSON으로 보존 가능한 부분을 재사용하며, 필요한 보완만 한다.
신규 settlement_cashflow_snapshots에 id, settlement_run_id, cashflow_kind, category, subcategory, amount, transaction_count, currency를 저장한다. settlement_run_id는 asset_snapshot_runs.id 외래키이며, (settlement_run_id, cashflow_kind, category, subcategory, currency)를 유일하게 하고 마감 ID 조회 인덱스를 둔다. 상세 구분은 income/expense/excluded다. 대분류·세부분류의 빈 문자열은 명시적으로 보존하고 NULL로 유일 제약을 우회하지 않는다.
월 수입·지출은 해당 월 1일 이상 다음 달 1일 미만의 활성 거래에서 계산한다. 자산·부채는 초안의 기존 달력 월말 기준일 평가자료를 사용한다. 생활비+저축+대출상환을 실무 지출로 포함하고 이체·초기잔액 조정은 총계에서 제외한다. 상품권 충전 등 자산 이동과 실제 소비가 중복 지출되지 않도록 집계 정책과 근거를 연결한다. 환급은 환급 거래월의 원래 지출 카테고리에 음수 지출로 반영하며 과거 마감은 수정하지 않는다. 대응 분류가 없으면 미분류 경고를 표시하고 판단 확정 전 종합 승인을 차단한다. 저축은 사용자가 검토/승인 전에 지정한 대분류/세부분류 목록으로만 식별하고 초안 정책·지문·당시 근거에 보존한다. 일반 이체·상품권 충전 제외를 우선하며 investment 전체를 저축으로 포함하지 않고 debt_payment은 지출로 매핑한다. 포함/제외가 결정되지 않은 미분류는 재분류 또는 명시적 정책 판단 후 재검토한다. 정책상 확정 제외 거래는 blocker가 아니며 자산 조건부 승인 플래그로 현금흐름 blocker를 우회하지 않는다.
source_snapshot_json에는 거래 ID뿐 아니라 당시 날짜·금액·통화·거래 유형·대분류·세부분류·적용 분류와 정책·포함/제외 판단 등 계산 재현에 필요한 값을 보존한다. 자산 영역은 선택된 평가자료·적용 금액·출처·원본 평가일·이월/대체·누락/제외를 기존 고정 근거와 연결한다. 앱 직접 확인값과 거래 역산값의 출처를 구분한다.
검토·승인 지문에 거래 집합과 당시 값, 카테고리 분류, 집계 정책·버전, 카테고리별 결과·여섯 총계, 선택 평가자료, 포함·제외·미확정 상태를 결합한다. 거래 추가·삭제·변경, 재분류, 정책 변경 및 평가자료 변경을 감지하며 승인 결과와 저장 결과가 다르면 마감을 거부한다.
하나의 DB 트랜잭션에서 헤더·양 영역 상세·근거와 초안 완료/최종 run 연결·저장 응답을 기록한다. income_total = income 상세 합계, expense_total = expense 상세 합계, net_cashflow = income_total - expense_total, total_assets = 반영 자산 상세 합계, total_liabilities = 반영 부채 상세 합계, net_worth = total_assets - total_liabilities를 검증한다. 제외·중복·금액 미확인 항목은 합산하지 않는다. 불일치나 중간 저장 실패는 전체 롤백하고 초안 완료 상태도 남기지 않는다.
같은 멱등 키와 같은 승인 요청은 동일 run과 저장 결과를 반환하고, 다른 요청은 기존 충돌 계약대로 거부한다. 마감 후 원장 변경으로 기존 헤더·상세·근거를 갱신하지 않는다. 정정은 기존 단일 child·최신 parent 검증을 유지하며 새로운 run과 상세를 append하고 최초·정정 각각을 조회할 수 있게 한다.
additive/idempotent 이관으로 기존 자산 전용 마감을 구버전으로 구분한다. 저장되지 않은 총계는 NULL로 두고 현재 원장으로 소급 backfill하거나 0으로 꾸미지 않는다. 기존 미마감 초안은 유지하되 종합 검토·재승인을 거쳐야 새 구조로 마감할 수 있다.
MCP 및 인증된 Dashboard의 초안·마감 조회를 확장한다. 저장된 마감 결과와 현재 원장 보고를 출처·마감 ID·버전으로 구분하며 구버전·부분 자료·누락/제외를 표시한다. 기존 자산 API의 입력·응답·오류·별칭과 자산 전용 직접 마감의 의미는 보존한다. 자산 전용 직접 마감을 종합 마감으로 오인하지 않게 구분한다.
synthetic 테스트로 여섯 검증식, 이체·환급·저축·대출상환·상품권 충전·초기잔액 조정, 정책/원장/분류/평가자료 drift, 재시도·동시성, 롤백, 역사 불변성, 정정 분리, NULL 이관, 기존 API·MCP·Dashboard 호환을 검증한다. 환급월 귀속·음수 지출 상세/총계와 과거 마감 불변, 저축 목록의 적격/비적격 및 이체/상품권 제외 우선·정책 변경 시 재승인, 미분류 승인 차단·확정 제외 비차단·자산 조건부 승인으로 우회 불가를 포함한다.
제외 범위
독립 현금흐름 정산 또는 별도 승인 lifecycle, 원장 자동 재작성·추정 거래 생성, 마감 행 직접 덮어쓰기, 과거 수입·지출 소급 재계산 저장.
초안용 카테고리 상세 테이블을 필수로 도입하는 변경, Dashboard 인증·cookie·logout·메뉴 정체성 변경, 신규 MCP 인증 체계. 환급을 수입으로 처리하는 대안, investment 전체를 저축으로 일괄 포함하거나 거래별 저축 지정 목록을 별도 채택하는 대안, 미결정 미분류를 별도 승인으로 제외한 현금흐름 조건부 마감은 채택하지 않는다. 이는 기존 자산 조건부 승인 계약을 폐지하는 뜻이 아니다.
외부 가격/환율 API 또는 다중 통화 환산의 신규 도입. 현재 정산의 KRW 권위 경계를 유지하고 이종 통화를 무환산 합산하지 않는다. 향후 환산을 별도 승인하면 정책·실제 사용 환율 보존을 함께 정의해야 한다.
Discovery 작업 중 코드 구현, DB 이관 실행, 후속 정본 문서 편집, 제품 테스트 실행, PR 생성 및 Issue 라벨·담당자·상태 변경.
제약 조건
조사 기준은 main의 exact pinned revision f67c07adf01d1f17415bf7cf836f46997ca6f21c다. 구현 시 새 revision으로 바뀌면 관련 근거와 호환성 계약을 다시 확인한다.
금액은 기존 정수 정밀도를 유지하고 NULL과 명시적 0을 구별한다. 총계와 반영 상세의 통화를 일치시키며 현재 KRW 권위 제한을 완화하지 않는다.
기존 verified 평가 권위, exact/이월/명시적 대체 승인, 대표 부채만 합산, 투자 평가 중복 합산 금지, 누락/제외 사유·조건부 승인, 월별 활성 초안 하나, 읽기 전용 조회/no-create DB 경계를 유지한다.
cashflow_kind의 원장 분류(investment, debt_payment 등)와 마감 상세의 세 구분은 별개다. 당시 원래 분류와 정책에 따른 최종 구분을 모두 근거에 남기며, 이 기능을 위해 기존 실시간 보고 정책을 조용히 바꾸지 않는다.
비밀번호·토큰·cookie 등 인증정보를 집계 근거나 오류·로그에 저장하지 않는다. 문서·fixture·테스트는 synthetic 자료를 사용하고 기존 Dashboard auth-before-DB를 유지한다.
원본의 열 가지 수용 기준과 사용자가 선택한 환급 음수 지출·저축 분류 목록·미분류 승인 차단 정책을 후속 요구사항·인수 기준·검증에 추적한다. 환급 차감으로 지출 상세·지출 총계가 음수가 될 수 있으므로 이를 0으로 clamp하거나 양수 원장 금액 규칙으로 거부하지 않는다. 정책 편집과 분류/판단 변경은 기존 version/지문/승인 무효화 경계를 따르며 같은 거래는 한 번만 집계한다. 구현 후 제품 검증은 후속 작업이며 사용자 정책 선택만으로 테스트 통과를 주장하지 않는다.
확정된 결정
원본 Issue #18이 여섯 총계의 영속 저장, 기존 asset_snapshot_runs 확장·명칭 유지, 기존 자산 상세 유지, 신규 카테고리 상세의 마감 ID 연결을 명시했다.
초안 현금흐름은 재계산하고 최종 마감에서 고정한다. 생활비+저축+대출상환을 지출 기준으로 사용하며 이체·초기잔액 조정을 총계에서 제외하고 상품권 자산 이동/소비의 중복 집계를 금지한다.
승인 지문 확장·관련 변경 시 재검토/재승인, 원자적 저장·합계 검증·롤백, 멱등 재시도, 불변 마감 및 append-only 정정은 필수다.
구버전의 미보존 총계는 NULL이며 소급 집계를 하지 않는다. 기존 미마감 초안과 자산 API 호환성을 유지하고 MCP·Dashboard에서 종합 조회를 제공한다.
문서 분류상 기존 계좌 평가·자산 마감·월정산 초안 기능을 확장하므로 기존 REQ-003을 갱신한다. 별도의 REQ-004를 할당하지 않는다. 확정된 새 표와 사용자가 선택한 현금흐름 정책을 함께 반영한다.
환급은 해당 환급 거래월의 원래 지출 카테고리에 음수 지출로 반영한다. 대응 분류가 없으면 미분류 경고로 처리하고, 재분류 또는 명시적 정책 판단으로 포함/제외가 확정될 때까지 종합 승인을 차단한다. 과거 마감은 수정하지 않고 지출 상세·총계에 음수 가능성을 명세한다.
저축 대상 대분류/세부분류의 명시적 목록을 초안 정책에 저장하고 적격 거래만 저축 지출로 매핑한다. 일반 이체·상품권 충전의 제외를 우선하며 investment 전체를 일괄 포함하지 않는다. 목록은 검토/승인 전에 사용자가 지정하고 지문·근거에 고정한다. debt_payment은 지출로 매핑하며 원장 분류는 자동 수정하지 않는다.
집계 포함/제외가 결정되지 않은 미분류 거래는 종합 승인을 막고 재분류 또는 명시적 정책 판단 후 재검토한다. 일반 이체 등 정책상 확정 제외 거래는 blocker가 아니다. 기존 자산 조건부 승인 플래그로 미분류 현금흐름 차단을 우회하지 않으며 자산의 기존 누락/이월/대체 조건부 승인 계약은 유지한다.
저장소 근거
모든 경로는 대상 저장소 기준이며 아래 근거는 pinned revision f67c07adf01d1f17415bf7cf836f46997ca6f21c에서 직접 읽었다. 테스트 파일은 정적 검토 근거이며 이번 Discovery 작업에서 제품 테스트를 실행하지 않았다.
jarca_ledger_mcp/infrastructure/sqlite/schema.py:150-197: 기존 asset_snapshots, asset_snapshot_runs 필드·멱등성·최초 마감/정정 parent 유일 인덱스. 원본 요청의 추가 헤더 필드 및 신규 현금흐름 표는 현재 tree에 없다.
jarca_ledger_mcp/infrastructure/sqlite/migrations.py, docs/data-model.md: additive/idempotent 이관과 settlement_drafts, settlement_draft_items, 초안 승인·최종 응답 저장 구조.
jarca_ledger_mcp/application/shared.py:31-51: 거래 유형과 cashflow_kind SQL fallback. investment·debt_payment·unknown을 구별한다.
jarca_ledger_mcp/application/transaction_report_services.py:51-97: 월 범위, 현재 활성 카테고리 join, 현금흐름 및 지출 카테고리 재집계. 현재 보고에는 선택된 저축 목록·대출상환 지출 mapping·환급 음수 지출 정책이 구현되어 있지 않으므로, 기존 보고를 보존하면서 종합 정산의 정책 적용을 확장해야 한다.
jarca_ledger_mcp/application/settlement_draft_service.py:63-150,319-390: 자산 평가 중심의 검토 지문·세 자산 총계, 승인 비교, BEGIN IMMEDIATE, run/rows·완료 응답 저장 및 멱등 replay. 현재 지문에는 현금흐름 거래·분류·정책이 없다.
jarca_ledger_mcp/application/assets_service.py의 _snapshot_rows_for_run, _snapshot_rows_for_latest_run, get_asset_snapshot_report: exact run별 고정 상세, 최신/legacy 마감 조회. jarca_ledger_mcp/application/monthly_settlement_service.py:89-104는 실시간 현금흐름과 자산 보고를 조합한다.
jarca_ledger_mcp/adapters/mcp/server.py, jarca_ledger_mcp/adapters/mcp/tool_contracts.py, jarca_ledger_mcp/adapters/streamlit/settlement_gateway.py, dashboard/page_views/settlement_drafts.py, dashboard/page_views/monthly_settlement.py: 공통 service를 이용하는 기존 도구·gateway·명시적 승인/마감 UI. UI의 자산 전용 설명을 종합 정산 경계에 맞춰 수정할 지점이다.
tests/test_asset_snapshot_idempotency.py, tests/test_legacy_snapshot_migration.py, tests/test_settlement_draft_policy_consistency.py: 저장 근거 replay·legacy 읽기·정수/통화·자산 총계 및 stale 승인 회귀의 기존 검증 계약.
docs/requirements/req-001_dashboard-administrator-authentication.md, req-002_dashboard-menu-transition.md, req-003_account-valuation-history.md는 ID 001003이 연속이고 중복이 없다. REQ-003 revision 1.1의 .06.11, 특히 .11은 기존 초안·승인·원자적 마감을 규정한다. 이번 요청은 이를 확장하므로 같은 ID·경로를 유지한다.
docs/data-model.md, docs/interface-contract.md, docs/project-structure.md, docs/runtime-flow.md는 모두 존재하며 정본을 update-in-place한다.
선택 정책의 구현 경로는 기존 settlement_drafts의 durable 정책 확장, service 계층의 집계 mapping과 승인 지문/거부 guard 확장, 신규 현금흐름 상세의 signed INTEGER 저장이다. 기존 ledger_categories와 거래의 대분류/세부분류를 목록 식별 근거로 사용할 수 있으며 기존 자산 승인 플래그와 현금흐름 미분류 blocker를 구별한다. 이는 pinned 소스의 확장 가능 지점을 확인한 결과이며 새 집계·승인 기능이 이미 구현되었거나 제품 테스트를 통과했다는 주장이 아니다.
변경 내용: 기존 revision 1.1에서 변경 revision을 올리고 .11의 종합 월정산 초안·승인·마감 확장과 관련 .06~.10의 영향 범위·인수 기준을 정합하게 갱신한다. 기존 계좌 평가·부채 정의·호환성·인증·메뉴 계약을 유지한다. 기존 Issue/PR 추적성을 삭제하지 않고 이번 변경은 Issue #18, 구현 PR 미정으로 추가하며 실제 PR 생성 후 같은 저장소 #번호로 갱신한다.
포함할 내용: 여섯 총계/계산식·월 범위/월말 기준·정수/NULL/통화, 헤더/양 상세/근거 연결 및 제약, 사용자가 선택한 저축 분류 목록·debt_payment 지출 mapping·일반 이체/상품권 제외 우선·환급 거래월의 음수 지출·대응 분류 부재 및 미분류 승인 차단 정책, 승인 지문의 전체 입력과 drift 거부, 원자적 저장·완료 연결·롤백, 동일/다른 멱등 요청·동시성, append-only 최초/정정 불변성과 exact run 조회, legacy NULL 이관·미마감 재승인, 현재 보고와 저장 마감 구분, MCP/UI/기존 API/읽기 전용/보안 호환. 원본 수용 기준별 정상·경계·실패 synthetic 검증과 기존 named regression 보존을 명시한다.
제외할 내용: 독립 정산, 자동 승인·원장 추정/재작성·소급 집계, 새로운 환산 서비스·인증/메뉴 변경, 원본에 없는 필수 기능 추가.
근거: REQ-003.11과 기존 .06~.09의 lifecycle·마감·월정산 전파를 확장하는 변경이다. 기존 제외 범위였다는 이유로 새 요구사항을 만들지 않으며 기존 ID·경로·제목을 유지한다.
데이터 모델
조치: 갱신
경로: docs/data-model.md
변경 내용: 확정된 헤더 추가 필드별 타입·NULL/legacy 의미·필수성, 정책/버전/근거 JSON의 재현 입력, 초안 정책·집계 버전·승인 지문·최종 응답을 명세한다. 환급을 표현하는 현금흐름 상세 amount와 지출/순현금흐름 총계의 signed INTEGER 의미, 원장 양수 금액과 적용 음수의 구별, 원래 환급 유형/분류·환급월 및 계산 근거를 기록한다. 저축 목록은 대분류/세부분류 쌍과 제외 우선 정책을 초안·마감 정책 JSON에 보존하며 미분류 판단 상태·경고를 승인 지문에 결합한다. 미결정 미분류는 final 상세로 승인 제외 처리하지 않는다. 신규 현금흐름 상세의 전체 필드, 헤더 FK·삭제/수정으로 역사 손실 금지, 다섯 요소 유일 제약·빈 분류 의미·조회 인덱스 및 run별 연결을 기록한다. 자산 상세의 분류·평가일/ID·exact/이월/대체·포함/제외 보존 위치와 총계 불변식을 연결한다. additive/idempotent 이관, 구버전 식별·미보존 NULL·무소급 정책, 기존 초안 재검토 및 동시성/원자성을 기록한다.
근거: 현재 두 마감 테이블과 초안 표가 정본에 존재하며 원본이 그 확장 및 새로운 상세 표를 요구한다.
인터페이스 계약
조치: 갱신
경로: docs/interface-contract.md
변경 내용: 기존 create/get/list/update/review/approve/finalize 초안 operation에 정책·버전·여섯 총계·카테고리 상세·누락/경고의 입력/출력과 필수성을 정의한다. 기존 파라미터·envelope·오류·db_path의 MCP 비노출을 유지하며 정책 편집은 version 증가와 승인 무효화를 따른다. 저축 대분류/세부분류 목록의 명시적 편집·조회, 환급 차감/음수 출력, 대응 분류 부재·미분류 경고와 종합 승인 차단의 오류/재검토 동작을 정의한다. 자산 조건부 승인 플래그는 이 차단을 우회하지 못하고 정책상 확정 제외는 승인을 막지 않는다. 저장 마감 조회는 exact run과 최신 run, 구버전 NULL 및 현재 원장 집계의 구분을 명시하고 동일/충돌 재시도·stale approval·합계 불일치·저장 실패의 no-partial-write 오류를 정의한다. MCP·Dashboard가 같은 business policy/read model을 쓰고 인증·기존 자산 직접 마감 계약을 유지하는 범위를 기록한다. 실제 공개 함수/도구 시그니처와 응답 예시는 구현과 대조하며 아직 존재하지 않는 API를 현재 계약으로 주장하지 않는다.
근거: 기존 draft service·tool 계약·settlement gateway·asset report의 확장이다. 신규 조회가 필요하면 기존 인터페이스를 깨지 않는 additive 구현과 문서 매핑이 필요하다.
프로젝트 구조
조치: 갱신
경로: docs/project-structure.md
변경 내용: SQLite schema.py/migrations.py가 저장 제약/이관, application/settlement_draft_service.py가 정책·검토·승인·원자적 마감, assets_service.py가 기존 자산 권위/read model, monthly_settlement_service.py가 월별 종합 조회를 담당하는 책임 경계를 갱신한다. 기존 transaction_report_services.py의 실시간 보고와 저장 마감 계산을 혼동하지 않으며 공통 business 집계를 service 계층에서 재사용한다. 선택된 환급 부호·저축 목록·제외 우선·미분류 차단은 이 공통 계층에서 결정하며 MCP와 Dashboard는 목록 입력·경고·승인 불가 사유를 전달/표시하고 정책을 따로 판단하지 않는다. facade→MCP/Streamlit gateway→page의 기존 의존 방향을 유지하고 UI/transport에 SQL·독립 집계/승인 정책을 복제하지 않는다. 기존 lifecycle·idempotency·legacy·adapter/UI regression과 종합 cashflow 신규 synthetic 테스트의 배치·검증 책임을 기록한다. 새 모듈 경로는 실제 필요와 구현 결과에 결속하며 불필요한 새 directory 구조를 요구하지 않는다.
근거: pinned tree의 기존 application/SQLite/adapter/page 구조와 네 정본 문서의 shared ownership 계약.
런타임 흐름
조치: 갱신
경로: docs/runtime-flow.md
변경 내용: 명시적 초안 생성·정책 설정→현재 원장 재집계와 자산 선택→종합 검토 지문→명시적 승인→동일 트랜잭션 안의 현재 입력 재검증·여섯 식 검증→헤더/양 상세/근거/완료 응답 원자 저장→저장 마감 조회 흐름을 기록한다. 조회·render는 쓰지 않는다. 집계에서는 일반 이체/상품권 자산 이동 제외를 우선하고 저축 목록의 적격 거래·대출상환을 지출로 반영하며 환급은 환급월 원래 지출 카테고리에 음수로 반영한다. 대응 분류 부재와 미결정 미분류는 경고 및 승인 차단 후 명시적 판단·재검토 경로로 돌아가고, 확정 제외 거래는 차단하지 않는다. 승인/최종 마감은 이 정책과 판단 상태를 다시 검증하며 자산 조건부 승인으로 우회할 수 없다. 정책·원장·분류·평가자료 변경의 재검토/재승인, 합계/통화/근거 오류·중간 실패 롤백, 동시 요청·멱등 replay, 신규 정정 run 생성과 최초/정정 조회 분리를 명시한다. 외부 가격/FX 호출은 도입하지 않고 원래 증거 출처를 유지한다. 관찰 항목은 월·run·draft version·지문·집계/저장 구조 버전·구버전/부분 상태·경고/제외이며 credential/raw 민감자료를 로그에 노출하지 않는다.
근거: 기존 BEGIN IMMEDIATE와 completion/replay 경계를 양 영역으로 확장하며 기존 read-only/auth·correction 계약을 유지한다.
미해결 질문
없음
<!-- gitea-agent:discovery-draft/v2 -->
## 요약
Issue #18의 기존 월정산 초안·승인·마감을 종합 월정산으로 확장한다. 기존 `asset_snapshot_runs`를 마감 헤더로 유지하고 여섯 총계와 집계 정책·버전·당시 근거를 고정 저장하며, 자산·부채 상세 및 카테고리별 수입·지출 상세를 동일 마감 ID에 연결한다. 별도 독립 현금흐름 정산은 만들지 않는다. 초안은 재계산 가능한 미리보기, 마감은 불변 스냅샷이라는 경계를 MCP와 Dashboard에 동일하게 적용한다.
핵심 저장·승인·정정·호환성 범위는 원본 요청에서 확정됐다. 사용자가 선택한 정책에 따라 환급은 환급 거래월의 원래 지출 카테고리에 음수 지출로 반영하고, 저축은 초안 정책의 명시적 대분류/세부분류 목록으로 식별하며, 포함/제외가 결정되지 않은 미분류 거래는 종합 승인을 차단한다. 미해결 질문은 없다. 이 검토는 Discovery 승인이나 구현 handoff를 수행하지 않는다.
## 문제
현재 `get_monthly_cashflow_report()`는 활성 거래와 현재 활성 카테고리의 `cashflow_kind`를 조회할 때마다 집계한다. 수입은 `income`, 지출은 `expense`만 합산하므로 원본의 생활비+저축+대출상환 정책과 동일하지 않다. 환급의 방향도 별도 정책으로 처리하지 않는다.
월정산 요약은 현재 거래 집계와 자산 마감 보고서를 조합하며, 초안 검토 지문과 최종 저장은 자산 평가 중심이다. 현금흐름 근거·정책·카테고리별 결과가 마감에 함께 고정되지 않아 원장이나 분류 변경 후 과거 월 결과가 달라질 수 있다. 최초 마감과 정정 마감을 월만으로 묶으면 서로 다른 결과가 섞일 위험도 있다.
## 포함 범위
- 기존 초안 lifecycle의 생성→수정→검토→승인→최종 마감을 확장한다. 초안에 현금흐름 집계 정책과 집계 로직 버전을 보존하고, 조회·검토에서 여섯 총계, 카테고리별 수입·지출 및 제외 내역, 자산·부채 평가자료, 미분류·자료 누락·검토 경고를 제공한다. 초안용 카테고리 상세 테이블은 필수로 추가하지 않는다.
- `asset_snapshot_runs`에 `settlement_month`, `currency`, `income_total`, `expense_total`, `net_cashflow`, `total_assets`, `total_liabilities`, `net_worth`, `cashflow_policy_json`, `aggregation_version`, `snapshot_schema_version`, `source_snapshot_json`을 추가한다. 기존 기준일·승인·초안 연결·정정 연결·사유·멱등성 정보를 유지한다.
- 기존 `asset_snapshots.snapshot_run_id` 연결을 유지한다. 자산/부채 구분, 계좌·실물자산별 금액, 원본 평가일·평가자료 ID, exact/이월/대체 적용, 포함·제외 및 사유를 저장 근거와 반환 계약에서 보존한다. 기존 필드·근거 JSON으로 보존 가능한 부분을 재사용하며, 필요한 보완만 한다.
- 신규 `settlement_cashflow_snapshots`에 `id`, `settlement_run_id`, `cashflow_kind`, `category`, `subcategory`, `amount`, `transaction_count`, `currency`를 저장한다. `settlement_run_id`는 `asset_snapshot_runs.id` 외래키이며, `(settlement_run_id, cashflow_kind, category, subcategory, currency)`를 유일하게 하고 마감 ID 조회 인덱스를 둔다. 상세 구분은 `income`/`expense`/`excluded`다. 대분류·세부분류의 빈 문자열은 명시적으로 보존하고 NULL로 유일 제약을 우회하지 않는다.
- 월 수입·지출은 해당 월 1일 이상 다음 달 1일 미만의 활성 거래에서 계산한다. 자산·부채는 초안의 기존 달력 월말 기준일 평가자료를 사용한다. 생활비+저축+대출상환을 실무 지출로 포함하고 이체·초기잔액 조정은 총계에서 제외한다. 상품권 충전 등 자산 이동과 실제 소비가 중복 지출되지 않도록 집계 정책과 근거를 연결한다. 환급은 환급 거래월의 원래 지출 카테고리에 음수 지출로 반영하며 과거 마감은 수정하지 않는다. 대응 분류가 없으면 미분류 경고를 표시하고 판단 확정 전 종합 승인을 차단한다. 저축은 사용자가 검토/승인 전에 지정한 대분류/세부분류 목록으로만 식별하고 초안 정책·지문·당시 근거에 보존한다. 일반 이체·상품권 충전 제외를 우선하며 `investment` 전체를 저축으로 포함하지 않고 `debt_payment`은 지출로 매핑한다. 포함/제외가 결정되지 않은 미분류는 재분류 또는 명시적 정책 판단 후 재검토한다. 정책상 확정 제외 거래는 blocker가 아니며 자산 조건부 승인 플래그로 현금흐름 blocker를 우회하지 않는다.
- `source_snapshot_json`에는 거래 ID뿐 아니라 당시 날짜·금액·통화·거래 유형·대분류·세부분류·적용 분류와 정책·포함/제외 판단 등 계산 재현에 필요한 값을 보존한다. 자산 영역은 선택된 평가자료·적용 금액·출처·원본 평가일·이월/대체·누락/제외를 기존 고정 근거와 연결한다. 앱 직접 확인값과 거래 역산값의 출처를 구분한다.
- 검토·승인 지문에 거래 집합과 당시 값, 카테고리 분류, 집계 정책·버전, 카테고리별 결과·여섯 총계, 선택 평가자료, 포함·제외·미확정 상태를 결합한다. 거래 추가·삭제·변경, 재분류, 정책 변경 및 평가자료 변경을 감지하며 승인 결과와 저장 결과가 다르면 마감을 거부한다.
- 하나의 DB 트랜잭션에서 헤더·양 영역 상세·근거와 초안 완료/최종 run 연결·저장 응답을 기록한다. `income_total = income 상세 합계`, `expense_total = expense 상세 합계`, `net_cashflow = income_total - expense_total`, `total_assets = 반영 자산 상세 합계`, `total_liabilities = 반영 부채 상세 합계`, `net_worth = total_assets - total_liabilities`를 검증한다. 제외·중복·금액 미확인 항목은 합산하지 않는다. 불일치나 중간 저장 실패는 전체 롤백하고 초안 완료 상태도 남기지 않는다.
- 같은 멱등 키와 같은 승인 요청은 동일 run과 저장 결과를 반환하고, 다른 요청은 기존 충돌 계약대로 거부한다. 마감 후 원장 변경으로 기존 헤더·상세·근거를 갱신하지 않는다. 정정은 기존 단일 child·최신 parent 검증을 유지하며 새로운 run과 상세를 append하고 최초·정정 각각을 조회할 수 있게 한다.
- additive/idempotent 이관으로 기존 자산 전용 마감을 구버전으로 구분한다. 저장되지 않은 총계는 NULL로 두고 현재 원장으로 소급 backfill하거나 0으로 꾸미지 않는다. 기존 미마감 초안은 유지하되 종합 검토·재승인을 거쳐야 새 구조로 마감할 수 있다.
- MCP 및 인증된 Dashboard의 초안·마감 조회를 확장한다. 저장된 마감 결과와 현재 원장 보고를 출처·마감 ID·버전으로 구분하며 구버전·부분 자료·누락/제외를 표시한다. 기존 자산 API의 입력·응답·오류·별칭과 자산 전용 직접 마감의 의미는 보존한다. 자산 전용 직접 마감을 종합 마감으로 오인하지 않게 구분한다.
- synthetic 테스트로 여섯 검증식, 이체·환급·저축·대출상환·상품권 충전·초기잔액 조정, 정책/원장/분류/평가자료 drift, 재시도·동시성, 롤백, 역사 불변성, 정정 분리, NULL 이관, 기존 API·MCP·Dashboard 호환을 검증한다. 환급월 귀속·음수 지출 상세/총계와 과거 마감 불변, 저축 목록의 적격/비적격 및 이체/상품권 제외 우선·정책 변경 시 재승인, 미분류 승인 차단·확정 제외 비차단·자산 조건부 승인으로 우회 불가를 포함한다.
## 제외 범위
- 독립 현금흐름 정산 또는 별도 승인 lifecycle, 원장 자동 재작성·추정 거래 생성, 마감 행 직접 덮어쓰기, 과거 수입·지출 소급 재계산 저장.
- 초안용 카테고리 상세 테이블을 필수로 도입하는 변경, Dashboard 인증·cookie·logout·메뉴 정체성 변경, 신규 MCP 인증 체계. 환급을 수입으로 처리하는 대안, `investment` 전체를 저축으로 일괄 포함하거나 거래별 저축 지정 목록을 별도 채택하는 대안, 미결정 미분류를 별도 승인으로 제외한 현금흐름 조건부 마감은 채택하지 않는다. 이는 기존 자산 조건부 승인 계약을 폐지하는 뜻이 아니다.
- 외부 가격/환율 API 또는 다중 통화 환산의 신규 도입. 현재 정산의 KRW 권위 경계를 유지하고 이종 통화를 무환산 합산하지 않는다. 향후 환산을 별도 승인하면 정책·실제 사용 환율 보존을 함께 정의해야 한다.
- Discovery 작업 중 코드 구현, DB 이관 실행, 후속 정본 문서 편집, 제품 테스트 실행, PR 생성 및 Issue 라벨·담당자·상태 변경.
## 제약 조건
- 조사 기준은 `main`의 exact pinned revision `f67c07adf01d1f17415bf7cf836f46997ca6f21c`다. 구현 시 새 revision으로 바뀌면 관련 근거와 호환성 계약을 다시 확인한다.
- 금액은 기존 정수 정밀도를 유지하고 NULL과 명시적 0을 구별한다. 총계와 반영 상세의 통화를 일치시키며 현재 KRW 권위 제한을 완화하지 않는다.
- 기존 verified 평가 권위, exact/이월/명시적 대체 승인, 대표 부채만 합산, 투자 평가 중복 합산 금지, 누락/제외 사유·조건부 승인, 월별 활성 초안 하나, 읽기 전용 조회/no-create DB 경계를 유지한다.
- `cashflow_kind`의 원장 분류(`investment`, `debt_payment` 등)와 마감 상세의 세 구분은 별개다. 당시 원래 분류와 정책에 따른 최종 구분을 모두 근거에 남기며, 이 기능을 위해 기존 실시간 보고 정책을 조용히 바꾸지 않는다.
- 비밀번호·토큰·cookie 등 인증정보를 집계 근거나 오류·로그에 저장하지 않는다. 문서·fixture·테스트는 synthetic 자료를 사용하고 기존 Dashboard auth-before-DB를 유지한다.
- 원본의 열 가지 수용 기준과 사용자가 선택한 환급 음수 지출·저축 분류 목록·미분류 승인 차단 정책을 후속 요구사항·인수 기준·검증에 추적한다. 환급 차감으로 지출 상세·지출 총계가 음수가 될 수 있으므로 이를 0으로 clamp하거나 양수 원장 금액 규칙으로 거부하지 않는다. 정책 편집과 분류/판단 변경은 기존 version/지문/승인 무효화 경계를 따르며 같은 거래는 한 번만 집계한다. 구현 후 제품 검증은 후속 작업이며 사용자 정책 선택만으로 테스트 통과를 주장하지 않는다.
## 확정된 결정
- 원본 Issue #18이 여섯 총계의 영속 저장, 기존 `asset_snapshot_runs` 확장·명칭 유지, 기존 자산 상세 유지, 신규 카테고리 상세의 마감 ID 연결을 명시했다.
- 초안 현금흐름은 재계산하고 최종 마감에서 고정한다. 생활비+저축+대출상환을 지출 기준으로 사용하며 이체·초기잔액 조정을 총계에서 제외하고 상품권 자산 이동/소비의 중복 집계를 금지한다.
- 승인 지문 확장·관련 변경 시 재검토/재승인, 원자적 저장·합계 검증·롤백, 멱등 재시도, 불변 마감 및 append-only 정정은 필수다.
- 구버전의 미보존 총계는 NULL이며 소급 집계를 하지 않는다. 기존 미마감 초안과 자산 API 호환성을 유지하고 MCP·Dashboard에서 종합 조회를 제공한다.
- 문서 분류상 기존 계좌 평가·자산 마감·월정산 초안 기능을 확장하므로 기존 `REQ-003`을 갱신한다. 별도의 `REQ-004`를 할당하지 않는다. 확정된 새 표와 사용자가 선택한 현금흐름 정책을 함께 반영한다.
- 환급은 해당 환급 거래월의 원래 지출 카테고리에 음수 지출로 반영한다. 대응 분류가 없으면 미분류 경고로 처리하고, 재분류 또는 명시적 정책 판단으로 포함/제외가 확정될 때까지 종합 승인을 차단한다. 과거 마감은 수정하지 않고 지출 상세·총계에 음수 가능성을 명세한다.
- 저축 대상 대분류/세부분류의 명시적 목록을 초안 정책에 저장하고 적격 거래만 저축 지출로 매핑한다. 일반 이체·상품권 충전의 제외를 우선하며 `investment` 전체를 일괄 포함하지 않는다. 목록은 검토/승인 전에 사용자가 지정하고 지문·근거에 고정한다. `debt_payment`은 지출로 매핑하며 원장 분류는 자동 수정하지 않는다.
- 집계 포함/제외가 결정되지 않은 미분류 거래는 종합 승인을 막고 재분류 또는 명시적 정책 판단 후 재검토한다. 일반 이체 등 정책상 확정 제외 거래는 blocker가 아니다. 기존 자산 조건부 승인 플래그로 미분류 현금흐름 차단을 우회하지 않으며 자산의 기존 누락/이월/대체 조건부 승인 계약은 유지한다.
## 저장소 근거
모든 경로는 대상 저장소 기준이며 아래 근거는 pinned revision `f67c07adf01d1f17415bf7cf836f46997ca6f21c`에서 직접 읽었다. 테스트 파일은 정적 검토 근거이며 이번 Discovery 작업에서 제품 테스트를 실행하지 않았다.
- `jarca_ledger_mcp/infrastructure/sqlite/schema.py:150-197`: 기존 `asset_snapshots`, `asset_snapshot_runs` 필드·멱등성·최초 마감/정정 parent 유일 인덱스. 원본 요청의 추가 헤더 필드 및 신규 현금흐름 표는 현재 tree에 없다.
- `jarca_ledger_mcp/infrastructure/sqlite/migrations.py`, `docs/data-model.md`: additive/idempotent 이관과 `settlement_drafts`, `settlement_draft_items`, 초안 승인·최종 응답 저장 구조.
- `jarca_ledger_mcp/application/shared.py:31-51`: 거래 유형과 `cashflow_kind` SQL fallback. `investment`·`debt_payment`·`unknown`을 구별한다.
- `jarca_ledger_mcp/application/transaction_report_services.py:51-97`: 월 범위, 현재 활성 카테고리 join, 현금흐름 및 지출 카테고리 재집계. 현재 보고에는 선택된 저축 목록·대출상환 지출 mapping·환급 음수 지출 정책이 구현되어 있지 않으므로, 기존 보고를 보존하면서 종합 정산의 정책 적용을 확장해야 한다.
- `jarca_ledger_mcp/application/settlement_draft_service.py:63-150,319-390`: 자산 평가 중심의 검토 지문·세 자산 총계, 승인 비교, `BEGIN IMMEDIATE`, run/rows·완료 응답 저장 및 멱등 replay. 현재 지문에는 현금흐름 거래·분류·정책이 없다.
- `jarca_ledger_mcp/application/assets_service.py`의 `_snapshot_rows_for_run`, `_snapshot_rows_for_latest_run`, `get_asset_snapshot_report`: exact run별 고정 상세, 최신/legacy 마감 조회. `jarca_ledger_mcp/application/monthly_settlement_service.py:89-104`는 실시간 현금흐름과 자산 보고를 조합한다.
- `jarca_ledger_mcp/adapters/mcp/server.py`, `jarca_ledger_mcp/adapters/mcp/tool_contracts.py`, `jarca_ledger_mcp/adapters/streamlit/settlement_gateway.py`, `dashboard/page_views/settlement_drafts.py`, `dashboard/page_views/monthly_settlement.py`: 공통 service를 이용하는 기존 도구·gateway·명시적 승인/마감 UI. UI의 자산 전용 설명을 종합 정산 경계에 맞춰 수정할 지점이다.
- `tests/test_asset_snapshot_idempotency.py`, `tests/test_legacy_snapshot_migration.py`, `tests/test_settlement_draft_policy_consistency.py`: 저장 근거 replay·legacy 읽기·정수/통화·자산 총계 및 stale 승인 회귀의 기존 검증 계약.
- `docs/requirements/req-001_dashboard-administrator-authentication.md`, `req-002_dashboard-menu-transition.md`, `req-003_account-valuation-history.md`는 ID 001~003이 연속이고 중복이 없다. `REQ-003` revision 1.1의 `.06`~`.11`, 특히 `.11`은 기존 초안·승인·원자적 마감을 규정한다. 이번 요청은 이를 확장하므로 같은 ID·경로를 유지한다.
- `docs/data-model.md`, `docs/interface-contract.md`, `docs/project-structure.md`, `docs/runtime-flow.md`는 모두 존재하며 정본을 update-in-place한다.
- 선택 정책의 구현 경로는 기존 `settlement_drafts`의 durable 정책 확장, service 계층의 집계 mapping과 승인 지문/거부 guard 확장, 신규 현금흐름 상세의 signed INTEGER 저장이다. 기존 `ledger_categories`와 거래의 대분류/세부분류를 목록 식별 근거로 사용할 수 있으며 기존 자산 승인 플래그와 현금흐름 미분류 blocker를 구별한다. 이는 pinned 소스의 확장 가능 지점을 확인한 결과이며 새 집계·승인 기능이 이미 구현되었거나 제품 테스트를 통과했다는 주장이 아니다.
## 문서 영향 및 산출물
### 요구사항명세서
- 조치: 갱신
- 경로: `docs/requirements/req-003_account-valuation-history.md`
- 문서 ID: `REQ-003`
- 제목: 계좌 기준일 평가 이력과 자산 마감 연동
- 변경 내용: 기존 revision 1.1에서 변경 revision을 올리고 `.11`의 종합 월정산 초안·승인·마감 확장과 관련 `.06`~`.10`의 영향 범위·인수 기준을 정합하게 갱신한다. 기존 계좌 평가·부채 정의·호환성·인증·메뉴 계약을 유지한다. 기존 Issue/PR 추적성을 삭제하지 않고 이번 변경은 Issue `#18`, 구현 PR `미정`으로 추가하며 실제 PR 생성 후 같은 저장소 `#번호`로 갱신한다.
- 포함할 내용: 여섯 총계/계산식·월 범위/월말 기준·정수/NULL/통화, 헤더/양 상세/근거 연결 및 제약, 사용자가 선택한 저축 분류 목록·`debt_payment` 지출 mapping·일반 이체/상품권 제외 우선·환급 거래월의 음수 지출·대응 분류 부재 및 미분류 승인 차단 정책, 승인 지문의 전체 입력과 drift 거부, 원자적 저장·완료 연결·롤백, 동일/다른 멱등 요청·동시성, append-only 최초/정정 불변성과 exact run 조회, legacy NULL 이관·미마감 재승인, 현재 보고와 저장 마감 구분, MCP/UI/기존 API/읽기 전용/보안 호환. 원본 수용 기준별 정상·경계·실패 synthetic 검증과 기존 named regression 보존을 명시한다.
- 제외할 내용: 독립 정산, 자동 승인·원장 추정/재작성·소급 집계, 새로운 환산 서비스·인증/메뉴 변경, 원본에 없는 필수 기능 추가.
- 근거: `REQ-003.11`과 기존 `.06`~`.09`의 lifecycle·마감·월정산 전파를 확장하는 변경이다. 기존 제외 범위였다는 이유로 새 요구사항을 만들지 않으며 기존 ID·경로·제목을 유지한다.
### 데이터 모델
- 조치: 갱신
- 경로: `docs/data-model.md`
- 변경 내용: 확정된 헤더 추가 필드별 타입·NULL/legacy 의미·필수성, 정책/버전/근거 JSON의 재현 입력, 초안 정책·집계 버전·승인 지문·최종 응답을 명세한다. 환급을 표현하는 현금흐름 상세 `amount`와 지출/순현금흐름 총계의 signed INTEGER 의미, 원장 양수 금액과 적용 음수의 구별, 원래 환급 유형/분류·환급월 및 계산 근거를 기록한다. 저축 목록은 대분류/세부분류 쌍과 제외 우선 정책을 초안·마감 정책 JSON에 보존하며 미분류 판단 상태·경고를 승인 지문에 결합한다. 미결정 미분류는 final 상세로 승인 제외 처리하지 않는다. 신규 현금흐름 상세의 전체 필드, 헤더 FK·삭제/수정으로 역사 손실 금지, 다섯 요소 유일 제약·빈 분류 의미·조회 인덱스 및 run별 연결을 기록한다. 자산 상세의 분류·평가일/ID·exact/이월/대체·포함/제외 보존 위치와 총계 불변식을 연결한다. additive/idempotent 이관, 구버전 식별·미보존 NULL·무소급 정책, 기존 초안 재검토 및 동시성/원자성을 기록한다.
- 근거: 현재 두 마감 테이블과 초안 표가 정본에 존재하며 원본이 그 확장 및 새로운 상세 표를 요구한다.
### 인터페이스 계약
- 조치: 갱신
- 경로: `docs/interface-contract.md`
- 변경 내용: 기존 create/get/list/update/review/approve/finalize 초안 operation에 정책·버전·여섯 총계·카테고리 상세·누락/경고의 입력/출력과 필수성을 정의한다. 기존 파라미터·envelope·오류·`db_path`의 MCP 비노출을 유지하며 정책 편집은 version 증가와 승인 무효화를 따른다. 저축 대분류/세부분류 목록의 명시적 편집·조회, 환급 차감/음수 출력, 대응 분류 부재·미분류 경고와 종합 승인 차단의 오류/재검토 동작을 정의한다. 자산 조건부 승인 플래그는 이 차단을 우회하지 못하고 정책상 확정 제외는 승인을 막지 않는다. 저장 마감 조회는 exact run과 최신 run, 구버전 NULL 및 현재 원장 집계의 구분을 명시하고 동일/충돌 재시도·stale approval·합계 불일치·저장 실패의 no-partial-write 오류를 정의한다. MCP·Dashboard가 같은 business policy/read model을 쓰고 인증·기존 자산 직접 마감 계약을 유지하는 범위를 기록한다. 실제 공개 함수/도구 시그니처와 응답 예시는 구현과 대조하며 아직 존재하지 않는 API를 현재 계약으로 주장하지 않는다.
- 근거: 기존 draft service·tool 계약·settlement gateway·asset report의 확장이다. 신규 조회가 필요하면 기존 인터페이스를 깨지 않는 additive 구현과 문서 매핑이 필요하다.
### 프로젝트 구조
- 조치: 갱신
- 경로: `docs/project-structure.md`
- 변경 내용: SQLite `schema.py`/`migrations.py`가 저장 제약/이관, `application/settlement_draft_service.py`가 정책·검토·승인·원자적 마감, `assets_service.py`가 기존 자산 권위/read model, `monthly_settlement_service.py`가 월별 종합 조회를 담당하는 책임 경계를 갱신한다. 기존 `transaction_report_services.py`의 실시간 보고와 저장 마감 계산을 혼동하지 않으며 공통 business 집계를 service 계층에서 재사용한다. 선택된 환급 부호·저축 목록·제외 우선·미분류 차단은 이 공통 계층에서 결정하며 MCP와 Dashboard는 목록 입력·경고·승인 불가 사유를 전달/표시하고 정책을 따로 판단하지 않는다. facade→MCP/Streamlit gateway→page의 기존 의존 방향을 유지하고 UI/transport에 SQL·독립 집계/승인 정책을 복제하지 않는다. 기존 lifecycle·idempotency·legacy·adapter/UI regression과 종합 cashflow 신규 synthetic 테스트의 배치·검증 책임을 기록한다. 새 모듈 경로는 실제 필요와 구현 결과에 결속하며 불필요한 새 directory 구조를 요구하지 않는다.
- 근거: pinned tree의 기존 application/SQLite/adapter/page 구조와 네 정본 문서의 shared ownership 계약.
### 런타임 흐름
- 조치: 갱신
- 경로: `docs/runtime-flow.md`
- 변경 내용: 명시적 초안 생성·정책 설정→현재 원장 재집계와 자산 선택→종합 검토 지문→명시적 승인→동일 트랜잭션 안의 현재 입력 재검증·여섯 식 검증→헤더/양 상세/근거/완료 응답 원자 저장→저장 마감 조회 흐름을 기록한다. 조회·render는 쓰지 않는다. 집계에서는 일반 이체/상품권 자산 이동 제외를 우선하고 저축 목록의 적격 거래·대출상환을 지출로 반영하며 환급은 환급월 원래 지출 카테고리에 음수로 반영한다. 대응 분류 부재와 미결정 미분류는 경고 및 승인 차단 후 명시적 판단·재검토 경로로 돌아가고, 확정 제외 거래는 차단하지 않는다. 승인/최종 마감은 이 정책과 판단 상태를 다시 검증하며 자산 조건부 승인으로 우회할 수 없다. 정책·원장·분류·평가자료 변경의 재검토/재승인, 합계/통화/근거 오류·중간 실패 롤백, 동시 요청·멱등 replay, 신규 정정 run 생성과 최초/정정 조회 분리를 명시한다. 외부 가격/FX 호출은 도입하지 않고 원래 증거 출처를 유지한다. 관찰 항목은 월·run·draft version·지문·집계/저장 구조 버전·구버전/부분 상태·경고/제외이며 credential/raw 민감자료를 로그에 노출하지 않는다.
- 근거: 기존 `BEGIN IMMEDIATE`와 completion/replay 경계를 양 영역으로 확장하며 기존 read-only/auth·correction 계약을 유지한다.
## 미해결 질문
없음
excluded 상세는 이체·초기잔액 조정 등 집계 제외 내역의 설명용이며, 수입·지출 총계에는 포함하지 않는다.
4. 기존 정산 초안 확장
초안에 다음을 보존한다.
현금흐름 집계 정책
집계 로직 버전
초안 조회 시 다음을 함께 제공한다.
여섯 총계 미리보기
카테고리별 수입·지출
자산·부채 평가자료
미분류·자료 누락·검토 경고
초안 단계의 카테고리별 금액은 원장에서 재계산하고, 최종 마감 시 고정 저장한다. 초안용 카테고리 상세 테이블은 우선 필수로 추가하지 않는다.
집계 기준 및 근거 보존
실무 지출 기준인 생활비 + 저축 + 대출상환, 이체 제외를 명시한다.
환급의 수입 처리 또는 지출 차감 여부를 정책으로 명확히 정한다.
초기잔액 조정은 실제 소득·생활비와 구분하고 수입·지출 총계에서 제외한다.
상품권 충전 등 자산 이동과 실제 소비가 중복 지출로 집계되지 않도록 한다.
기존 cashflow_kind 분류와 집계 정책의 일관성을 검증한다.
총계 통화와 상세 통화를 일치시킨다. 다중 통화를 합산할 경우 환산 정책과 사용 환율을 보존한다.
월말 앱 직접 확인값과 거래내역 역산값의 출처를 구분하여 유지한다.
source_snapshot_json에는 거래 ID뿐 아니라 당시 금액·분류·집계 구분 등 계산 재현에 필요한 값을 보존한다. 거래 ID만 저장하면 이후 원장 수정 시 마감 당시 내용을 재현할 수 없다.
비밀번호·토큰 등 인증정보는 저장하지 않는다.
검토·승인·마감 규칙
검토 및 승인 지문에 다음을 포함한다.
수입·지출 집계 및 카테고리별 결과
집계 정책과 근거
선택된 자산·부채 평가자료
포함·제외 및 미확정 상태
검토·승인 이후 거래, 분류, 집계 정책 또는 평가자료가 변경되면 재검토·재승인이 필요하다.
마감 시 다음을 하나의 DB 트랜잭션으로 저장한다.
월정산 헤더
자산·부채 상세
카테고리별 수입·지출 상세
집계 근거
검증식:
income_total = 수입 상세 합계
expense_total = 지출 상세 합계
net_cashflow = income_total − expense_total
total_assets = 반영된 자산 상세 합계
total_liabilities = 반영된 부채 상세 합계
net_worth = total_assets − total_liabilities
합계 불일치 시 전체 저장을 롤백한다.
승인한 결과와 저장할 결과가 다르면 마감을 거부한다.
동일 요청 재시도 시 중복 마감이 생성되지 않아야 한다.
마감 불변성 및 정정
마감 후 원장을 변경해도 기존 마감 스냅샷은 바뀌지 않는다.
마감 헤더와 상세를 직접 덮어쓰지 않는다.
수정이 필요하면 기존 정정 연결 구조를 이용하여 새 마감 헤더와 상세를 생성한다.
최초 마감과 정정 마감의 근거 및 결과를 각각 보존한다.
기존 데이터 이관
기존 자산 전용 마감은 구버전 스냅샷으로 구분한다.
보존되지 않은 총계는 NULL로 유지한다. 누락을 0원으로 표현하지 않는다.
현재 원장으로 재계산한 과거 수입·지출을 당시 마감값으로 소급 저장하지 않는다.
필요하면 명시적인 보완·정정 마감으로 생성한다.
기존 미마감 초안은 유지하고, 종합 검토·승인을 거쳐 마감할 수 있도록 한다.
API 및 화면 변경
정산 초안 조회에서 여섯 총계와 양 영역의 상세를 제공한다.
마감 조회에서는 저장된 스냅샷을 반환하며, 현재 원장 집계와 구분한다.
MCP 및 대시보드에서 종합 월정산 조회를 지원한다.
기존 자산 정산 API와의 호환성을 유지한다.
부분 자료로 승인된 마감이라면, 총계가 전체 자산을 의미하는 것으로 오해되지 않도록 누락·제외 정보를 함께 제공한다.
수용 기준
헤더에 여섯 총계를 모두 저장한다.
자산·부채 상세와 카테고리별 수입·지출 상세를 같은 마감 ID로 연결한다.
헤더·상세·근거를 원자적으로 저장하고 합계 불일치 시 롤백한다.
승인 이후 관련 원장·분류·정책·평가자료 변경을 감지하여 마감을 거부한다.
원장 수정 후에도 기존 마감 결과가 유지된다.
최초·정정 마감의 상세가 섞이지 않으며 이력이 보존된다.
이체·환급·저축·대출상환·상품권 충전·초기잔액 조정 처리 테스트를 제공한다.
기존 API 호환 및 구버전 데이터의 NULL 이관 테스트를 제공한다.
재시도 중복 방지 및 부분 저장 실패 테스트를 제공한다.
MCP·대시보드에서 종합 초안과 저장된 마감 결과를 조회할 수 있다. (3/3)
<!-- gitea-agent:discovery-origin/v1 owner=jsfamily repository=jarca-ledger-mcp issue=18 body-sha256=7c5b4238af5a8a441663b0e3338e71d04a4e4ef977789e6bc4a6836bb67a64fc -->
## Discovery 작성 전 이슈 원본
## 배경 및 목적
현재 정산 초안과 최종 마감은 자산·부채 중심으로 구성되어 있다. 월 수입·지출 및 카테고리별 금액은 거래 원장에서 조회 시 재계산되므로, 과거 거래나 분류를 변경하면 이전 월의 보고 결과도 달라질 수 있다.
기존 정산 구조를 **종합 월정산**으로 확장하여, 마감 당시의 수입·지출과 월말 자산·부채를 함께 보존한다. 별도의 독립 현금흐름 정산은 만들지 않는다.
## 확정 결정
월정산 헤더에 다음 여섯 값을 모두 저장한다.
- `income_total`: 월 수입
- `expense_total`: 월 지출
- `net_cashflow`: 수입 − 지출
- `total_assets`: 월말 자산
- `total_liabilities`: 월말 부채
- `net_worth`: 자산 − 부채
수입·지출은 해당 월 전체 기간의 흐름이며, 자산·부채는 월말 기준일의 잔액이다.
## 전체 구조
```text
월정산 초안
└─ 검토 → 승인 → 최종 마감
│
▼
월정산 헤더 — 기존 asset_snapshot_runs 확장
├─ 여섯 총계
├─ 집계 기준·승인·정정 이력
├─ 자산·부채 상세 — 기존 asset_snapshots 유지
└─ 카테고리별 수입·지출 상세 — 신규 테이블
```
카테고리별 상세는 정산월만이 아니라 **고유한 월정산 마감 ID**로 연결한다. 최초 마감과 정정 마감의 상세가 섞이지 않아야 한다.
## DB 변경 사항
### 1. 기존 마감 헤더 확장 — `asset_snapshot_runs`
기존 테이블을 종합 월정산 마감 헤더로 확장한다. 테이블명은 우선 유지하고 기존 API와의 호환성을 확보한다.
추가 필드:
- `settlement_month`
- `currency`
- `income_total`
- `expense_total`
- `net_cashflow`
- `total_assets`
- `total_liabilities`
- `net_worth`
- `cashflow_policy_json`: 집계 기준
- `aggregation_version`: 집계 로직 버전
- `snapshot_schema_version`: 저장 구조 버전
- `source_snapshot_json`: 마감 당시 집계 근거
기존 기준일, 승인 정보, 초안 연결, 정정 대상 및 사유 등은 유지한다.
### 2. 기존 자산·부채 상세 유지 — `asset_snapshots`
기존 `snapshot_run_id`로 월정산 헤더와 연결하는 구조를 유지한다.
다음 정보가 마감 상세에 보존되는지 점검하고 부족한 부분은 보완한다.
- 자산·부채 구분
- 계좌·실물자산별 금액
- 원본 평가일 및 평가자료 ID
- 기준일 일치·이월·대체 적용 여부
- 포함·제외 여부 및 사유
헤더 총계에는 실제 반영 대상 상세만 합산한다. 중복 제외 항목과 금액 미확인 항목은 합산하지 않는다.
### 3. 카테고리별 상세 추가 — `settlement_cashflow_snapshots`
필드:
- `id`
- `settlement_run_id`: `asset_snapshot_runs.id`를 참조하는 외래키
- `cashflow_kind`: `income` / `expense` / `excluded`
- `category`
- `subcategory`
- `amount`
- `transaction_count`
- `currency`
제약 및 인덱스:
- 월정산 헤더 외래키 적용
- 다음 조합의 유일 제약:
- 마감 ID
- 현금흐름 구분
- 대분류
- 세부분류
- 통화
- 마감 ID 조회 인덱스
`excluded` 상세는 이체·초기잔액 조정 등 집계 제외 내역의 설명용이며, 수입·지출 총계에는 포함하지 않는다.
### 4. 기존 정산 초안 확장
초안에 다음을 보존한다.
- 현금흐름 집계 정책
- 집계 로직 버전
초안 조회 시 다음을 함께 제공한다.
- 여섯 총계 미리보기
- 카테고리별 수입·지출
- 자산·부채 평가자료
- 미분류·자료 누락·검토 경고
초안 단계의 카테고리별 금액은 원장에서 재계산하고, 최종 마감 시 고정 저장한다. 초안용 카테고리 상세 테이블은 우선 필수로 추가하지 않는다.
## 집계 기준 및 근거 보존
- 실무 지출 기준인 **생활비 + 저축 + 대출상환, 이체 제외**를 명시한다.
- 환급의 수입 처리 또는 지출 차감 여부를 정책으로 명확히 정한다.
- 초기잔액 조정은 실제 소득·생활비와 구분하고 수입·지출 총계에서 제외한다.
- 상품권 충전 등 자산 이동과 실제 소비가 중복 지출로 집계되지 않도록 한다.
- 기존 `cashflow_kind` 분류와 집계 정책의 일관성을 검증한다.
- 총계 통화와 상세 통화를 일치시킨다. 다중 통화를 합산할 경우 환산 정책과 사용 환율을 보존한다.
- 월말 앱 직접 확인값과 거래내역 역산값의 출처를 구분하여 유지한다.
`source_snapshot_json`에는 거래 ID뿐 아니라 당시 금액·분류·집계 구분 등 계산 재현에 필요한 값을 보존한다. 거래 ID만 저장하면 이후 원장 수정 시 마감 당시 내용을 재현할 수 없다.
비밀번호·토큰 등 인증정보는 저장하지 않는다.
## 검토·승인·마감 규칙
검토 및 승인 지문에 다음을 포함한다.
- 수입·지출 집계 및 카테고리별 결과
- 집계 정책과 근거
- 선택된 자산·부채 평가자료
- 포함·제외 및 미확정 상태
검토·승인 이후 거래, 분류, 집계 정책 또는 평가자료가 변경되면 재검토·재승인이 필요하다.
마감 시 다음을 **하나의 DB 트랜잭션**으로 저장한다.
1. 월정산 헤더
2. 자산·부채 상세
3. 카테고리별 수입·지출 상세
4. 집계 근거
검증식:
```text
income_total = 수입 상세 합계
expense_total = 지출 상세 합계
net_cashflow = income_total − expense_total
total_assets = 반영된 자산 상세 합계
total_liabilities = 반영된 부채 상세 합계
net_worth = total_assets − total_liabilities
```
- 합계 불일치 시 전체 저장을 롤백한다.
- 승인한 결과와 저장할 결과가 다르면 마감을 거부한다.
- 동일 요청 재시도 시 중복 마감이 생성되지 않아야 한다.
## 마감 불변성 및 정정
- 마감 후 원장을 변경해도 기존 마감 스냅샷은 바뀌지 않는다.
- 마감 헤더와 상세를 직접 덮어쓰지 않는다.
- 수정이 필요하면 기존 정정 연결 구조를 이용하여 새 마감 헤더와 상세를 생성한다.
- 최초 마감과 정정 마감의 근거 및 결과를 각각 보존한다.
## 기존 데이터 이관
- 기존 자산 전용 마감은 구버전 스냅샷으로 구분한다.
- 보존되지 않은 총계는 `NULL`로 유지한다. 누락을 0원으로 표현하지 않는다.
- 현재 원장으로 재계산한 과거 수입·지출을 당시 마감값으로 소급 저장하지 않는다.
- 필요하면 명시적인 보완·정정 마감으로 생성한다.
- 기존 미마감 초안은 유지하고, 종합 검토·승인을 거쳐 마감할 수 있도록 한다.
## API 및 화면 변경
- 정산 초안 조회에서 여섯 총계와 양 영역의 상세를 제공한다.
- 마감 조회에서는 저장된 스냅샷을 반환하며, 현재 원장 집계와 구분한다.
- MCP 및 대시보드에서 종합 월정산 조회를 지원한다.
- 기존 자산 정산 API와의 호환성을 유지한다.
- 부분 자료로 승인된 마감이라면, 총계가 전체 자산을 의미하는 것으로 오해되지 않도록 누락·제외 정보를 함께 제공한다.
## 수용 기준
- [ ] 헤더에 여섯 총계를 모두 저장한다.
- [ ] 자산·부채 상세와 카테고리별 수입·지출 상세를 같은 마감 ID로 연결한다.
- [ ] 헤더·상세·근거를 원자적으로 저장하고 합계 불일치 시 롤백한다.
- [ ] 승인 이후 관련 원장·분류·정책·평가자료 변경을 감지하여 마감을 거부한다.
- [ ] 원장 수정 후에도 기존 마감 결과가 유지된다.
- [ ] 최초·정정 마감의 상세가 섞이지 않으며 이력이 보존된다.
- [ ] 이체·환급·저축·대출상환·상품권 충전·초기잔액 조정 처리 테스트를 제공한다.
- [ ] 기존 API 호환 및 구버전 데이터의 `NULL` 이관 테스트를 제공한다.
- [ ] 재시도 중복 방지 및 부분 저장 실패 테스트를 제공한다.
- [ ] MCP·대시보드에서 종합 초안과 저장된 마감 결과를 조회할 수 있다. (3/3)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
요약
Issue #18의 기존 월정산 초안·승인·마감을 종합 월정산으로 확장한다. 기존
asset_snapshot_runs를 마감 헤더로 유지하고 여섯 총계와 집계 정책·버전·당시 근거를 고정 저장하며, 자산·부채 상세 및 카테고리별 수입·지출 상세를 동일 마감 ID에 연결한다. 별도 독립 현금흐름 정산은 만들지 않는다. 초안은 재계산 가능한 미리보기, 마감은 불변 스냅샷이라는 경계를 MCP와 Dashboard에 동일하게 적용한다.핵심 저장·승인·정정·호환성 범위는 원본 요청에서 확정됐다. 사용자가 선택한 정책에 따라 환급은 환급 거래월의 원래 지출 카테고리에 음수 지출로 반영하고, 저축은 초안 정책의 명시적 대분류/세부분류 목록으로 식별하며, 포함/제외가 결정되지 않은 미분류 거래는 종합 승인을 차단한다. 미해결 질문은 없다. 이 검토는 Discovery 승인이나 구현 handoff를 수행하지 않는다.
문제
현재
get_monthly_cashflow_report()는 활성 거래와 현재 활성 카테고리의cashflow_kind를 조회할 때마다 집계한다. 수입은income, 지출은expense만 합산하므로 원본의 생활비+저축+대출상환 정책과 동일하지 않다. 환급의 방향도 별도 정책으로 처리하지 않는다.월정산 요약은 현재 거래 집계와 자산 마감 보고서를 조합하며, 초안 검토 지문과 최종 저장은 자산 평가 중심이다. 현금흐름 근거·정책·카테고리별 결과가 마감에 함께 고정되지 않아 원장이나 분류 변경 후 과거 월 결과가 달라질 수 있다. 최초 마감과 정정 마감을 월만으로 묶으면 서로 다른 결과가 섞일 위험도 있다.
포함 범위
asset_snapshot_runs에settlement_month,currency,income_total,expense_total,net_cashflow,total_assets,total_liabilities,net_worth,cashflow_policy_json,aggregation_version,snapshot_schema_version,source_snapshot_json을 추가한다. 기존 기준일·승인·초안 연결·정정 연결·사유·멱등성 정보를 유지한다.asset_snapshots.snapshot_run_id연결을 유지한다. 자산/부채 구분, 계좌·실물자산별 금액, 원본 평가일·평가자료 ID, exact/이월/대체 적용, 포함·제외 및 사유를 저장 근거와 반환 계약에서 보존한다. 기존 필드·근거 JSON으로 보존 가능한 부분을 재사용하며, 필요한 보완만 한다.settlement_cashflow_snapshots에id,settlement_run_id,cashflow_kind,category,subcategory,amount,transaction_count,currency를 저장한다.settlement_run_id는asset_snapshot_runs.id외래키이며,(settlement_run_id, cashflow_kind, category, subcategory, currency)를 유일하게 하고 마감 ID 조회 인덱스를 둔다. 상세 구분은income/expense/excluded다. 대분류·세부분류의 빈 문자열은 명시적으로 보존하고 NULL로 유일 제약을 우회하지 않는다.investment전체를 저축으로 포함하지 않고debt_payment은 지출로 매핑한다. 포함/제외가 결정되지 않은 미분류는 재분류 또는 명시적 정책 판단 후 재검토한다. 정책상 확정 제외 거래는 blocker가 아니며 자산 조건부 승인 플래그로 현금흐름 blocker를 우회하지 않는다.source_snapshot_json에는 거래 ID뿐 아니라 당시 날짜·금액·통화·거래 유형·대분류·세부분류·적용 분류와 정책·포함/제외 판단 등 계산 재현에 필요한 값을 보존한다. 자산 영역은 선택된 평가자료·적용 금액·출처·원본 평가일·이월/대체·누락/제외를 기존 고정 근거와 연결한다. 앱 직접 확인값과 거래 역산값의 출처를 구분한다.income_total = income 상세 합계,expense_total = expense 상세 합계,net_cashflow = income_total - expense_total,total_assets = 반영 자산 상세 합계,total_liabilities = 반영 부채 상세 합계,net_worth = total_assets - total_liabilities를 검증한다. 제외·중복·금액 미확인 항목은 합산하지 않는다. 불일치나 중간 저장 실패는 전체 롤백하고 초안 완료 상태도 남기지 않는다.제외 범위
investment전체를 저축으로 일괄 포함하거나 거래별 저축 지정 목록을 별도 채택하는 대안, 미결정 미분류를 별도 승인으로 제외한 현금흐름 조건부 마감은 채택하지 않는다. 이는 기존 자산 조건부 승인 계약을 폐지하는 뜻이 아니다.제약 조건
main의 exact pinned revisionf67c07adf01d1f17415bf7cf836f46997ca6f21c다. 구현 시 새 revision으로 바뀌면 관련 근거와 호환성 계약을 다시 확인한다.cashflow_kind의 원장 분류(investment,debt_payment등)와 마감 상세의 세 구분은 별개다. 당시 원래 분류와 정책에 따른 최종 구분을 모두 근거에 남기며, 이 기능을 위해 기존 실시간 보고 정책을 조용히 바꾸지 않는다.확정된 결정
asset_snapshot_runs확장·명칭 유지, 기존 자산 상세 유지, 신규 카테고리 상세의 마감 ID 연결을 명시했다.REQ-003을 갱신한다. 별도의REQ-004를 할당하지 않는다. 확정된 새 표와 사용자가 선택한 현금흐름 정책을 함께 반영한다.investment전체를 일괄 포함하지 않는다. 목록은 검토/승인 전에 사용자가 지정하고 지문·근거에 고정한다.debt_payment은 지출로 매핑하며 원장 분류는 자동 수정하지 않는다.저장소 근거
모든 경로는 대상 저장소 기준이며 아래 근거는 pinned revision
f67c07adf01d1f17415bf7cf836f46997ca6f21c에서 직접 읽었다. 테스트 파일은 정적 검토 근거이며 이번 Discovery 작업에서 제품 테스트를 실행하지 않았다.jarca_ledger_mcp/infrastructure/sqlite/schema.py:150-197: 기존asset_snapshots,asset_snapshot_runs필드·멱등성·최초 마감/정정 parent 유일 인덱스. 원본 요청의 추가 헤더 필드 및 신규 현금흐름 표는 현재 tree에 없다.jarca_ledger_mcp/infrastructure/sqlite/migrations.py,docs/data-model.md: additive/idempotent 이관과settlement_drafts,settlement_draft_items, 초안 승인·최종 응답 저장 구조.jarca_ledger_mcp/application/shared.py:31-51: 거래 유형과cashflow_kindSQL fallback.investment·debt_payment·unknown을 구별한다.jarca_ledger_mcp/application/transaction_report_services.py:51-97: 월 범위, 현재 활성 카테고리 join, 현금흐름 및 지출 카테고리 재집계. 현재 보고에는 선택된 저축 목록·대출상환 지출 mapping·환급 음수 지출 정책이 구현되어 있지 않으므로, 기존 보고를 보존하면서 종합 정산의 정책 적용을 확장해야 한다.jarca_ledger_mcp/application/settlement_draft_service.py:63-150,319-390: 자산 평가 중심의 검토 지문·세 자산 총계, 승인 비교,BEGIN IMMEDIATE, run/rows·완료 응답 저장 및 멱등 replay. 현재 지문에는 현금흐름 거래·분류·정책이 없다.jarca_ledger_mcp/application/assets_service.py의_snapshot_rows_for_run,_snapshot_rows_for_latest_run,get_asset_snapshot_report: exact run별 고정 상세, 최신/legacy 마감 조회.jarca_ledger_mcp/application/monthly_settlement_service.py:89-104는 실시간 현금흐름과 자산 보고를 조합한다.jarca_ledger_mcp/adapters/mcp/server.py,jarca_ledger_mcp/adapters/mcp/tool_contracts.py,jarca_ledger_mcp/adapters/streamlit/settlement_gateway.py,dashboard/page_views/settlement_drafts.py,dashboard/page_views/monthly_settlement.py: 공통 service를 이용하는 기존 도구·gateway·명시적 승인/마감 UI. UI의 자산 전용 설명을 종합 정산 경계에 맞춰 수정할 지점이다.tests/test_asset_snapshot_idempotency.py,tests/test_legacy_snapshot_migration.py,tests/test_settlement_draft_policy_consistency.py: 저장 근거 replay·legacy 읽기·정수/통화·자산 총계 및 stale 승인 회귀의 기존 검증 계약.docs/requirements/req-001_dashboard-administrator-authentication.md,req-002_dashboard-menu-transition.md,req-003_account-valuation-history.md는 ID 001003이 연속이고 중복이 없다.REQ-003revision 1.1의.06.11, 특히.11은 기존 초안·승인·원자적 마감을 규정한다. 이번 요청은 이를 확장하므로 같은 ID·경로를 유지한다.docs/data-model.md,docs/interface-contract.md,docs/project-structure.md,docs/runtime-flow.md는 모두 존재하며 정본을 update-in-place한다.settlement_drafts의 durable 정책 확장, service 계층의 집계 mapping과 승인 지문/거부 guard 확장, 신규 현금흐름 상세의 signed INTEGER 저장이다. 기존ledger_categories와 거래의 대분류/세부분류를 목록 식별 근거로 사용할 수 있으며 기존 자산 승인 플래그와 현금흐름 미분류 blocker를 구별한다. 이는 pinned 소스의 확장 가능 지점을 확인한 결과이며 새 집계·승인 기능이 이미 구현되었거나 제품 테스트를 통과했다는 주장이 아니다.문서 영향 및 산출물
요구사항명세서
docs/requirements/req-003_account-valuation-history.mdREQ-003.11의 종합 월정산 초안·승인·마감 확장과 관련.06~.10의 영향 범위·인수 기준을 정합하게 갱신한다. 기존 계좌 평가·부채 정의·호환성·인증·메뉴 계약을 유지한다. 기존 Issue/PR 추적성을 삭제하지 않고 이번 변경은 Issue#18, 구현 PR미정으로 추가하며 실제 PR 생성 후 같은 저장소#번호로 갱신한다.debt_payment지출 mapping·일반 이체/상품권 제외 우선·환급 거래월의 음수 지출·대응 분류 부재 및 미분류 승인 차단 정책, 승인 지문의 전체 입력과 drift 거부, 원자적 저장·완료 연결·롤백, 동일/다른 멱등 요청·동시성, append-only 최초/정정 불변성과 exact run 조회, legacy NULL 이관·미마감 재승인, 현재 보고와 저장 마감 구분, MCP/UI/기존 API/읽기 전용/보안 호환. 원본 수용 기준별 정상·경계·실패 synthetic 검증과 기존 named regression 보존을 명시한다.REQ-003.11과 기존.06~.09의 lifecycle·마감·월정산 전파를 확장하는 변경이다. 기존 제외 범위였다는 이유로 새 요구사항을 만들지 않으며 기존 ID·경로·제목을 유지한다.데이터 모델
docs/data-model.mdamount와 지출/순현금흐름 총계의 signed INTEGER 의미, 원장 양수 금액과 적용 음수의 구별, 원래 환급 유형/분류·환급월 및 계산 근거를 기록한다. 저축 목록은 대분류/세부분류 쌍과 제외 우선 정책을 초안·마감 정책 JSON에 보존하며 미분류 판단 상태·경고를 승인 지문에 결합한다. 미결정 미분류는 final 상세로 승인 제외 처리하지 않는다. 신규 현금흐름 상세의 전체 필드, 헤더 FK·삭제/수정으로 역사 손실 금지, 다섯 요소 유일 제약·빈 분류 의미·조회 인덱스 및 run별 연결을 기록한다. 자산 상세의 분류·평가일/ID·exact/이월/대체·포함/제외 보존 위치와 총계 불변식을 연결한다. additive/idempotent 이관, 구버전 식별·미보존 NULL·무소급 정책, 기존 초안 재검토 및 동시성/원자성을 기록한다.인터페이스 계약
docs/interface-contract.mddb_path의 MCP 비노출을 유지하며 정책 편집은 version 증가와 승인 무효화를 따른다. 저축 대분류/세부분류 목록의 명시적 편집·조회, 환급 차감/음수 출력, 대응 분류 부재·미분류 경고와 종합 승인 차단의 오류/재검토 동작을 정의한다. 자산 조건부 승인 플래그는 이 차단을 우회하지 못하고 정책상 확정 제외는 승인을 막지 않는다. 저장 마감 조회는 exact run과 최신 run, 구버전 NULL 및 현재 원장 집계의 구분을 명시하고 동일/충돌 재시도·stale approval·합계 불일치·저장 실패의 no-partial-write 오류를 정의한다. MCP·Dashboard가 같은 business policy/read model을 쓰고 인증·기존 자산 직접 마감 계약을 유지하는 범위를 기록한다. 실제 공개 함수/도구 시그니처와 응답 예시는 구현과 대조하며 아직 존재하지 않는 API를 현재 계약으로 주장하지 않는다.프로젝트 구조
docs/project-structure.mdschema.py/migrations.py가 저장 제약/이관,application/settlement_draft_service.py가 정책·검토·승인·원자적 마감,assets_service.py가 기존 자산 권위/read model,monthly_settlement_service.py가 월별 종합 조회를 담당하는 책임 경계를 갱신한다. 기존transaction_report_services.py의 실시간 보고와 저장 마감 계산을 혼동하지 않으며 공통 business 집계를 service 계층에서 재사용한다. 선택된 환급 부호·저축 목록·제외 우선·미분류 차단은 이 공통 계층에서 결정하며 MCP와 Dashboard는 목록 입력·경고·승인 불가 사유를 전달/표시하고 정책을 따로 판단하지 않는다. facade→MCP/Streamlit gateway→page의 기존 의존 방향을 유지하고 UI/transport에 SQL·독립 집계/승인 정책을 복제하지 않는다. 기존 lifecycle·idempotency·legacy·adapter/UI regression과 종합 cashflow 신규 synthetic 테스트의 배치·검증 책임을 기록한다. 새 모듈 경로는 실제 필요와 구현 결과에 결속하며 불필요한 새 directory 구조를 요구하지 않는다.런타임 흐름
docs/runtime-flow.mdBEGIN IMMEDIATE와 completion/replay 경계를 양 영역으로 확장하며 기존 read-only/auth·correction 계약을 유지한다.미해결 질문
없음
Discovery 작성 전 이슈 원본
배경 및 목적
현재 정산 초안과 최종 마감은 자산·부채 중심으로 구성되어 있다. 월 수입·지출 및 카테고리별 금액은 거래 원장에서 조회 시 재계산되므로, 과거 거래나 분류를 변경하면 이전 월의 보고 결과도 달라질 수 있다.
기존 정산 구조를 종합 월정산으로 확장하여, 마감 당시의 수입·지출과 월말 자산·부채를 함께 보존한다. 별도의 독립 현금흐름 정산은 만들지 않는다.
확정 결정
월정산 헤더에 다음 여섯 값을 모두 저장한다.
income_total: 월 수입expense_total: 월 지출net_cashflow: 수입 − 지출total_assets: 월말 자산total_liabilities: 월말 부채net_worth: 자산 − 부채수입·지출은 해당 월 전체 기간의 흐름이며, 자산·부채는 월말 기준일의 잔액이다.
전체 구조
카테고리별 상세는 정산월만이 아니라 고유한 월정산 마감 ID로 연결한다. 최초 마감과 정정 마감의 상세가 섞이지 않아야 한다.
DB 변경 사항
1. 기존 마감 헤더 확장 —
asset_snapshot_runs기존 테이블을 종합 월정산 마감 헤더로 확장한다. 테이블명은 우선 유지하고 기존 API와의 호환성을 확보한다.
추가 필드:
settlement_monthcurrencyincome_totalexpense_totalnet_cashflowtotal_assetstotal_liabilitiesnet_worthcashflow_policy_json: 집계 기준aggregation_version: 집계 로직 버전snapshot_schema_version: 저장 구조 버전source_snapshot_json: 마감 당시 집계 근거기존 기준일, 승인 정보, 초안 연결, 정정 대상 및 사유 등은 유지한다.
2. 기존 자산·부채 상세 유지 —
asset_snapshots기존
snapshot_run_id로 월정산 헤더와 연결하는 구조를 유지한다.다음 정보가 마감 상세에 보존되는지 점검하고 부족한 부분은 보완한다.
헤더 총계에는 실제 반영 대상 상세만 합산한다. 중복 제외 항목과 금액 미확인 항목은 합산하지 않는다.
3. 카테고리별 상세 추가 —
settlement_cashflow_snapshots필드:
idsettlement_run_id:asset_snapshot_runs.id를 참조하는 외래키cashflow_kind:income/expense/excludedcategorysubcategoryamounttransaction_countcurrency제약 및 인덱스:
excluded상세는 이체·초기잔액 조정 등 집계 제외 내역의 설명용이며, 수입·지출 총계에는 포함하지 않는다.4. 기존 정산 초안 확장
초안에 다음을 보존한다.
초안 조회 시 다음을 함께 제공한다.
초안 단계의 카테고리별 금액은 원장에서 재계산하고, 최종 마감 시 고정 저장한다. 초안용 카테고리 상세 테이블은 우선 필수로 추가하지 않는다.
집계 기준 및 근거 보존
cashflow_kind분류와 집계 정책의 일관성을 검증한다.source_snapshot_json에는 거래 ID뿐 아니라 당시 금액·분류·집계 구분 등 계산 재현에 필요한 값을 보존한다. 거래 ID만 저장하면 이후 원장 수정 시 마감 당시 내용을 재현할 수 없다.비밀번호·토큰 등 인증정보는 저장하지 않는다.
검토·승인·마감 규칙
검토 및 승인 지문에 다음을 포함한다.
검토·승인 이후 거래, 분류, 집계 정책 또는 평가자료가 변경되면 재검토·재승인이 필요하다.
마감 시 다음을 하나의 DB 트랜잭션으로 저장한다.
검증식:
마감 불변성 및 정정
기존 데이터 이관
NULL로 유지한다. 누락을 0원으로 표현하지 않는다.API 및 화면 변경
수용 기준
NULL이관 테스트를 제공한다.gitea-impl-agent referenced this issue2026-10-13 14:35:31 +00:00