diff --git a/jarca_ledger_mcp/application/accounts_write_services.py b/jarca_ledger_mcp/application/accounts_write_services.py index fd8399f..9d3e9b1 100644 --- a/jarca_ledger_mcp/application/accounts_write_services.py +++ b/jarca_ledger_mcp/application/accounts_write_services.py @@ -27,6 +27,12 @@ def create_ledger_account(name: str, type: str = "bank", institution: str = "", except Exception as e: return fail("validation_error", str(e)) if basis_date and (last_balance is None or last_balance == ""): return fail("validation_error", "last_balance_basis_date requires last_balance") + if last_balance is not None: + try: + from .account_valuation_service import _strict_int + last_balance = _strict_int(last_balance, "last_balance", allow_none=False) + except (ValueError, TypeError) as e: + return fail("validation_error", str(e)) with db(db_path) as conn: conn.execute("INSERT INTO accounts(id,name,type,institution,currency,active,memo,account_number_masked,holder_name,last_balance,last_balance_basis_date,last_synced_at,statement_status,liquidity_bucket,created_at,updated_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", (account_id,name.strip(),type,institution,currency.upper(),int(active),memo,account_number_masked,holder_name,last_balance,basis_date,last_synced_at,statement_status_norm,liquidity_bucket_norm,ts,ts)) row = conn.execute("SELECT * FROM accounts WHERE id=?", (account_id,)).fetchone() diff --git a/tests/test_import_current_balance_authority.py b/tests/test_import_current_balance_authority.py index fa60d16..1b9a32e 100644 --- a/tests/test_import_current_balance_authority.py +++ b/tests/test_import_current_balance_authority.py @@ -101,6 +101,55 @@ def test_empty_balance_with_basis_date_is_rejected_without_mutation_before_impor assert {(item["basis_date"], item["amount"]) for item in valuations} == {("2026-06-30", 900)} +def test_create_rejects_whitespace_or_non_integer_dated_balance_without_mutation(tmp_path): + path = str(tmp_path / "ledger.sqlite3") + baseline = service.create_ledger_account("baseline", db_path=path) + before = service.list_ledger_accounts(active_only=False, db_path=path)["accounts"] + before_valuations = service.list_account_valuations(baseline["account"]["id"], db_path=path)["valuations"] + + for invalid_balance in (" ", "not-an-integer", True, False, 1.2, -1.2): + rejected = service.create_ledger_account( + "invalid", + last_balance=invalid_balance, + last_balance_basis_date="2026-07-01", + db_path=path, + ) + + assert rejected["success"] is False + assert rejected["error_code"] == "validation_error" + assert service.list_ledger_accounts(active_only=False, db_path=path)["accounts"] == before + assert service.list_account_valuations(baseline["account"]["id"], db_path=path)["valuations"] == before_valuations + + +def test_zero_dated_balance_and_valid_import_recovery(tmp_path, monkeypatch): + path = str(tmp_path / "ledger.sqlite3") + zero = service.create_ledger_account( + "zero", + last_balance=0, + last_balance_basis_date="2026-06-01", + db_path=path, + ) + + assert zero["success"] is True + assert zero["account"]["last_balance"] == 0 + assert [(item["basis_date"], item["amount"]) for item in service.list_account_valuations(zero["account"]["id"], db_path=path)["valuations"]] == [("2026-06-01", 0)] + + account = service.create_ledger_account("recoverable", db_path=path)["account"]["id"] + monkeypatch.setattr( + imports_service, + "parse_kb_bank_pdf", + lambda *_: _parsed(_transaction("2026-06-30", 900, 1), "hash-valid"), + ) + monkeypatch.setattr(imports_service, "now_iso", lambda: "2026-07-02T09:00:00") + + imported = imports_service.import_kb_bank_pdf("synthetic", "pw", account_id=account, db_path=path) + + current = next(item for item in service.list_ledger_accounts(active_only=False, db_path=path)["accounts"] if item["id"] == account) + assert imported["success"] is True + assert current["last_balance"] == 900 + assert current["last_balance_basis_date"] == "2026-06-30" + + def test_account_write_sync_timestamp_does_not_infer_balance_basis_or_candidate(tmp_path): path = str(tmp_path / "ledger.sqlite3") unknown = service.create_ledger_account(