T3: Decimal for money — all monetary fields migrated from float
- 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.
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import json
|
||||
from decimal import Decimal
|
||||
|
||||
from infrastructure.ai import prompts
|
||||
from domain import CurrencyConverter, FinancialModel
|
||||
@@ -9,6 +10,15 @@ class AssistantError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class _DecimalEncoder(json.JSONEncoder):
|
||||
"""JSON-сериализатор: Decimal → str (для AI-промптов)."""
|
||||
|
||||
def default(self, o):
|
||||
if isinstance(o, Decimal):
|
||||
return str(o)
|
||||
return super().default(o)
|
||||
|
||||
|
||||
class AssistantService:
|
||||
def __init__(
|
||||
self,
|
||||
@@ -26,8 +36,8 @@ class AssistantService:
|
||||
summary = forecast_service.summary(months)
|
||||
|
||||
prompt = prompts.format_context(
|
||||
model_json=json.dumps(self.model.to_dict(), indent=2, ensure_ascii=False),
|
||||
forecast_json=json.dumps(forecast_result, indent=2, ensure_ascii=False),
|
||||
model_json=json.dumps(self.model.to_dict(), indent=2, ensure_ascii=False, cls=_DecimalEncoder),
|
||||
forecast_json=json.dumps(forecast_result, indent=2, ensure_ascii=False, cls=_DecimalEncoder),
|
||||
months=months,
|
||||
base_currency=self.model.base_currency,
|
||||
display_currency=self.display_currency,
|
||||
@@ -45,8 +55,8 @@ class AssistantService:
|
||||
forecast_result = forecast_service.forecast_cashflow(months)
|
||||
|
||||
prompt = prompts.ADVICE_PROMPT.format(
|
||||
model_json=json.dumps(self.model.to_dict(), indent=2, ensure_ascii=False),
|
||||
forecast_json=json.dumps(forecast_result, indent=2, ensure_ascii=False),
|
||||
model_json=json.dumps(self.model.to_dict(), indent=2, ensure_ascii=False, cls=_DecimalEncoder),
|
||||
forecast_json=json.dumps(forecast_result, indent=2, ensure_ascii=False, cls=_DecimalEncoder),
|
||||
question=question,
|
||||
base_currency=self.model.base_currency,
|
||||
display_currency=self.display_currency,
|
||||
|
||||
Reference in New Issue
Block a user