Субсчета баллов¶
Субсчёт — это целевой счёт внутри карты клиента: «Приветственные», «Подарочные ко дню рождения», «Кешбэк за категорию», «Партнёрские». Баланс клиента перестал быть одной суммой: карта объединяет субсчета, у каждого из них своё правило сгорания, своя область списания и своё место в очереди списания.
Субсчета ведёт маркетолог в разделе «Администрирование» → «Субсчета баллов». Закрытого перечня типов баллов нет: новое назначение заводится записью справочника и не требует доработки.
Как это устроено¶
- Субсчёт — самостоятельный счёт леджера, а не пометка в проводке: баланс субсчёта остаётся производной от проводок, а запрет уйти в минус работает тем же механизмом, что и раньше.
- Субсчёт принадлежит одной валюте. Под разные валюты заводятся разные субсчета; очередь списания у каждой валюты своя.
- Начисление всегда попадает ровно на один субсчёт: указанный в запросе начисления; у начисления по акции — «Тип начисляемых баллов» акции (по умолчанию «Акционные баллы»); если субсчёт не указан — базовый субсчёт валюты. Счёт клиента на базовом субсчёте открывается при заведении клиента, на остальных субсчетах — первой проводкой.
- Списание идёт по очереди субсчетов с учётом области списания каждого и может затронуть несколько сразу.
- Сторно возвращает баллы на тот субсчёт, куда легло исходное начисление.
- Субсчёт с проводками не удаляется: иначе история перестанет сходиться с леджером. Вместо удаления — закрытие.
Экран «Субсчета баллов»¶
Заголовок — «Субсчета баллов», подзаголовок — «Целевые счета внутри карты клиента: приветственные, партнёрские, акционные. Очередь списания у каждой валюты своя».
Сверху — выбор «Валюта баллов»: список ведётся по балльной валюте из справочника «Валюты». Выключенная валюта помечена «(выключена)»: её очередь видна, но новый субсчёт в неё не заводится — кнопка недоступна с подсказкой «В выключенную валюту новые субсчета не заводятся». Рядом с выбором валюты — её курс фразой (например, «1 PTS = 0,50 ₽») и ссылка «Изменить курс»: курс — свойство валюты, он меняется в карточке валюты раздела «Валюты», куда и ведёт ссылка. Действия — «Завести субсчёт» и «Сохранить порядок».
Колонки таблицы:
| Колонка | Содержание |
|---|---|
| «Очередь» | Место в очереди списания; стрелками субсчёт двигается вверх и вниз |
| «Код» | Код субсчёта; у базового рядом пометка «Базовый» |
| «Название» | Название субсчёта |
| «Состояние» | «Черновик», «Активен» или «Закрыт» |
| «Сгорание» | «Общий срок», «Через N дней» или дата |
| «Область списания» | «Нет» либо до трёх строк вида «Категории: …», остальное — «+N» с полным списком в подсказке |
Действия над строкой: «Итоги за период», изменение, «Активировать» (у черновика) и закрытие с подтверждением «Закрыть субсчёт? Новые начисления на него перестанут приходить». У базового субсчёта действия закрытия нет.
Все изменения справочника требуют роль LOYALTY_DICTIONARY_WRITE, просмотр — LOYALTY_DICTIONARY_READ.
Карточка субсчёта¶
| Поле | Правила |
|---|---|
| «Код» | Латиница, цифры и _, до 64 символов. Задаётся только при создании и не меняется — код часть контракта интеграций |
| «Название» | До 255 символов |
| «Описание» | До 1000 символов |
| «Валюта баллов» | Задаётся только при создании и не меняется |
| «Курс валюты» | Курс выбранной валюты справкой и ссылка «Изменить курс» на карточку валюты |
| «Правило сгорания» | «Общий срок», «Через N дней», «На дату». Под «Общим сроком» серым — действующий срок: «Общий срок: 365 дней», «Общий срок: зависит от уровня клиента» или «Общий срок: не сгорают». После первой проводки не меняется |
| «Через сколько дней сгорают» | Для «Через N дней»: целое число дней больше нуля |
| «Действуют до» | Для «На дату»: дата и время, по умолчанию 23:59:59 выбранного дня. Момент задаётся в часовом поясе программы — под полем подсказка «Часовой пояс: UTC+3»; прошедшие дни и часы недоступны |
| «Где и на что можно тратить баллы» | Область списания: «Товары», «Категории», «Мерчанты», «Торговые точки» — значения только из справочников продукта. Подсказка: «Пусто — без ограничений. Значения одного вида — «или», разные виды — «и»» |
Что замораживается после первой операции
Код и валюта субсчёта задаются только при создании: пока проводок нет, сохранение
изменяющей их правки не отклоняется ошибкой, а молча их игнорирует. После первой проводки по
субсчёту смена валюты и правила сгорания отклоняется (422 subaccount_immutable_after_use):
изменение срока жизни задним числом поменяло бы уже начисленное, а восстановить исходное
состояние нечем. Название, описание, очередь списания и область списания правятся всегда. В
карточке базового субсчёта правятся только название и описание.
Состояния¶
flowchart LR
D["Черновик"] -->|Активировать| A["Активен"]
A -->|Закрыть| C["Закрыт"]
Кнопка «Активировать» показывается у черновика; вернуть закрытый субсчёт в оборот можно тем же действием через API.
| Состояние | Начисления | Списание и сгорание |
|---|---|---|
| «Черновик» | Нет | Остатка нет |
| «Активен» | Да | Да |
| «Закрыт» | Нет (409 subaccount_not_active) |
Остаток продолжает жить: списывается и сгорает как обычно |
Новый субсчёт создаётся черновиком и встаёт в конец очереди списания.
Закрыть нельзя:
- базовый субсчёт — он закрывается только вместе с валютой (
409 subaccount_base_cannot_close); - субсчёт, выбранный «Типом начисляемых баллов» у незавершённых акций (в том числе архивных), —
иначе каждое срабатывание такой акции роняло бы покупку (
409 subaccount_used_by_promotions, в ответе — коды акций; экран показывает текст отказа с их перечнем). Сначала у этих акций выбирается другой тип баллов либо они завершаются.
Базовый субсчёт¶
В каждой валюте есть базовый субсчёт — код BASE, название «Основной», в таблице пометка
«Базовый». Он создаётся вместе с валютой; на него попадают начисления без указанного субсчёта и на
нём открываются счета клиентов.
- Базовый субсчёт закреплён за валютой: он не переназначается на другой субсчёт и не
закрывается отдельно от валюты (
409 subaccount_base_cannot_close). - Своего срока у него нет — режим сгорания стоит на «Общий срок», его баллы живут по общему
сроку. Другой режим отклоняется (
422 subaccount_base_expiry_fixed). - Области списания у него нет: это баллы программы «на всё», и ограничение закрыло бы их для
части покупок (
422 subaccount_base_restrictions_fixed). - В карточке правятся только название и описание.
BREAKING (26.3.0)
- Признак базового субсчёта в ответе —
isBaseвместоisDefault. - Операция «Сделать основным» (
POST /v1/points-subaccounts/{id}/default) удалена. - Отказ закрытия базового субсчёта —
409 subaccount_base_cannot_closeвместоsubaccount_default_cannot_close. - Свой срок, заданный базовому субсчёту раньше, при обновлении сброшен на «Общий срок»; сроки уже начисленных лотов не меняются. Сохранённые ограничения базовых субсчетов сняты.
«Акционные баллы»¶
Субсчёт «Акционные баллы» (код PROMO) выбран по умолчанию в поле «Тип начисляемых баллов»
акции, механика которой начисляет баллы: если маркетолог не выбрал другой тип, баллы акции попадают
сюда. В балльных валютах, заведённых до обновления, он создан сразу; в новой валюте появляется
автоматически с первым начислением по акции. Это обычный, не базовый субсчёт: своего срока у него нет
(режим «Общий срок»), при необходимости администратор задаёт ему правило сгорания, как любому
другому субсчёту.
Очередь списания¶
Очередь задаёт порядок, в котором баллы тратятся: не хватило на первом субсчёте — берётся его
остаток и списание переходит на следующий, пока не наберётся сумма. Не хватило суммарно — операция
отклоняется целиком (402 not_enough_funds), частичного списания нет.
- Порядок меняется на экране стрелками и сохраняется одной кнопкой «Сохранить порядок» — целиком по валюте.
- Повтор субсчёта в порядке отклоняется (
422 subaccount_priority_taken): иначе очередь молча оказалась бы не той, что показал экран. - Субсчёт с областью списания участвует в очереди только в той мере, в какой подходит покупке.
- Резерв (hold) фиксирует разбивку по субсчетам в момент создания и подтверждается по ней, даже если очередь успела измениться.
Разбивка по субсчетам возвращается в ответах операций (parts) и в балансе клиента — см.
Баланс и операции.
Область списания¶
Область списания отвечает на вопрос «где и на что можно тратить баллы субсчёта» — например, «баллы партнёра тратятся только в его сети» или «кешбэк за кофе — только на кофе». Пустая область — без ограничений.
| Вид | Что ограничивает | Как проверяется |
|---|---|---|
«Товары» (PRODUCT) |
На что: артикулы товаров | По позициям чека |
«Категории» (CATEGORY) |
На что: товарные категории | По позициям чека; категория покрывает позицию, если есть среди её категорий (у товара каталога — все три уровня) |
«Мерчанты» (MERCHANT) |
Где: код мерчанта (MID) партнёрской транзакции | По запросу целиком |
«Торговые точки» (STORE) |
Где: код торговой точки покупки по чеку | По запросу целиком |
Значения одного вида объединяются через «или», разные виды — через «и»: субсчёт с категорией «Кофе» и торговой точкой «Точка 1» тратится только на кофе и только в этой точке.
Область проверяется при списании за покупку — резерв и прямое списание
(POST /v1/customers/{externalId}/holds, POST /v1/customers/{externalId}/redemptions) и команды
HOLD/REDEEM:
- «Где». Запрос несёт необязательные
storeCode(покупка по чеку) иmerchantCode(партнёрская транзакция). Субсчёт участвует, если точка запроса есть среди точек его области либо мерчант запроса — среди мерчантов. Запрос без места субсчёт с ограничением «где» пропускает. - «На что». Субсчёт отдаёт не больше стоимости подходящих ему позиций чека (
items:sku,category,amountMinor), переведённой в баллы по курсу валюты баллов с округлением вниз; остальное берётся со следующих субсчетов по очереди. Безitemsтакие субсчета в списании не участвуют. Позиция, подходящая двум субсчетам с ограничениями, расходуется ими по очереди списания. - Не хватило подходящих баллов в сумме — прежний отказ
402 not_enough_funds. - Сгорание, корректировка, обмен и списание
/v1/withdrawобласть списания не проверяют.
При сохранении значения обрезаются по краям; пустое или повторённое значение отклоняется
(422 subaccount_restriction_invalid). В интерфейсе значения выбираются только из справочников
продукта (каталог товаров, мерчанты, торговые точки), свободного ввода нет.
Правило сгорания¶
| Правило | Как считается срок лота |
|---|---|
«Общий срок» (NONE) |
Своего срока у субсчёта нет: действует общий срок — срок уровня клиента в его конфигурации программы, иначе базовый срок валюты («Срок жизни баллов, дней» в карточке валюты). Если нет и его — баллы не сгорают |
«Через N дней» (PERIOD) |
Срок отсчитывается от момента начисления |
«На дату» (FIXED_DATE) |
Фиксированный момент «Действуют до» — дата и время со смещением; в сам этот момент баллы ещё действуют |
Срок лота определяется в момент начисления по цепочке: срок, заданный начислением (срок акции или
expireAt запроса) → своё правило субсчёта → срок уровня клиента → базовый срок валюты → не сгорают.
Срок фиксируется в лоте и потом не пересчитывается. Сгорание считается по каждому субсчёту
отдельно — клиент видит, что именно сгорело. Подробнее — Сгорание баллов.
Режим «Общий срок» (26.3.0)
Прежде режим NONE назывался «Без сгорания» и означал бессрочные баллы. Теперь это «Общий срок»:
баллы такого субсчёта сгорают по сроку уровня клиента или базовому сроку валюты (по умолчанию —
365 дней).
Проверки правила:
- нулевой или отрицательный период отклоняется (
422 subaccount_expiry_invalid): такой субсчёт выдавал бы лоты, сгорающие в момент начисления; - момент «Действуют до» в прошлом при создании, при смене правила и при активации черновика —
422 subaccount_expiry_in_past; - начисление на субсчёт, момент «Действуют до» которого уже наступил, —
422 subaccount_expiry_passed. Субсчёт при этом автоматически не закрывается.
Итоги субсчёта за период¶
Действие «Итоги за период» открывает панель с показателями:
| Показатель | Смысл |
|---|---|
| «Начислено за период» | Сумма начислений на субсчёт |
| «Списано за период» | Всё, что ушло с субсчёта, кроме сгорания |
| «Сгорело за период» | Сумма сгоревших баллов субсчёта |
| «Остаток на субсчёте» | Текущий остаток (на сейчас, а не за период) |
| «Клиентов с остатком» | Число клиентов с ненулевым остатком (на сейчас) |
Показатели считаются по проводкам леджера, а не по отдельным счётчикам, поэтому отчёт всегда сходится с историей. Даты периода — календарные сутки в часовом поясе программы, включительно.
Когда «Сгорело» равно нулю
Списание и сгорание различаются только системным счётом-получателем. С 26.3.0 эти счета по
умолчанию разведены (RDM — списание, BRK — сгорание), и «Сгорело» считается. Если на контуре
счёт сгорания совпадает со счётом списания, выделить сгорание нечем: всё ушедшее показывается в
«Списано», «Сгорело» равно нулю, а в ответе API признак expirySeparable = false. Ноль в этом
случае означает «не выделяется», а не «не сгорело»; на экране итогов отдельной пометки об этом
нет.
Разбивка в карточке клиента¶
В карточке клиента, на вкладке «Счета и балансы»:
- когда у клиента два субсчёта и больше, под плиткой «Доступно баллов» — плитки «Доступно — <название субсчёта>» в порядке очереди списания: сколько баллов каждого типа можно потратить. В сумме они дают «Доступно баллов»;
- под итоговыми величинами — секция «Разбивка по субсчетам»: название, код и остаток каждого субсчёта в порядке очереди списания. Секция не показывается, пока разбивки нет.
Сроки сгорания по субсчетам показывает вкладка «Сроки сгорания» — см. Сгорание баллов.
Субсчёт выбирается оператором при ручных операциях — поле «Субсчёт» обязательно и у начисления, и у корректировки.
API¶
| Метод и путь | Назначение | Право |
|---|---|---|
GET /v1/points-subaccounts?currency= |
Субсчета валюты; без параметра — всех валют | LOYALTY_DICTIONARY_READ либо любое право ручной операции |
GET /v1/points-subaccounts/{id} |
Субсчёт по идентификатору | LOYALTY_DICTIONARY_READ |
GET /v1/points-subaccounts/{id}/totals?from=&to= |
Итоги за период | LOYALTY_DICTIONARY_READ |
POST /v1/points-subaccounts |
Завести субсчёт (создаётся черновиком) | LOYALTY_DICTIONARY_WRITE |
PUT /v1/points-subaccounts/{id} |
Изменить субсчёт | LOYALTY_DICTIONARY_WRITE |
POST /v1/points-subaccounts/{id}/activate |
Пустить субсчёт в оборот | LOYALTY_DICTIONARY_WRITE |
POST /v1/points-subaccounts/{id}/close |
Закрыть субсчёт для начислений | LOYALTY_DICTIONARY_WRITE |
PUT /v1/points-subaccounts/priority |
Переставить очередь списания валюты | LOYALTY_DICTIONARY_WRITE |
Поля субсчёта: code, name, description, currency, state, isBase, expiryMode
(NONE/PERIOD/FIXED_DATE), expiryPeriod, expiryAt, redeemPriority, restrictions
([{kind, value}], виды PRODUCT, CATEGORY, MERCHANT, STORE), hasEntries — по субсчёту уже
есть проводки.
Период сгорания задаётся в формате ISO-8601 в днях или часах (например, P30D): месяцы и годы не
принимаются — «месяц» как срок жизни балла неоднозначен, для календарной даты есть отдельное правило.
В ответе целые дни отдаются днями (P30D), а не часами. Момент expiryAt — дата и время со смещением
(например, 2027-01-31T23:59:59+03:00).
BREAKING (26.3.0)
У субсчёта поле expiryDate (дата) заменено на expiryAt (дата и время со смещением). Даты,
заданные раньше, перенесены как 00:00 UTC.
Итоги за период дополнительно отдают признак expirySeparable (см. выше).
Витрины обмена данными (DWH_TRANSACTIONS, DWH_BALANCES) отдают код субсчёта.
Коды ошибок¶
| Код | HTTP | Когда возникает |
|---|---|---|
subaccount_not_found |
404 | Субсчёт не найден — в том числе по коду в валюте операции, отличной от валюты субсчёта (код зарезервированного subaccount_currency_mismatch для этого случая не используется) |
subaccount_code_duplicate |
409 | Код уже занят в этой валюте |
subaccount_not_active |
409 | Начисление на неактивный субсчёт |
subaccount_base_cannot_close |
409 | Закрытие базового субсчёта |
subaccount_used_by_promotions |
409 | Закрытие субсчёта, в который начисляют незавершённые акции |
subaccount_immutable_after_use |
422 | Смена валюты или правила сгорания после первой проводки |
subaccount_base_expiry_fixed |
422 | Базовому субсчёту задаётся режим сгорания, отличный от «Общий срок» |
subaccount_base_restrictions_fixed |
422 | Базовому субсчёту задаётся область списания |
subaccount_restriction_invalid |
422 | Пустое или повторённое значение области списания |
subaccount_expiry_invalid |
422 | Правило сгорания не согласовано с параметрами; нулевой или отрицательный период |
subaccount_expiry_in_past |
422 | Момент «Действуют до» в прошлом при создании, смене правила или активации |
subaccount_expiry_passed |
422 | Начисление на субсчёт, момент сгорания которого уже наступил |
subaccount_priority_taken |
422 | В порядке очереди повторы или перечислены не все субсчета валюты |
subaccount_period_invalid |
422 | Начало периода итогов позже конца |