- All domain monetary fields now use Decimal: - Account.balance, Asset.value, Asset.growth_rate - Liability.balance, Liability.interest, Liability.payment - Transaction.amount, RecurringCashflow.amount - ExchangeRate.rate - ForecastScenario.income_multiplier, expense_multiplier, growth_multiplier - CurrencyConverter: all arithmetic in Decimal, quantize to 0.01 with ROUND_HALF_UP - ForecastService: Decimal arithmetic throughout (income, expenses, balance, growth, liability cost) - ScenarioService: Decimal multipliers, deepcopy safe with Decimal fields - assistant.py: _DecimalEncoder for json.dumps (Decimal -> str in JSON) Pydantic v2 + Decimal: - model_dump(mode='json') converts Decimal to str (JSON-safe) - model_validate() parses str back to Decimal - Round-trip preserves precision (100.50 stays 100.50) Tests: 63/63 pass.
19 lines
486 B
Python
19 lines
486 B
Python
from decimal import Decimal
|
|
from uuid import UUID, uuid4
|
|
|
|
from pydantic import BaseModel, Field, field_validator
|
|
|
|
|
|
class Account(BaseModel):
|
|
id: UUID = Field(default_factory=uuid4)
|
|
name: str = ""
|
|
currency: str = "USD"
|
|
balance: Decimal = Field(default=Decimal("0"))
|
|
|
|
@field_validator("balance")
|
|
@classmethod
|
|
def _balance_non_negative(cls, v: Decimal) -> Decimal:
|
|
if v < 0:
|
|
raise ValueError("balance must be non-negative")
|
|
return v
|