При внедрении и настройке бонусной системы в типовом решении 1С:Розница 2.2 (а также в аналогичных механизмах 1С:УТ 11 и 1С:ERP) специалисты часто сталкиваются со следующей проблемой: когда у начисленных ранее бонусных баллов истекает срок действия, регламентное задание списывает всю сумму первоначального начисления, совершенно не учитывая, что клиент уже успел потратить часть этих бонусов. В итоге баланс бонусной карты пересчитывается некорректно и может даже уйти в «технический минус».
В этой статье мы подробно разберем, почему так происходит с точки зрения архитектуры регистров накопления 1С, проанализируем узкие места типового программного кода и рассмотрим практические варианты решения этой проблемы.
Для того чтобы понять логику работы системы, проанализируем тестовую ситуацию:
Давайте разберемся, почему типовой механизм ведет себя именно так.
Проанализируем структуру регистра накопления БонусныеБаллы. Движения по нему осуществляются в разрезе бонусных программ и дисконтных карт. В регистре присутствуют два ключевых ресурса:
Начислено — отражает текущий доступный баланс бонусов для оплаты.КСписанию — технический ресурс, в котором фиксируется плановая дата и сумма будущего сгорания баллов.Выясним, как именно документы делают движения по этим ресурсам:
При начислении бонусов (документ «Начисление бонусных баллов» или автоматическое начисление при покупке) система делает два движения «Приход»:
Начислено записывается вся сумма (например, 1000 баллов).КСписанию записывается эта же сумма (1000 баллов).При оплате бонусами (документ «Чек ККМ» или «Реализация товаров и услуг») программа делает движение «Расход» только по ресурсу Начислено. Будущие запланированные записи по ресурсу КСписанию при этом никак не корректируются! Они продолжают «висеть» в базе в первоначальном объеме.
Когда наступает плановая дата сгорания, регламентное задание вызывает процедуру автоматического списания. Программа обращается к СКД СписаниеБонусныхБалловПредопределенный, которая просто выбирает все плановые записи из ресурса КСписанию за указанный период и безусловно списывает их. Анализа того, были ли эти баллы уже потрачены, в типовой схеме компоновки данных не предусмотрено. В этом и заключается главная методическая ошибка архитектуры бонусного модуля.
Стоит отметить еще одну важную деталь. В общем модуле бонусной системы присутствует функция ТаблицаОстаткиИДвиженияБонусныхБаллов(ДисконтнаяКарта, ДатаЗапроса). Эта функция пытается динамически «на лету» сопоставить приходы и расходы по датам первоначального начисления, чтобы понять реальные остатки по «срокам годности» бонусов.
Однако этот алгоритм реализован крайне неоптимально. Программа перебирает и виртуально разворачивает всю историю движений по конкретной карте. На крупных базах с высокой интенсивностью продаж и большим количеством дисконтных карт выполнение этой функции приводит к серьезным задержкам и зависаниям на рабочем месте кассира (РМК) при сканировании карты лояльности.
Для исправления этой логической ошибки на практике применяют несколько подходов. Рассмотрим подробно каждый из них.
Самый простой путь, не требующий изменения конфигурации на уровне кода, — это редактирование схемы компоновки данных в самом правиле списания бонусных баллов.
В справочнике «Правила начисления бонусных баллов» для операции списания вместо предопределенного шаблона мы можем загрузить и использовать свою измененную схему компоновки данных (СКД). Посмотрим, как должен выглядеть алгоритм в запросе схемы:
БонусныеБаллы.Остатки.Пример укрупненного текста запроса для СКД списания:
ВЫБРАТЬ
РегКСписанию.ДисконтнаяКарта КАК ДисконтнаяКарта,
РегКСписанию.БонуснаяПрограммаЛояльности КАК БонуснаяПрограммаЛояльности,
ВЫБОР
КОГДА РегКСписанию.СуммаПлановогоСписания > ЕСТЬNULL(РегОстатки.НачисленоОстаток, 0)
ТОГДА ЕСТЬNULL(РегОстатки.НачисленоОстаток, 0)
ИНАЧЕ РегКСписанию.СуммаПлановогоСписания
КОНЕЦ КАК КоличествоСписания
ИЗ
(ВЫБРАТЬ
БонусныеБаллы.ДисконтнаяКарта КАК ДисконтнаяКарта,
БонусныеБаллы.БонуснаяПрограммаЛояльности КАК БонуснаяПрограммаЛояльности,
СУММА(БонусныеБаллы.КСписанию) КАК СуммаПлановогоСписания
ИЗ
РегистрНакопления.БонусныеБаллы КАК БонусныеБаллы
ГДЕ
БонусныеБаллы.Период <= &ТекущаяДата
СГРУППИРОВАТЬ ПО
БонусныеБаллы.ДисконтнаяКарта,
БонусныеБаллы.БонуснаяПрограммаЛояльности) КАК РегКСписанию
ЛЕВОЕ СОЕДИНЕНИЕ РегистрНакопления.БонусныеБаллы.Остатки(&ТекущаяДата, ) КАК РегОстатки
ПО РегКСписанию.ДисконтнаяКарта = РегОстатки.ДисконтнаяКарта
И РегКСписанию.БонуснаяПрограммаЛояльности = РегОстатки.БонуснаяПрограммаЛояльности
ГДЕ
ЕСТЬNULL(РегОстатки.НачисленоОстаток, 0) > 0
Этот способ гарантирует, что баланс карты никогда не уйдет в минус при автоматическом регламентном списании. Однако он решает проблему лишь частично, так как не учитывает очередность сгорания разных партий бонусов по методу FIFO.
Более надежный, но требующий программирования способ — доработка проведения документов, которые регистрируют оплату бонусами (например, обработка проведения чеков ККМ и отчетов о розничных продажах).
Идея заключается в том, чтобы при расходовании бонусов система уменьшала не только ресурс Начислено, но и пропорционально уменьшала будущие записи в ресурсе КСписанию. Для этого нам необходимо списывать будущие планы по методу FIFO — то есть в первую очередь уменьшать те плановые списания, которые должны произойти раньше всего.
Разберем пример кода, который можно внедрить в процедуру проведения документа оплаты через расширение конфигурации:
Процедура КорректировкаПлановСписанияБонусов(ДокументСсылка, ТаблицаСписанияБонусов) Export
Для Каждого СтрокаТаблицы Из ТаблицаСписанияБонусов Цикл
// Получаем будущие запланированные списания по карте по возрастанию даты
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| БонусныеБаллы.Период КАК ПериодПланирования,
| БонусныеБаллы.КСписанию КАК ОжидаетСписания,
| БонусныеБаллы.Регистратор КАК ДокументНачисления
|ИЗ
| РегистрНакопления.БонусныеБаллы КАК БонусныеБаллы
|ГДЕ
| БонусныеБаллы.ДисконтнаяКарта = &ДисконтнаяКарта
| И БонусныеБаллы.БонуснаяПрограммаЛояльности = &БонуснаяПрограмма
| И БонусныеБаллы.КСписанию > 0
| И БонусныеБаллы.Период > &ТекущаяДата
|УПОРЯДОЧИТЬ ПО
| ПериодПланирования ВОЗР";
Запрос.УстановитьПараметр("ДисконтнаяКарта", СтрокаТаблицы.ДисконтнаяКарта);
Запрос.УстановитьПараметр("БонуснаяПрограмма", СтрокаТаблицы.БонуснаяПрограмма);
Запрос.УстановитьПараметр("ТекущаяДата", ДокументСсылка.Дата);
Выборка = Запрос.Выполнить().Выбрать();
ОсталосьРаспределить = СтрокаТаблицы.СуммаОплатыБонусами;
Пока Выборка.Следующий() И ОсталосьРаспределить > 0 Цикл
СписываемыйОбъем = Мин(ОсталосьРаспределить, Выборка.ОжидаетСписания);
// Формируем сторнирующее движение по ресурсу "К списанию" на будущую дату
Движение = Движения.БонусныеБаллы.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Расход;
Движение.Период = Выборка.ПериодПланирования;
Движение.ДисконтнаяКарта = СтрокаТаблицы.ДисконтнаяКарта;
Движение.БонуснаяПрограммаЛояльности = СтрокаТаблицы.БонуснаяПрограмма;
Движение.КСписанию = СписываемыйОбъем;
ОсталосьРаспределить = ОсталосьРаспределить - СписываемыйОбъем;
КонецЦикла;
КонецЦикла;
КонецПроцедуры
При таком подходе при оплате бонусами в регистре будут рождаться корректирующие движения «Расход» по ресурсу КСписанию на будущие даты. Когда наступит дата регламентного сгорания, система увидит уже уменьшенный (актуальный) объем баллов к списанию и спишет ровно ту сумму, которая действительно сгорела, не затрагивая новые начисления.
Многие компании, использующие 1С:Розница в высоконагруженном ритейле, полностью отключают типовые регламентные задания автоматического списания по правилам бонусных программ. Вместо этого они разрабатывают собственную внешнюю обработку.
Преимущества кастомного списания:
Выбор конкретного метода зависит от масштаба вашей торговой сети и требований бизнеса:
Если у вас небольшая база и изменения нужно внести оперативно без привлечения программиста — используйте Решение 1 с доработкой запроса в пользовательской схеме компоновки данных (СКД).
Если вам важен абсолютно точный учет сгорания партий бонусов по классическому принципу FIFO, внедряйте Решение 2 через механизм расширений конфигурации.
Для крупных розничных сетей с активным маркетингом и программами лояльности наиболее предпочтительным является Решение 3 с переходом на внешние полуавтоматические обработки списания.