Commit Graph
5 Commits
Author SHA1 Message Date
Dmitry ff3b76871d feat(ledger): импорт отчётов в леджер — приём, дедупликация, pending_instrument
Поток: upload -> raw_report_file (sha256 UNIQUE) -> parse -> raw_report_line,
событий в леджере ещё нет -> preview -> POST /imports/{id}/commit ->
ledger/ingest.py резолвит инструмент, считает dedupe_key, пишет event
confirmed | shadow (plan §1.6 B) — сравнивая account.primary_event_source
с источником отчёта, а не гадая. Нерезолвленный инструмент ждёт в
pending_instrument, никогда не угадывается; POST /instruments/pending/{id}/resolve
привязывает и пересобирает лоты.

ledger/dedupe.py — shadow-матчинг случая B двумя проходами (точная дата, затем
±1 рабочий день, жадно 1:1, |price| ±0,5 %). Шаги shadow_dedupe и
report_reconcile зарегистрированы перед quality: оба говорят через FINDINGS.

/instruments/pending регистрируется в app.py ДО routers/instruments.py:
FastAPI сопоставляет маршруты по порядку, и /instruments/{id} с типом int
отвечает 422 на нечисловой сегмент, а не проваливается дальше.

Контракт — docs/ai/import-contract.md, общий для бэкенда и Flutter.
2026-09-19 10:40:04 +03:00
Dmitry 6b32c79e79 feat(analytics): брокерские потоки по месяцам и ручные цены инструментов
GET /analytics/cashflow-broker читает metric_cash_flow_broker. POST
/instruments/{id}/prices апсертит price_manual по (instrument_id, d);
pricing/prices.py подмешивает его в ту же серию, что price_daily, с тем же
протягиванием и порогом устаревания.
2026-09-19 10:38:59 +03:00
Dmitry ae82df6fa7 feat(ledger): резолв несматченных линков ZenMoney↔брокер
GET /links/unmatched, POST /links, DELETE /links/{id}: ручная привязка того,
что ledger/matching.py не смог сопоставить автоматически.
2026-09-19 10:38:39 +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