From 367583522b98adaa1650c38514f25b8c5307c36c Mon Sep 17 00:00:00 2001 From: gitea-impl-agent Date: Tue, 6 Oct 2026 10:01:46 +0000 Subject: [PATCH] fix: classify conditional previews and normalize currency --- .../application/assets_service.py | 13 ++++++++--- tests/test_issue_12_regressions.py | 23 +++++++++++++++++++ 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/jarca_ledger_mcp/application/assets_service.py b/jarca_ledger_mcp/application/assets_service.py index b5309eb..5cc76c2 100644 --- a/jarca_ledger_mcp/application/assets_service.py +++ b/jarca_ledger_mcp/application/assets_service.py @@ -92,7 +92,7 @@ def _account_valuation_items(conn: sqlite3.Connection, basis_date: str = "", sel if basis_date: valuation_rows = conn.execute( "SELECT * FROM account_valuations WHERE account_id=? AND currency=? ORDER BY basis_date DESC, recorded_at DESC, id DESC", - (account["id"], account.get("currency") or "KRW"), + (account["id"], base["currency"]), ).fetchall() leaves = [v for v in valuation_rows if conn.execute("SELECT 1 FROM account_valuations WHERE supersedes_id=?", (v["id"],)).fetchone() is None] if account_type == "investment": @@ -427,8 +427,15 @@ def preview_asset_snapshot_close(basis_date: str = "", db_path: str | None = Non with db(db_path) as conn: preview = _snapshot_preview_from_conn(conn, basis, selection_policy) preview["selection_policy"] = selection_policy - blocking = [w for w in preview["warnings"] if w.get("blocking", True) and w.get("code", "").startswith(("missing_", "future_", "unknown_", "unverified", "ambiguous_", "stale_"))] - preview["close_status"] = "complete_exact" if selection_policy == "exact" and not blocking else "incomplete_draft" + approval_eligible = [w for w in preview["warnings"] if w.get("blocking", True) and w.get("code", "").startswith(("missing_", "future_", "unknown_", "unverified", "ambiguous_"))] + carried_items = [item for item in preview["included_items"] if item.get("carried_forward")] + other_blocking = [w for w in preview["warnings"] if w.get("blocking", True) and w.get("code", "").startswith("stale_") and not carried_items] + if other_blocking: + preview["close_status"] = "incomplete_draft" + elif approval_eligible or carried_items: + preview["close_status"] = "conditional" + else: + preview["close_status"] = "complete_exact" preview.pop("_candidate_state_hashes", None) return ok(**preview) diff --git a/tests/test_issue_12_regressions.py b/tests/test_issue_12_regressions.py index 33d54ad..e58c2bd 100644 --- a/tests/test_issue_12_regressions.py +++ b/tests/test_issue_12_regressions.py @@ -31,6 +31,15 @@ def test_empty_conditional_close_is_closed_and_second_close_requires_correction( assert second["success"] is False and second["error_code"] == "validation_error" +def test_preview_marks_approval_eligible_missing_valuation_as_conditional(tmp_path): + path = str(tmp_path / "ledger.sqlite3") + service.create_ledger_account("미확인 계좌", db_path=path) + preview = service.preview_asset_snapshot_close("2026-06-30", db_path=path) + assert preview["success"] is True + assert preview["close_status"] == "conditional" + assert any(warning["code"] == "missing_verified_exact_valuation" for warning in preview["warnings"]) + + def test_reconciliation_uses_corrected_investment_leaf_without_transaction_count_change(tmp_path): path = str(tmp_path / "ledger.sqlite3") account = service.create_ledger_account("투자", type="investment", last_balance=100, last_synced_at="2026-06-30", db_path=path)["account"]["id"] @@ -421,6 +430,20 @@ def test_non_krw_account_valuation_is_excluded_without_fx_conversion(tmp_path): assert any(item.get("reason") == "non_krw_without_fx" for item in report["excluded_items"]) +def test_legacy_lowercase_krw_account_currency_selects_normalized_valuation(tmp_path): + path = str(tmp_path / "ledger.sqlite3") + account = service.create_ledger_account("계좌", currency="KRW", db_path=path)["account"]["id"] + service.register_account_valuation( + account, "2026-06-30", 100, currency="KRW", + verification_status="verified", db_path=path, + ) + with sqlite3.connect(path) as conn: + conn.execute("UPDATE accounts SET currency='krw' WHERE id=?", (account,)) + report = service.get_asset_snapshot_report("2026-06-30", db_path=path) + assert report["total_assets"] == 100 + assert report["excluded_items"] == [] + + def test_rejected_valuation_is_not_an_unverified_candidate(tmp_path): path = str(tmp_path / "ledger.sqlite3") account = service.create_ledger_account("계좌", db_path=path)["account"]["id"]