fix: harden valuation concurrency and currency scope

This commit is contained in:
2026-10-06 09:22:19 +00:00
parent a4cad342a6
commit 6455c70413
3 changed files with 98 additions and 7 deletions
@@ -78,12 +78,16 @@ def _account_valuation_items(conn: sqlite3.Connection, basis_date: str = "", sel
"authority_source": "accounts.last_balance",
"name": account.get("name", ""),
"type": account.get("type", ""),
"currency": (account.get("currency") or "KRW").upper(),
"liquidity_bucket": bucket,
"valuation_date": account.get("last_synced_at", ""),
"last_synced_at": account.get("last_synced_at", ""),
"source_note": account.get("memo", ""),
"memo": account.get("memo", ""),
}
if base["currency"] != "KRW":
excluded.append({**base, "reason": "non_krw_without_fx"})
continue
balance = account.get("last_balance")
if basis_date:
valuation_rows = conn.execute(
@@ -118,7 +122,7 @@ def _account_valuation_items(conn: sqlite3.Connection, basis_date: str = "", sel
if verified:
selected, selected_purpose = verified[0], purpose
break
candidate_rows.extend(v for v in scoped if v["verification_status"] != "verified" and v["basis_date"] <= basis_date and (v["basis_date"] == basis_date if selection_policy == "exact" else True))
candidate_rows.extend(v for v in scoped if v["verification_status"] == "unverified" and v["basis_date"] <= basis_date and (v["basis_date"] == basis_date if selection_policy == "exact" else True))
if selected is None:
excluded.append({**base, "reason": "missing_verified_exact_valuation" if selection_policy == "exact" else "missing_verified_valuation", "unverified_candidates": [_public_candidate(v) for v in candidate_rows]})
continue
@@ -126,7 +130,7 @@ def _account_valuation_items(conn: sqlite3.Connection, basis_date: str = "", sel
candidate_rows.extend(
v for v in leaves
if v["valuation_kind"] == selected_purpose
and v["verification_status"] != "verified"
and v["verification_status"] == "unverified"
and v["basis_date"] <= basis_date
and (v["basis_date"] == basis_date if selection_policy == "exact" else True)
)
@@ -523,9 +527,9 @@ def close_asset_snapshot(basis_date: str = "", source_note: str = "", memo: str
carried_evidence = [{k: item.get(k) for k in ("source_type", "source_id", "amount", "currency", "valuation_id", "valuation_date", "verification_status")} for item in included if item.get("carried_forward")]
try:
conn.execute("INSERT INTO asset_snapshot_runs(id,basis_date,close_status,selection_policy,idempotency_key,approved_by,approved_at,approval_reason,approval_reference,correction_of_run_id,correction_reason,applied_evidence,excluded_evidence,carried_evidence,request_fingerprint,created_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", (run_id, basis, "conditional" if conditional else "complete_exact", selection_policy, idempotency_key, approved_by, ts if approved_by else "", approval_reason, approval_reference, correction_of_run_id, correction_reason.strip(), json.dumps(applied_evidence, ensure_ascii=False, sort_keys=True), json.dumps(excluded_evidence, ensure_ascii=False, sort_keys=True), json.dumps(carried_evidence, ensure_ascii=False, sort_keys=True), fingerprint, ts))
except sqlite3.IntegrityError as exc:
except sqlite3.IntegrityError:
conn.rollback()
return fail("snapshot_conflict", "another snapshot close already owns this basis date", basis_date=basis, detail=str(exc))
return fail("snapshot_conflict", "another snapshot close already owns this basis date", basis_date=basis)
return ok(
basis_date=basis,
source="snapshot",