Карточка клиента¶
Карточка клиента — рабочее окно оператора по одному участнику: идентификационные данные, баланс и уровень, вкладки с операциями и историей, действия над клиентом. Открывается из каталога клиентов двойным кликом по строке или пунктом меню «Открыть карточку»; закрывается крестиком в правом верхнем углу — возврат в каталог.
Состав карточки — какие поля в каких блоках, какие разделы и вкладки справа — задаёт администратор в конфигураторе «Настройка профиля клиента», см. Настройка карточки клиента. Карточка собирается по активной версии этой настройки.
Где «живёт» клиент
Модуль лояльности не является мастером персональных данных: ФИО, контакты, город, часовой пояс,
дата рождения и дата регистрации приходят из мастер-системы (MDM). Сегменты клиента приходят
из системы кампаний (Batch Flow) и хранятся в лояльности составом сегментов. В лояльности хранится
«технический» клиент — носитель внешнего идентификатора, его счета, баланс, уровень, теги,
история и значения дополнительных полей карточки. Карточка объединяет эти источники. Режим
работы с MDM задаётся настройкой loyalty.mdm.adapter; по умолчанию (local) мастер-данные
ведутся в собственном хранилище модуля.
Идентификация¶
Клиент всегда адресуется внешним идентификатором (externalId) — сквозным идентификатором из
контура платформы. Внутренних суррогатных идентификаторов в публичном API нет. Дополнительно у
клиента есть собственный неизменяемый реферальный код, сгенерированный при создании (см.
Реферальная программа).
Состав карточки¶
Шапка и действия¶
В шапке — ФИО клиента, пилюли уровня и статуса и панель действий.
| Кнопка | Роль | Поведение |
|---|---|---|
| Редактировать | LOYALTY_CUSTOMER_WRITE |
Включает инлайн-правку полей левой колонки; появляются «Сохранить» и «Отмена». Кнопки нет, если в настройке карточки нет редактируемых полей |
| Начислить | LOYALTY_ACCRUAL_WRITE |
Ручное начисление — см. Ручные операции |
| Корректировка | LOYALTY_REDEMPTION_WRITE |
Снятие баллов корректировкой с причиной из справочника — см. Ручные операции |
| Изменить уровень | LOYALTY_TIER_WRITE |
Ручное назначение уровня и сброс к расчётному — см. Уровни |
| Заблокировать / Разблокировать | LOYALTY_CUSTOMER_WRITE |
Смена статуса, после подтверждения в модалке |
| Архивировать, Удалить | — | В карточке пока неактивны («Скоро»); выполняются из каталога |
Заблокированному клиенту ручные операции запрещены: кнопки «Начислить», «Корректировка» и «Изменить уровень» выключены с подсказкой «Клиент заблокирован».
Левая колонка: карточки с полями¶
Левая колонка состоит из блоков-«карточек» в порядке, заданном в настройке карточки клиента: заголовок блока — название карточки, внутри — её видимые поля в заданном порядке. По умолчанию блоков два — «Клиент» и «Баллы и уровень». Скрытые и архивные поля не выводятся, карточка без видимых полей не показывается. Если раскладку карточек прочитать не удалось, поля показываются одним блоком «Основные данные».
Преднастроенные (системные) поля берутся из данных клиента:
| Поле схемы | Подпись по умолчанию | Значение |
|---|---|---|
client_id |
ID клиента | Внешний идентификатор клиента |
full_name |
ФИО | ФИО из профиля (редактируемое) |
phone, email |
Телефон, Email | Контакты из профиля (редактируемые) |
birth_date |
Дата рождения | Дата рождения из профиля; строки нет, если дата не заполнена. В карточке только просмотр |
points_balance |
Баллы | Баланс баллов |
loyalty_level |
Уровень | Действующий уровень |
segments |
Сегменты | Сегменты клиента — пилюлями с названиями; на пилюле подсказка «В сегменте с {дата}. Состав сегмента рассчитан {дата и время}». Нет сегментов — «Не входит ни в один сегмент». Только чтение |
status |
Статус | Статус клиента |
registered_at |
Дата регистрации | Дата регистрации |
В первом блоке всегда выводится поле «Часовой пояс» — только чтение, например «UTC+7 — Asia/Novosibirsk». Под значением — подсказка, откуда взят пояс: «Пришёл в профиле клиента из внешней системы», «Определён по городу в профиле — {город}», «Определён по магазинам, где клиент обычно покупает» или «Город не определён — используется часовой пояс программы» со смещением пояса программы в скобках. Пояс берётся из первого доступного источника: пояс из профиля → город профиля по справочнику городов → самый частый город покупок (окно по умолчанию — 90 суток) → пояс программы. После сохранения профиля пояс пересчитывается сразу. В интерфейсе лояльности пояс не правится.
Остальные поля — дополнительные атрибуты, заведённые администратором:
- введённые вручную — значения хранятся в лояльности и правятся здесь же;
- из системы — атрибут привязан к колонке таблицы-источника, значение читается из неё по внешнему идентификатору клиента и в карточке не правится. Если источник недоступен или строки по клиенту нет, поле пустое, а остальная карточка открывается как обычно.
Значение показывается по типу поля: дата — датой, число — с разрядами, логическое — «Да» / «Нет». Атрибут с множественным выбором показывает значения отдельными пилюлями. Значение выпадающего списка показывается кодом записи справочника — и в карточке, и в списке выбора при правке.
Правка профиля¶
Кнопка «Редактировать» превращает редактируемые строки в поля ввода, «Сохранить» отправляет изменения: поля золотого профиля — в MDM через бэкенд лояльности, значения дополнительных атрибутов — в хранилище лояльности. Тост «Профиль сохранён».
- ФИО вводится одной строкой и разбирается по пробелам: первое слово — фамилия, второе — имя, остальное — отчество.
- Телефон и email проверяются и в форме, и на сервере (см. ниже).
- Контрол правки атрибута задаётся типом поля: строка, число, дата, дата со временем, «Да/Нет».
Поле со способом ввода «Выпадающий список» предлагает действующие записи своего справочника, с
множественным выбором — несколько значений сразу. Записи читаются из реестра справочников, поэтому
без роли
LOYALTY_DICTIONARY_READсписок пуст и значение вводится вручную. Пустое значение очищает поле. - Поля, которых нет в настройке карточки, правка не затрагивает.
- Запись профиля заводит клиента в реестре лояльности, если его там ещё нет, — вместе со счетами во всех включённых валютах.
Дата со временем
Правка атрибута типа «Дата со временем» в карточке пока не работает: выбранное значение уходит
на сервер не в формате ISO-8601, сервер отклоняет его (400 validation_failed), и сохранение
профиля не проходит. Задавайте такое значение через API — в формате ISO-8601 с часовым поясом
(см. REST API карточки).
Проверка значений на сервере (26.3.0)
Сервер принимает значение атрибута только в формате его типа (см. REST API карточки) и отклоняет запись нового значения в поле, которого нет в настройке, в преднастроенное поле, в поле с колонкой источника, в архивное и в скрытое поле. Очистить значение можно в любом из этих случаев.
Правая часть: разделы и вкладки¶
Правая часть карточки — переключатель разделов и вкладки внутри раздела. Разделы, их порядок и порядок вкладок в разделе задаёт настройка карточки клиента: вкладки продукта и таблицы данных, заведённые администратором, стоят там, куда их поставил администратор. По умолчанию разделов три:
| Раздел | Вкладка | Содержимое |
|---|---|---|
| Баллы и уровень | «Счета и балансы» | Доступный и полный баланс, резерв, доступное по типам баллов, уровень, разбивка по субсчетам, счета — Баланс и операции |
| «Бонусные операции» | История операций клиента с фильтрами — Баланс и операции | |
| «Сроки сгорания» | Партии баллов и их сроки, ближайшее сгорание — Сгорание баллов | |
| «История уровней» | Текущий уровень, пересмотр и переходы с источником — Уровни | |
| Клиент | «Статус клиента» | Текущий статус (см. Статусы, архив и признак «Тест») |
| «Достижения» | Полученные бейджи — Достижения и бейджи | |
| «Рефералы» | Приглашённые клиенты — Реферальная программа | |
| Активность и коммуникации | «Участие в акциях» | Срабатывания акций по покупкам клиента (см. ниже) |
Вкладка таблицы данных, заведённой администратором, показывает строки таблицы-источника, связанные с клиентом по внешнему идентификатору: только видимые столбцы, в порядке и с подписями из настройки, с постраничным выводом и сортировкой по столбцу. Раздел без таблиц не показывается.
Данные вкладки запрашиваются при её открытии.
Вкладка «Участие в акциях»¶
Вкладка показывает, когда по покупке клиента сработала акция и сколько баллов начислено. Название и механика акции — те, что действовали в момент участия (версия акции, применившаяся к покупке).
| Колонка | Содержимое |
|---|---|
| Дата | Момент операции; единственная сортируемая колонка, по умолчанию новые сверху |
| Акция | Название версии акции; при роли LOYALTY_PROMOTION_READ — ссылка на карточку акции |
| Механика | Механика акции; фильтр по механике — несколько значений по «ИЛИ» (при роли LOYALTY_PROMOTION_READ: без неё справочника механик нет, и фильтра тоже) |
| Бонус | Начисленные баллы; «0» — акция сработала, начисления не было |
| ID транзакции | Идентификатор операции |
Колонки «Метки акции», «Сумма чека», «К оплате» и «Источник оплаты» появляются, только если их
включает настройка развёртывания интерфейса (PROMOTION_PARTICIPATION); по умолчанию их нет.
Сторно начисления по акции (возврат покупки) участием не считается и во вкладку не попадает. Пустая вкладка — «Клиент пока не участвовал в акциях»; пустой результат фильтра — «Ничего не найдено» с кнопкой «Сбросить фильтры». Сортировка, фильтр и страница запоминаются в браузере.
Теги в карточке
Присвоенные клиенту теги отображаются колонкой в каталоге и приходят в ответе
карточки (tags), но в интерфейсе карточки список пока не выводится. Присвоение и снятие
выполняются из каталога — в том числе для одного клиента — и из
ручных операций по списку.
Статусы, архив и признак «Тест»¶
Это три независимые характеристики клиента.
| Статус | Смысл |
|---|---|
ACTIVE («Активен») |
Обычный участник |
BLOCKED («Заблокирован») |
Операции с баллами запрещены; клиент остаётся в каталоге |
MERGED |
Клиент слит в другого и выведен из операций |
DELETED |
Клиент мягко удалён: скрыт из каталога, поиска и карточки |
- Архив — обратимое скрытие из основного каталога, статус не меняет.
- Тест — признак тестового клиента: бейдж «Тест», исключение из отчётов и рассылок; контакт для него не обязателен.
Блокировка и разблокировка идемпотентны. Заблокировать или заархивировать удалённого клиента нельзя: для всех операций он выглядит как «не найден» — в том числе если удаление пришло одновременно с блокировкой. Разблокировка открывает клиенту счета во включённых валютах, которые не были открыты, пока он был заблокирован.
История смены статуса¶
Каждая фактическая смена статуса записывается в историю: блокировка, разблокировка и удаление (одиночные и массовые) — с автором из токена, слияние — ещё и с причиной из запроса. Повтор операции, не изменивший статус, записи не делает. История ведётся с момента выкладки версии 26.3.0: у клиентов, заблокированных раньше, деталей блокировки нет.
История доступна через API GET /v1/customers/{externalId}/status-history — страница строк
{id, occurredAt, status, previousStatus, operator, operatorId, reason}, новые сверху. На вкладке
«Статус клиента» интерфейс пока показывает только текущий статус и пустую таблицу с
пояснением «История смены статуса появится после доработки бэкенда.», у заблокированного клиента —
ещё и предупреждение, что причину, дату и автора блокировки интерфейс пока не показывает.
Удаление и обезличивание¶
| Операция | Что происходит |
|---|---|
Мягкое удаление DELETE /v1/customers/{externalId} |
Статус → DELETED. Клиент скрыт из каталога, поиска и карточки и недоступен для операций (404), но счета, проводки и история сохраняются: двойная запись неизменяема. Идемпотентно, ответ 204 |
Обезличивание POST /v1/customers/{externalId}/anonymize |
Затирает выбранные поля ПДн профиля. Клиент остаётся в каталоге. Идемпотентно, поддерживает Idempotency-Key |
«Удалить» в каталоге (bulk-delete) |
Обезличивание всех полей ПДн + мягкое удаление, одной транзакцией на клиента: полуобезличенных клиентов не остаётся |
Обезличиваемые поля выбираются набором fields: PHONE, EMAIL, FULL_NAME, CITY,
BIRTH_DATE, SEGMENTS. Текстовые поля заменяются заглушкой «[удалён]» (у ФИО фамилия — заглушка,
имя и отчество очищаются), дата рождения обнуляется, по SEGMENTS клиент выводится из состава всех
сегментов. Финансовая история не затрагивается — она нужна для сверок и отчётности.
Валидация и нормализация контактов¶
При сохранении профиля телефон и email приводятся к единому виду; невалидное значение отклоняется
с 422, а не сохраняется молча.
| Поле | Правило |
|---|---|
| Телефон | Принимаются +7…, 8…, 7… и 10 цифр номера с любыми разделителями. Результат — каноничная форма +7XXXXXXXXXX (по умолчанию) или 8XXXXXXXXXX. Неверная длина или чужой код страны — 422 invalid_phone |
Обрезаются пробелы, значение приводится к нижнему регистру, проверяется формат. Неверный — 422 invalid_email |
Код страны и каноничная форма настраиваются (loyalty.contact.phone.default-country,
loyalty.contact.phone.canonical-form; значение формы — PLUS для +7XXXXXXXXXX или LOCAL для
8XXXXXXXXXX); значения по умолчанию — 7 и PLUS. Пустое поле
означает «не менять». Те же проверки выполняет форма карточки и строгий режим
импорта клиентов.
Даты: регистрация и заведение в реестре¶
- Дата регистрации — из профиля клиента; если её нет, показывается дата заведения клиента в реестре лояльности.
- Дата заведения в реестре (
createdAt) проставляется при создании клиента и служит порядком по умолчанию в каталоге: новый клиент виден первым на любой странице. Сортировать по ней можно через API (sort=createdAt,desc).
Недоступность MDM¶
Сбой внешнего реестра клиентов (таймаут, обрыв связи, 5xx, просроченный сервисный токен) отдаётся
как 503 mdm_unavailable, а не как «клиент не найден»: ответ не утверждает факт о клиенте,
который никто не проверил. Отсутствие клиента по-прежнему 404 customer_not_found — в том числе
когда MDM сам отвечает «не найден».
- Интерфейс показывает «Клиент не найден» только на
404; на прочие ошибки — «Не удалось загрузить данные», и запрос можно повторить. - Чтение полной записи и сохранение карточки тоже отдают
503, а не «внутреннюю ошибку»; после записи сервер не выдаёт форму за сохранённый профиль, если перечитать запись не удалось. - Список клиентов при недоступном MDM не показывает страницу из одних идентификаторов, а сообщает об аварии.
- Начисление по покупке продолжает работать: клиент отдаётся без ФИО и контактов, чтобы чужая авария не роняла операцию. Сегменты клиента от MDM не зависят — они хранятся в лояльности.
Изменение контракта (26.3.0)
Для потребителей карточки клиента сбой реестра теперь 503 mdm_unavailable вместо прежнего
404 customer_not_found.
Слияние клиентов «1 телефон = 1 профиль»¶
POST /v1/customers/{externalId}/merge сливает клиентов: в пути — выживший клиент, в теле
sourceExternalId — источник, чьи данные переносятся, и необязательная причина reason для аудита.
Переносятся счета источника (вместе с историей проводок) и его журнал покупок и возвратов — по нему
считается накопление уровня. Уровень выжившего после этого пересчитывается так же, как после
покупки (см. Уровни). Ручное назначение уровня переносится, если у выжившего его нет;
история переходов уровня остаётся у источника. Источник получает статус MERGED и ссылку на
выжившего, в истории статуса источника появляется запись с причиной.
- Повторное слияние источника в того же выжившего ничего не меняет и отвечает
200. - Источник, уже слитый в другого клиента, —
409 version_conflict. - Источник, мягко удалённый до или во время слияния, —
404 customer_not_found, перенос откатывается.
Пересечение по валюте
Счета есть у каждого клиента в каждой включённой валюте, поэтому конфликт возникает, только
если операции в одной и той же валюте были и у источника, и у выжившего: слияние отклоняется —
422 merge_account_conflict, остаток переносится вручную через леджер. Счёт без операций место
уступает и конфликта не даёт. Слияние клиента с самим собой — 422 merge_same_customer.
flowchart LR
S["Клиент-источник"] -->|счета, покупки и возвраты| W["Выживший клиент"]
S -->|статус → MERGED| X["Источник выведен<br/>из операций"]
REST API карточки¶
| Метод и путь | Роль | Назначение |
|---|---|---|
GET /v1/customers/{externalId} |
LOYALTY_CUSTOMER_READ |
Клиент по внешнему идентификатору |
GET /v1/customers/{externalId}/card |
LOYALTY_PROFILE_READ и LOYALTY_MONITORING_READ |
Карточка: профиль MDM (с birthDate), статус, флаг archived, теги tags, лоялти-блок (в нём пояс клиента timezone {zone, source, resolvedAt}), значения атрибутов attributes |
GET /v1/customers/{externalId}/profile |
LOYALTY_PROFILE_READ |
Профиль: идентичность MDM + сводка по баллам |
PUT /v1/customers/{externalId}/profile |
LOYALTY_CUSTOMER_WRITE |
Сохранить профиль в MDM и завести клиента в реестре; null-поле — «не менять». Принимает и birthDate (YYYY-MM-DD; очистить дату можно только обезличиванием), city и timezone (идентификатор IANA, например Asia/Novosibirsk; пустая строка очищает; иначе 422 customer_timezone_invalid) |
PUT /v1/customers/{externalId}/attributes |
LOYALTY_CUSTOMER_WRITE |
Значения атрибутов карточки: ключ поля → список значений; пустой список очищает |
GET /v1/customers/{externalId}/segments |
LOYALTY_PROFILE_READ и LOYALTY_MONITORING_READ |
Сегменты клиента с датой входа |
GET /v1/customers/{externalId}/status-history |
LOYALTY_PROFILE_READ и LOYALTY_MONITORING_READ |
История смены статуса |
GET /v1/customers/{externalId}/profile-tables/{groupId}/rows |
LOYALTY_CUSTOMER_READ |
Строки таблицы данных, настроенной в конфигураторе |
GET /v1/client-profile/schema, /structure |
LOYALTY_CUSTOMER_READ |
Поля активной настройки карточки и раскладка по карточкам, разделам и таблицам |
GET /v1/client-profile/dictionaries/{key} |
LOYALTY_CUSTOMER_READ |
Записи справочника выпадающего списка активной настройки |
POST /v1/customers/{externalId}/block, /unblock |
LOYALTY_CUSTOMER_WRITE |
Блокировка / разблокировка |
POST /v1/customers/{externalId}/archive, /unarchive |
LOYALTY_CUSTOMER_WRITE |
Архивирование / возврат из архива (204) |
DELETE /v1/customers/{externalId} |
LOYALTY_CUSTOMER_WRITE |
Мягкое удаление (204, идемпотентно) |
POST /v1/customers/{externalId}/anonymize |
LOYALTY_CUSTOMER_WRITE или LOYALTY_INTEGRATION_WRITE |
Обезличивание выбранных полей ПДн |
POST /v1/customers/{externalId}/merge |
LOYALTY_CUSTOMER_WRITE |
Слияние клиентов |
Значение атрибута в PUT …/attributes передаётся строкой в формате типа поля:
| Тип | Формат | Пример |
|---|---|---|
STRING |
Любая строка | Москва |
NUMBER |
Десятичное число через точку, без экспоненты | -12.5 |
DATE |
YYYY-MM-DD |
2026-09-21 |
DATETIME |
ISO-8601 с часовым поясом | 2026-09-21T10:00:00+03:00 |
BOOLEAN |
true или false |
true |
Значение выпадающего списка — код записи справочника, по формату не проверяется; значение, уже сохранённое у клиента, принимается как есть. Ошибки записи:
| Код | HTTP | Когда |
|---|---|---|
validation_failed |
400 | Значение не в формате типа поля; ключ поля — в начале detail |
unknown_attribute |
422 | Такого поля нет в настройке карточки |
system_field_immutable |
422 | Поле преднастроенное или привязано к колонке источника — значение приходит из системы |
field_archived |
422 | Поле в архиве |
field_hidden |
422 | Поле скрыто с карточки |
attribute_not_multiple |
422 | Больше одного значения у поля без множественного выбора |
Изменение контракта (26.3.0)
attributes в карточке и тело PUT …/attributes — словарь «ключ → список строк» вместо
«ключ → строка»: одиночное значение передаётся списком из одного элемента. Значение не в формате
типа поля и запись в поле, которого нет в настройке (или которое заполняется системой, в архиве,
скрыто), теперь отклоняются, а не сохраняются молча. Сегменты из профиля MDM (identity.segments)
больше не отдаются — сегменты клиента читаются через GET …/segments.
Создание клиента, каталог и массовые операции описаны в разделе Каталог клиентов.
Доступ и персональные данные¶
Все эндпоинты работают по принципу deny-by-default: доступ только при наличии роли в токене (OAuth2/JWT из Keycloak).
| Роль | Доступ |
|---|---|
LOYALTY_CUSTOMER_READ |
Каталог, клиент по идентификатору, раскладка карточки и строки настроенных таблиц |
LOYALTY_CUSTOMER_WRITE |
Создание, правка профиля и атрибутов, блокировка, архив, удаление, обезличивание, слияние |
LOYALTY_PROFILE_READ |
Профиль клиента (персональные данные) |
LOYALTY_PROFILE_READ + LOYALTY_MONITORING_READ |
Карточка клиента (профиль вместе с историей операций), сегменты клиента, история смены статуса |
Персональные данные
Карточка и профиль возвращают ПДн клиента. Обращения к ним, как и поиск по каталогу, фиксируются в журнале обращений к персональным данным; роли выдаются только тем, кому просмотр ПДн разрешён регламентом.