Перейти к основному содержимому

POST /api/v1/platform/wallet/statement-exports — Постановка выписки по любому счёту (XLSX)

Асинхронная выгрузка выписки по одному счёту кошелька для оператора ПИН в XLSX (ADR-0041): операция создаёт задачу выгрузки, фоновый исполнитель формирует файл и кладёт его в приватное объектное хранилище; статус и короткоживущая ссылка на скачивание — get-wallet-statement-export-job.md. Контракт отчёта — ../../reports/wallet-statement.md.

Назначение

Оператор ПИН выгружает банковскую выписку по любому счёту кошелька — холдерскому (AgentCommission / ReferralCommission / AgencyCommission) или системному (PlatformSettlement / PlatformRevenue) — по явному AccountId из реестра (list-wallet-accounts.md). Инструмент финансового контроля, сверки и разбора инцидентов рассинхрона (ADR-0021). Feature group — platform-wallet.

Metadata

ПолеЗначение
Сервис/контекстwallet
API areaplatform
Feature groupplatform-wallet
Статусdraft
Документdocs/06-services/wallet/api/platform/create-wallet-statement-export.md

Актор и доступ

ПроверкаЗначение
Actor typeOperator(Admin) (оператор ПИН)
Auth policy / Permissionswallet.accounts.view-all
Scope (RBAC)платформа
Record-levelне применяется (доступ ко всем счетам)
AuditExportSensitive (+ read-sensitive): запись на каждую постановку (актор, AccountId, период, время); финальный аудит — при завершении задачи

HTTP-контракт

ПолеЗначение
MethodPOST
Route/api/v1/platform/wallet/statement-exports
Success status202 (+ Location: /api/v1/platform/wallet/statement-exports/{jobId})
Idempotency headerне применяется; естественная защита от двойного клика: повтор с теми же (AccountId, период, OperationKind), пока активна незавершённая задача, возвращает 202 с тем же JobId
Correlationсквозной trace через заголовок traceparent

Входные данные / параметры запроса

ПолеИсточникТипОбязательностьВалидация / ОграниченияБезопасность
ActorUserIdidentity claims (sub)GuidДаRequiredaudit
AccountIdbodyGuidДаRequired, непустой; счёт должен существовать — иначе WALLET_STATEMENT_ACCOUNT_NOT_FOUND
PeriodFrombodyDateTimeOffsetДаRequired; строки: OccurredAt >= PeriodFrom
PeriodTobodyDateTimeOffsetДаPeriodTo > PeriodFrom (исключительно); диапазон ≤ 1 года — иначе WALLET_STATEMENT_PERIOD_TOO_WIDE
OperationKindbodystring? (enum WalletOperationKind)Нетпусто = все виды; влияет только на строки

В отличие от user/agency, счёт задаётся явным AccountId (не резолвится из холдера); допустимы системные счета, у которых сальдо может быть отрицательным (INV-WLT-3, ../../domain/wallet-account.md).

Модель ответа WalletStatementExportJobModel

Дом модели — ../../reports/wallet-statement.md. На постановке: Status = Queued, DownloadUrl = null, FileName = null.

Алгоритм

  1. Контекст и доступ. Операцию выполняет оператор ПИН; scope — платформа; требуется право wallet.accounts.view-all.
  2. Проверка входа. AccountId непустой; PeriodTo > PeriodFrom; диапазон ≤ 1 года — иначе WALLET_STATEMENT_PERIOD_TOO_WIDE; OperationKind — известное значение enum, если задан.
  3. Существование счёта. Счёт с AccountId существует — иначе WALLET_STATEMENT_ACCOUNT_NOT_FOUND (счета создаются lazy; несуществующий id — не пустая выписка, а ошибка, т.к. явный id ошибочен).
  4. Дедупликация двойного клика. Если у актора уже есть незавершённая задача (Queued/Running) с теми же AccountId, периодом и OperationKind — новая не создаётся, возвращается 202 с её JobId.
  5. Лимит конкурентности. Активных задач (Queued/Running) у актора — не более 2; превышение → WALLET_STATEMENT_CONCURRENCY_LIMIT.
  6. Постановка задачи. В одном согласованном изменении создаются задача выгрузки (Status = Queued, AccountId, снапшот фильтров, актор) и audit-запись ExportSensitive. Ответ — 202 + Location.
  7. Фоновая генерация (вне запроса). Исполнитель формирует выписку по контракту ../../reports/wallet-statement.md, проверяет лимит 100 000 строк (превышение → задача Failed с WALLET_STATEMENT_ROW_LIMIT_EXCEEDED), сериализует XLSX и кладёт файл в приватное хранилище; задача → Completed (или Failed — частичный файл не публикуется). Retention файла — 7 дней, после — Expired.

Диаграмма

Побочные эффекты

ТипДетали
Интеграционные событияnone (задача выгрузки — внутренняя сущность wallet)
Внешние вызовыфоновый исполнитель: identity (ФИО/название холдера в шапку — enrichment; для системных счетов не требуется); объектное хранилище (файл)
Уведомленияnone (клиент опрашивает статус задачи)

Ошибки и failure modes

Общие коды (ValidationError, FORBIDDEN, UNAUTHORIZED) — дом ../README.md «Общие ошибки». Дельта операции (WALLET_STATEMENT_ROW_LIMIT_EXCEEDED фиксируется в задаче, не в HTTP-ответе):

КодHTTP statusУсловиеСообщениеRetry
WALLET_STATEMENT_ACCOUNT_NOT_FOUND404Счёт с AccountId не существует«Счёт не найден»no retry (проверить id в реестре)
WALLET_STATEMENT_PERIOD_TOO_WIDE400PeriodTo − PeriodFrom > 1 года«Период выписки не может превышать один год»no retry (сузить период)
WALLET_STATEMENT_CONCURRENCY_LIMIT429У актора уже 2 активные задачи (Queued/Running)«Дождитесь завершения текущих выгрузок»retry после завершения
WALLET_STATEMENT_ROW_LIMIT_EXCEEDED— (статус задачи Failed)Выборка превысила 100 000 строк«Слишком большой период — сузьте выборку»no retry (сузить период)

Acceptance criteria

  • AC-1 (холдерский счёт). Given оператор с wallet.accounts.view-all, существующий AccountId холдерского счёта, период ≤ 1 года; When POST; Then 202 + Location с JobId, задача доводится до Completed, файл доступен через get-wallet-statement-export-job.md.
  • AC-2 (системный счёт). Given AccountId системного счёта (PlatformSettlement); When POST и генерация; Then выписка формируется, сальдо в строках/итогах может быть отрицательным (INV-WLT-3) — не ошибка.
  • AC-3 (несуществующий счёт). Given AccountId, которого нет в реестре; When POST; Then WALLET_STATEMENT_ACCOUNT_NOT_FOUND (404), задача не создаётся.
  • AC-4 (период шире года). Given PeriodTo − PeriodFrom = 18 месяцев; When POST; Then WALLET_STATEMENT_PERIOD_TOO_WIDE (400), задача не создаётся.
  • AC-5 (лимит конкурентности). Given у актора уже 2 активные задачи; When POST с другими фильтрами; Then WALLET_STATEMENT_CONCURRENCY_LIMIT (429); активные задачи не прерываются.
  • AC-6 (превышение лимита строк). Given выборка > 100 000 строк; When фоновая генерация; Then задача → Failed с WALLET_STATEMENT_ROW_LIMIT_EXCEEDED, частичный файл не публикуется.
  • AC-7 (доступ). Given актор без wallet.accounts.view-all; When POST; Then FORBIDDEN (403), задача и аудит-запись выгрузки не создаются (пишется запись отказа доступа).

Совместимость и наблюдаемость

  • Контракт additive-only: новые optional фильтры и значения enum — additive; смена структуры файла — версия контракта отчёта.
  • Logs: JobId, AccountId, период, актор, result (суммы/балансы не логируются); metrics: wallet_statement_export_jobs_total{area=platform,result}, wallet_statement_export_duration_seconds, wallet_statement_export_rows; traces: OTel, correlation через traceparent.
  • НФТ (ADR-0056): единицы выгрузок; генерация — минуты (фон, не SLA API).

Обратные ссылки

Автоссылки: где используется этот документ.

Связанные документы