Отчет/Export: Выписка по счёту кошелька (wallet-statement)
XLSX-выписка одного счёта кошелька за период — строка = одна проводка этого счёта: дата операции, тип операции, основание (сделка/поручение/корректировка), причина корректировки, дебет, кредит и сальдо после операции. Над таблицей — сальдо на начало периода и обороты; под таблицей — сальдо на конец. Генерируется асинхронной задачей (../api/user/create-wallet-statement-export.md / ../api/agency/create-wallet-statement-export.md / ../api/platform/create-wallet-statement-export.md) по конвейеру ADR-0041.
Назначение
Даёт холдеру счёта (агенту/партнёру), руководителю агентства и оператору ПИН банковскую выписку по
внутреннему счёту: как из отдельных начислений, выплат и корректировок сложился текущий баланс, в
хронологическом порядке с нарастающим сальдо. Decision value — сверка с бухгалтерией, разбор «почему такой
остаток», подтверждение проведённых выплат, аудит корректировок. Owning service — wallet.
Выписка строится строго по журналу (../domain/ledger-transaction.md):
одна строка на проводку выбранного счёта. wallet не расшифровывает основание в термины сделки — SourceId
отдаётся как непрозрачная ссылка (расшифровка — в rewards по своим permissions,
../api/README.md «Masking»); банковских реквизитов и PII инвесторов в выписке нет.
Классификация
| Поле | Значение |
|---|---|
| Тип отчета | XLSX |
| Аудитория | холдер счёта (агент/партнёр) · руководитель агентства · оператор ПИН |
| Чувствительность | Financial/Sensitive — суммы и балансы счёта; PII инвесторов и банковские реквизиты в выписку не попадают |
| Freshness SLA | состояние журнала wallet на момент генерации (согласованный снапшот OLTP); балансы догоняют события rewards асинхронно (../api/README.md «Свежесть данных») |
| Retention | файл — 7 дней в приватном объектном хранилище; данные-источник (журнал) — бессрочно (append-only, финансовая история) |
Доступ
| Actor | Permission | Scope | Границы счёта | Audit |
|---|---|---|---|---|
| холдер счёта (агент/партнёр) | wallet.transactions.view-own | платформа (личные данные) | только свой счёт (HolderUserId = актор), вид — AgentCommission/ReferralCommission | ExportSensitive |
| руководитель агентства | wallet.team.view | организация | счёт AgencyCommission своей компании либо AgentCommission-счёт агента текущего состава | ExportSensitive |
| оператор ПИН | wallet.accounts.view-all | платформа | любой счёт по AccountId (включая системные) | ExportSensitive (+ read-sensitive) |
Выписка всегда охватывает ровно один счёт: нарастающее сальдо определено только в разрезе одного счёта. Выбор счёта — в операциях постановки (per-area), см. «Фильтры».
Исходные Source Data
| Источник | Владелец | Тип | Freshness | Join key |
|---|---|---|---|---|
wallet.ledger_entries (проводки счёта) | wallet | source of truth | строго согласовано | AccountId + CreatedAt (период) |
wallet.ledger_transactions (тип операции, OccurredAt, SourceId, Comment) | wallet | source of truth | строго согласовано | LedgerEntryEntity.TransactionId |
wallet.wallet_accounts (вид, холдер, валюта счёта) | wallet | source of truth | строго согласовано | AccountId |
Пользователь/компания identity (ФИО холдера-агента / название агентства — только в шапке) | identity | enrichment при генерации (не хранится в wallet) | на момент генерации | HolderUserId / HolderCompanyId |
ФИО холдера и название компании подставляются только в шапку выписки в момент генерации; при
недоступности identity задача ретраится, частичный файл не публикуется; если холдер не найден — в шапку
пишется id (fail-open отображения, warn-лог). Строки выписки от identity не зависят — только журнал.
Фильтры
Селектор счёта задаётся per-area (постановка), период — общий. Дом полей селектора — в файлах постановки:
user (свой счёт по AccountKind),
agency (AgentUserId? → счёт агента или AgencyCommission),
platform (явный AccountId).
| Поле | Тип | Обязательность | Default | Ограничения |
|---|---|---|---|---|
AccountId | Guid | Да | резолвится из селектора area | ровно один счёт; должен существовать (иначе WALLET_STATEMENT_ACCOUNT_NOT_FOUND) |
PeriodFrom | DateTimeOffset | Да | — | строки: OccurredAt >= PeriodFrom |
PeriodTo | DateTimeOffset | Да | — | > PeriodFrom (исключительно: OccurredAt < PeriodTo); диапазон ≤ 1 года — иначе WALLET_STATEMENT_PERIOD_TOO_WIDE |
OperationKind | string? (enum WalletOperationKind) | Нет | все виды | фильтр по виду операции (../domain/ledger-transaction.md); влияет только на строки — сальдо на начало считается по всем проводкам до PeriodFrom |
Строка Отчета WalletStatementRowModel
Одна строка = одна проводка (LedgerEntryEntity) выбранного счёта за период, в хронологическом порядке.
Все суммы — в валюте счёта (CurrencyId, RUB-only по ADR-0048).
| Поле | Тип | Обязательность | Расчет | Masking |
|---|---|---|---|---|
OccurredAt | DateTimeOffset | Да | LedgerTransactionEntity.OccurredAt (момент бизнес-факта: получение денег / выплата / корректировка) | — |
RecordedAt | DateTimeOffset | Да | LedgerTransactionEntity.RecordedAt (момент проведения) | — |
OperationKind | string (enum WalletOperationKind) | Да | OperationKind (CommissionCredit/ReferralCredit/PayoutDebit/Adjustment/Correction) | — |
Source | Guid | Да | SourceId — непрозрачная ссылка-основание (начисление / реферальное начисление / позиция поручения / исходная транзакция / ключ корректировки); расшифровка в rewards | — |
AdjustmentReason | string? | Нет (null кроме Adjustment/Correction) | LedgerTransactionEntity.Comment (причина ручной корректировки / причина отмены источника) | — |
Debit | decimal? | Нет (null у кредитовой проводки) | Amount.Value, если Direction = Debit (уменьшение баланса) | Financial |
Credit | decimal? | Нет (null у дебетовой проводки) | Amount.Value, если Direction = Credit (увеличение баланса) | Financial |
Balance | decimal | Да | BalanceAfter.Value проводки — сальдо счёта сразу после операции (зафиксировано при проведении) | Financial |
В каждой строке заполнена ровно одна из колонок Debit/Credit (сумма проводки всегда положительна, знак
задаёт Direction — ../domain/ledger-transaction.md). Balance у
холдерских счетов >= 0; у системных счетов (platform-area) может быть отрицательным (INV-WLT-3).
Модель Totals WalletStatementTotalsModel
Сальдо на начало/конец и обороты за период (шапка и подвал выписки).
| Поле | Тип | Обязательность | Расчет | Precision |
|---|---|---|---|---|
OpeningBalance | decimal | Да | сальдо счёта на момент PeriodFrom = Σ Credit − Σ Debit по всем проводкам с OccurredAt < PeriodFrom (при пустой предыстории — 0) | scale 2, banker's; CurrencyId счёта |
TurnoverDebit | decimal | Да | Σ Debit строк периода | scale 2 |
TurnoverCredit | decimal | Да | Σ Credit строк периода | scale 2 |
ClosingBalance | decimal | Да | OpeningBalance + TurnoverCredit − TurnoverDebit | scale 2 |
RowsCount | int | Да | count строк выборки периода | целое |
Контроль сходимости: ClosingBalance должен равняться Balance последней строки выписки (при непустой
выборке без фильтра OperationKind); расхождение — дефект целостности журнала (задача → Failed, алерт,
ADR-0021). При заданном OperationKind обороты считаются по отфильтрованным строкам, а сальдо на
начало/конец — по всем проводкам (сходимость с Balance строки не гарантируется — это штатно).
Формат Export-а
Лист XLSX statement; заголовки русские; суммы — числовой формат scale 2; шапка листа — счёт (вид, холдер,
валюта), период, OpeningBalance, применённый фильтр OperationKind, время генерации (UTC); подвал —
TurnoverDebit/TurnoverCredit/ClosingBalance.
| Column | Заголовок | Тип | Формат | Nullable | Источник |
|---|---|---|---|---|---|
OccurredAt | Дата операции | DateTimeOffset | dd.MM.yyyy | Нет | WalletStatementRowModel.OccurredAt |
OperationKind | Тип операции | string | текст (рус. подпись вида) | Нет | .OperationKind |
Source | Основание | Guid | текст | Нет | .Source |
AdjustmentReason | Причина корректировки | string | текст | Да | .AdjustmentReason |
Debit | Дебет | decimal | # ##0.00 | Да | .Debit |
Credit | Кредит | decimal | # ##0.00 | Да | .Credit |
Balance | Сальдо | decimal | # ##0.00 | Нет | .Balance |
Сортировка строк: OccurredAt ASC, при совпадении — RecordedAt ASC, затем TransactionId (стабильный
порядок — нарастающее сальдо читается сверху вниз).
Модель задачи WalletStatementExportJobModel
Единый дом модели задачи для всех трёх area (постановка и опрос статуса ссылаются сюда).
| Поле | Тип | Обязательность | Источник | Описание |
|---|---|---|---|---|
JobId | Guid | Да | задача выгрузки | Идентификатор задачи. |
Status | string (enum WalletExportJobStatus) | Да | задача | Queued / Running / Completed / Failed / Expired. |
CreatedAt | DateTimeOffset | Да | задача | Момент постановки. |
FileName | string? | Нет (null до Completed) | задача | Имя файла: wallet-statement-{accountShort}-{yyyyMMdd}-{yyyyMMdd}.xlsx. |
DownloadUrl | string? | Нет | signed URL | Подписанная ссылка на файл — только при Status = Completed и непросроченном файле (TTL 1 час); иначе null. |
ExpiresAt | DateTimeOffset? | Нет | задача | Момент истечения retention файла (7 дней от готовности); null до Completed. |
FailureCode | string? | Нет | задача | Код причины — только при Status = Failed (например, WALLET_STATEMENT_ROW_LIMIT_EXCEEDED). |
Enum WalletExportJobStatus
| Значение | Описание |
|---|---|
Queued | Задача поставлена, ожидает исполнителя. |
Running | Идёт формирование файла. |
Completed | Файл готов и лежит в хранилище (retention не истёк). |
Failed | Генерация завершилась ошибкой (FailureCode); частичный файл не публикуется. |
Expired | Файл удалён по retention (7 дней) — требуется новая постановка. |
Процесс Генерации
- Постановка задачи, проверка permission, границ счёта (per-area), периода, лимитов и audit — файлы постановки (user / agency / platform).
- Фоновый исполнитель резолвит счёт по
AccountId, считаетOpeningBalance(проводки сOccurredAt < PeriodFrom) — согласованный снапшот на момент чтения. - Читает проводки счёта за период (+ фильтр
OperationKind) со связанными транзакциями (тип,OccurredAt,SourceId,Comment) в хронологическом порядке. - Обогащение шапки: ФИО холдера / название компании из
identity(одиночный lookup). - Проверка лимита строк (≤ 100 000, иначе задача →
FailedcWALLET_STATEMENT_ROW_LIMIT_EXCEEDED). - Формирование
WalletStatementRowModel[](потоково) иWalletStatementTotalsModel; контроль сходимостиClosingBalanceсBalanceпоследней строки (при непустой выборке без фильтра вида) — расхождение — дефект целостности, задача →Failed, алерт. - Сериализация XLSX → приватное объектное хранилище (retention 7 дней); задача →
Completed; финальная audit-запись (число строк, исход). Ссылка на скачивание — короткоживущий signed URL (get-job операция соответствующей area).
Лимиты
| Лимит | Значение | Ошибка |
|---|---|---|
| Max rows | 100 000 строк | WALLET_STATEMENT_ROW_LIMIT_EXCEEDED (задача Failed) |
| Max date range | 1 год | WALLET_STATEMENT_PERIOD_TOO_WIDE (400 на постановке) |
| Concurrent exports | 2 активные задачи на актора | WALLET_STATEMENT_CONCURRENCY_LIMIT (429 на постановке) |
| Форматы | XLSX | — |
Наблюдаемость
| Тип | Правило |
|---|---|
| Metrics | wallet_statement_export_duration_seconds, wallet_statement_export_rows, wallet_statement_export_file_bytes, wallet_statement_export_jobs_total{area,result} |
| Logs | постановка / генерация / выдача ссылки / истечение / отказ — с актором, AccountId, периодом; суммы и балансы не логируются (Financial) |
| Traces | traceparent, AccountId, JobId |
| Audit | ExportSensitive на постановку, выдачу ссылки и исход генерации |
Обратные ссылки
Автоссылки: где используется этот документ.
- API (6): GET /api/v1/agency/wallet/statement-exports/{jobId} — Статус выписки и ссылка на скачивание, GET /api/v1/my/wallet/statement-exports/{jobId} — Статус выписки и ссылка на скачивание, GET /api/v1/platform/wallet/statement-exports/{jobId} — Статус выписки и ссылка на скачивание, POST /api/v1/agency/wallet/statement-exports — Постановка выписки по счёту агентства/агента (XLSX), POST /api/v1/my/wallet/statement-exports — Постановка выписки по моему счёту (XLSX), POST /api/v1/platform/wallet/statement-exports — Постановка выписки по любому счёту (XLSX)
Связанные документы
- Операции конвейера (постановка + опрос): ../api/user/create-wallet-statement-export.md, ../api/user/get-wallet-statement-export-job.md, ../api/agency/create-wallet-statement-export.md, ../api/agency/get-wallet-statement-export-job.md, ../api/platform/create-wallet-statement-export.md, ../api/platform/get-wallet-statement-export-job.md.
- Дома полей: ../domain/ledger-transaction.md (проводки,
OccurredAt,SourceId,Comment,BalanceAfter, INV-WLT-5..7), ../domain/wallet-account.md (виды счетов, холдеры,Balance, VOMoney). - Общие модели и masking API: ../api/README.md.
- ADR-0041 (async pipeline, signed URL, лимиты), ADR-0065 (границы, счета), ADR-0048 (RUB-only), ADR-0052 (tenancy), ADR-0056 (simplicity-first).