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
+8 -6
View File
@@ -119,17 +119,19 @@ class ExcelSync:
def _add_to_model(self, model: FinancialModel, sheet_name: str, data: dict) -> None:
try:
cls = _SHEET_CONFIG[sheet_name]["cls"]
instance = cls.model_validate(data)
if sheet_name == "Accounts":
model.accounts.append(Account.from_dict(data))
model.accounts.append(instance)
elif sheet_name == "Transactions":
model.transactions.append(Transaction.from_dict(data))
model.transactions.append(instance)
elif sheet_name == "Recurring":
model.recurring.append(RecurringCashflow.from_dict(data))
model.recurring.append(instance)
elif sheet_name == "Assets":
model.assets.append(Asset.from_dict(data))
model.assets.append(instance)
elif sheet_name == "Liabilities":
model.liabilities.append(Liability.from_dict(data))
model.liabilities.append(instance)
elif sheet_name == "ExchangeRates":
model.exchange_rates.append(ExchangeRate.from_dict(data))
model.exchange_rates.append(instance)
except Exception as e:
raise SyncError(f"Failed to parse row in {sheet_name}: {e}") from e