diff --git a/.gitea/workflows/commits.yml b/.gitea/workflows/commits.yml index ab86bce..feb38bd 100644 --- a/.gitea/workflows/commits.yml +++ b/.gitea/workflows/commits.yml @@ -2,7 +2,7 @@ name: Conventional commits on: pull_request: - types: [opened, synchronize, reopened] + types: [opened, synchronize, reopened, edited] concurrency: group: docs-commits-${{ gitea.event.pull_request.number }} diff --git a/architecture/containers.md b/architecture/containers.md index d471fc9..f5b0da6 100644 --- a/architecture/containers.md +++ b/architecture/containers.md @@ -140,12 +140,15 @@ membership текущей revision перед каждым финансовым личные данные. Ротация refresh token остаётся атомарной. SQLite хранит полный последний portfolio, операции и их подтверждённые -диапазоны, private прогнозы, общие instrument/market sources и intent/staging. +диапазоны, private прогнозы, сохранённые точки истории оценки, общие +instrument/market sources и intent/staging. Нормализованные финансовые значения сохраняются decimal-строками. Полнота -отделена от свежести и пагинации. Ответ не ждёт внешних RPC; запрос при -необходимости запускает работу в том же backend process через существующий -лимитер. На startup импорт не запускается; восстановление требует следующего -запроса. Подробности — [модель свежести](cross-cutting/data-freshness.md). +отделена от свежести и пагинации. Ответ не ждёт внешних RPC; обновлением +личных данных владеет часовой обход в том же backend process через +существующий лимитер, недостающие общие источники он же ставит в фоновую +очередь. На startup обход начинает следующую попытку по обычному часовому +сроку; после нового подключения первая попытка начинается сразу. +Подробности — [модель свежести](cross-cutting/data-freshness.md). PUT подключения сначала проверяет token у T-Bank, затем одной транзакцией сохраняет encrypted token, новую revision и проверенный membership. diff --git a/architecture/http-api.md b/architecture/http-api.md index a1c9bf9..e9e6216 100644 --- a/architecture/http-api.md +++ b/architecture/http-api.md @@ -61,6 +61,17 @@ Envelope несовместим с прежними bare-array ответами: поставляются согласованной парой. Финальный artifact передаётся явно; локальная генерация и CI frontend читают только собственный snapshot. +## Income analytics + +`GET /api/v1/brokerage-accounts/{accountId}/income-analytics` и общий маршрут +`all` возвращают результат только выбранного периода: итоги по исходным +валютам, точные записи выплат, разбиение по инструментам и временные +интервалы. Данных сравнения с предыдущим периодом или годом ранее в ответе +нет — ни в теле, ни среди зависимостей `meta.dependencies`, роль которых +ограничена значением `selected`. Публичную машинную схему задаёт +backend-generated OpenAPI; frontend получает её обновлённый snapshot отдельно. +Точную семантику поведения описывает [доход](../domains/income/overview.md). + ## Validation и identity Backend преобразует и проверяет входные данные на общей HTTP-границе. diff --git a/domains/accounts/overview.md b/domains/accounts/overview.md index dc185c0..8a52b2e 100644 --- a/domains/accounts/overview.md +++ b/domains/accounts/overview.md @@ -71,9 +71,10 @@ ## Свежесть и границы Обычное открытие использует query owner и локальный server snapshot. -«Обновить» запрашивает membership и сводки доступных счетов; активная работа -наблюдается без нового импорта. Истечение TTL само не запускает запрос. -Membership имеет пятиминутную свежесть, portfolio — минутную. Время списка +«Обновить» перечитывает сохранённые membership и сводки доступных счетов; +активная работа наблюдается без нового импорта. Истечение TTL само не +запускает запрос: обновлением личных данных владеет часовой обход. +Membership и portfolio source имеют часовую свежесть. Время списка не подменяет время каждой сводки. Список не заменяет полный [портфель](../portfolio/overview.md), [журнал](../operations/overview.md) или историю закрытых счетов. Для общего результата доступен diff --git a/domains/income/overview.md b/domains/income/overview.md index 25dbe6d..28653fb 100644 --- a/domains/income/overview.md +++ b/domains/income/overview.md @@ -4,7 +4,7 @@ купонами и дивидендами за выбранный период». Пользователь открывает `/accounts/:accountId/income` из обзора счёта или [общий доход](../portfolio/all-accounts.md) по доступным счетам. -Экран показывает итоги по валютам, сравнение и отдельную карточку каждой +Экран показывает итоги по валютам и отдельную карточку каждой включённой выплаты. Источником служат [исполненные операции](../operations/overview.md), а не изменение цены портфеля и не календарь будущих выплат. @@ -38,7 +38,7 @@ При первой загрузке или новом периоде без сохранённых данных показан каркас страницы. Пустой результат объясняет отсутствие исполненных купонов и -дивидендов, сохраняя фильтры, налоговое пояснение и сравнения. Ошибка запроса +дивидендов, сохраняя фильтры и налоговое пояснение. Ошибка запроса или нарушение принадлежности выплат даёт локальное состояние с действием «Повторить». При фоновом обновлении того же периода сохранённый ответ и черновик остаются видимыми: загрузку обозначает индикатор, неуспешное обновление — @@ -51,32 +51,15 @@ - Backend проверяет доступ локально и читает сохранённые операции. Фоновая загрузка должна завершить все страницы; ошибка страницы или повреждённая цепочка курсоров не подтверждают полноту. - Только точные типы купона и дивиденда источника формируют результат. Суммы остаются точными десятичными строками в исходной валюте; отрицательные корректировки сохраняют знак. Налоги, комиссии, сделки, переводы, погашения и переводы дивидендов не входят в доход. -- Итоги, сравнения, временные интервалы и разбиение по инструментам не смешивают валюты и не создают общий конвертированный итог. Страница показывает сравнения с предыдущим периодом и годом ранее, когда календарная дата и история счёта позволяют. Неполное сравнение имеет отдельное сообщение, без нулевой базы. +- Итоги, временные интервалы и разбиение по инструментам не смешивают валюты и не создают общий конвертированный итог. Система не загружает другие периоды ради сравнения полученного дохода с выбранным. - Временные интервалы по-прежнему приходят с backend: дни для 1–45 дней, недели для 46–180 и месяцы для более длинного периода, включая пустые интервалы. Страница дохода не строит из них график или текстовую динамику; ответ API не меняется. -- Раздел «Выплаты» показывает единый список карточек выплат в исходном порядке `entries`: одна карточка на каждую выплату. Внешних групп по инструментам или валютам, итогов и количества выплат инструмента нет. Описание, дата, доступные идентификаторы инструмента и сумма со знаком видны в каждой карточке; валюты по-прежнему разделены в итогах и сравнениях. Дополнительный запрос за составом не нужен. - -Сравнение с непосредственно предыдущим периодом использует предыдущий полный -месяц для полного выбранного месяца, иначе — непосредственно предшествующий -интервал той же включительной длительности. Оно не подменяет сравнение годом -ранее. Незагруженное сравнение и ошибка его источника отличаются от -недоступности годового сравнения по календарю или дате открытия счёта. - -Каждый раздел сравнения показывает цветные панели без общей белой подложки и -внутренних отступов у внешнего блока. Точные периоды показаны в заголовках двух панелей с русским форматом диапазона -и UTC для календарных дат. -Отдельного заголовка с датами и валютного бейджа нет; валюта остаётся у каждой суммы. -Панель «Разница» сохраняет серверную абсолютную разницу и дополняет итог, купоны -и дивиденды процентом: абсолютная разница / сумма годом ранее × 100. -Это округлённый показатель для отображения, а не пересчёт Money или инвестиционная -доходность. Форматирование сохраняет знак и не более одного десятичного разряда. -При нулевой или отрицательной базе либо непредставимом числовом отношении -процентный бейдж не отображается; абсолютная разница остаётся видимой. +- Раздел «Выплаты» показывает единый список карточек выплат в исходном порядке `entries`: одна карточка на каждую выплату. Внешних групп по инструментам или валютам, итогов и количества выплат инструмента нет. Описание, дата, доступные идентификаторы инструмента и сумма со знаком видны в каждой карточке; валюты разделены в итогах. Дополнительный запрос за составом не нужен. ## Полнота и принадлежность выплат Доходы читают сохранённые операции и coverage в согласованной транзакции. -Selected, previous и yearAgo имеют независимую готовность. Неполный selected -не получает окончательных итогов; неполное сравнение не создаёт нулевую базу. +Только выбранный период определяет готовность результата. Неполный период +не получает окончательных итогов. Подтверждённый пустой период отличается от ещё не загруженного. При смене периода прежний snapshot не показывается под новым названием. @@ -103,11 +86,12 @@ Backend обеспечивает принадлежность выплат, ва ## Будущие события и ограничения -На странице дохода нет блоков «Динамика» и «Ближайший прогноз». Она не запрашивает +На странице дохода нет блоков «Динамика» и «Ближайший прогноз», блоков +сравнения периодов и сообщений об их недоступности. Она не запрашивает `/events` и не зависит от его доступности. Самостоятельная страница [«Будущие события»](upcoming-events.md) по-прежнему доступна из обзора выбранного счёта и сохраняет прежнее поведение. Будущие выплаты -не включаются в итоги полученного дохода или сравнения. +не включаются в итоги полученного дохода. Аналитика читает сохранённую историю операций и не рассчитывает налоги, инвестиционную эффективность, доходность, валютную конвертацию или общий мультивалютный итог. diff --git a/domains/income/upcoming-events.md b/domains/income/upcoming-events.md index 4d18cfe..be397aa 100644 --- a/domains/income/upcoming-events.md +++ b/domains/income/upcoming-events.md @@ -39,13 +39,23 @@ ## Период и список -Без фильтра период начинается сегодня в UTC и заканчивается через 30 дней -после начала, обе даты включены. `from` и `to` принимают только строгие даты -`YYYY-MM-DD`; без `from` используется текущая дата UTC, без `to` — `from + 30` -календарных дней. Поля «С» и «По» сохраняют выбранные календарные даты в URL, -а «Сбросить» возвращает период по умолчанию. +Без фильтра страница применяет период по умолчанию из семи календарных +дат включительно: сегодняшней и шести следующих календарных дней. API при +отсутствии параметров использует своё окно (от текущей даты UTC до `from + 30` +календарных дней), обе даты включены; `from` и `to` принимают только строгие +даты `YYYY-MM-DD`. Календарь фильтра разрешает выбор сегодняшней и будущих дат +и не позволяет выбрать даты раньше сегодняшней. Прямая ссылка с валидным +прошедшим периодом сохраняет указанные даты без замены; некорректный формат, +несуществующая календарная дата или обратный диапазон нормализуются к периоду +по умолчанию с удалением параметров из URL. Поля «С» и «По» сохраняют +выбранные календарные даты в URL, а «Сбросить» возвращает период по умолчанию. -События упорядочены по календарной дате. У каждой строки видны тип, +События упорядочены по календарной дате. Купоны и дивиденды показываются +единым предметным представлением выплаты с явной пометкой прогноза — тем же, +что у исполненных выплат в доходе и журнале; неизвестная сумма остаётся +«сумма неизвестна» и не превращается в ноль или полученные деньги. +Дата дивиденда обозначена как дата реестра. Offer и погашение сохраняют своё +значение без суммы выплаты. У каждой строки видны тип, инструмент, дата и доступная сумма в исходной валюте. Список не имеет пагинации и не конвертирует валюты. Пустой результат означает отсутствие известных поддерживаемых событий в выбранном окне, а не доказательство diff --git a/domains/instruments/bonds.md b/domains/instruments/bonds.md index ef47b3e..a5f2595 100644 --- a/domains/instruments/bonds.md +++ b/domains/instruments/bonds.md @@ -89,7 +89,11 @@ HTML-источник, общий кэш и поведение при измен Календарь охватывает пять прошлых лет и следующий год. Будущие купоны сортируются по дате выплаты, прошедшие и недатированные доступны отдельно. -Показаны сумма на облигацию, номер, тип, длительность периода и дата реестра, +Будущие и прошедшие события купонного графика показываются единым предметным +представлением выплаты с пометкой события по графику — тем же, что у +исполненных выплат и прогноза; исполнение подтверждает только личная история. +Показаны сумма на облигацию, отдельная личная оценка, номер, тип, длительность +периода и дата реестра, если они известны. Неопределённый будущий купон не заменяется нулём. Личная оценка доступна только при положительных позициях, известной сумме, @@ -117,8 +121,8 @@ HTML-источник, общий кэш и поведение при измен portfolio; они не попадают в общие источники. BondBy переиспользуется зависимостями выпуска. Discovery принадлежит query, -свежесть — конкретному source/range. Missing/stale зависимости запрашиваются -в фоне через прежний limiter; обычный HTTP-ответ не ждёт брокера. +свежесть — конкретному source/range. Missing/stale зависимости ставятся +в фоновую очередь через прежний limiter; обычный HTTP-ответ не ждёт брокера. Покрытые завершённые свечи не перечитываются после restart. Открытый хвост остаётся изменяемым; ручной refresh охватывает показанный период. @@ -146,6 +150,6 @@ BondBy переиспользуется зависимостями выпуск ## Online reference -Bond detail, search, and ticker lookup are read from the upstream integration -on each request. Saved quotes, candles, events, ratings, and portfolio labels -retain their own freshness and failure boundaries. +Страница облигации, поиск и переход по тикеру обращаются к онлайн-справочнику +брокера; сохранённые quotes, свечи, события, ratings и метки портфеля +сохраняют собственные границы свежести и ошибок. diff --git a/domains/instruments/stocks.md b/domains/instruments/stocks.md index e058cf6..4f35923 100644 --- a/domains/instruments/stocks.md +++ b/domains/instruments/stocks.md @@ -124,7 +124,11 @@ UID включён в ключ кэша и подпись общего курс ## Дивиденды и финансовые показатели -События компании отделены от личных зачислений. Известные будущие выплаты +События компании отделены от личных зачислений. Будущие и прошедшие события +дивидендного графика показываются единым предметным представлением выплаты +с пометкой «событие по графику» — тем же, что у исполненных выплат и прогноза; +сумма на акцию, отдельная личная оценка и дата реестра сохраняют свои подписи +и не выдаются за личное зачисление. Известные будущие выплаты определяются по `paymentDate`, при её отсутствии — по `recordDate`; прошедшие и недатированные события помещены в отдельную историю компании. Отменённые события исключаются. Личная оценка умножает сумму на акцию на текущее положительное @@ -155,9 +159,10 @@ TTM означает последние 12 месяцев, MRQ — послед источники переиспользуются между пользователями, но личные позиции и выплаты всегда проверяют доступ. -Обычное чтение не ждёт RPC; missing/stale зависимости запрашиваются в фоне -через существующий T-Bank limiter. Минутная свежесть применяется к quotes -и открытому хвосту свечей, суточная — к metadata/company/календарям. +Обычное чтение не ждёт RPC; missing/stale зависимости ставятся в фоновую +очередь через существующий T-Bank limiter. Часовая свежесть применяется +к quotes и открытому хвосту свечей; metadata/company/календари, будучи +сохранёнными, не перечитываются обычным чтением. Закрытые полные свечные интервалы, включая пустые, сохраняются после restart. Новый день не делает прежнюю незавершённую свечу полной. @@ -193,7 +198,6 @@ scope. Частичные строки видны с предупреждени ## Online reference -Stock detail, company data, search, and ticker lookup are read from the -upstream integration on each request. Saved positions, quotes, candles, and -events remain independent local resources and are not replaced by a reference -read failure. +Страница акции, поиск и переход по тикеру обращаются к онлайн-справочнику +брокера; сохранённые позиции, quotes, свечи и события остаются независимыми +локальными ресурсами и не подменяются ошибкой справочника. diff --git a/domains/operations/overview.md b/domains/operations/overview.md index d3e06fe..b786c21 100644 --- a/domains/operations/overview.md +++ b/domains/operations/overview.md @@ -84,8 +84,10 @@ Coverage/status без изменения rowset cursor не ломают. Пе Полный журнал и превью используют одинаковую компактную строку: логотип или буквенная заглушка, описание, дата, инструмент и денежное движение. -Для купона и дивиденда денежная метка называет точное событие; для прочих -операций — категорию. Она показывается один раз, без повтора рядом с датой. +Исполненные купоны и дивиденды показываются единым предметным представлением +выплаты — тем же, что на странице дохода, в прогнозе и графиках инструментов; +денежная метка называет точное исполненное событие. Для прочих +операций метка называет категорию. Она показывается один раз, без повтора рядом с датой. Знак и цвет обозначают направление денег; отсутствие движения нейтрально. Если описание отсутствует, показывается локализованный тип операции. diff --git a/domains/portfolio/all-accounts.md b/domains/portfolio/all-accounts.md index edc843a..d028588 100644 --- a/domains/portfolio/all-accounts.md +++ b/domains/portfolio/all-accounts.md @@ -53,13 +53,10 @@ Backend публикует статические маршруты под `/api/ - Журнал объединяет операции по убыванию времени, при равном времени использует порядок идентификаторов счетов и операций. ID операций разных счетов не считаются дублями. Переводы не схлопываются. -- Доходы рассчитываются из согласованной локальной выборки операций за общие периоды. - Итоги, сравнения, временные интервалы и принадлежность выплат рассчитывает - сервис дохода. - Для сравнения с годом ранее используется текущий состав счетов; новый счёт - добавляет только реально полученные выплаты. При известных датах открытия всех счетов - доступность сравнения определяется самым ранним открытием в этом составе. - Закрытые счета в историческое сравнение не добавляются. +- Доходы собираются из всех страниц операций каждого счёта за выбранный период. + Итоги, временные интервалы и принадлежность выплат рассчитывает + сервис дохода. Данные сравнения периодов не загружаются и не возвращаются; + состав счетов влияет только на фактические выплаты выбранного периода. - Будущие события сортируются по дате и сохраняют отдельную строку каждого счёта. Прогноз не включается в реализованные доходы. diff --git a/domains/portfolio/data-reference.md b/domains/portfolio/data-reference.md index 4912199..69dd72e 100644 --- a/domains/portfolio/data-reference.md +++ b/domains/portfolio/data-reference.md @@ -32,7 +32,7 @@ | Cash Balance | Суммы из `GetPositions.money`, объединённые по валюте | Заблокированные деньги не прибавляются; торгуемый инструмент `CURRENCY` остаётся позицией | | `totalsByCurrency` | Стоимости позиций + доступные деньги той же валюты | Не включает невычислимую стоимость; не обещает полную оценку портфеля | | `lastSuccessfulAt` | Время формирования успешного снимка, UTC | Не является временем каждой котировки; запасной снимок сохраняет исходное время | -| `isStale` | Истёк минутный срок свежести сохранённого portfolio source | Не описывает состояние клиентского кэша или ошибку фонового запроса | +| `isStale` | Истёк часовой срок свежести сохранённого portfolio source | Не описывает состояние клиентского кэша или ошибку фонового запроса | Ненулевые денежные остатки одной валюты суммируются. Позиции одинаковых инструментов в ответе отдельного счёта не объединяются; визуальная группировка diff --git a/domains/portfolio/overview.md b/domains/portfolio/overview.md index 9f0f791..c6d5314 100644 --- a/domains/portfolio/overview.md +++ b/domains/portfolio/overview.md @@ -79,18 +79,22 @@ Backend получает данные через серверную не содержат данных и не получают фокус. При обычном фоновом обновлении сохраняются предыдущие суммы и время успешного получения в UTC. -Серверный `isStale` обозначает истечение минутной свежести снимка. +Серверный `isStale` обозначает возраст снимка старше часа: обновлением +владеет часовой обход, а не запрос экрана. Клиентское предупреждение означает неудачную попытку обновить уже показанный ответ. Эти состояния независимы и могут появиться одновременно. Успешный повторный запрос обновляет суммы и время, снимает клиентскую ошибку; серверное предупреждение определяется новым `isStale`. Правила времени и ошибок приведены в [потоке загрузки](flows/portfolio-loading.md). -В текущем решении нет долговременной истории снимков, графиков стоимости -портфеля, валютной конвертации, учёта виртуальных позиций или заблокированных -средств. Последний полный снимок сохраняется после перезапуска; выполнение -синхронизации рассчитано на один экземпляр backend. Действие «Обновить» -перечитывает зависимости открытого экрана независимо от их свежести. +Отдельного чтения истории стоимости нет, но каждый полный снимок +сохраняет точку оценки по счёту и валюте для будущих поддерживаемых +представлений; публичного графика в текущем решении нет. Нет также +валютной конвертации, учёта виртуальных позиций или заблокированных +средств. Последний полный снимок сохраняется после перезапуска; +обновлением личных данных владеет часовой обход, рассчитанный на один +экземпляр backend. Действие «Обновить» выполняет только локальное +перечитывание открытого экрана и не запускает работу с T-Bank. Для проверки конкретной суммы используйте [руководство по расхождениям](how-to/investigate-discrepancy.md).