Перейти к содержанию

Субсчета баллов

Субсчёт — это целевой счёт внутри карты клиента: «Приветственные», «Подарочные ко дню рождения», «Кешбэк за категорию», «Партнёрские». Баланс клиента перестал быть одной суммой: карта объединяет субсчета, у каждого из них своё правило сгорания, своя область списания и своё место в очереди списания.

Субсчета ведёт маркетолог в разделе «Администрирование» → «Субсчета баллов». Закрытого перечня типов баллов нет: новое назначение заводится записью справочника и не требует доработки.

Как это устроено

  • Субсчёт — самостоятельный счёт леджера, а не пометка в проводке: баланс субсчёта остаётся производной от проводок, а запрет уйти в минус работает тем же механизмом, что и раньше.
  • Субсчёт принадлежит одной валюте. Под разные валюты заводятся разные субсчета; очередь списания у каждой валюты своя.
  • Начисление всегда попадает ровно на один субсчёт: указанный в запросе начисления; у начисления по акции — «Тип начисляемых баллов» акции (по умолчанию «Акционные баллы»); если субсчёт не указан — базовый субсчёт валюты. Счёт клиента на базовом субсчёте открывается при заведении клиента, на остальных субсчетах — первой проводкой.
  • Списание идёт по очереди субсчетов с учётом области списания каждого и может затронуть несколько сразу.
  • Сторно возвращает баллы на тот субсчёт, куда легло исходное начисление.
  • Субсчёт с проводками не удаляется: иначе история перестанет сходиться с леджером. Вместо удаления — закрытие.

Экран «Субсчета баллов»

Заголовок — «Субсчета баллов», подзаголовок — «Целевые счета внутри карты клиента: приветственные, партнёрские, акционные. Очередь списания у каждой валюты своя».

Сверху — выбор «Валюта баллов»: список ведётся по балльной валюте из справочника «Валюты». Выключенная валюта помечена «(выключена)»: её очередь видна, но новый субсчёт в неё не заводится — кнопка недоступна с подсказкой «В выключенную валюту новые субсчета не заводятся». Рядом с выбором валюты — её курс фразой (например, «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 Начало периода итогов позже конца