import json from dataclasses import dataclass, field from pathlib import Path from domain.account import Account from domain.asset import Asset from domain.currency import DEFAULT_RATES, ExchangeRate from domain.liability import Liability from domain.recurring import RecurringCashflow from domain.scenario import ForecastScenario from domain.transaction import Transaction @dataclass class FinancialModel: base_currency: str = "RUB" accounts: list[Account] = field(default_factory=list) transactions: list[Transaction] = field(default_factory=list) recurring: list[RecurringCashflow] = field(default_factory=list) assets: list[Asset] = field(default_factory=list) liabilities: list[Liability] = field(default_factory=list) scenarios: list[ForecastScenario] = field(default_factory=list) exchange_rates: list[ExchangeRate] = field(default_factory=lambda: DEFAULT_RATES.copy()) SCHEMA_VERSION = 1 def to_dict(self) -> dict: return { "version": self.SCHEMA_VERSION, "base_currency": self.base_currency, "accounts": [a.to_dict() for a in self.accounts], "transactions": [t.to_dict() for t in self.transactions], "recurring": [r.to_dict() for r in self.recurring], "assets": [a.to_dict() for a in self.assets], "liabilities": [li.to_dict() for li in self.liabilities], "scenarios": [s.to_dict() for s in self.scenarios], "exchange_rates": [r.to_dict() for r in self.exchange_rates], } @classmethod def from_dict(cls, data: dict) -> "FinancialModel": version = data.get("version", 0) if version == 0: return cls._from_v0(data) if version == 1: return cls._from_v1(data) raise ValueError(f"Unsupported FinancialModel version: {version}") @classmethod def _from_v1(cls, data: dict) -> "FinancialModel": return cls( base_currency=data.get("base_currency", "RUB"), accounts=[Account.from_dict(a) for a in data.get("accounts", [])], transactions=[Transaction.from_dict(t) for t in data.get("transactions", [])], recurring=[RecurringCashflow.from_dict(r) for r in data.get("recurring", [])], assets=[Asset.from_dict(a) for a in data.get("assets", [])], liabilities=[Liability.from_dict(li) for li in data.get("liabilities", [])], scenarios=[ForecastScenario.from_dict(s) for s in data.get("scenarios", [])], exchange_rates=[ExchangeRate.from_dict(r) for r in data.get("exchange_rates", [])], ) @classmethod def _from_v0(cls, data: dict) -> "FinancialModel": # Legacy: файлы, сохранённые до введения version. Совпадает по структуре с v1. return cls._from_v1(data) def save(self, path: str | Path) -> None: path = Path(path) path.parent.mkdir(parents=True, exist_ok=True) with open(path, "w", encoding="utf-8") as f: json.dump(self.to_dict(), f, indent=2, ensure_ascii=False) @classmethod def load(cls, path: str | Path) -> "FinancialModel": path = Path(path) with open(path, "r", encoding="utf-8") as f: data = json.load(f) return cls.from_dict(data)