- 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.
21 lines
523 B
Python
21 lines
523 B
Python
from decimal import Decimal
|
|
from uuid import UUID, uuid4
|
|
|
|
from pydantic import BaseModel, Field, field_validator
|
|
|
|
|
|
class Transaction(BaseModel):
|
|
id: UUID = Field(default_factory=uuid4)
|
|
date: str = ""
|
|
account: str = ""
|
|
category: str = ""
|
|
amount: Decimal = Field(default=Decimal("0"))
|
|
description: str = ""
|
|
|
|
@field_validator("amount")
|
|
@classmethod
|
|
def _amount_nonzero(cls, v: Decimal) -> Decimal:
|
|
if v == 0:
|
|
raise ValueError("amount must be non-zero")
|
|
return v
|