feat: deliver schema 15 monthly settlement lifecycle
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sqlite3
|
||||
|
||||
import pytest
|
||||
|
||||
from jarca_ledger_mcp.application.account_valuation_service import register_account_valuation
|
||||
from jarca_ledger_mcp.application.monthly_settlement_service import create_monthly_settlement
|
||||
from jarca_ledger_mcp.infrastructure.sqlite.migrations import init_db
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def ledger(tmp_path):
|
||||
path = tmp_path / "actual-service.sqlite3"
|
||||
init_db(str(path))
|
||||
with sqlite3.connect(path) as conn:
|
||||
conn.execute(
|
||||
"INSERT INTO accounts(id,name,currency,created_at,updated_at) VALUES(?,?,?,?,?)",
|
||||
("acct-1", "주계좌", "KRW", "2026-10-01T00:00:00Z", "2026-10-01T00:00:00Z"),
|
||||
)
|
||||
conn.commit()
|
||||
settlement = create_monthly_settlement("2026-10", db_path=str(path))
|
||||
return path, settlement["settlement_id"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("amount", "expected_amount"),
|
||||
[(0, 0), (123456, 123456)],
|
||||
)
|
||||
def test_actual_service_saves_zero_and_amount_with_fk_and_unverified(ledger, amount, expected_amount):
|
||||
path, settlement_id = ledger
|
||||
result = register_account_valuation(
|
||||
"acct-1", "2026-10-31", amount, "KRW", monthly_settlement_id=settlement_id, db_path=str(path)
|
||||
)
|
||||
assert result["success"] is True
|
||||
with sqlite3.connect(path) as conn:
|
||||
row = conn.execute(
|
||||
"SELECT amount,currency,basis_date,monthly_settlement_id,verification_status,source_type,source_id FROM account_snapshot"
|
||||
).fetchone()
|
||||
assert row == (expected_amount, "KRW", "2026-10-31", settlement_id, "unverified", "account", "acct-1")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("existing_amount", [None, 0])
|
||||
def test_actual_service_rejects_duplicate_even_when_existing_amount_is_null_or_zero(ledger, existing_amount):
|
||||
path, settlement_id = ledger
|
||||
with sqlite3.connect(path) as conn:
|
||||
conn.execute(
|
||||
"""INSERT INTO account_snapshot
|
||||
(id,basis_date,source_type,source_id,name,amount,currency,monthly_settlement_id,created_at)
|
||||
VALUES(?,?,?,?,?,?,?,?,?)""",
|
||||
("existing", "2026-10-31", "account", "acct-1", "주계좌", existing_amount, "KRW", settlement_id, "2026-10-31T00:00:00Z"),
|
||||
)
|
||||
conn.commit()
|
||||
result = register_account_valuation(
|
||||
account_id="acct-1", basis_date="2026-10-31", amount=99, currency="KRW",
|
||||
monthly_settlement_id=settlement_id, db_path=str(path),
|
||||
)
|
||||
assert result == {
|
||||
"success": False,
|
||||
"error_code": "duplicate",
|
||||
"error": "account evaluation already registered",
|
||||
"snapshot_id": "existing",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize("status", ["closed", "approved"])
|
||||
def test_actual_service_blocks_closed_and_approved_settlements(ledger, status):
|
||||
path, settlement_id = ledger
|
||||
with sqlite3.connect(path) as conn:
|
||||
conn.execute(
|
||||
"""UPDATE monthly_settlement
|
||||
SET status=?, approved_by='test', approved_at='2026-10-31T00:00:00Z',
|
||||
approved_version=1, approved_fingerprint='fp',
|
||||
closed_by=CASE WHEN ?='closed' THEN 'test' ELSE closed_by END,
|
||||
closed_at=CASE WHEN ?='closed' THEN '2026-10-31T00:00:00Z' ELSE closed_at END
|
||||
WHERE id=?""",
|
||||
(status, status, status, settlement_id),
|
||||
)
|
||||
conn.commit()
|
||||
result = register_account_valuation(
|
||||
"acct-1", "2026-10-31", 10, "KRW", monthly_settlement_id=settlement_id, db_path=str(path)
|
||||
)
|
||||
assert result["success"] is False
|
||||
assert result["error_code"] in {"closed_settlement", "invalid_settlement_status"}
|
||||
with sqlite3.connect(path) as conn:
|
||||
assert conn.execute("SELECT COUNT(*) FROM account_snapshot").fetchone()[0] == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
("kwargs", "error_code"),
|
||||
[
|
||||
({"currency": "USD"}, "validation_error"),
|
||||
({"basis_date": "2026-09-30"}, "validation_error"),
|
||||
({"monthly_settlement_id": "missing"}, "not_found"),
|
||||
],
|
||||
)
|
||||
def test_actual_service_validates_currency_date_and_settlement_and_rolls_back(ledger, kwargs, error_code):
|
||||
path, settlement_id = ledger
|
||||
args = {
|
||||
"account_id": "acct-1", "basis_date": "2026-10-31", "amount": 10, "currency": "KRW",
|
||||
"monthly_settlement_id": settlement_id, "db_path": str(path),
|
||||
}
|
||||
args.update(kwargs)
|
||||
result = register_account_valuation(**args)
|
||||
assert result["success"] is False
|
||||
assert result["error_code"] == error_code
|
||||
with sqlite3.connect(path) as conn:
|
||||
assert conn.execute("SELECT COUNT(*) FROM account_snapshot").fetchone()[0] == 0
|
||||
assert conn.execute("SELECT COUNT(*) FROM monthly_settlement").fetchone()[0] == 1
|
||||
Reference in New Issue
Block a user