Как реализовать механизм сгорания бонусов по истечении срока действия в 1С?

Программист 1С v8.3 (Управляемые формы) Управленческий учет Торговля и дистрибуция
← На главную

Приветствуем всех разработчиков и архитекторов систем лояльности! В современной розничной торговле и сфере услуг бонусные программы стали неотъемлемым инструментом удержания клиентов. Однако при проектировании собственного модуля лояльности в 1С перед разработчиком неизбежно встает сложная архитектурная задача: как эффективно и без потери производительности реализовать механизм сгорания бонусов по истечении их срока действия?

В этой статье мы подробно разберем несколько подходов к решению этой задачи — от классического партионного учета до изящных архитектурных решений, применяемых в типовых конфигурациях компании «1С» (таких как ERP, «Управление торговлей» и «Комплексная автоматизация»). Мы проанализируем их плюсы и минусы, посмотрим на примеры кода и выясним, как избежать проблем с производительностью системы при работе с десятками тысяч клиентов.

Давайте по шагам разберем варианты реализации этой логики.

---

Вариант 1. Партионный учет бонусов (ФИФО)

Первое решение, которое интуитивно приходит на ум любому разработчику 1С, знакомому со складским учетом — это организация партионного учета бонусов. Суть метода заключается в том, что каждому начислению бонусов присваивается уникальный идентификатор партии (обычно это сам документ начисления или специальный элемент справочника).

При такой архитектуре регистр накопления остатков БонусныеБаллы будет содержать следующие измерения:

При начислении бонусов мы делаем движение "Приход" с указанием конкретной партии и даты сгорания. Когда клиент тратит бонусы, система выполняет списание по методу ФИФО (первым пришел — первым ушел): сначала списываются бонусы из самых старых партий. Ежедневно регламентное задание анализирует остатки регистра, находит партии, у которых ДатаСгорания меньше текущей даты, и полностью списывает их остатки в ноль.

Рассмотрим пример алгоритма списания бонусов при оплате покупки по методу ФИФО:


// Пример получения остатков партий бонусов для списания по ФИФО
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   БонусыОстатки.ПартияНачисления КАК ПартияНачисления,
    |   БонусыОстатки.ПартияНачисления.ДатаСгорания КАК ДатаСгорания,
    |   БонусыОстатки.НачисленоОстаток КАК Остаток
    |ИЗ
    |   РегистрНакопления.БонусныеБаллы.Остатки(&ТекущаяДата, Клиент = &Клиент) КАК БонусыОстатки
    |ГДЕ
    |   БонусыОстатки.НачисленоОстаток > 0
    |   И БонусыОстатки.ПартияНачисления.ДатаСгорания > &ТекущаяДата
    |УПОРЯДОЧИТЬ ПО
    |   ДатаСгорания ВОЗР";

Запрос.УстановитьПараметр("Клиент", ПараметрыКлиента);
Запрос.УстановитьПараметр("ТекущаяДата", ТекущаяДата());

РезультатЗапроса = Запрос.Выполнить();
// Далее в коде выполняется стандартный обход выборки с распределением списываемой суммы по партиям

Преимущества партионного учета:

  1. Предельная прозрачность и понятность для аудита: всегда видно, из какого именно начисления были списаны или сгорели баллы.
  2. Легко построить точный прогноз сгорания баллов для информирования клиентов.

Недостатки и подводные камни:

  1. Проблема производительности (Блокировки): При проведении чеков в пиковые часы на кассах система будет накладывать управляемые блокировки на остатки по конкретным партиям. В крупных розничных сетях это неминуемо приведет к задержкам на кассовых узлах и ошибкам блокировок БД.
  2. Раздувание таблиц итогов: Каждая новая покупка с начислением бонусов плодит уникальную партию. Таблицы итогов регистра накопления начнут лавинообразно расти, что со временем критически замедлит любые запросы к бонусам.

---

Вариант 2. Метод встречных (двойных) движений на две даты

Интересный альтернативный подход заключается в том, чтобы отказаться от хранения партий, но производить движения по регистру "наперед".

При начислении бонусов документ делает сразу две записи в регистр накопления остатков:

  1. Текущей датой (Дата Начисления): Движение "Приход" на сумму +100 баллов.
  2. Будущей датой (Дата Сгорания): Движение "Расход" на сумму -100 баллов.

Если клиент не успевает потратить бонусы, они автоматически сгорают в указанную дату за счет заранее сделанного "минусового" движения. Однако возникает логический вопрос: что делать, если клиент потратил часть бонусов (например, 50 из 100 начисленных)?

При оформлении расхода нам придется программно искать будущее плановое списание на дату сгорания и корректировать его (делать компенсирующее движение "Приход" на +50 баллов на дату сгорания, чтобы в будущем списалось только фактически оставшиеся 50). Поиск этой даты сгорания и логика корректировок "будущих периодов" делает систему очень хрупкой и склонной к накоплению ошибок при частых изменениях чеков или возвратах.

---

Вариант 3. Типовой механизм 1С (Ресурс «К списанию») — золотой стандарт

Давайте подробно разберем, как разработчики фирмы «1С» элегантно решили эту задачу в своих типовых конфигурациях. Они отказались от партионного учета, но сохранили быстродействие системы и простоту расчетов с помощью специальной структуры регистра.

В регистре накопления остатков БонусныеБаллы создается два ресурса:

В качестве ключевого измерения используется ДатаОкончанияДействия (или просто дата сгорания).

Шаг 1. Начисление бонусов

Когда клиенту начисляют 100 бонусов со сроком действия 30 дней, документ делает два движения типа Приход:

  1. С текущей датой: Начислено = +100, КСписанию = 0, ДатаОкончанияДействия = ПустаяДата.
  2. С датой сгорания (через 30 дней): Начислено = 0, КСписанию = +100, ДатаОкончанияДействия = ДатаСгорания.

Шаг 2. Расход бонусов (покупка)

Когда клиент тратит 50 бонусов, документ списания делает движение Расход только по ресурсу Начислено с текущей датой. При этом будущие плановые движения по ресурсу КСписанию никак не корректируются! Это позволяет избежать сложных расчетов в транзакции продажи и гарантирует высокую скорость работы кассы.

Шаг 3. Регламентное списание (сгорание)

Ежедневно ночью запускается регламентное задание, которое рассчитывает сумму реального сгорания по следующему алгоритму:

  1. Система получает общий остаток бонусов по ресурсу Начислено (на текущий момент).
  2. Получает остаток по ресурсу КСписанию на текущую дату.
  3. Реальная сумма к списанию рассчитывается по формуле: Минимум(Остаток Начислено, Остаток К Списанию).
  4. Создается документ "Списание бонусных баллов", который делает движение Расход на рассчитанную сумму по обоим ресурсам (закрывая плановое списание).

Рассмотрим пример запроса для регламентного задания, определяющего сумму сгорания бонусов:


// Запрос для расчета сумм сгорания бонусов на текущую дату
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ОстаткиБонусов.Клиент КАК Клиент,
    |   ОстаткиБонусов.НачисленоОстаток КАК ДоступныйОстаток,
    |   ОстаткиБонусовПоДатам.КСписаниюОстаток КАК ПланируемоеСписание
    |ИЗ
    |   РегистрНакопления.БонусныеБаллы.Остатки(&ТекущаяДата, ) КАК ОстаткиБонусов
    |
    |   ВНУТРЕННЕЕ СОЕДИНЕНИЕ РегистрНакопления.БонусныеБаллы.Остатки(&ТекущаяДата, ДатаОкончанияДействия <= &ТекущаяДата) КАК ОстаткиБонусовПоДатам
    |   ПО ОстаткиБонусов.Клиент = ОстаткиБонусовПоДатам.Клиент
    |ГДЕ
    |   ОстаткиБонусовПоДатам.КСписаниюОстаток > 0";

Запрос.УстановитьПараметр("ТекущаяДата", НачалоДня(ТекущаяДата()));
Результат = Запрос.Выполнить();

В процессе обхода результата запроса мы рассчитываем сумму сгорания как Минимум(ДоступныйОстаток, ПланируемоеСписание) и формируем движения списания, уменьшая оба ресурса. Этот подход работает чрезвычайно быстро и не нагружает систему блокировками при совершении розничных продаж.

---

Важные бизнес-нюансы при проектировании модуля лояльности

Помимо выбора технической архитектуры регистра, обязательно проанализируйте следующие бизнес-сценарии, с которыми вы неизбежно столкнетесь при запуске системы:

  1. Проблема информирования клиентов о сгорании:

    Используя схему «Начислено / К списанию», сложно напрямую из остатков регистра получить информацию в духе: «У вас 1 декабря сгорит 50 бонусов», так как расходы не уменьшают будущие записи КСписанию до момента регламентного расчета. Чтобы показать клиенту корректный прогноз в СМС или личном кабинете, вам потребуется написать виртуальный алгоритм распределения текущего остатка бонусов по будущим датам сгорания "на лету" (имитируя ФИФО только в момент формирования отчета или отправки сообщения).

  2. Возвраты товаров и логика бонусов:

    Что делать, если клиент возвращает товар, за покупку которого были начислены бонусы? Если он их уже потратил, баланс бонусного счета может уйти в минус — это нормальная практика для большинства ритейлеров. А если возвращается товар, который был частично оплачен бонусами? В этом случае баллы нужно вернуть клиенту на карту. Но с каким сроком действия? Если их первоначальный срок действия уже истек, пока товар находился у покупателя, хорошим тоном будет дать им "вторую жизнь" (например, продлить срок действия возвращенных бонусов еще на 14 дней).

  3. Использование Сервера лояльности:

    Если у вашей компании широкая филиальная сеть с распределенными базами данных (РИБ), локальные кассы не должны напрямую писать движения по бонусам в свои базы. Клиент может совершить покупку в одном магазине и сразу же поехать в другой, потратив бонусы дважды до прохождения синхронизации. Для решения этой проблемы кассовые программы отправляют быстрые Web- или HTTP-запросы к центральной базе 1С ("Серверу лояльности"), которая выступает единственным и неделимым источником правды для бонусных балансов.

Подводя итог, можно сказать, что для небольших баз данных (до 10-15 тысяч клиентов) вполне жизнеспособен классический партионный учет по методу ФИФО. Однако, если вы закладываете архитектуру "на вырост" и хотите избежать проблем с производительностью и блокировками в будущем, мы настоятельно рекомендуем использовать типовую схему 1С с ресурсами Начислено и КСписанию.

← На главную