T2: Pydantic v2 — replace @dataclass with BaseModel

- All domain models now use pydantic.BaseModel
- Removed manual to_dict()/from_dict() — use model_dump(mode='json')/model_validate()
- Added field validators:
  - Account.balance >= 0
  - Transaction.amount != 0
  - RecurringCashflow.amount != 0, frequency in {daily, weekly, monthly, yearly}
  - Liability.interest, payment >= 0
  - ExchangeRate.rate > 0
  - ForecastScenario.multipliers >= 0
- pydantic v2 native UUID handling (auto str in JSON)
- Updated excel_sync.py to use model_validate()
- Updated tests/test_model.py and tests/test_currency.py
- Added pydantic>=2.0 to pyproject.toml

Tests: 63/63 pass.
This commit is contained in:
2026-10-08 16:42:04 +03:00
parent feb77ef217
commit c765f3672a
12 changed files with 112 additions and 187 deletions
+9 -23
View File
@@ -1,33 +1,19 @@
from dataclasses import dataclass, field
from uuid import UUID, uuid4
from pydantic import BaseModel, Field, field_validator
@dataclass
class ForecastScenario:
id: UUID = field(default_factory=uuid4)
class ForecastScenario(BaseModel):
id: UUID = Field(default_factory=uuid4)
name: str = "baseline"
income_multiplier: float = 1.0
expense_multiplier: float = 1.0
growth_multiplier: float = 1.0
description: str = ""
def to_dict(self) -> dict:
return {
"id": str(self.id),
"name": self.name,
"income_multiplier": self.income_multiplier,
"expense_multiplier": self.expense_multiplier,
"growth_multiplier": self.growth_multiplier,
"description": self.description,
}
@field_validator("income_multiplier", "expense_multiplier", "growth_multiplier")
@classmethod
def from_dict(cls, data: dict) -> "ForecastScenario":
return cls(
id=UUID(data["id"]),
name=data["name"],
income_multiplier=data.get("income_multiplier", 1.0),
expense_multiplier=data.get("expense_multiplier", 1.0),
growth_multiplier=data.get("growth_multiplier", 1.0),
description=data.get("description", ""),
)
def _non_negative(cls, v: float) -> float:
if v < 0:
raise ValueError("multiplier must be non-negative")
return v