Ручные операции¶
Ручные операции — это действия оператора с баллами, уровнем и тегами клиента вне автоматических механик: начислить баллы, снять их корректировкой, перевести клиента на уровень, добавить или снять теги. Операции выполняются по одному клиенту (из карточки клиента или каталога) либо пакетом — по списку идентификаторов или по файлу.
Операции с баллами и уровнем требуют причину из справочника, записываются в журнал операций клиента с именем оператора и попадают в аудит. Операции с тегами записываются в историю тегов клиента от имени оператора пакета.
Экран «Ручные операции»¶
Пункт меню «Ручные операции» доступен оператору, у которого есть право хотя бы на одну из операций экрана (начисление, корректировка, перевод уровня, операции с тегами). Экран устроен как мастер из трёх шагов в одной карточке.
flowchart LR
S1["Шаг 1<br/>Список клиентов"] --> S2["Шаг 2<br/>Операция"] --> S3["Шаг 3<br/>Результат"]
| Шаг | Содержание |
|---|---|
| «Список клиентов» — «Загрузка и проверка» | Вкладки «Вставить список» и «Файл», кнопка «Найти», сводка проверки |
| «Операция» — «Что делаем с выбранными клиентами» | Вкладки «Начислить», «Корректировка», «Перевод уровня», «Добавить теги», «Снять теги», параметры и блок «Что получат клиенты» |
| «Результат» — «Что прошло, что упало» | Ход выполнения, итог по счётчикам, «Скачать отчёт CSV» |
Шаг 1. Список клиентов¶
Список задаётся одним из двух способов.
| Способ | Требования |
|---|---|
| «Вставить список» | ID клиентов — через Enter, запятую или точку с запятой. Пробел разделителем не является: значение с пробелом внутри отклоняется как ошибка формата |
| «Файл» | Файл .csv или .txt в кодировке UTF-8, до 20 МБ. В строке — один ID клиента, без заголовка. Строки с лишними колонками в операцию не войдут. Есть кнопка «Скачать образец» |
Сумма в файле не задаётся — она общая для всего пакета и указывается на шаге 2.
По кнопке «Найти» список уходит на сервер и проверяется целиком. Дальше проходят только строки, прошедшие проверку: клиент найден и ID не повторяется (из повторов остаётся первая строка). Для операций с баллами и уровнем клиент ещё должен быть не заблокирован и иметь бонусный счёт; для операций с тегами заблокированные клиенты и клиенты без счёта годятся. Если готовность к операциям с тегами отличается от общей, оператору с правом на теги показывается отдельная строка «Для операций с тегами готово: N из M строк».
Сводка проверки показывает «Готово к операции: N из M строк» и теги по типам проблем — «Формат», «Дубль», «Не найден», «Заблокирован», «Нет счёта», «Мало баллов», «Уже проведено», «Ошибка». Счётчики приходят с сервера по каждому коду без усечения; построчной таблицы на экране нет — полный список строк выгружается кнопкой «Скачать отчёт о проверке CSV».
Для корректировки проверка ставит в строку доступный остаток целевого субсчёта (колонка
availableMinor отчёта) — то же число, до которого корректировку ограничит выполнение: без баллов с
наступившим, но ещё не сожжённым сроком и с учётом резервов (см.
Сгорание баллов). Строка, готовая по проверке, может
отказать при выполнении, только если остаток клиента изменился между проверкой и проводкой.
Предел списка
В одном пакете — не больше LOYALTY_MANUAL_OPERATIONS_MAX_ROWS строк (по умолчанию 1 000 000).
Файл больше 20 МБ и превышение предела строк отклоняются целиком с понятным сообщением («Файл
больше 20 МБ», «В списке больше строк, чем принимает один пакет — разбейте его на несколько»).
Шаг 2. Операция¶
Операции выбираются вкладками; вкладка без права у оператора скрыта.
| Вкладка | Что делает | Право |
|---|---|---|
| «Начислить» | Начисляет каждому клиенту списка одну и ту же сумму баллов | LOYALTY_ACCRUAL_WRITE |
| «Корректировка» | Снимает у каждого клиента одну и ту же сумму на системный счёт корректировок | LOYALTY_REDEMPTION_WRITE |
| «Перевод уровня» | Назначает всем клиентам списка один уровень (ручное назначение поверх расчётного) | LOYALTY_TIER_WRITE |
| «Добавить теги» | Добавляет выбранные теги каждому клиенту списка — к уже стоящим | LOYALTY_CUSTOMER_TAG_WRITE |
| «Снять теги» | Снимает выбранные теги у тех клиентов списка, у кого они стоят | LOYALTY_CUSTOMER_TAG_WRITE |
Поля шага:
| Поле | Операции | Правила |
|---|---|---|
| «Сумма, баллы» / «Сумма снятия, баллы» | Начисление, корректировка | Целое, больше нуля; одна сумма на каждого клиента списка |
| «Списывать доступное, если баллов не хватает» | Корректировка | Включено — при нехватке остатка снимается доступное вместо отказа строки; при нулевом остатке строка всё равно отказывает |
| «Субсчёт» | Начисление, корректировка | Обязателен, значения по умолчанию нет. У начисления в списке только активные субсчета, у корректировки — все, кроме черновиков |
| «Уровень» | Перевод уровня | Из каталога уровней |
| «Теги» | Добавить и снять теги | Теги из справочника, не больше 1 000 в одной операции; выбор общий для обеих вкладок. Причины, суммы, субсчёта и срока у операций с тегами нет |
| «Когда выполнить» | Начисление, корректировка | «Сразу после подтверждения» или «Отложенно» с датой и временем |
| «Причина» и «Комментарий» | Начисление, корректировка, перевод уровня | См. Причина операции |
Блок «Что получат клиенты» заменяет модальное окно предпросмотра: он показывает итог со знаком (сумма × число прошедших проверку клиентов), число клиентов, выбранную причину и — у отложенной операции — срок выполнения. У операций с тегами — «Добавить теги: …» или «Снять теги: …» (больше пяти тегов — первые пять и «и ещё N»). Кнопка «Скачать предварительный отчёт по операции CSV» отдаёт построчный расчёт с сервера: кто и сколько получит, с отказами за нехватку баллов.
Кнопка выполнения названа глаголом операции — «Начислить», «Скорректировать», «Перевести уровень», «Добавить теги», «Снять теги»; у отложенной операции — «Запланировать начисление» / «Запланировать корректировку».
Шаг 3. Результат¶
Пакет принимается сразу, а выполняется в фоне, поэтому шаг показывает ход выполнения: «Обработано N из M» с полосой прогресса (или «В очереди на выполнение») и подсказку, что со страницы можно уйти — результат сохранится, и при следующем открытии экрана оператор вернётся к нему.
По завершении показываются теги по счётчикам:
| Тег | Смысл |
|---|---|
| «Успешно» | Строки, по которым операция проведена |
| «Ошибки при выполнении» | Отказы в момент выполнения (коды с префиксом execution_) |
| «Не прошли проверку» | Строки, отбракованные проверкой |
| «Уже проведено» | Строка уже проводилась под той же ссылкой пакета |
| «Не обработаны» | Пакет прерван системным сбоем, до строк дело не дошло |
Кнопка «Скачать отчёт CSV» отдаёт исход каждой строки пакета (номер строки, клиент, ФИО, исход, код и причина отказа). Построчной таблицы на экране нет намеренно: пакет бывает на миллион строк.
Внизу — «Новая операция». Пока текущий пакет в очереди или выполняется, кнопка недоступна («Дождитесь завершения текущей операции»).
Отмены пакета нет
Отмена выполненных операций не предусмотрена — при ошибке проводится обратная операция. Повторного запуска «для непрошедших» тоже нет: непрошедшие строки берутся из CSV-отчёта, и новый список — это новый пакет.
Причина операции¶
Причина у всех ручных операций с баллами и уровнем — код активной записи справочника «Причины операций» плюс необязательный комментарий (до 500 символов). У операций с тегами причины нет.
| Поле | Правило |
|---|---|
«Причина» (reasonCode) |
Обязательна. Список — активные записи справочника в порядке сортировки |
«Комментарий» (comment) |
Необязателен, кроме причины «Прочее» (OTHER) — там обязателен |
Причина «Прочее» видна и доступна только оператору с ролью LOYALTY_OPERATION_REASON_OTHER_WRITE
(до сентября 2026 роль называлась LOYALTY_CORRECTION_OTHER_WRITE): категория без объяснения быстро
превращается в свалку.
Справочник один на все операции с баллами и уровнем. Читать его нужно каждому оператору этих
операций, поэтому вместе с правом на операцию требуется LOYALTY_DICTIONARY_READ. Если справочник недоступен, вместо поля
показывается предупреждение «Справочник причин недоступен — операция невозможна», и операция не
выполняется.
Предустановленные причины:
| Код | Подпись | Назначение |
|---|---|---|
DUPLICATE_ACCRUAL |
Двойное начисление | Одна покупка начислена дважды |
OPERATOR_ERROR |
Ошибка оператора | Начислено не тому клиенту или не та сумма |
PROMOTION_ERROR |
Ошибка настройки акции | Механика начислила лишнее |
REFUND_MANUAL |
Возврат покупки | Возврат не обработался автоматическим сторно |
FRAUD |
Злоупотребление | Накрутка, фрод — по заключению разбора |
RECONCILIATION |
Расхождение при сверке | Сверка или миграция выявила лишние баллы |
CUSTOMER_REQUEST |
По обращению клиента | Клиент сам попросил снять баллы |
COMPENSATION |
Компенсация | Компенсация за сбой, ошибку или неудобство |
MISSED_ACCRUAL |
Не начислено автоматически | Покупка не начислилась из-за сбоя интеграции |
MARKETING |
Маркетинговая акция | Ручная выдача баллов или уровня вне механик |
MANAGER_DECISION |
Решение менеджера | Индивидуальное решение по клиенту |
OTHER |
Прочее | Всё остальное; комментарий обязателен, нужно отдельное право |
Справочник пополняется в администрировании, как остальные локальные справочники.
BREAKING (26.3.0): свободного текста причины больше нет
У начисления и перевода уровня причина была свободным текстом, у корректировки — кодом
справочника. Теперь у всех трёх операций — reasonCode + comment; поле reason из тел
запросов POST /v1/customers/{externalId}/accruals, POST /v1/customers/bulk-accruals,
PUT /v1/customers/{externalId}/tier, POST /v1/customers/tier-bulk и из параметров приёма
пакета убрано. Интеграционные точки приёма (RT Flow, Promo, очередь команд) не затронуты — там
reason остаётся техническим текстом источника.
Одиночные операции из карточки клиента и каталога¶
В карточке клиента доступны действия «Начислить», «Корректировка» и «Изменить уровень», в каталоге клиентов — те же операции по выделению («Начислить», «Корректировка», «Перевод уровня»). Форма та же: сумма, обязательный субсчёт, причина из справочника и комментарий; у массовых действий каталога — чек-лист выбранных клиентов со счётчиком «Будет начислено: N, пропущено: M».
У заблокированного клиента действия недоступны (подсказка «Клиент заблокирован»); попытка провести
операцию отвечает 422 customer_blocked — «Клиент заблокирован — ручные операции недоступны».
Корректировка¶
Корректировка — это снятие баллов оператором на отдельный системный счёт корректировок (CORR),
а не трата клиента: в отчётности она отличается от списания и имеет собственный тип движения
CORRECTION («Корректировка» в истории операций).
POST /v1/customers/{externalId}/corrections — роль LOYALTY_REDEMPTION_WRITE, обязателен
Idempotency-Key.
| Поле | Обязательность | Описание |
|---|---|---|
pointsMinor |
да | Сумма снятия в минорных единицах |
reasonCode, comment |
да / при OTHER |
Причина из справочника и комментарий |
reference |
да | Ссылка операции |
subaccountCode |
нет | Субсчёт; не указан — базовый субсчёт. Добора с соседних субсчетов нет |
Не хватает доступного остатка субсчёта — 422 insufficient_points с availableMinor в ответе
(отдельно от платёжного 402 not_enough_funds). Доступный остаток — тот же, что в балансе клиента:
без баллов с наступившим сроком и за вычетом резервов.
Пакет по списку (JSON)¶
Массовые ручки принимают готовый список идентификаторов:
| Операция | Метод и путь | Право |
|---|---|---|
| Начисление | POST /v1/customers/bulk-accruals |
LOYALTY_ACCRUAL_WRITE |
| Корректировка | POST /v1/customers/bulk-corrections |
LOYALTY_REDEMPTION_WRITE |
| Перевод уровня | POST /v1/customers/tier-bulk |
LOYALTY_TIER_WRITE |
Тело начисления и корректировки: externalIds[], pointsMinor, reference, reasonCode,
comment, необязательные subaccountCode, expireAt, deductAvailable, scheduledAt. Без
expireAt срок начисленных баллов определяется по цепочке — см.
Сгорание баллов.
Тело перевода уровня — своё, короче: externalIds[], tierCode, reasonCode, comment; полей
счёта (subaccountCode, expireAt и т. д.) у него нет — уровень не связан со счётом. Idempotency-Key
обязателен у начисления и корректировки, у перевода уровня — необязателен.
Ответ — единая форма для всех трёх ручек:
{
"batchId": "0f6f...",
"status": "completed",
"total": 3,
"succeeded": ["CUST-1", "CUST-2"],
"errors": [
{ "externalId": "CUST-3", "fullName": "Иванов Иван", "code": "customer_not_found", "reason": "..." }
],
"scheduledAt": null
}
- Обработка поштучная: отказ по одному клиенту не отменяет остальных.
- Этап отказа виден в коде: код проверки (например,
customer_not_found), тот же код с префиксомexecution_— отказ при выполнении,batch_aborted— до строки не дошли. reasonв ответе — технический текст; оператору текст строится интерфейсом по коду.- Проводки и балансы в ответе не возвращаются — они в отчёте по
batchId.
BREAKING (26.3.0)
Прежние формы ответов (succeeded[{transactionId, balanceMinor}], assigned[],
failed/rejected/unprocessed) убраны, поле source из тел запросов убрано — источник списка
сервер проставляет сам.
Предел одного запроса — LOYALTY_MANUAL_OPERATIONS_BULK_MAX_IDS (по умолчанию 1000 уникальных
идентификаторов), сверх — 400 bulk_size_exceeded до обработки любой строки. То же число — предел
массовых статус-действий каталога клиентов и размер чанка, на которые режется файл при выполнении.
Асинхронный пакет по файлу¶
Пакет по файлу (или по вставленному списку) принимается одним запросом и выполняется в фоне — запрос не держит соединение на время обработки миллиона строк.
POST /v1/customers/operations/batches — multipart с файлом, параметры операции в строке запроса
(source, operation, reference, pointsMinor, reasonCode, comment, deductAvailable,
tierCode, subaccountCode, expireAt, scheduledAt, tagIds), Idempotency-Key обязателен.
Операция — ACCRUAL, CORRECTION, TIER_ASSIGN, TAG_ASSIGN или TAG_REMOVE. Право — любое из
прав ручных операций, при этом проверяется право именно на выбранную операцию.
Ответ — 202:
{ "batchId": "0f6f...", "status": "queued", "total": 250000, "chunkSize": 1000, "chunkCount": 250,
"scheduledAt": null }
Что происходит до ответа: проверка файла (формат, кодировка, размер, антивирус), разбор и отбраковка повторов, проверка прав и параметров операции, правила отложенного выполнения, запись пакета и нарезка его на чанки. Строки списка (клиент, счёт, остаток) проверяются уже при выполнении.
Повторы идентификаторов и неразбираемые строки входят в пакет как отбракованные — чтобы номера строк отчёта совпадали с номерами строк файла оператора.
Операции с тегами¶
«Добавить теги» (TAG_ASSIGN) и «Снять теги» (TAG_REMOVE) размечают тегами список до миллиона
клиентов тем же асинхронным конвейером.
- Единственный параметр —
tagIds: от 1 доLOYALTY_TAGS_MAX_LOOKUP_VALUES(по умолчанию 1 000) тегов справочника. Причина, сумма, субсчёт и отложенное выполнение у операций с тегами запрещены —400 validation_failed. - Тег, которого нет в справочнике, отклоняет запрос целиком —
422 unknown_tagс перечнемunknownTagIds; интерфейс снимает такие теги из поля («Некоторые теги удалены из справочника — выберите теги заново»). - Добавление объединяет выбранные теги с уже стоящими; снятие снимает только те, что стоят. Строка без изменений считается успехом и в историю не пишется. Автор записи в истории тегов — оператор пакета.
- Тег, удалённый или слитый после приёма пакета, отказывает строкам кодом
execution_unknown_tag: набор применяется к клиенту целиком или никак. - Статус пакета отдаёт поле
tags— теги (id, значение) на момент приёма.
В каталоге клиентов массовое действие «Удалить теги» переименовано в «Снять теги».
Наблюдение за ходом¶
| Метод и путь | Назначение |
|---|---|
GET /v1/customers/operations/batches/{batchId} |
Статус и счётчики: total, processed, succeeded, failed, rejected, unprocessed, chunksDone, problemCounts, параметры пакета |
GET /v1/customers/operations/batches/{batchId}/events |
Поток событий о ходе (Server-Sent Events): progress при изменении счётчиков, done на завершении |
GET /v1/customers/operations/batches/{batchId}/report |
Отчёт CSV: исход каждой строки |
GET /v1/customers/operations/batches?active=true |
Незавершённые пакеты текущего оператора |
Статусы пакета: scheduled (ждёт срока), queued (принят), running (выполняется), completed
(завершён), aborted (прерван системным сбоем).
Отчёт по пакету отдаётся только под правом на операцию этого пакета: отчёт по корректировке с ФИО и балансами недоступен тому, у кого есть только право на перевод уровня. Оба CSV-отчёта — выгрузка персональных данных и фиксируются в журнале обращений к ним.
Одна активная загрузка на оператора¶
У одного оператора одновременно может выполняться только один пакет. Попытка подать второй —
409 batch_already_running («У вас уже выполняется операция. Дождитесь её завершения»); интерфейс
переходит к активному пакету. Отложенные пакеты в это ограничение не входят.
Экран запрашивает активные пакеты при открытии, при возврате фокуса на вкладку и при нажатии «Новая операция» — незавершённый пакет открывается сразу на шаге «Результат».
Надёжность выполнения¶
- Исход каждой строки записывается в транзакции самой строки, поэтому повтор чанка ничего не задваивает: строки с готовым исходом повторно не выполняются.
- Пакет, брошенный из-за перезапуска сервиса, подхватывается автоматически: незавершённые чанки публикуются заново, а пакет с готовыми итогами закрывается — оператор не остаётся заблокированным навсегда.
- Системный сбой посреди пакета закрывает его статусом
aborted: строки без исхода помечаются «не обработаны», остальные чанки доводятся до конца. Ручного повтора чанка или пакета нет. - Брокер выполнения недоступен в момент приёма —
503 batch_transport_unavailable, пакет не создан, повтор возможен тем жеIdempotency-Key.
Отложенное выполнение¶
У начисления и корректировки можно задать момент выполнения: пакет принимается сразу, а выполняется в срок — оператору не нужно дежурить у экрана.
- Поле «Когда выполнить»: «Сразу после подтверждения» либо «Отложенно» с датой и временем
(
ДД.ММ.ГГГГ ЧЧ:мм). - Время трактуется в часовом поясе программы — системная настройка «Часовой пояс программы» (по
умолчанию
Europe/Moscow); под полем — подсказка «Часовой пояс: UTC+3» со смещением на выбранный момент. - Перевод уровня и операции с тегами не откладываются.
- До срока отчёт по пакету отдаёт строки в состоянии «ожидает»: ФИО, коды и исходы появятся после выполнения.
- Параметры пакета (ссылка, сумма, причина, право на «Прочее», субсчёт) проверяются на приёме, а клиент, счёт и остаток — в момент выполнения, когда они и действуют.
Отказы приёма:
| Код | Когда |
|---|---|
422 batch_schedule_in_past |
Момент выполнения не в будущем |
422 batch_schedule_too_far |
Дальше предельной отсрочки (по умолчанию 366 дней) |
422 batch_scheduling_disabled |
Отложенное выполнение выключено на контуре |
Шедулер в срок переводит пакет в работу и выполняет его тем же конвейером. Если проверка списка в момент выполнения отказывает целиком (например, субсчёт успели закрыть), пакет закрывается как прерванный — он не остаётся «выполняющимся» навсегда.
Права и коды ошибок¶
| Право | Что открывает |
|---|---|
LOYALTY_ACCRUAL_WRITE |
Начисление — одиночное, пакетом и по файлу |
LOYALTY_REDEMPTION_WRITE |
Корректировка (и списание, ручное сгорание) |
LOYALTY_TIER_WRITE |
Перевод уровня — одиночный и массовый |
LOYALTY_CUSTOMER_TAG_WRITE |
«Добавить теги» и «Снять теги» по списку клиентов |
LOYALTY_OPERATION_REASON_OTHER_WRITE |
Причина «Прочее» у любой ручной операции |
LOYALTY_DICTIONARY_READ |
Чтение справочника причин — нужно каждому оператору операций с баллами и уровнем |
Экран «Ручные операции» открыт при наличии хотя бы одного из четырёх прав на операции; вкладки без права скрыты. Оператор только с правом на теги видит экран с двумя вкладками тегов.
| Код | HTTP | Когда возникает |
|---|---|---|
validation_failed |
400 | Параметры операции не согласованы: нет суммы, причины или ссылки |
reason_comment_required |
400 | Причина «Прочее» без комментария |
bulk_size_exceeded |
400 | В запросе больше идентификаторов, чем принимает одна ручка |
access_denied |
403 | Нет права на операцию или на причину «Прочее» |
operation_batch_not_found |
404 | Отчёт или статус запрошен по неизвестному пакету |
already_processed |
409 | Строка уже проведена под той же ссылкой пакета |
batch_already_running |
409 | У оператора уже есть активный пакет |
subaccount_not_active |
409 | Начисление на неактивный субсчёт |
unknown_reason_code |
422 | Код причины неизвестен или запись неактивна |
insufficient_points |
422 | У клиента меньше доступных баллов, чем сумма корректировки |
customer_blocked |
422 | Клиент заблокирован — ручные операции с баллами и уровнем запрещены |
unknown_tag |
422 | В операции с тегами указан тег, которого нет в справочнике |
batch_schedule_in_past, batch_schedule_too_far, batch_scheduling_disabled |
422 | Правила отложенного выполнения |
bulk_rate_limited |
429 | Слишком часто запускаются массовые операции |
batch_transport_unavailable |
503 | Сервис выполнения пакетов временно недоступен |