Follow-up to T3 (Decimal migration): display layer still hardcodes :,.2f everywhere, which is wrong for JPY (0 dp) and 3-dp currencies like BHD/KWD/OMR. T8 = display-only refactor: - CURRENCY_DECIMALS + CurrencyConverter.format_money() in domain/ - replace 14 :,.2f literals across infrastructure/cli/ - rework 2 i18n templates that bake the format into the message string - extend tests/test_currency.py with JPY/BHD/USD/CHF cases Locked decisions (HALF_UP, ',' separator, suffix for unknown codes, internal precision untouched) recorded in req-T8.json for the executing node.
8.2 KiB
Task Manifest
Session: metaagent-005 (initial) → metaagent-006 (T8 pending)
Goal: Серьёзный архитектурный рефактор
Date: 2026-10-08
Total tasks: 8 (7 archived, 1 pending)
T1: Реструктуризация в domain/application/infrastructure + version=1
Зависимости: — Файлы: domain/ (new), application/ (new), infrastructure/{cli,sync,ai}/ (new), tests/
Что: Ввести явные слои. Переместить пакеты. Добавить version: 1 в FinancialModel.
Acceptance:
- Структура domain/application/infrastructure создана
- Все исходные файлы перемещены
- Все импорты обновлены
FinancialModel.to_dict()→{'version': 1, ...}from_dict()поддерживает v0 (без version) и v1- pytest 63/63
T2: Pydantic v2 — миграция моделей
Зависимости: T1 Файлы: domain/*.py, pyproject.toml, infrastructure/sync/excel_sync.py
Что: @dataclass → pydantic.BaseModel. Удалить ручные to_dict/from_dict.
Acceptance:
- Все модели —
BaseModel - Удалены ручные to_dict/from_dict
- UUID в JSON как str
- Валидация (balance >= 0, и т.п.)
- Excel-sync адаптирован
- pytest 63/63
T3: Decimal для денег
Зависимости: T2 Файлы: domain/.py, application/.py, infrastructure/sync/excel_sync.py, infrastructure/cli/{main,config}.py
Что: float → Decimal для всех monetary полей. Арифметика engine, форматирование.
Acceptance:
- monetary поля —
Decimal - CurrencyConverter с Decimal
- ForecastService арифметика — Decimal
- Excel-sync читает числа как Decimal
- rich.print форматирует Decimal (2 знака)
- JSON-сериализация Decimal работает
- pytest 63/63
T4: Repository pattern — ModelRepository
Зависимости: T1 Файлы: application/repositories/model_repository.py, infrastructure/repositories/{json_file,excel}_repository.py, infrastructure/sync/excel_sync.py
Что: ModelRepository Protocol + JsonFileRepository + ExcelRepository (адаптер над ExcelSync).
Acceptance:
ModelRepositoryProtocol (load/save)JsonFileRepositoryреализует ProtocolExcelRepositoryреализует ProtocolFinancialModel.save/loadудалены- Тесты на каждый репозиторий
- pytest 63/63
T5: Dependency Injection в сервисах
Зависимости: T4 Файлы: application/forecast.py, scenarios.py, infrastructure/ai/assistant.py, infrastructure/cli/{main,config}.py
Что: Зависимости через конструктор. Composition root в CLI.
Acceptance:
ForecastService.__init__(model, repository, converter)- ScenarioService, AssistantService — то же
- Нет
new ForecastService()внутри других сервисов - CLI собирает граф зависимостей
- pytest 63/63
T6: Декомпозиция CLI
Зависимости: T5 Файлы: infrastructure/cli/main.py → app.py, infrastructure/cli/commands/*.py, infrastructure/cli/{paths,services}.py
Что: 8 команд в отдельных файлах. config.py → paths.py + services.py.
Acceptance:
- Каждая команда в cli/commands/*.py
- cli/paths.py — только пути
- cli/services.py — composition root
- Размер файлов < 100-150 LOC
- Поведение идентично
- pytest 63/63
T7: Финальная валидация
Зависимости: T6 Файлы: tests/, README.md, pyproject.toml
Что: Прогнать pytest, ручная проверка CLI, обновить README.
Acceptance:
- pytest 63/63+
- cf init, cf forecast, cf scenario, cf compare — работают
- cf import data.xlsx → cf export — round-trip
- README.md обновлён под новую структуру
T8: ISO 4217-aware форматирование денег (per-currency decimals)
Зависимости: T3 (Decimal уже мигрирован)
Файлы: domain/currency.py, infrastructure/cli/services.py, infrastructure/cli/commands/{info,config_cmd}.py, infrastructure/cli/i18n.py, tests/test_currency.py, tests/test_i18n.py
Контекст: Миграция на Decimal (T3) устранила арифметические ошибки, но отображение денег осталось наивным: во всех местах захардкожен формат :,.2f / :.2f. Это даёт два бага: (1) JPY показывается как ¥1,234.00 вместо ¥1,234; (2) BHD/KWD/OMR (3 знака) вообще не поддерживаются. Словарь CURRENCY_SYMBOLS в domain/currency.py объявляет 8 валют, но семантика ISO 4217 не реализована.
Что:
- Ввести
CURRENCY_DECIMALS: dict[str, int]вdomain/currency.py(минимум: JPY=0, BHD/KWD/OMR=3, остальные из поддерживаемого списка=2). - Добавить
CurrencyConverter.format_money(amount: Decimal, currency: str) -> str— единая точка форматирования (символ + количество знаков). - Заменить все 14 захардкоженных
:,.2fвservices.py,info.py,config_cmd.pyна вызовformat_money()(или новый форматтер в i18n-шаблонах). - Переработать i18n-шаблоны
cmd.config.transaction_add.okиcmd.config.recurring_add.ok— убрать{amount:.2f}, передавать уже отформатированную строку. - Сохранить
_q()вforecast.pyдля сериализации прогноза (JSON-результат должен быть стабильным), но не трогать внутреннюю арифметику — она остаётся на полной точности Decimal. - Граничный кейс: при конвертации
0.1 USD → JPYсейчас даёт 8.07 JPY вместо ~7.86 JPY из-заquantize("0.01")вconvert(). Решить — квантовать только при отображении, не при конверсии.
Acceptance:
format_money(Decimal("1234.567"), "JPY") == "¥1,235"(округление банкирское или HALF_UP — зафиксировать)format_money(Decimal("1234.567"), "USD") == "$1,234.57"format_money(Decimal("1.234"), "BHD") == "BHD 1.234"(3 знака)- Ни одного литерала
:,.2f/:.2fдля денег вinfrastructure/cli/(за исключением явных UNIT-тестов форматтера) - Новые тесты в
tests/test_currency.py: по одному на JPY, BHD, USD, плюс round-tripformat_money(format_money(x))стабилен pytestзелёный, существующие 67 тестов не сломаныcf infoна модели с JPY-счётом показывает целые йены, без.00
Открытые вопросы (на согласование перед выполнением):
- Округление для отображения —
ROUND_HALF_UP(банковское кругление) илиROUND_HALF_EVEN(banker's rounding)? По умолчанию предлагаюHALF_UPдля совместимости с T3. - Формат
1,234vs1 234(разделитель тысяч) — текущий:,.2fдаёт запятую; оставляем? - Для валют без символа в
CURRENCY_SYMBOLS(например CHF) — выводить код префиксом (CHF 1,234.56) или суффиксом (1,234.56 CHF)?