Files
nifodea/domain/model.py
T
oqyude 3f7268d14c T4: Repository pattern — ModelRepository, JsonFileRepository, ExcelRepository
- application/repositories/model_repository.py: ModelRepository Protocol
  with load(path) and save(model, path)
- infrastructure/repositories/json_file_repository.py: JsonFileRepository
  (вынесен из FinancialModel.save/load)
- infrastructure/repositories/excel_repository.py: ExcelRepository
  (бывший ExcelSync, переименован)
- FinancialModel.save/load удалены — теперь только данные
- infrastructure/sync/ удалён (ExcelSync перенесён в repositories/)
- infrastructure/cli/main.py и config.py используют JsonFileRepository +
  ExcelRepository через локальные singletons
- test_excel_sync.py переименован в test для ExcelRepository
- Новый test_repositories.py: 5 тестов для JsonFileRepository + протокол
- test_model.py: использован JsonFileRepository

Tests: 68/68 pass (было 63 + 5 новых).
2026-10-08 16:50:06 +03:00

72 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
from pydantic import BaseModel, Field
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
class FinancialModel(BaseModel):
"""Корневая модель финансового плана.
Сериализация (save/load) вынесена в `application.repositories.ModelRepository` —
JsonFileRepository и ExcelRepository. Сам класс хранит только данные.
"""
SCHEMA_VERSION: int = 1 # NB: не Field — это class-level metadata, не pydantic field
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: [r.model_copy() for r in DEFAULT_RATES]
)
def to_dict(self) -> dict:
"""Сериализация в dict с version и UUID-as-str (для JSON)."""
return {
"version": self.SCHEMA_VERSION,
"base_currency": self.base_currency,
"accounts": [a.model_dump(mode="json") for a in self.accounts],
"transactions": [t.model_dump(mode="json") for t in self.transactions],
"recurring": [r.model_dump(mode="json") for r in self.recurring],
"assets": [a.model_dump(mode="json") for a in self.assets],
"liabilities": [li.model_dump(mode="json") for li in self.liabilities],
"scenarios": [s.model_dump(mode="json") for s in self.scenarios],
"exchange_rates": [e.model_dump(mode="json") for e in self.exchange_rates],
}
@classmethod
def from_dict(cls, data: dict) -> "FinancialModel":
version = data.get("version", 0)
if version == 0:
return cls._from_legacy(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.model_validate(a) for a in data.get("accounts", [])],
transactions=[Transaction.model_validate(t) for t in data.get("transactions", [])],
recurring=[RecurringCashflow.model_validate(r) for r in data.get("recurring", [])],
assets=[Asset.model_validate(a) for a in data.get("assets", [])],
liabilities=[Liability.model_validate(li) for li in data.get("liabilities", [])],
scenarios=[ForecastScenario.model_validate(s) for s in data.get("scenarios", [])],
exchange_rates=[ExchangeRate.model_validate(r) for r in data.get("exchange_rates", [])],
)
@classmethod
def _from_legacy(cls, data: dict) -> "FinancialModel":
# Legacy: файлы без version. Структура совпадает с v1.
return cls._from_v1(data)