Commit Graph
4 Commits
Author SHA1 Message Date
Dmitry f1f336f94f feat(api): портфели, ручные события, правка инструментов и обзор по скоупам
CRUD портфелей (/portfolios), ручные события (POST /events, DELETE только для manual), PATCH /instruments/{id}, GET /analytics/overview — карточка на каждый скоуп одним запросом. Холдинги отдают logo_url и logo_color. openapi.json обновлён.
2026-09-19 22:13:55 +03:00
Dmitry baac83149f fix(api): Decimal-параметр ребалансировки ломал генерацию Dart-клиента
cash_available был bare Decimal | None в Query(...) — pydantic отдаёт для
Decimal anyOf[number, pattern-string], который openapi-generator не умеет
свернуть в один тип и вместо этого синтезирует пустой класс CashAvailable
без полей (синтаксически битый .dart, компиляция всего пакета падает).
Остальные Decimal-поля в API давно оборачиваются в Money/MoneyOpt
(api/schemas/common.py) ровно за счёт этого — anyOf[string, null] генератор
понимает. cash_available обошёл соглашение, потому что это единственный
Decimal-параметр запроса во всём API, а не тело ответа.
2026-09-19 10:56:33 +03:00
Dmitry 3727419506 feat(api): аналитика инвестиций, события и карточка инструмента
/analytics/{scopes,summary,value-series,holdings,returns,allocation}, /events с
фильтрами и /instruments/{id}. На запрос ничего не считается — это чтение metric_*,
благодаря чему экраны читаются быстро и показывают одно и то же число.

scope (all | account:<id> | portfolio:<id>) резолвится через ту же функцию, что его
построила, valuation.account_scopes: scope, для которого метрик нет, отдаёт 404, а не
пустой график, который читался бы как пустой портфель.

asset_class уходит наружу строкой, а не енумом. Одно из его значений — index, а
Dart-енум не может назвать член index: он конфликтует с Enum.index, и сгенерированный
клиент перестаёт компилироваться. flutter analyze это пропускает, flutter test ловит.

Фильтр /events?external_flow=false сравнивает meta через is_not_distinct_from, а не
через равенство: у события без meta сравнение даёт NULL, NOT NULL это тоже NULL, и
равенство выбрасывало бы такие события из ОБЕИХ половин фильтра.
2026-09-18 14:20:50 +03:00
Dmitry 3fc7a954b9 feat(api): FastAPI — auth, RFC 7807 и роуты фазы 1
Префикс /api/v1, operationId = "<tag>_<name>", чтобы Dart-клиент получил методы
вроде accountsList, а не list_accounts_api_v1_accounts_get. Ошибки —
application/problem+json. Auth: access-JWT на час + refresh на 30 дней, который
хранится хэшем и ротируется, логин ограничен по частоте в памяти.

Деньги в JSON — всегда позиционные строки (api/schemas/common.py): float в
проводе потерял бы копейки, которые NUMERIC(24,10) бережёт.

Тестовый harness поднимает свой Postgres через pg_ctl (pytest-postgresql),
мигрирует его один раз на сессию и усекает таблицы после каждого теста.
2026-09-18 13:43:49 +03:00